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:
78
SIGNALS_FUTURES.md
Normal file
78
SIGNALS_FUTURES.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# Signaux non disponibles actuellement — Feuille de route
|
||||||
|
|
||||||
|
Ce document liste les signaux macro et de marché que nous ne pouvons pas encore
|
||||||
|
récupérer gratuitement via yfinance, et les sources qui les rendraient accessibles.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## A. Disponibles gratuitement mais non encore intégrés
|
||||||
|
|
||||||
|
Ces sources sont accessibles sans abonnement — implémentation possible.
|
||||||
|
|
||||||
|
| Signal | Source | Délai | Intérêt pour le trade |
|
||||||
|
|--------|---------|-------|----------------------|
|
||||||
|
| **CFTC Commitments of Traders (COT)** | cftc.gov (CSV hebdomadaire) | 3 jours ouvrés | Positionnement net des spéculateurs sur or, pétrole, blé, forex — signal de retournement contrarian fort |
|
||||||
|
| **Baltic Dry Index (BDI)** | Scrapage web (Investing.com) | J+1 | Leading indicator du commerce mondial — baisse = ralentissement industriel global |
|
||||||
|
| **Fed H.4.1 — Bilan de la Fed (weekly)** | federalreserve.gov (CSV) | 1 semaine | Expansion/contraction du bilan Fed = signal quantitative easing/tightening |
|
||||||
|
| **US Money Supply M2** | FRED API (gratuit) | ~6 semaines | Croissance M2 → inflation future ; contraction M2 → récession (2022-2023 signal) |
|
||||||
|
| **CNN Fear & Greed Index** | Scrapage CNN | J | Sentiment extrême (0 = peur max, 100 = avidité max) — contrarian signal timing |
|
||||||
|
| **AAII Investor Sentiment Survey** | aaii.com (hebdo) | 1 semaine | % bulls/bears retail — extrêmes signalent retournement |
|
||||||
|
| **FRED Economic Data (40+ séries)** | api.stlouisfed.org (gratuit) | Variable | CPI, PCE, taux chômage U6, leading indicators Conference Board, etc. |
|
||||||
|
| **US 2Y Treasury yield** | yfinance `^IRX` proxy ou FRED | J | Segment le plus Fed-sensitive — différentiel 2Y/10Y = meilleur signal récession |
|
||||||
|
| **Breakeven inflation 5Y (TIPS)** | FRED: T5YIE | J+1 | Taux réels implicites = ce que le marché anticipe pour l'inflation |
|
||||||
|
| **Indice PMI manufacturier (ISM)** | Scrapage ou FRED | Mensuel | PMI < 50 = contraction manufacturière ; PMI > 55 = expansion forte |
|
||||||
|
| **Google Trends (mots-clés macro)** | pytrends (gratuit) | J | "Recession", "unemployment", "inflation" — sentiment populaire précède souvent les données |
|
||||||
|
| **Données OPEC production** | opec.org (PDF/RSS) | Mensuel | Production réelle vs quota = signal énergie avant les chiffres EIA |
|
||||||
|
| **EIA Crude Oil Inventories** | eia.gov (CSV) | Hebdo (mercredi) | Stocks pétrole US = driver court terme du WTI |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## B. Nécessitent un abonnement payant (prix modéré)
|
||||||
|
|
||||||
|
| Signal | Source | Coût estimé | Intérêt |
|
||||||
|
|--------|---------|-------------|---------|
|
||||||
|
| **CBOE Put/Call Ratio (temps réel)** | CBOE DataShop | ~$50/mois | Sentiment options en temps réel — P/C > 1.2 = panique = timing long |
|
||||||
|
| **Gamma Exposure (GEX) SPX** | SpotGamma / SqueezeMetrics | ~$50-150/mois | Exposition gamma des market makers = niveaux magnétiques prix, vol des options |
|
||||||
|
| **Options Flow (dark pools)** | Unusual Whales / FlowAlgo | ~$50-200/mois | Gros blocs institutionnels sur options = signal directionnel fort |
|
||||||
|
| **Short Interest Ratio** | Ortex / S3 Partners | ~$100/mois | % float vendu à découvert = squeeze potential ou bearish conviction institutionnel |
|
||||||
|
| **Credit Default Swaps (CDS IG/HY)** | Bloomberg / Markit | $500+/mois | Spread CDS direct = meilleur indicateur crédit (vs HYG qui est proxy) |
|
||||||
|
| **VIX Term Structure (VX Futures)** | CBOE / IVolatility | ~$30/mois | Contango/backwardation exact de la courbe VIX futures — clé pour stratégies vol |
|
||||||
|
| **Skew par sous-jacent (GLD, USO, etc.)** | IVolatility / OptionMetrics | ~$50/mois | Skew spécifique par ETF = évalue si les puts/calls sont chers sur TON sous-jacent |
|
||||||
|
| **Insider Trading en temps réel** | OpenInsider (partiellement gratuit) | ~$50/mois pour alertes | Achats/ventes dirigeants = signal fondamental avant résultats |
|
||||||
|
| **Earnings Revision Score** | Factset / Bloomberg | $500+/mois | Révisions de bénéfices = meilleur driver des actions sur 3-6 mois |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## C. Signaux structurellement difficiles à obtenir
|
||||||
|
|
||||||
|
Ces données sont soit OTC, soit publiées avec un délai important, soit agrégées uniquement
|
||||||
|
par des institutions qui ne les partagent pas publiquement.
|
||||||
|
|
||||||
|
| Signal | Problème d'accès | Intérêt si disponible |
|
||||||
|
|--------|-----------------|----------------------|
|
||||||
|
| **Réserves d'or des banques centrales** | FMI publie avec 2-3 mois de délai ; achats réels souvent cachés | Demande physique institutionnelle = pression prix or moyen terme |
|
||||||
|
| **Bilans détaillés BCE/PBoC/BoJ** | Données disponibles mais complexes à parser (formats hétérogènes, langues) | Expansion monétaire mondiale = signal reflation/inflation globale |
|
||||||
|
| **Positionnement hedge funds (13F)** | SEC 13F = 45 jours de délai après fin du trimestre | Trop tardif pour le trading — anecdotique |
|
||||||
|
| **Dettes souveraines EM (libellées en USD)** | Banque Mondiale, FMI — trimestriels/annuels | Signal de crise de dette EM avant que ça se voit sur EEM/EMB |
|
||||||
|
| **Prix physiques vs prix futures (basis)** | OTC / brokers spécialisés matières premières | Basis pétrole = signal supply/demand physique réel vs spéculatif |
|
||||||
|
| **Dark pool prints (actions)** | FINRA publie avec délai ; temps réel = ~$500+/mois | Flow institutionnel avant les mouvements visibles |
|
||||||
|
| **Inventaires métaux LME (Cu, Al, Zn, Ni)** | LME.com — partiellement gratuit | Stocks physiques cuivre/aluminium = confirmation Dr Copper signal |
|
||||||
|
| **Taux SOFR / repo overnight** | NY Fed publie J+1 — utilisable | Stress du marché repo = signal crise liquidité avant VIX |
|
||||||
|
| **Global PMI composite (JP Morgan)** | IHS Markit / S&P — abonnement | PMI monde = leading indicator croissance globale avant EEM |
|
||||||
|
| **Inflation swap rates (5Y5Y forward)** | Bloomberg uniquement | Anticipations inflation long terme des pros — différent du breakeven TIPS |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## D. Priorités d'implémentation suggérées
|
||||||
|
|
||||||
|
Par ordre de valeur/effort :
|
||||||
|
|
||||||
|
1. **FRED API (gratuit)** — M2, breakeven 5Y, US 2Y yield, leading indicators → ±2h de dev
|
||||||
|
2. **CFTC COT (gratuit, hebdo)** — COT or + pétrole + blé → signal contrarian puissant → ±4h de dev
|
||||||
|
3. **EIA inventories pétrole (gratuit)** — driver court terme WTI → ±2h de dev
|
||||||
|
4. **CNN Fear & Greed (scrapage)** — sentiment composite → ±1h de dev
|
||||||
|
5. **Baltic Dry Index (scrapage)** — commerce mondial → ±1h de dev
|
||||||
|
6. **LME stocks cuivre** — confirmation signal cuivre → ±2h de dev
|
||||||
|
7. **SpotGamma GEX** (~$50/mois) — niveaux gamma SPX → impact direct stratégie
|
||||||
|
8. **CBOE P/C ratio** (~$50/mois) — timing options → impact direct
|
||||||
|
9. **VIX term structure** (~$30/mois) → améliore pilier 3e
|
||||||
@@ -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
|
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
|
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
|
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)
|
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€
|
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,
|
"dominant_scenario": dominant,
|
||||||
"scenario_scores": scenarios.get("scores", {}),
|
"scenario_scores": scenarios.get("scores", {}),
|
||||||
"asset_class_bias": bias_for_class,
|
"asset_class_bias": bias_for_class,
|
||||||
|
# ── Signaux historiques (phase 1) ─────────────────────────────
|
||||||
"vix": gauges.get("vix", {}).get("value"),
|
"vix": gauges.get("vix", {}).get("value"),
|
||||||
"yield_slope_pct": gauges.get("slope_10y3m", {}).get("value"),
|
"yield_slope_pct": gauges.get("slope_10y3m", {}).get("value"),
|
||||||
"gold_copper_ratio": gauges.get("gold_copper_ratio", {}).get("value"),
|
"gold_copper_ratio": gauges.get("gold_copper_ratio", {}).get("value"),
|
||||||
"brent_1d_pct": gauges.get("brent", {}).get("change_pct"),
|
"brent_1d_pct": gauges.get("brent", {}).get("change_pct"),
|
||||||
"spx_vs_200d_pct": gauges.get("spx_vs_200d", {}).get("value"),
|
"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({
|
pattern_blocks.append({
|
||||||
@@ -451,17 +488,50 @@ def score_patterns_with_context(
|
|||||||
macro_section = ""
|
macro_section = ""
|
||||||
if macro_regime:
|
if macro_regime:
|
||||||
sc = macro_regime.get("scenarios", {})
|
sc = macro_regime.get("scenarios", {})
|
||||||
|
gauges_g = macro_regime.get("gauges", {})
|
||||||
dom = sc.get("dominant", "incertain")
|
dom = sc.get("dominant", "incertain")
|
||||||
sc_scores = sc.get("scores", {})
|
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"""
|
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)}
|
- 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é
|
SURFACE DE VOLATILITÉ (pilier 3e — impact direct stratégie options):
|
||||||
(bullish+/bullish/neutral/bearish/bearish+/defensive) de sa classe d'actif avec le scénario dominant.
|
- SKEW Index: {_fmt(_skew, '', 0)} | Régime vol: {_vol_r} | VVIX: {_fmt(_vvix, '', 0)} | OVX: {_fmt(_ovx, '', 0)}
|
||||||
→ "bullish+" = conditions très favorables pour ce pattern → majore 3b ou 3d selon la classe
|
→ SKEW >135 = queues chères → SPREADS (pas de straddles). VVIX >100 = primes gonflées → directionnels.
|
||||||
→ "bearish" ou "bearish+" = conditions défavorables → minore 3b ou 3d
|
→ {_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"}
|
||||||
Indique dans "summary": [GOLDILOCKS|STAGFLATION|RÉCESSION|DÉSINFLATION|CRISE] + [SUPPORTING|NEUTRAL|CONTRA]
|
|
||||||
|
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:
|
user = f"""CONTEXTE GLOBAL:
|
||||||
|
|||||||
@@ -292,21 +292,44 @@ def get_economic_calendar() -> List[Dict[str, Any]]:
|
|||||||
|
|
||||||
MACRO_GAUGE_CONFIG = [
|
MACRO_GAUGE_CONFIG = [
|
||||||
# (id, label, ticker, unit, bloc)
|
# (id, label, ticker, unit, bloc)
|
||||||
|
# ── Liquidité / Taux ─────────────────────────────────────────────────────
|
||||||
("dxy", "Dollar DXY", "DX-Y.NYB", "index", "liquidite"),
|
("dxy", "Dollar DXY", "DX-Y.NYB", "index", "liquidite"),
|
||||||
("us10y", "UST 10Y", "^TNX", "%", "liquidite"),
|
("us10y", "UST 10Y", "^TNX", "%", "liquidite"),
|
||||||
("us3m", "UST 3M", "^IRX", "%", "liquidite"),
|
("us3m", "UST 3M", "^IRX", "%", "liquidite"),
|
||||||
("tips", "TIPS ETF", "TIP", "$", "liquidite"),
|
("tips", "TIPS ETF", "TIP", "$", "liquidite"),
|
||||||
|
("tlt", "Obligations 20Y+ (TLT)", "TLT", "$", "liquidite"),
|
||||||
|
# ── Crédit ───────────────────────────────────────────────────────────────
|
||||||
("vix", "VIX", "^VIX", "pts", "credit"),
|
("vix", "VIX", "^VIX", "pts", "credit"),
|
||||||
("hyg", "HY Bonds (HYG)", "HYG", "$", "credit"),
|
("hyg", "HY Bonds (HYG)", "HYG", "$", "credit"),
|
||||||
("lqd", "IG Bonds (LQD)", "LQD", "$", "credit"),
|
("lqd", "IG Bonds (LQD)", "LQD", "$", "credit"),
|
||||||
("ief", "Trésor 7-10Y (IEF)", "IEF", "$", "credit"),
|
("ief", "Trésor 7-10Y (IEF)", "IEF", "$", "credit"),
|
||||||
|
# ── Énergie ──────────────────────────────────────────────────────────────
|
||||||
("brent", "Brent", "BZ=F", "$", "energie"),
|
("brent", "Brent", "BZ=F", "$", "energie"),
|
||||||
("ng", "Gaz naturel", "NG=F", "$", "energie"),
|
("ng", "Gaz naturel", "NG=F", "$", "energie"),
|
||||||
|
# ── Métaux ───────────────────────────────────────────────────────────────
|
||||||
("gold", "Or", "GC=F", "$", "metaux"),
|
("gold", "Or", "GC=F", "$", "metaux"),
|
||||||
|
("silver", "Argent (Silver)", "SI=F", "$/oz", "metaux"),
|
||||||
("copper", "Cuivre", "HG=F", "$/lb", "metaux"),
|
("copper", "Cuivre", "HG=F", "$/lb", "metaux"),
|
||||||
|
# ── Croissance US ─────────────────────────────────────────────────────────
|
||||||
("spx", "S&P 500", "^GSPC", "pts", "croissance"),
|
("spx", "S&P 500", "^GSPC", "pts", "croissance"),
|
||||||
("iwm", "Russell 2000", "IWM", "$", "croissance"),
|
("iwm", "Russell 2000", "IWM", "$", "croissance"),
|
||||||
("xli", "Industriels XLI", "XLI", "$", "croissance"),
|
("xli", "Industriels XLI", "XLI", "$", "croissance"),
|
||||||
|
# ── Secteurs US (rotation) ────────────────────────────────────────────────
|
||||||
|
("xlk", "Tech (XLK)", "XLK", "$", "secteurs"),
|
||||||
|
("xlf", "Financières (XLF)", "XLF", "$", "secteurs"),
|
||||||
|
("xlp", "Conso. défensif (XLP)", "XLP", "$", "secteurs"),
|
||||||
|
("xlu", "Utilities (XLU)", "XLU", "$", "secteurs"),
|
||||||
|
# ── Volatilité de surface ─────────────────────────────────────────────────
|
||||||
|
("vvix", "VVIX (vol-of-vol)", "^VVIX", "pts", "volatilite"),
|
||||||
|
("skew", "CBOE SKEW", "^SKEW", "pts", "volatilite"),
|
||||||
|
("ovx", "Pétrole Vol (OVX)", "^OVX", "pts", "volatilite"),
|
||||||
|
("gvz", "Or Vol (GVZ)", "^GVZ", "pts", "volatilite"),
|
||||||
|
# ── Global / EM ───────────────────────────────────────────────────────────
|
||||||
|
("eem", "EM Actions (EEM)", "EEM", "$", "global"),
|
||||||
|
("emb", "EM Bonds (EMB)", "EMB", "$", "global"),
|
||||||
|
("fxi", "Chine Large Cap (FXI)", "FXI", "$", "global"),
|
||||||
|
# ── Forex risk-off ────────────────────────────────────────────────────────
|
||||||
|
("usdjpy", "USD/JPY", "USDJPY=X", "pts", "forex_ro"),
|
||||||
]
|
]
|
||||||
|
|
||||||
SCENARIO_META = {
|
SCENARIO_META = {
|
||||||
@@ -337,7 +360,7 @@ def get_macro_gauges() -> Dict[str, Any]:
|
|||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
|
||||||
raw: Dict[str, Any] = {}
|
raw: Dict[str, Any] = {}
|
||||||
with ThreadPoolExecutor(max_workers=min(len(MACRO_GAUGE_CONFIG), 12)) as exe:
|
with ThreadPoolExecutor(max_workers=min(len(MACRO_GAUGE_CONFIG), 20)) as exe:
|
||||||
futures = {
|
futures = {
|
||||||
exe.submit(get_quote, ticker): (gid, label, ticker, unit, bloc)
|
exe.submit(get_quote, ticker): (gid, label, ticker, unit, bloc)
|
||||||
for gid, label, ticker, unit, bloc in MACRO_GAUGE_CONFIG
|
for gid, label, ticker, unit, bloc in MACRO_GAUGE_CONFIG
|
||||||
@@ -414,6 +437,72 @@ def get_macro_gauges() -> Dict[str, Any]:
|
|||||||
else ("parité" if rel_perf > -0.2 else "large caps dominants (défensif)")),
|
else ("parité" if rel_perf > -0.2 else "large caps dominants (défensif)")),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Derived: Silver / Gold ratio (Silver oz / Gold oz — proxy risk-on metals)
|
||||||
|
# <0.012 = or surperforme = risk-off; >0.016 = argent surperforme = risk-on industrie
|
||||||
|
sv = raw.get("silver", {}).get("value")
|
||||||
|
gv_ = raw.get("gold", {}).get("value")
|
||||||
|
sgr = round(sv / gv_, 5) if (sv and gv_) else None
|
||||||
|
raw["silver_gold_ratio"] = {
|
||||||
|
"id": "silver_gold_ratio", "label": "Ratio Argent/Or",
|
||||||
|
"ticker": None, "value": sgr, "change_pct": None, "unit": "ratio", "bloc": "derive",
|
||||||
|
"note": ("argent surperforme (risk-on industriel)" if sgr and sgr > 0.016
|
||||||
|
else ("neutre" if sgr and sgr > 0.012 else ("or surperforme (risk-off)" if sgr else None))),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Derived: Tech vs Consumer Staples relative performance (XLK vs XLP)
|
||||||
|
# Positive = tech > défensifs = risk-on; negative = rotation défensive = ralentissement
|
||||||
|
xlk_c = raw.get("xlk", {}).get("change_pct") or 0.0
|
||||||
|
xlp_c = raw.get("xlp", {}).get("change_pct") or 0.0
|
||||||
|
tech_vs_staples = round(xlk_c - xlp_c, 2)
|
||||||
|
raw["xlk_xlp_momentum"] = {
|
||||||
|
"id": "xlk_xlp_momentum", "label": "Tech vs Défensifs (XLK-XLP)",
|
||||||
|
"ticker": None, "value": tech_vs_staples, "change_pct": None, "unit": "pts%", "bloc": "derive",
|
||||||
|
"note": ("tech > défensifs (risk-on fort)" if tech_vs_staples > 0.5
|
||||||
|
else ("parité" if tech_vs_staples > -0.5 else "rotation défensive ⚠️")),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Derived: Financials vs S&P relative performance (XLF vs SPX)
|
||||||
|
# XLF is a leading indicator — underperformance signals credit/economic stress ahead
|
||||||
|
xlf_c = raw.get("xlf", {}).get("change_pct") or 0.0
|
||||||
|
xlf_vs_spx = round(xlf_c - spx_c_val, 2)
|
||||||
|
raw["xlf_spx_ratio"] = {
|
||||||
|
"id": "xlf_spx_ratio", "label": "Financières vs S&P (XLF-SPX)",
|
||||||
|
"ticker": None, "value": xlf_vs_spx, "change_pct": None, "unit": "pts%", "bloc": "derive",
|
||||||
|
"note": ("banques > marché (expansion crédit)" if xlf_vs_spx > 0.3
|
||||||
|
else ("parité" if xlf_vs_spx > -0.3 else "banques < marché ⚠️ (stress crédit)")),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Derived: EM vs US equity relative performance (EEM vs SPX)
|
||||||
|
# Positive = global risk-on; negative = fuite vers US (dollar strength, EM stress)
|
||||||
|
eem_c = raw.get("eem", {}).get("change_pct") or 0.0
|
||||||
|
eem_vs_spx = round(eem_c - spx_c_val, 2)
|
||||||
|
raw["eem_spx_ratio"] = {
|
||||||
|
"id": "eem_spx_ratio", "label": "EM vs S&P (EEM-SPX)",
|
||||||
|
"ticker": None, "value": eem_vs_spx, "change_pct": None, "unit": "pts%", "bloc": "derive",
|
||||||
|
"note": ("EM > US (croissance globale)" if eem_vs_spx > 0.3
|
||||||
|
else ("parité" if eem_vs_spx > -0.5 else "fuite vers US ⚠️ (EM stress/dollar fort)")),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Derived: Vol surface regime — composite classification from VIX + VVIX + SKEW
|
||||||
|
vix_v = raw.get("vix", {}).get("value") or 20.0
|
||||||
|
vvix_v = raw.get("vvix", {}).get("value") or 85.0
|
||||||
|
skew_v = raw.get("skew", {}).get("value") or 115.0
|
||||||
|
if vix_v < 15 and vvix_v < 90 and skew_v < 120:
|
||||||
|
vol_regime = "contango_calm" # ideal pour vendre vol ou spreads bon marché
|
||||||
|
elif vix_v > 30 or vvix_v > 110:
|
||||||
|
vol_regime = "backwardation_panic" # stress aigu — options chères, spreads larges
|
||||||
|
elif skew_v > 140 and vix_v > 20:
|
||||||
|
vol_regime = "tail_risk_elevated" # marché achète protection queue = méfiance
|
||||||
|
elif vix_v < 20 and skew_v > 130:
|
||||||
|
vol_regime = "complacency_hedged" # calme apparent mais queues protégées
|
||||||
|
else:
|
||||||
|
vol_regime = "normal"
|
||||||
|
raw["vol_surface_regime"] = {
|
||||||
|
"id": "vol_surface_regime", "label": "Régime Surface de Vol",
|
||||||
|
"ticker": None, "value": None, "change_pct": None, "unit": "regime", "bloc": "derive",
|
||||||
|
"note": vol_regime,
|
||||||
|
}
|
||||||
|
|
||||||
return _sanitize_floats(raw)
|
return _sanitize_floats(raw)
|
||||||
|
|
||||||
|
|
||||||
@@ -449,6 +538,22 @@ def score_macro_scenarios(gauges: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
iwm_c = gc("iwm")
|
iwm_c = gc("iwm")
|
||||||
xli_c = gc("xli")
|
xli_c = gc("xli")
|
||||||
rel_perf = gv("iwm_spx_ratio") or 0.0 # Russell vs S&P relative perf
|
rel_perf = gv("iwm_spx_ratio") or 0.0 # Russell vs S&P relative perf
|
||||||
|
# ── Nouveaux signaux (phase 2 — 50 compteurs) ────────────────────────────
|
||||||
|
skew_v = gv("skew") or 115.0 # CBOE SKEW: normal ~115, élevé >130, extrême >145
|
||||||
|
vvix_v = gv("vvix") or 85.0 # Vol-of-vol: normal ~85, élevé >100, panique >115
|
||||||
|
ovx_v = gv("ovx") or 25.0 # Oil vol implicite
|
||||||
|
gvz_v = gv("gvz") or 17.0 # Gold vol implicite
|
||||||
|
tlt_c = gc("tlt") # Long bonds 20Y+ (hausse = flight to quality)
|
||||||
|
xlk_c = gc("xlk") # Tech (hausse = risk-on sectoriel)
|
||||||
|
xlf_c = gc("xlf") # Financières (baisse = stress crédit)
|
||||||
|
xlp_c = gc("xlp") # Conso. défensif (hausse = rotation défensive)
|
||||||
|
xlu_c = gc("xlu") # Utilities (hausse = rotation défensive)
|
||||||
|
eem_c = gc("eem") # EM actions (hausse = croissance globale)
|
||||||
|
emb_c = gc("emb") # EM bonds (baisse = crise liquidité EM)
|
||||||
|
fxi_c = gc("fxi") # Chine equities
|
||||||
|
usdjpy_c = gc("usdjpy") # USD/JPY (baisse = JPY s'apprécie = risk-off/panique carry)
|
||||||
|
silver_c = gc("silver") # Argent (surperf or = risk-on industriel)
|
||||||
|
tech_vs_staples = gv("xlk_xlp_momentum") or 0.0
|
||||||
|
|
||||||
scores: Dict[str, int] = {}
|
scores: Dict[str, int] = {}
|
||||||
reasons: Dict[str, List[str]] = {}
|
reasons: Dict[str, List[str]] = {}
|
||||||
@@ -470,6 +575,12 @@ def score_macro_scenarios(gauges: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
if vs200 > 5: s += 15; r.append(f"S&P+{vs200}% vs 200j")
|
if vs200 > 5: s += 15; r.append(f"S&P+{vs200}% vs 200j")
|
||||||
elif vs200 > 0: s += 7
|
elif vs200 > 0: s += 7
|
||||||
if copper_c > 0.5: s += 10; r.append("Cuivre↑")
|
if copper_c > 0.5: s += 10; r.append("Cuivre↑")
|
||||||
|
# Signaux phase 2
|
||||||
|
if skew_v < 115: s += 6; r.append(f"SKEW {skew_v:.0f} (no tail hedge = complacency)")
|
||||||
|
if vvix_v < 85: s += 5; r.append(f"VVIX {vvix_v:.0f} (vol stable)")
|
||||||
|
if tech_vs_staples > 0.5: s += 7; r.append("Tech > Défensifs (risk-on sectoriel)")
|
||||||
|
if eem_c > 0.3: s += 6; r.append("EM↑ (croissance globale)")
|
||||||
|
if usdjpy_c > 0.2: s += 4; r.append("JPY↓ (carry actif = risk-on)")
|
||||||
scores["goldilocks"] = min(100, s); reasons["goldilocks"] = r
|
scores["goldilocks"] = min(100, s); reasons["goldilocks"] = r
|
||||||
|
|
||||||
# DÉSINFLATION / BAISSE DE TAUX
|
# DÉSINFLATION / BAISSE DE TAUX
|
||||||
@@ -483,6 +594,10 @@ def score_macro_scenarios(gauges: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
if vs200 is not None and vs200 > 0: s += 20; r.append("S&P au-dessus 200j")
|
if vs200 is not None and vs200 > 0: s += 20; r.append("S&P au-dessus 200j")
|
||||||
if hyg_c > 0: s += 10; r.append("HYG↑")
|
if hyg_c > 0: s += 10; r.append("HYG↑")
|
||||||
if gold_c > 0 and brent_c < 0: s += 10; r.append("Or↑+Brent↓ (taux réels ↓)")
|
if gold_c > 0 and brent_c < 0: s += 10; r.append("Or↑+Brent↓ (taux réels ↓)")
|
||||||
|
# Signaux phase 2
|
||||||
|
if tlt_c > 0.5: s += 12; r.append("TLT↑↑ (taux 20Y baissent = désinflation confirmée)")
|
||||||
|
elif tlt_c > 0.2: s += 6; r.append("TLT↑ (bonds longs soutiennent)")
|
||||||
|
if xlf_c > 0: s += 5; r.append("XLF↑ (banques = anticipent baisse taux)")
|
||||||
scores["desinflation"] = min(100, s); reasons["desinflation"] = r
|
scores["desinflation"] = min(100, s); reasons["desinflation"] = r
|
||||||
|
|
||||||
# STAGFLATION — inflation + croissance faible
|
# STAGFLATION — inflation + croissance faible
|
||||||
@@ -497,6 +612,11 @@ def score_macro_scenarios(gauges: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
if gold_c > 0.5: s += 15; r.append("Or↑ (protection inflation)")
|
if gold_c > 0.5: s += 15; r.append("Or↑ (protection inflation)")
|
||||||
if copper_c < 0: s += 15; r.append("Cuivre↓ (demande faible)")
|
if copper_c < 0: s += 15; r.append("Cuivre↓ (demande faible)")
|
||||||
if vix > 18: s += 10; r.append("VIX élevé")
|
if vix > 18: s += 10; r.append("VIX élevé")
|
||||||
|
# Signaux phase 2
|
||||||
|
if xlp_c > xlk_c + 0.5: s += 8; r.append("Défensifs > Tech (rotation stagflationniste)")
|
||||||
|
if xlu_c > 0.4: s += 6; r.append("Utilities↑ (rotation vers revenus stables)")
|
||||||
|
if skew_v > 130: s += 5; r.append(f"SKEW {skew_v:.0f} (tail risk croissant)")
|
||||||
|
if tlt_c < -0.3: s += 5; r.append("TLT↓ (taux longs remontent = inflation persistante)")
|
||||||
scores["stagflation"] = min(100, s); reasons["stagflation"] = r
|
scores["stagflation"] = min(100, s); reasons["stagflation"] = r
|
||||||
|
|
||||||
# RÉCESSION
|
# RÉCESSION
|
||||||
@@ -514,6 +634,15 @@ def score_macro_scenarios(gauges: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
if hyg_c < -0.5: s += 15; r.append("HYG↓ (spreads s'écartent)")
|
if hyg_c < -0.5: s += 15; r.append("HYG↓ (spreads s'écartent)")
|
||||||
elif hyg_c < 0: s += 5
|
elif hyg_c < 0: s += 5
|
||||||
if gold_c > 0.3: s += 10; r.append("Or↑ (refuge)")
|
if gold_c > 0.3: s += 10; r.append("Or↑ (refuge)")
|
||||||
|
# Signaux phase 2
|
||||||
|
if tlt_c > 0.5: s += 15; r.append("TLT↑↑ (fuite vers bonds longs = signal recessionnaire fort)")
|
||||||
|
elif tlt_c > 0.2: s += 7; r.append("TLT↑ (obligations soutenues)")
|
||||||
|
if xlf_c < -1.0: s += 12; r.append("Financières↓↓ (banques = leading indicator récession)")
|
||||||
|
elif xlf_c < -0.3: s += 5
|
||||||
|
if eem_c < -1.0: s += 8; r.append("EM↓ (global slowdown)")
|
||||||
|
if usdjpy_c < -1.0: s += 10; r.append("JPY↑↑ (carry unwind = risk-off global)")
|
||||||
|
elif usdjpy_c < -0.5: s += 5
|
||||||
|
if skew_v > 135: s += 8; r.append(f"SKEW {skew_v:.0f} (tail risk extrême)")
|
||||||
scores["recession"] = min(100, s); reasons["recession"] = r
|
scores["recession"] = min(100, s); reasons["recession"] = r
|
||||||
|
|
||||||
# CRISE DE LIQUIDITÉ
|
# CRISE DE LIQUIDITÉ
|
||||||
@@ -530,6 +659,16 @@ def score_macro_scenarios(gauges: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
if gold_c > 1.0 and copper_c < -1.0: s += 20; r.append("Or↑+Cuivre↓ (fuite sécurité)")
|
if gold_c > 1.0 and copper_c < -1.0: s += 20; r.append("Or↑+Cuivre↓ (fuite sécurité)")
|
||||||
if dxy_c > 1.0: s += 15; r.append("Dollar↑↑")
|
if dxy_c > 1.0: s += 15; r.append("Dollar↑↑")
|
||||||
if ief_c > 0.5: s += 10; r.append("Obligations souveraines↑↑")
|
if ief_c > 0.5: s += 10; r.append("Obligations souveraines↑↑")
|
||||||
|
# Signaux phase 2 — les meilleurs indicateurs de crise liquide
|
||||||
|
if skew_v > 145: s += 15; r.append(f"SKEW {skew_v:.0f} — extrême tail risk (panique protection)")
|
||||||
|
elif skew_v > 135: s += 8
|
||||||
|
if vvix_v > 115: s += 15; r.append(f"VVIX {vvix_v:.0f} — vol-of-vol panique")
|
||||||
|
elif vvix_v > 100: s += 8; r.append(f"VVIX {vvix_v:.0f} — vol élevée")
|
||||||
|
if usdjpy_c < -1.5: s += 15; r.append("JPY↑↑↑ (carry trade unwind = panique globale)")
|
||||||
|
elif usdjpy_c < -0.8: s += 7; r.append("JPY↑ (risk-off carry)")
|
||||||
|
if xlf_c < -2.0: s += 15; r.append("Banques↓↓ (stress bancaire systémique)")
|
||||||
|
elif xlf_c < -1.0: s += 7
|
||||||
|
if emb_c < -1.0: s += 10; r.append("EM Bonds↓ (fuite liquidité EM)")
|
||||||
scores["crise_liquidite"] = min(100, s); reasons["crise_liquidite"] = r
|
scores["crise_liquidite"] = min(100, s); reasons["crise_liquidite"] = r
|
||||||
|
|
||||||
# REFLATION — croissance accélère + inflation remonte (cuivre, énergie, small caps explosent)
|
# REFLATION — croissance accélère + inflation remonte (cuivre, énergie, small caps explosent)
|
||||||
@@ -547,6 +686,12 @@ def score_macro_scenarios(gauges: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
if rel_perf > 0.3: s += 10; r.append("Small caps > large (risk-on large)")
|
if rel_perf > 0.3: s += 10; r.append("Small caps > large (risk-on large)")
|
||||||
elif rel_perf > 0: s += 4
|
elif rel_perf > 0: s += 4
|
||||||
if vix < 18: s += 5
|
if vix < 18: s += 5
|
||||||
|
# Signaux phase 2
|
||||||
|
if eem_c > 1.0: s += 10; r.append("EM↑↑ (croissance mondiale = reflation globale)")
|
||||||
|
elif eem_c > 0.3: s += 5; r.append("EM↑ (global risk-on)")
|
||||||
|
if xlk_c > 1.0: s += 8; r.append("Tech↑↑ (croissance+momentum)")
|
||||||
|
if silver_c > 1.5: s += 8; r.append("Argent↑↑ (industrial metals = reflation industrielle)")
|
||||||
|
if usdjpy_c > 0.5: s += 6; r.append("JPY↓ (carry trades actifs = risk-on global)")
|
||||||
scores["reflation"] = min(100, s); reasons["reflation"] = r
|
scores["reflation"] = min(100, s); reasons["reflation"] = r
|
||||||
|
|
||||||
# SOFT LANDING — croissance positive + inflation en repli, pas encore basse
|
# SOFT LANDING — croissance positive + inflation en repli, pas encore basse
|
||||||
@@ -562,6 +707,10 @@ def score_macro_scenarios(gauges: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
if xli_c > 0: s += 8; r.append("Industriels positifs")
|
if xli_c > 0: s += 8; r.append("Industriels positifs")
|
||||||
if copper_c > 0: s += 5; r.append("Cuivre stable")
|
if copper_c > 0: s += 5; r.append("Cuivre stable")
|
||||||
if ief_c > 0 and brent_c < 0: s += 7; r.append("Taux baissent + énergie recule")
|
if ief_c > 0 and brent_c < 0: s += 7; r.append("Taux baissent + énergie recule")
|
||||||
|
# Signaux phase 2
|
||||||
|
if xlf_c > 0: s += 8; r.append("Financières↑ (banques = économie saine, no recession)")
|
||||||
|
if eem_c > 0: s += 5; r.append("EM stable (croissance globale intacte)")
|
||||||
|
if skew_v < 130: s += 4; r.append(f"SKEW {skew_v:.0f} (tail risk non-extrême)")
|
||||||
scores["soft_landing"] = min(100, s); reasons["soft_landing"] = r
|
scores["soft_landing"] = min(100, s); reasons["soft_landing"] = r
|
||||||
|
|
||||||
# CHOC INFLATIONNISTE — spike énergie/supply soudain (guerre, OPEC, sécheresse)
|
# CHOC INFLATIONNISTE — spike énergie/supply soudain (guerre, OPEC, sécheresse)
|
||||||
@@ -578,6 +727,12 @@ def score_macro_scenarios(gauges: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
elif vix > 18: s += 5
|
elif vix > 18: s += 5
|
||||||
if copper_c < -0.5: s += 8; r.append("Cuivre↓ (demand destruction)")
|
if copper_c < -0.5: s += 8; r.append("Cuivre↓ (demand destruction)")
|
||||||
if ief_c < -0.2: s += 8; r.append("Trésor↓ (taux longs remontent)")
|
if ief_c < -0.2: s += 8; r.append("Trésor↓ (taux longs remontent)")
|
||||||
|
# Signaux phase 2
|
||||||
|
if ovx_v > 45: s += 15; r.append(f"OVX {ovx_v:.0f} — vol pétrole extreme (choc supply aigu)")
|
||||||
|
elif ovx_v > 35: s += 8; r.append(f"OVX {ovx_v:.0f} — vol pétrole élevée")
|
||||||
|
if gvz_v > 22: s += 8; r.append(f"GVZ {gvz_v:.0f} — vol or élevée (inflation/géo)")
|
||||||
|
if xlp_c > 0.5: s += 6; r.append("Défensifs↑ (rotation anti-inflation)")
|
||||||
|
if tlt_c < -0.5: s += 8; r.append("TLT↓↓ (taux 20Y montent = anticipation inflation)")
|
||||||
scores["inflation_shock"] = min(100, s); reasons["inflation_shock"] = r
|
scores["inflation_shock"] = min(100, s); reasons["inflation_shock"] = r
|
||||||
|
|
||||||
ranked = sorted(scores.items(), key=lambda x: x[1], reverse=True)
|
ranked = sorted(scores.items(), key=lambda x: x[1], reverse=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user