feat: portfolio context injection + AI call log viewer
Portfolio context (portfolio_context.py): - get_open_trades_with_moves(): fetches open trades + 1d/5d yfinance price moves - get_portfolio_concentration(): counts by asset_class - build_portfolio_context_block(): formatted prompt block with strict AI instructions (no double positions, flag contradictions, avoid overweight classes) AI call logging: - ai_call_logs table in DB (run_id, call_type, system/user prompt, response, tokens, ms) - _chat() now accepts log_meta dict → saves call to DB non-blocking after each call - suggest and score_batch calls pass run_id + call_type for full traceability auto_cycle.py: - Builds portfolio context before snapshot and both AI calls - Context snapshot now includes portfolio_open_positions key SystemLogs.tsx: - "Contexte IA" tab gains sub-tabs: Contexte / Appels IA - AiCallRow: expandable with 3 panes (user prompt / system prompt / response) shows model, tokens breakdown, duration, call type badge Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -183,6 +183,9 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
suggest_patterns_from_market_context, score_patterns_with_context,
|
||||
ai_score_news_batch, _chat, DEFAULT_ANALYSIS_TEMPLATE,
|
||||
)
|
||||
from services.portfolio_context import (
|
||||
get_open_trades_with_moves, get_portfolio_concentration, build_portfolio_context_block,
|
||||
)
|
||||
|
||||
# KB confidence decay (non-blocking)
|
||||
try:
|
||||
@@ -447,6 +450,18 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
except Exception as _te:
|
||||
logger.warning(f"[Cycle] Tech indicators failed (non-blocking): {_te}")
|
||||
|
||||
# ── Portfolio context (open positions + recent moves) ──────────
|
||||
_portfolio_block = ""
|
||||
_open_trades_snap = []
|
||||
_portfolio_conc = {}
|
||||
try:
|
||||
_open_trades_snap = get_open_trades_with_moves()
|
||||
_portfolio_conc = get_portfolio_concentration(_open_trades_snap)
|
||||
_portfolio_block = build_portfolio_context_block(_open_trades_snap, _portfolio_conc)
|
||||
logger.info(f"[Cycle {run_id[:16]}] Portfolio context: {len(_open_trades_snap)} open trades")
|
||||
except Exception as _pe:
|
||||
logger.warning(f"[Cycle] Portfolio context failed (non-blocking): {_pe}")
|
||||
|
||||
# ── Save full context snapshot ─────────────────────────────────
|
||||
try:
|
||||
from services.ai_analyzer import apply_news_decay as _apply_decay2, partition_news_by_age as _part
|
||||
@@ -473,6 +488,14 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
"iv_context_preview": iv_context[:500] if iv_context else "",
|
||||
"calendar": calendar[:8] if calendar else [],
|
||||
"quotes_summary": {cls: [{"symbol": q.get("symbol"), "price": q.get("price"), "change_pct": q.get("change_pct")} for q in qs[:3]] for cls, qs in quotes.items()},
|
||||
"portfolio_open_positions": {
|
||||
"count": len(_open_trades_snap),
|
||||
"concentration": _portfolio_conc,
|
||||
"trades": [
|
||||
{k: v for k, v in t.items() if k != "id"}
|
||||
for t in _open_trades_snap
|
||||
],
|
||||
},
|
||||
}
|
||||
save_cycle_context_snapshot(run_id, _context_snapshot)
|
||||
logger.info(f"[Cycle {run_id[:16]}] Context snapshot saved")
|
||||
@@ -488,6 +511,8 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
tech_indicators_block=_tech_block,
|
||||
fred_block=_fred_block,
|
||||
price_discovery_block=_price_discovery_block,
|
||||
portfolio_context_block=_portfolio_block,
|
||||
run_id=run_id,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"[Cycle] Suggestion step failed: {e}")
|
||||
@@ -614,6 +639,8 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
tech_indicators_block=_tech_block,
|
||||
fred_block=_fred_block,
|
||||
price_discovery_block=_price_discovery_block,
|
||||
portfolio_context_block=_portfolio_block,
|
||||
run_id=run_id,
|
||||
)
|
||||
scored_with_id = [s for s in scored if s.get("pattern_id")]
|
||||
scored_without_id = [s for s in scored if not s.get("pattern_id")]
|
||||
|
||||
Reference in New Issue
Block a user