feat: options technical agent — IV/skew/term structure validation per trade

- New options_technical_agent.py: rule engine (IVR, skew, term structure, flow)
  + GPT-4o narrative per trade; verdict OK/WARN/ALERT + fit_score
- options_trade_assessments table in DB for Journal badge persistence
- auto_cycle.py step 5.2: assess newly logged trades after log_trade_entries;
  results embedded in cycle report
- suggest_patterns_from_market_context: +iv_context param + explicit IV→strategy
  rules in prompt (IVR<30%→Long, 30-60%→Spread, >60%→no naked long, >80%→short)
- Pre-fetch iv_context at step 1.9 so suggestion step gets strategy rules
- reports.py: /api/reports/assessments/latest + /assessments/{run_id} endpoints
- RapportIA.tsx: "Validation Technique Options" section with per-trade IVBar,
  VerdictBadge, issues list, GPT-4o analysis, optimal strategy suggestion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-20 09:36:35 +02:00
parent 1aadf98fe4
commit 3ee39d5f08
6 changed files with 783 additions and 8 deletions

View File

@@ -461,6 +461,30 @@ def init_db():
except Exception:
pass
c.execute("""CREATE TABLE IF NOT EXISTS options_trade_assessments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
run_id TEXT NOT NULL,
trade_id INTEGER,
ticker TEXT,
strategy TEXT,
assessed_at TEXT,
iv_rank REAL,
iv_current_pct REAL,
skew_pct REAL,
term_structure TEXT,
fit_score INTEGER,
verdict TEXT,
issues_json TEXT,
optimal_strategy TEXT,
analysis TEXT,
when_to_enter TEXT
)""")
try:
c.execute("CREATE INDEX IF NOT EXISTS idx_ota_run ON options_trade_assessments(run_id)")
c.execute("CREATE INDEX IF NOT EXISTS idx_ota_trade ON options_trade_assessments(trade_id)")
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)")
@@ -3255,3 +3279,46 @@ def get_latest_cycle_report() -> Optional[Dict[str, Any]]:
return report
except Exception:
return None
def get_trade_assessments(run_id: str) -> List[Dict[str, Any]]:
import json as _json
conn = get_conn()
rows = conn.execute(
"SELECT * FROM options_trade_assessments WHERE run_id=? ORDER BY id",
(run_id,),
).fetchall()
conn.close()
result = []
for r in rows:
d = dict(r)
try:
d["issues"] = _json.loads(d.get("issues_json") or "[]")
except Exception:
d["issues"] = []
result.append(d)
return result
def get_latest_trade_assessments(limit: int = 20) -> List[Dict[str, Any]]:
"""Return most recent assessments (one per trade_id) — for Journal badges."""
import json as _json
conn = get_conn()
rows = conn.execute(
"""SELECT a.* FROM options_trade_assessments a
INNER JOIN (SELECT trade_id, MAX(id) as max_id FROM options_trade_assessments
WHERE trade_id IS NOT NULL GROUP BY trade_id) m
ON a.id = m.max_id
ORDER BY a.id DESC LIMIT ?""",
(limit,),
).fetchall()
conn.close()
result = []
for r in rows:
d = dict(r)
try:
d["issues"] = _json.loads(d.get("issues_json") or "[]")
except Exception:
d["issues"] = []
result.append(d)
return result