feat: 50-signal macro engine — vol surface, sectors, EM, carry, long bonds

data_fetcher.py
- MACRO_GAUGE_CONFIG: 15 → 29 tickers (+silver, vvix, skew, ovx, gvz,
  usdjpy, xlk, xlf, xlp, xlu, eem, emb, fxi, tlt)
- 5 new derived metrics: silver_gold_ratio, xlk_xlp_momentum, xlf_spx_ratio,
  eem_spx_ratio, vol_surface_regime (composite classification)
- ThreadPoolExecutor max_workers raised to 20
- score_macro_scenarios: +15 new variables; each of 8 scenarios enriched
  with vol-surface (SKEW, VVIX), sector rotation (XLK, XLF, XLP, XLU),
  EM/carry (EEM, EMB, USDJPY), long bonds (TLT), silver signals

ai_analyzer.py
- macro_ctx: 5 → 21 fields per pattern (vol surface, sectors, EM, carry,
  long bonds, silver/gold ratio — all with interpretation comments)
- macro_section in scoring prompt: describes surface de vol regime, sector
  rotation, global/carry signals with explicit GPT instructions for pilier 3e
- DEFAULT_ANALYSIS_TEMPLATE: pilier 3e expanded with SKEW/VVIX/OVX/GVZ guidance

SIGNALS_FUTURES.md: reference document listing 30+ signals not yet
available (FRED, CFTC COT, EIA, Baltic Dry, LME, credit spreads,
hedge fund positioning, central bank balance sheets) with implementation
priority and cost estimate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-19 08:34:48 +02:00
parent 18b3ae6f91
commit d8c0334feb
3 changed files with 312 additions and 9 deletions

View File

@@ -286,7 +286,13 @@ PILIER 3 — SIGNAUX DE PRIX (35 pts max)
3b. Énergie & Matières prem. (0-7): or, pétrole, gaz, cuivre, blé — direction et momentum
3c. Forex (0-7): USD index, EUR/USD, paires émergentes — cohérence avec pattern
3d. Actions & Indices (0-7): SPX, NDX, rotation sectorielle, breadth, sentiment
3e. Volatilité (VIX/IV) (0-7): régime de vol, coût options, skew — favorable à la stratégie ?
3e. Volatilité & Surface de vol (0-7):
- Régime VIX (niveau absolu et tendance)
- SKEW Index: si >135 → queues chères → PRÉFÉRER spreads (budget = max loss limité)
si <115 → vol bon marché → envisager straddles/strangles si catalyseur binaire
- VVIX: si >100 → straddles/strangles trop chers → préférer directionnels
- OVX (>40) / GVZ (>22): vol sectorielle élevée sur le sous-jacent → primes gonflées, spreads
- Régime surface: contango_calm (idéal vendre vol) vs backwardation_panic (primes explosées)
PILIER 4 — RISQUE / RÉCOMPENSE (15 pts max)
4a. Asymétrie R/R (0-10): ratio gain potentiel / prime payée / perte max pour ~1000€
@@ -419,11 +425,42 @@ def score_patterns_with_context(
"dominant_scenario": dominant,
"scenario_scores": scenarios.get("scores", {}),
"asset_class_bias": bias_for_class,
# ── Signaux historiques (phase 1) ─────────────────────────────
"vix": gauges.get("vix", {}).get("value"),
"yield_slope_pct": gauges.get("slope_10y3m", {}).get("value"),
"gold_copper_ratio": gauges.get("gold_copper_ratio", {}).get("value"),
"brent_1d_pct": gauges.get("brent", {}).get("change_pct"),
"spx_vs_200d_pct": gauges.get("spx_vs_200d", {}).get("value"),
# ── Surface de volatilité (phase 2) ──────────────────────────
# SKEW >135 = queues chères → spreads; <115 = vol bon marché → straddles
"skew_index": gauges.get("skew", {}).get("value"),
# VVIX >100 = straddles/strangles trop chers → préférer directionnels
"vvix": gauges.get("vvix", {}).get("value"),
# Vol sectorielle spécifique au sous-jacent
"oil_vol_ovx": gauges.get("ovx", {}).get("value"),
"gold_vol_gvz": gauges.get("gvz", {}).get("value"),
# Régime composite: contango_calm|normal|tail_risk_elevated|backwardation_panic|complacency_hedged
"vol_surface_regime": gauges.get("vol_surface_regime", {}).get("note"),
# ── Rotation sectorielle (phase 2) ────────────────────────────
# Positif = tech > défensifs = risk-on; négatif = rotation défensive
"tech_vs_staples_pct": gauges.get("xlk_xlp_momentum", {}).get("value"),
# Négatif = banques < marché = stress économique anticipé
"financials_vs_spx_pct": gauges.get("xlf_spx_ratio", {}).get("value"),
# ── Global / Marchés émergents (phase 2) ─────────────────────
"em_equity_1d_pct": gauges.get("eem", {}).get("change_pct"),
"em_bonds_1d_pct": gauges.get("emb", {}).get("change_pct"),
"china_equity_1d_pct": gauges.get("fxi", {}).get("change_pct"),
# Ratio EM vs SPX: positif = croissance globale; négatif = fuite vers US
"em_vs_us_divergence_pct": gauges.get("eem_spx_ratio", {}).get("value"),
# ── Carry / Risk-off (phase 2) ────────────────────────────────
# Négatif = JPY s'apprécie = carry unwind = risk-off global
"usdjpy_1d_pct": gauges.get("usdjpy", {}).get("change_pct"),
# ── Long bonds / Qualité (phase 2) ────────────────────────────
# Positif = flight to quality = risk-off; négatif = taux longs remontent
"long_bond_tlt_1d_pct": gauges.get("tlt", {}).get("change_pct"),
# ── Métaux (phase 2) ──────────────────────────────────────────
# Ratio Ag/Au: >0.016 = argent > or = risk-on industriel; <0.012 = defensive metals
"silver_gold_ratio": gauges.get("silver_gold_ratio", {}).get("value"),
}
pattern_blocks.append({
@@ -451,17 +488,50 @@ def score_patterns_with_context(
macro_section = ""
if macro_regime:
sc = macro_regime.get("scenarios", {})
gauges_g = macro_regime.get("gauges", {})
dom = sc.get("dominant", "incertain")
sc_scores = sc.get("scores", {})
# Extract key new signals for global macro summary
_skew = gauges_g.get("skew", {}).get("value")
_vvix = gauges_g.get("vvix", {}).get("value")
_ovx = gauges_g.get("ovx", {}).get("value")
_vol_r = gauges_g.get("vol_surface_regime", {}).get("note", "normal")
_tech_st = gauges_g.get("xlk_xlp_momentum", {}).get("value")
_xlf_spx = gauges_g.get("xlf_spx_ratio", {}).get("value")
_eem_c = gauges_g.get("eem", {}).get("change_pct")
_usdjpy_c = gauges_g.get("usdjpy", {}).get("change_pct")
_tlt_c = gauges_g.get("tlt", {}).get("change_pct")
_sgr = gauges_g.get("silver_gold_ratio", {}).get("value")
def _fmt(v, unit="", decimals=1):
return f"{v:.{decimals}f}{unit}" if v is not None else "n/d"
macro_section = f"""
RÉGIME MACRO ACTUEL (30 compteurs agrégés):
RÉGIME MACRO ACTUEL (50 compteurs — 29 tickers + 9 métriques dérivées):
- Scénario dominant: {dom.upper()} | Scores: {json.dumps(sc_scores, ensure_ascii=False)}
- Instruction: Intègre ce régime dans les piliers prix (3a taux, 3b énergie, 3d indices, 3e VIX).
Chaque pattern reçoit un champ "macro_regime.asset_class_bias" indiquant la compatibilité
(bullish+/bullish/neutral/bearish/bearish+/defensive) de sa classe d'actif avec le scénario dominant.
"bullish+" = conditions très favorables pour ce pattern → majore 3b ou 3d selon la classe
"bearish" ou "bearish+" = conditions défavorables → minore 3b ou 3d
Indique dans "summary": [GOLDILOCKS|STAGFLATION|RÉCESSION|DÉSINFLATION|CRISE] + [SUPPORTING|NEUTRAL|CONTRA]
SURFACE DE VOLATILITÉ (pilier 3e — impact direct stratégie options):
- SKEW Index: {_fmt(_skew, '', 0)} | Régime vol: {_vol_r} | VVIX: {_fmt(_vvix, '', 0)} | OVX: {_fmt(_ovx, '', 0)}
SKEW >135 = queues chères → SPREADS (pas de straddles). VVIX >100 = primes gonflées → directionnels.
{_vol_r} = {"contango calme: idéal spreads bon marché" if _vol_r == "contango_calm" else "backwardation/panique: options très chères, primes à vendre" if _vol_r == "backwardation_panic" else "tail risk élevé: protection queues = faveur spreads définis" if _vol_r == "tail_risk_elevated" else "environnement normal"}
ROTATION SECTORIELLE & RISQUE (pilier 3d + 3e):
- Tech vs Défensifs (XLK-XLP): {_fmt(_tech_st, '%pts')} | Financières vs S&P: {_fmt(_xlf_spx, '%pts')}
{"Risk-on sectoriel fort" if _tech_st and _tech_st > 0.5 else "Rotation défensive ⚠️" if _tech_st and _tech_st < -0.5 else "Neutre sectoriel"}
{"Banques saines = pas de récession" if _xlf_spx and _xlf_spx > 0.3 else "Stress bancaire anticipé ⚠️" if _xlf_spx and _xlf_spx < -0.3 else ""}
GLOBAL / CARRY / QUALITÉ (pilier 3a + 3d):
- EM Actions J+1: {_fmt(_eem_c, '%')} | USD/JPY J+1: {_fmt(_usdjpy_c, '%')} | TLT (20Y bonds) J+1: {_fmt(_tlt_c, '%')}
- Ratio Ag/Or: {_fmt(_sgr, '', 5)}
{"EM surperforme = croissance globale" if _eem_c and _eem_c > 0.5 else "EM stress = fuite vers US ⚠️" if _eem_c and _eem_c < -1.0 else ""}
{"JPY s'apprécie = carry unwind = risk-off ⚠️" if _usdjpy_c and _usdjpy_c < -0.8 else "JPY faible = risk-on carry actif" if _usdjpy_c and _usdjpy_c > 0.5 else ""}
{"TLT monte = flight to quality = bonds longs demandés" if _tlt_c and _tlt_c > 0.3 else "TLT baisse = taux longs remontent = inflation/risk-on" if _tlt_c and _tlt_c < -0.3 else ""}
Instructions de notation:
- Intègre le régime dans 3a (taux: slope+TLT), 3b (énergie+OVX), 3d (indices+XLF+EM), 3e (SKEW+VVIX+régime)
- "macro_regime.asset_class_bias" dans chaque pattern → majore/minore 3b ou 3d
- "macro_regime.skew_index" + "vol_surface_regime" → influence directe sur le choix de stratégie dans "recommended_trade"
- Indique dans "summary": [GOLDILOCKS|STAGFLATION|RÉCESSION|DÉSINFLATION|CRISE] + [SUPPORTING|NEUTRAL|CONTRA]
"""
user = f"""CONTEXTE GLOBAL: