signal_log: persist agent panel verdict + per-persona takes to SQLite
Add panel_verdict, panel_confidence, panel_rationale, panel_takes_json columns to the signals table (additive, idempotent ALTER TABLE migration for existing DBs). _report_row now reads the panel fields attach_panel writes onto the SignalReport, so LLM panel verdicts stop evaporating on process exit. Also documents the Oanda re-pin (round_trip_cost stays $0.30 for cross-book comparability and the live TP gate; $0.65 is the measured headline) and adds the feed-divergence / parity / shadow-scan study tooling referenced by CLAUDE.md.
This commit is contained in:
+33
-3
@@ -60,20 +60,34 @@ CREATE TABLE IF NOT EXISTS signals (
|
||||
news_description TEXT,
|
||||
news_summary TEXT,
|
||||
events_json TEXT,
|
||||
headlines_json TEXT
|
||||
headlines_json TEXT,
|
||||
panel_verdict TEXT,
|
||||
panel_confidence REAL,
|
||||
panel_rationale TEXT,
|
||||
panel_takes_json TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_signals_sym_tf_ts
|
||||
ON signals (symbol, timeframe, logged_at);
|
||||
"""
|
||||
|
||||
# Additive migration: older DBs created before the panel columns existed
|
||||
# get them added here. Idempotent — skips columns already present.
|
||||
_PANEL_COLS = (
|
||||
("panel_verdict", "TEXT"),
|
||||
("panel_confidence", "REAL"),
|
||||
("panel_rationale", "TEXT"),
|
||||
("panel_takes_json", "TEXT"),
|
||||
)
|
||||
|
||||
_INSERT = """
|
||||
INSERT INTO signals (
|
||||
logged_at, source, symbol, timeframe, signal, score, confidence,
|
||||
quality, action, price, spread, market_regime, volatility_regime,
|
||||
mtf_json, setup_json, reasons_json, warnings_json,
|
||||
event_risk, event_description, news_sentiment, news_description,
|
||||
news_summary, events_json, headlines_json
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
news_summary, events_json, headlines_json,
|
||||
panel_verdict, panel_confidence, panel_rationale, panel_takes_json
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
|
||||
|
||||
@@ -207,6 +221,8 @@ def _report_row(
|
||||
events_json, headlines_json = _context_news_json(context)
|
||||
|
||||
signal = getattr(report, "final_signal", None)
|
||||
panel_takes = getattr(report, "panel_takes", None) or []
|
||||
panel_takes_json = _dumps(panel_takes) if panel_takes else None
|
||||
return (
|
||||
datetime.now(timezone.utc).isoformat(),
|
||||
source,
|
||||
@@ -232,6 +248,10 @@ def _report_row(
|
||||
getattr(report, "news_summary", "") or "",
|
||||
events_json,
|
||||
headlines_json,
|
||||
getattr(report, "panel_verdict", None),
|
||||
_finite(getattr(report, "panel_confidence", None)),
|
||||
getattr(report, "panel_rationale", None),
|
||||
panel_takes_json,
|
||||
)
|
||||
|
||||
|
||||
@@ -251,11 +271,21 @@ class SignalLogger:
|
||||
self._lock = threading.Lock()
|
||||
with self._lock:
|
||||
self._conn.executescript(_SCHEMA)
|
||||
self._migrate_panel_columns()
|
||||
|
||||
@property
|
||||
def path(self) -> str:
|
||||
return self._path
|
||||
|
||||
def _migrate_panel_columns(self) -> None:
|
||||
"""Add panel_* columns to pre-existing tables (idempotent)."""
|
||||
existing = {r[1] for r in self._conn.execute("PRAGMA table_info(signals)")}
|
||||
for name, col_type in _PANEL_COLS:
|
||||
if name not in existing:
|
||||
self._conn.execute(
|
||||
f"ALTER TABLE signals ADD COLUMN {name} {col_type}")
|
||||
self._conn.commit()
|
||||
|
||||
def log(
|
||||
self,
|
||||
report: Any,
|
||||
|
||||
Reference in New Issue
Block a user