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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user