feat: Rapport de Cycle — auto-généré à chaque run avec contexte IA, delta, PnL/VaR snapshot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -443,6 +443,24 @@ def init_db():
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
c.execute("""CREATE TABLE IF NOT EXISTS cycle_reports (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
run_id TEXT NOT NULL UNIQUE,
|
||||
generated_at TEXT NOT NULL,
|
||||
macro_dominant TEXT,
|
||||
geo_score INTEGER,
|
||||
patterns_added INTEGER DEFAULT 0,
|
||||
trades_logged INTEGER DEFAULT 0,
|
||||
trades_closed INTEGER DEFAULT 0,
|
||||
pnl_snapshot_id INTEGER,
|
||||
var_snapshot_id INTEGER,
|
||||
full_report_json TEXT
|
||||
)""")
|
||||
try:
|
||||
c.execute("CREATE INDEX IF NOT EXISTS idx_cycle_reports_ts ON cycle_reports(generated_at DESC)")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
c.execute("CREATE INDEX IF NOT EXISTS idx_kb_category ON knowledge_base(category, status)")
|
||||
c.execute("CREATE INDEX IF NOT EXISTS idx_rs_version ON reasoning_state(version DESC)")
|
||||
@@ -3163,3 +3181,77 @@ def _build_risk_recommendation(
|
||||
"messages": messages,
|
||||
"summary": messages[0] if messages else "",
|
||||
}
|
||||
|
||||
|
||||
# ── Cycle reports ─────────────────────────────────────────────────────────────
|
||||
|
||||
def save_cycle_report(run_id: str, report: Dict[str, Any]) -> None:
|
||||
import json as _json
|
||||
conn = get_conn()
|
||||
conn.execute(
|
||||
"""INSERT OR REPLACE INTO cycle_reports
|
||||
(run_id, generated_at, macro_dominant, geo_score,
|
||||
patterns_added, trades_logged, trades_closed,
|
||||
pnl_snapshot_id, var_snapshot_id, full_report_json)
|
||||
VALUES (?, datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||
(
|
||||
run_id,
|
||||
report.get("macro_dominant"),
|
||||
report.get("geo_score"),
|
||||
report.get("patterns_added", 0),
|
||||
report.get("trades_logged", 0),
|
||||
report.get("trades_closed", 0),
|
||||
report.get("pnl_snapshot_id"),
|
||||
report.get("var_snapshot_id"),
|
||||
_json.dumps(report, ensure_ascii=False),
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def get_cycle_reports(limit: int = 20) -> List[Dict[str, Any]]:
|
||||
conn = get_conn()
|
||||
rows = conn.execute(
|
||||
"""SELECT run_id, generated_at, macro_dominant, geo_score,
|
||||
patterns_added, trades_logged, trades_closed
|
||||
FROM cycle_reports ORDER BY generated_at DESC LIMIT ?""",
|
||||
(limit,),
|
||||
).fetchall()
|
||||
conn.close()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
def get_cycle_report(run_id: str) -> Optional[Dict[str, Any]]:
|
||||
import json as _json
|
||||
conn = get_conn()
|
||||
row = conn.execute(
|
||||
"SELECT full_report_json, generated_at FROM cycle_reports WHERE run_id=?",
|
||||
(run_id,),
|
||||
).fetchone()
|
||||
conn.close()
|
||||
if not row or not row["full_report_json"]:
|
||||
return None
|
||||
try:
|
||||
report = _json.loads(row["full_report_json"])
|
||||
report["generated_at"] = row["generated_at"]
|
||||
return report
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def get_latest_cycle_report() -> Optional[Dict[str, Any]]:
|
||||
import json as _json
|
||||
conn = get_conn()
|
||||
row = conn.execute(
|
||||
"SELECT full_report_json, generated_at FROM cycle_reports ORDER BY generated_at DESC LIMIT 1"
|
||||
).fetchone()
|
||||
conn.close()
|
||||
if not row or not row["full_report_json"]:
|
||||
return None
|
||||
try:
|
||||
report = _json.loads(row["full_report_json"])
|
||||
report["generated_at"] = row["generated_at"]
|
||||
return report
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user