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:
OpenSquared
2026-06-20 07:57:41 +02:00
parent 85975f6248
commit e2d5bebef4
6 changed files with 760 additions and 473 deletions

View File

@@ -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