feat: cycle

This commit is contained in:
OpenSquared
2026-07-15 14:59:11 +02:00
parent 2d474c9194
commit 16ccc7c2c7
6 changed files with 466 additions and 45 deletions

View File

@@ -1754,6 +1754,79 @@ Réponds en JSON avec ce schéma EXACT:
return report
def generate_standalone_report() -> Dict[str, Any]:
"""Cycle Actions — standalone "generate-report" action. _generate_cycle_report()
itself is NOT modified (too tightly coupled to run_cycle_once()'s in-memory
state to safely change) — instead every one of its parameters is
reconstructed from an independent, DB-or-live source:
- scored <- get_last_scores() (config key "last_pattern_scores",
filled by save_pattern_scores() every real cycle)
- news/geo_score <- same live sequence as cycle Step 1
- dominant/scenarios/gauges <- same sequence as the update-regime action
- commentary <- _generate_cycle_commentary(), independently callable
- wavelet_signals <- compute_and_save_wavelet_signals(), independently callable
- portfolio_monitor <- analyze_simulation_portfolio() (+ AI only if alerts)
- added_patterns/options_assessment <- cycle-only artifacts, not
reconstructible after the fact -> empty/None (report is honest, just
thinner on these two fields than a live cycle's report)."""
from services.database import get_config, get_pattern_scores, save_cycle_report
from services.data_fetcher import fetch_geo_news, get_macro_gauges, score_macro_scenarios
from services.geo_analyzer import compute_geo_risk_score
from services.ai_analyzer import ai_score_news_batch, ai_score_geo_risk
run_id = datetime.utcnow().isoformat()
ai_key = get_config("openai_api_key") or ""
last_scores = get_pattern_scores()
scored = last_scores.get("scores") or []
scoring_run_id = last_scores.get("run_id") or run_id
news = ai_score_news_batch(fetch_geo_news())
algo_geo = compute_geo_risk_score(news)
try:
geo_obj = ai_score_geo_risk(news, algo_geo, log_meta={"run_id": run_id, "call_type": "geo_risk_score"})
except Exception:
geo_obj = algo_geo
geo_score_val = int(round(geo_obj.get("score") or 0))
gauges = get_macro_gauges()
scenarios = score_macro_scenarios(gauges)
dominant = scenarios.get("dominant", "incertain")
commentary = None
try:
commentary = _generate_cycle_commentary(scored, dominant, scenarios, geo_score_val, news, gauges)
except Exception as e:
logger.warning(f"[StandaloneReport] Commentary generation failed: {e}")
wavelet_signals: List[Dict] = []
try:
from services.wavelet_signals import compute_and_save_wavelet_signals
wavelet_signals = compute_and_save_wavelet_signals(run_id)
except Exception as e:
logger.warning(f"[StandaloneReport] Wavelet scan failed (non-blocking): {e}")
portfolio_monitor = None
try:
from services.portfolio_risk import analyze_simulation_portfolio
risk = analyze_simulation_portfolio()
if risk.get("alerts"):
portfolio_monitor = _run_portfolio_monitor(risk, run_id)
except Exception as e:
logger.warning(f"[StandaloneReport] Portfolio monitor failed (non-blocking): {e}")
report = _generate_cycle_report(
run_id=run_id, scored=scored, dominant=dominant, scenarios=scenarios,
geo_score_val=geo_score_val, news=news, gauges=gauges, ai_key=ai_key,
added_patterns=[], scoring_run_id=scoring_run_id,
portfolio_monitor=portfolio_monitor, commentary=commentary,
options_assessment=None, wavelet_signals=wavelet_signals,
)
if report:
save_cycle_report(run_id, report)
return {"run_id": run_id, "report": report}
# ── Auto portfolio snapshot ───────────────────────────────────────────────────
def _auto_portfolio_snapshot(ai_key: str) -> None: