feat: cycle
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user