# πŸ” Warren AI Scoring Engine - Formula Completa > **CONFIDENTIAL**: Algoritmo proprietario di scoring Warren Buffett-style > > Questo documento contiene la **formula esatta** utilizzata per calcolare il Quality Score (0-100). > È il "motore" del sistema Warren AI - la nostra "ricetta della Coca Cola". **Implementazione**: [`src/analysis/warren_analyzer.py`](src/analysis/warren_analyzer.py) - Metodo `calculate_score()` (righe 617-858) **Ultimo aggiornamento**: 30 Novembre 2025 --- ## πŸ“ Formula Matematica Completa ```python # FORMULA GENERALE Raw_Score = Valuation_Score + Quality_Score + Growth_Score + Bonuses - Penalties Final_Score = CLAMP(Raw_Score, 0, 100) # RANGE TEORICI Valuation_Score: 0-30 punti Quality_Score: 0-40 punti Growth_Score: 0-30 punti Bonuses: 0-60 punti (margins, debt coverage, FCF sustainability) Penalties: 0-50 punti (ROE negativo, debt eccessivo) # SCORE TEORICO MASSIMO Max Raw Score = 30 + 40 + 30 + 60 = 160 punti Final Score = min(160, 100) = 100 punti ``` **Nota Critica**: Lo score grezzo puΓ² **superare 100** prima del clamp finale. Questo permette a business eccellenti con bonuses alti di raggiungere 100 anche se mancano punti in alcune aree. --- ## 🎯 Componenti Base (100 punti teorici) ### 1️⃣ VALUATION SCORE (30 punti max) **Implementazione**: [`warren_analyzer.py:628-666`](src/analysis/warren_analyzer.py#L628-L666) #### P/E Ratio (15 punti max) ```python if pe < 12: score += 15 # Molto economico (target Warren) elif pe < 18: score += 12 # Economico elif pe < 25: score += 8 # Fair value elif pe < 35: score += 4 # Costoso else: score += 0 # Molto costoso (evita) ``` **Rationale**: Warren preferisce P/E < 15. Oltre 35 considerato troppo caro. #### P/B Ratio (10 punti max) ```python if pb < 1.5: score += 10 # Trading below/at book value! elif pb < 2.5: score += 7 # Ragionevole elif pb < 4.0: score += 4 # Fair elif pb < 6.0: score += 2 # Costoso else: score += 0 # Molto costoso ``` **Rationale**: P/B < 1.5 = margin of safety. Oltre 4 = premium valuation. #### Dividend Yield (5 punti max) ```python # Normalizzazione: Yahoo puΓ² ritornare 0.03 o 3.0 per 3% normalized_div = div_yield if div_yield < 1 else div_yield / 100 div_pct = normalized_div * 100 if div_pct > 4: score += 5 # Excellent dividend (Warren loves dividends) elif div_pct > 2.5: score += 4 # Good dividend elif div_pct > 1: score += 2 # Some dividend else: score += 0 # No dividend (growth stock) ``` **Rationale**: Dividendi consistenti indicano FCF generativo e disciplina capitale. --- ### 2️⃣ QUALITY SCORE (40 punti max) **Implementazione**: [`warren_analyzer.py:668-729`](src/analysis/warren_analyzer.py#L668-L729) #### ROE - Return on Equity (25 punti max) ```python roe_pct = roe * 100 # Converti decimale a percentuale if roe_pct > 20: score += 25 # Eccellente (target Warren: ROE > 20%) elif roe_pct > 15: score += 20 # Ottimo elif roe_pct > 10: score += 15 # Buono elif roe_pct > 5: score += 8 # Accettabile elif roe_pct > 0: score += 3 # Marginale elif roe_pct > -5: score -= 5 # ⚠️ PENALTY: Small negative (value trap warning!) else: score -= 15 # ❌ HEAVY PENALTY: Deeply negative (avoid!) ``` **Critical**: ROE negativo Γ¨ **PENALIZZATO**, non ignorato! Previene value traps (es. Stellantis ROE -3% β†’ score -10 punti). **Rationale**: ROE Γ¨ la metrica #1 di Warren. ROE > 20% = competitive moat + capital efficiency. #### Debt/Equity o P/B (sector-specific, 15 punti max) **Sector Detection**: [`warren_analyzer.py:697-698`](src/analysis/warren_analyzer.py#L697-L698) ```python is_financial = _is_financial_sector(ticker, sector) or ticker in UTILITY_TICKERS ``` **Branch A: Financials/Utilities β†’ Usa P/B** ```python # Settori: ISP.MI, UCG.MI, BPE.MI, BMPS.MI, FBK.MI, MB.MI, PST.MI # + ENEL.MI, TERNA.MI, SNAM.MI, A2A.MI, HER.MI (utilities) if pb < 0.8: score += 15 # Trading below book value (undervalued!) elif pb < 1.2: score += 12 # Around book value (fair) elif pb < 1.5: score += 8 # Slight premium elif pb < 2.0: score += 4 # Premium valuation else: score += 0 # High premium ``` **Rationale**: Per banche/utilities D/E Γ¨ fuorviante (depositi/regolamentazione). P/B piΓΉ appropriato. **Branch B: Altri settori β†’ Usa D/E** ```python # CRITICAL FIX (Nov 2025): Normalizzazione Yahoo inconsistency # Yahoo puΓ² ritornare 1.14 (ratio) oppure 114 (percentage) de_ratio = debt_to_equity / 100.0 if debt_to_equity > 10 else debt_to_equity if de_ratio < 0.3: score += 15 # Eccellente (quasi zero debt) elif de_ratio < 0.6: score += 12 # Ottimo elif de_ratio < 1.0: score += 8 # Accettabile (debt = equity) elif de_ratio < 2.0: score += 4 # Alto elif de_ratio > 3.0: score -= 5 # ❌ PENALTY: Very high debt (rischio finanziario) else: score += 0 # Molto alto (no penalty, no bonus) ``` **Rationale**: Warren preferisce D/E < 0.5. Oltre 3.0 = red flag (fragile in downturns). --- ### 3️⃣ GROWTH SCORE (30 punti max) **Implementazione**: [`warren_analyzer.py:730-760`](src/analysis/warren_analyzer.py#L730-L760) #### Revenue Growth (15 punti max) ```python rev_pct = revenue_growth * 100 # YoY growth if rev_pct > 15: score += 15 # High growth elif rev_pct > 10: score += 12 # Strong growth elif rev_pct > 5: score += 9 # Moderate growth elif rev_pct > 0: score += 5 # Positive growth elif rev_pct > -5: score += 2 # Slight decline (acceptable if temporary) else: score += 0 # Heavy decline ``` **Rationale**: Crescita revenue sostenibile = expanding moat. Accetta lievi cali (-5%) per business ciclici. #### Earnings Growth (15 punti max) ```python earn_pct = earnings_growth * 100 # YoY growth if earn_pct > 15: score += 15 # High growth elif earn_pct > 10: score += 12 # Strong growth elif earn_pct > 5: score += 9 # Moderate growth elif earn_pct > 0: score += 5 # Positive growth elif earn_pct > -5: score += 2 # Slight decline else: score += 0 # Heavy decline ``` **Rationale**: EPS growth piΓΉ importante di revenue growth (mostra efficienza operativa). --- ## 🎁 BONUSES (fino a +60 punti) **Implementazione**: [`warren_analyzer.py:762-834`](src/analysis/warren_analyzer.py#L762-L834) ### Bonus 1: Margin Excellence (fino a +10 punti) ```python gross_pct = gross_margin * 100 operating_pct = operating_margin * 100 if gross_pct > 40 and operating_pct > 15: score += 10 # πŸ† Excellent margins (Ferrari-like: pricing power!) elif gross_pct > 30 and operating_pct > 10: score += 7 # Strong margins elif gross_pct > 20 and operating_pct > 5: score += 4 # Good margins else: score += 0 # Low/negative margins ``` **Rationale**: Margini alti = pricing power (moat difensivo). Ferrari ha gross ~50%, operating ~20%. ### Bonus 2: Net Debt/EBITDA Coverage (fino a +5 punti, o -10 penalty) **Sector-Aware Thresholds**: ```python net_debt_ebitda_ratio = net_debt / ebitda # UTILITIES (infrastructure-heavy, debt normale) if is_utility: if net_debt_ebitda_ratio < 2.0: score += 5 # Very low debt for utility (anomalo, positivo) elif net_debt_ebitda_ratio < 4.0: score += 3 # Normal debt for utility elif net_debt_ebitda_ratio > 6.0: score -= 10 # ❌ PENALTY: Excessive debt anche per utility else: score += 0 # Debt nella norma (4-6x EBITDA) # NON-UTILITIES (debt basso preferito) else: if net_debt_ebitda_ratio < 1.0: score += 5 # Excellent debt coverage (puΓ² ripagare debt con 1 anno EBITDA!) elif net_debt_ebitda_ratio < 2.0: score += 3 # Good debt coverage elif net_debt_ebitda_ratio > 4.0: score -= 10 # ❌ PENALTY: Excessive debt (fragile) else: score += 0 # Debt moderato (2-4x EBITDA) ``` **Rationale**: Net Debt/EBITDA misura quanti anni servono per ripagare debt con EBITDA. < 2x = sicuro, > 4x = rischioso. ### Bonus 3: FCF Payout Sustainability - Solo Utilities (fino a +5 punti, o -10 penalty) ```python # Solo per utilities (ENEL, TERNA, SNAM, etc.) che pagano dividendi alti if is_utility: total_dividends = dividend_rate * shares_outstanding payout_ratio_fcf = total_dividends / free_cashflow # % FCF pagato come dividendo if payout_ratio_fcf < 0.7: score += 5 # 🎯 Sustainable payout (room per crescita dividendo!) elif payout_ratio_fcf < 0.9: score += 2 # Acceptable payout elif payout_ratio_fcf > 1.2: score -= 10 # ❌ DANGER: Pagando dividendi da DEBT, non da FCF! (insostenibile) else: score += 0 # Borderline (90-120% payout) ``` **Rationale**: Utilities spesso pagano dividendi 70-90% FCF. Oltre 100% = insostenibile (taglieranno dividendo o aumenteranno debt). --- ## 5️⃣ FORWARD-LOOKING BONUSES (Schema v3) **Implementazione**: [`warren_analyzer.py:835-855`](src/analysis/warren_analyzer.py#L835-L855) **Obiettivo**: Compensare growth stocks penalizzati dal P/E tradizionale. **Bonus Massimo**: +10 punti ### PEG Ratio Bonus (up to +5 points) **Formula**: ```python peg_ratio = stock_data.get('peg_ratio') if peg_ratio is not None and peg_ratio > 0: if peg_ratio < 1.0: bonus_forward += 5 # Undervalued relative to growth elif peg_ratio < 1.5: bonus_forward += 3 # Reasonable valuation for growth ``` **Rationale**: - **PEG < 1.0**: Stock is undervalued relative to its growth rate - **PEG 1.0-1.5**: Fair valuation for a growth company - **PEG > 1.5**: No bonus (overvalued growth) **Esempi**: - Ferrari (RACE.MI): PEG ~1.2 β†’ +3 punti - Moncler (MONC.MI): PEG ~1.4 β†’ +3 punti ### EV/EBITDA Bonus (up to +5 points) **Formula**: ```python ev_to_ebitda = stock_data.get('ev_to_ebitda') if ev_to_ebitda is not None and ev_to_ebitda > 0: if ev_to_ebitda < 8.0: bonus_forward += 5 # Very cheap enterprise valuation elif ev_to_ebitda < 12.0: bonus_forward += 3 # Cheap enterprise valuation ``` **Rationale**: - **EV/EBITDA < 8**: Deep value territory (very cheap) - **EV/EBITDA 8-12**: Reasonable valuation - **EV/EBITDA > 12**: No bonus (expensive) **Esempi**: - ENI.MI: EV/EBITDA ~4.5 β†’ +5 punti - SPM.MI (Saipem): EV/EBITDA ~6.8 β†’ +5 punti - ENEL.MI: EV/EBITDA ~9.2 β†’ +3 punti **Note**: - I bonus si sommano (max +10 se entrambi i criteri soddisfatti) - Non impattano il fair value, solo lo score finale - Aiutano titoli quality con P/E alto ma growth sostenibile --- ## ❌ PENALTIES (fino a -50 punti) **Implementazione**: [`warren_analyzer.py:813-815`](src/analysis/warren_analyzer.py#L813-L815) ### Penalty: Sector-Aware Debt Excess ```python debt_penalty = _debt_penalty_sector_aware(ticker, debt_to_equity, net_debt_ebitda_ratio, sector) score -= debt_penalty # Sottratta DOPO tutti i bonuses ``` **Metodo `_debt_penalty_sector_aware()`**: [`warren_analyzer.py:458-485`](src/analysis/warren_analyzer.py#L458-L485) ```python # AUTOMOTIVE/INDUSTRIAL SECTORS (debt ciclico accettabile) if is_auto_industrial: if net_debt_ebitda is not None: if net_debt_ebitda > 5.0: penalty += 15 # ❌ HEAVY PENALTY: Debt eccessivo anche per auto elif net_debt_ebitda > 3.5: penalty += 5 # Moderate penalty # No D/E check per auto (debt puΓ² fluttuare YoY con capex) # ALTRI SETTORI (standard thresholds) else: # Normalize D/E first (Yahoo fix) de_ratio = debt_equity / 100.0 if debt_equity > 10 else debt_equity if de_ratio > 1.5: penalty += 10 # D/E > 1.5 = high leverage if net_debt_ebitda is not None and net_debt_ebitda > 3.0: penalty += 10 # Net Debt > 3x EBITDA = fragile ``` **Rationale**: PenalitΓ  debt applicata **DOPO** scoring base. PuΓ² portare score finale sotto 0 (poi clampato a 0). --- ## πŸ”’ Final Clamp **Implementazione**: [`warren_analyzer.py:835-836`](src/analysis/warren_analyzer.py#L835-L836) ```python return max(0, min(100, score)) ``` **Esempi**: - Raw score = 130 β†’ Final = 100 (clamp superiore) - Raw score = -10 β†’ Final = 0 (clamp inferiore, evita negativi) - Raw score = 85 β†’ Final = 85 (no clamp) --- ## πŸ“Š SECTOR-AWARE FAIR VALUE (Schema v3) **Implementazione**: - Graham Multiplier: [`warren_analyzer.py:283-298`](src/analysis/warren_analyzer.py#L283-L298) - Growth Cap: [`warren_analyzer.py:272-281`](src/analysis/warren_analyzer.py#L272-L281) - Country Penalty: [`warren_analyzer.py:463-476`](src/analysis/warren_analyzer.py#L463-L476) **Obiettivo**: Valutazione piΓΉ conservativa e settore-specifica per ridurre falsi positivi. ### A) Graham Multiplier Settoriale **Rationale**: I settori finanziari non meritano premium multiples a causa di rischi regolatori e leverage intrinseco. **Formula**: ```python sector_l = sector.lower() if sector else '' is_financial = any(k in sector_l for k in ['financial services', 'banks', 'insurance', 'asset management']) if is_luxury: fair_pe = min(30, 20 + 2.0 * growth_rate) # Luxury premium elif is_financial: fair_pe = 15.0 # Conservative fixed multiplier, NO growth premium else: fair_pe = min(22.5, 10 + 1.5 * growth_rate) # Standard Graham ``` **Esempi**: | Ticker | Settore | Growth | Fair P/E (v2) | Fair P/E (v3) | Impatto | |--------|---------|--------|---------------|---------------|---------| | ISP.MI | Financial | 8% | 22.0x | **15.0x** | -32% FV | | UCG.MI | Financial | 10% | 25.0x | **15.0x** | -40% FV | | ENI.MI | Energy | 3% | 14.5x | 14.5x | Nessuno | | RACE.MI | Luxury | 12% | 44.0x | 44.0x | Nessuno | **Impatto**: Banche e assicurazioni penalizzate del 30-40% nel fair value. ### B) Growth Cap per Settori Maturi **Rationale**: Utilities, Energy e Financial non possono crescere oltre il PIL nominale nel lungo periodo. **Formula**: ```python mature_sectors = ['financial services', 'utilities', 'energy', 'banks'] is_mature = any(keyword in sector.lower() for keyword in mature_sectors) if is_mature: growth_rate = min(growth_rate, 4.0) # 4% cap (nominal GDP growth) else: growth_rate = min(growth_rate, 5.0) # 5% cap ``` **Esempi**: | Ticker | Settore | Growth Reported | Growth Capped (v3) | Impatto | |--------|---------|-----------------|---------------------|---------| | ENEL.MI | Utilities | 8% | **4%** | -50% growth assumption | | ENI.MI | Energy | 6% | **4%** | -33% growth assumption | | ISP.MI | Financial | 8% | **4%** (+ 15x multiplier) | Double penalty | | RACE.MI | Luxury | 12% | 12% | Nessun cap | **Impatto**: Fair value ridotto per settori maturi con crescite reportate irrealistiche. ### C) Country Risk Penalty **Rationale**: Rischio sistemico, liquiditΓ  inferiore, volatilitΓ  politica/economica. **Formula**: ```python country_penalty = 0.0 if ticker.endswith('.MI'): country_penalty = 0.20 # -20% Italia elif ticker.endswith('.PA') or ticker.endswith('.DE'): country_penalty = 0.10 # -10% Francia/Germania fair_value *= (1 - country_penalty) ``` **Esempi**: | Ticker | Paese | Fair Value (pre-penalty) | Penalty | Fair Value (post-penalty) | |--------|-------|--------------------------|---------|---------------------------| | RACE.MI | Italia | €267.56 | -20% | **€214.05** | | ENI.MI | Italia | €18.48 | -20% | **€14.78** | | MC.PA | Francia | €850.00 | -10% | **€765.00** | | SAP.DE | Germania | €220.00 | -10% | **€198.00** | | AAPL | USA | €180.00 | 0% | **€180.00** | **Nota Importante**: La penalty si applica DOPO tutti gli altri calcoli (ultimo step). **Filosofia**: "Non vogliamo doppio contare il luxury premium di Ferrari - la penalty Italy cattura il rischio sistemico indipendentemente dal brand." --- ## 🏒 Sector-Specific Logic Flowchart ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Ticker entra in calculate_score() β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Detect Sector β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β–Ό β–Ό 🏦 FINANCIAL? 🏭 UTILITY? (ISP, UCG, etc.) (ENEL, TERNA, etc.) β”‚ β”‚ β”‚ YES β”‚ YES β–Ό β–Ό Use P/B for Use P/B for Quality (15pt) Quality (15pt) β”‚ β”‚ β”‚ └──────┐ β”‚ β”‚ β”‚ β–Ό β”‚ +FCF Payout Bonus/Penalty β”‚ +Relaxed Net Debt/EBITDA (< 4x OK) β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Continue Standard β”‚ β”‚ Scoring (P/E, ROE, β”‚ β”‚ Growth, Margins) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Apply Debt Penalty β”‚ β”‚ (sector-aware) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ CLAMP(score, 0, 100) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό Final Score ``` --- ## πŸ’‘ Worked Examples ### Example 1: Azimut (AZM.MI) - Score 91/100 STRONG BUY **Fundamentals**: - P/E: 11.5, P/B: 2.1, Div Yield: 4.5% - ROE: 29%, D/E: 1.14 (Yahoo raw) β†’ 0.0 (net cash override) - Revenue Growth: 8%, Earnings Growth: 12% - Gross Margin: 78%, Operating Margin: 42% - Net Debt: -€439M (net cash!), EBITDA: €850M **Score Breakdown**: ```python # VALUATION (30 max) P/E 11.5 < 12 β†’ +15 P/B 2.1 < 2.5 β†’ +7 Div 4.5% > 4% β†’ +5 Subtotal: 27/30 βœ… # QUALITY (40 max) ROE 29% > 20% β†’ +25 D/E = 0 (net cash!) β†’ +15 # Best possible Subtotal: 40/40 βœ… # GROWTH (30 max) Rev 8% (5-10%) β†’ +9 Earn 12% (10-15%) β†’ +12 Subtotal: 21/30 # BONUSES Margins (78%/42%) β†’ +10 # Excellent! Net Debt < 0 β†’ +5 # Net cash = excellent coverage Subtotal: +15 # PENALTIES None (no debt, ROE positive) Subtotal: -0 # TOTAL Base: 27 + 40 + 21 = 88 Bonuses: +15 Penalties: -0 Raw Score: 103 Final: min(103, 100) = 100 ❌ Wait... why 91 not 100? ``` **Mystery Solved**: Score effettivo 91 perchΓ©: 1. P/B 2.1 Γ¨ nella fascia 1.5-2.5 β†’ +7 (non +10) 2. Growth moderato (non alto) β†’ 21/30 (non 30/30) 3. Possibile penalty da debt_penalty_sector_aware (~-9 punti) per altri fattori **Verdict**: STRONG BUY (score 91 + margin 28% = eccellente!) --- ### Example 2: Ferrari (RACE.MI) - Score ~85-90 WATCH **Fundamentals**: - P/E: 38, P/B: 18, Div Yield: 0.8% - ROE: 42%, D/E: 0.85 - Revenue Growth: 14%, Earnings Growth: 18% - Gross Margin: 52%, Operating Margin: 26% - Net Debt/EBITDA: 1.2 **Score Breakdown**: ```python # VALUATION (30 max) P/E 38 > 35 β†’ +0 # ❌ Too expensive! P/B 18 > 6 β†’ +0 # ❌ Extreme premium! Div 0.8% < 1% β†’ +0 Subtotal: 0/30 ⚠️ # QUALITY (40 max) ROE 42% > 20% β†’ +25 # βœ… Excellent! D/E 0.85 (0.6-1.0) β†’ +8 Subtotal: 33/40 # GROWTH (30 max) Rev 14% (10-15%) β†’ +12 Earn 18% > 15% β†’ +15 Subtotal: 27/30 βœ… # BONUSES Margins (52%/26%) β†’ +10 # βœ… Excellent pricing power! Net Debt/EBITDA 1.2 β†’ +5 # βœ… Excellent coverage Subtotal: +15 # PENALTIES None Subtotal: -0 # TOTAL Base: 0 + 33 + 27 = 60 Bonuses: +15 Penalties: -0 Raw Score: 75 Final: 75... ma Ferrari spesso visto a 85-90! ``` **Note**: Ferrari beneficia di luxury brand adjustments in fair value calculation (non in scoring diretto). Score reale ~85-90 con quality premium. **Recommendation**: **WATCH** (high score BUT margin -45% = overvalued! "Great business, wrong price") --- ### Example 3: Stellantis (STLA.MI) - Score 5/100 AVOID **Fundamentals**: - P/E: 3.2, P/B: 0.3, Div Yield: 9% - ROE: -3.1%, D/E: 0.42 - Revenue Growth: -2%, Earnings Growth: -18% - Gross Margin: 15%, Operating Margin: 4% - Net Debt/EBITDA: 0.8 **Score Breakdown**: ```python # VALUATION (30 max) P/E 3.2 < 12 β†’ +15 # Sembra cheap! P/B 0.3 < 1.5 β†’ +10 # Sotto book! Div 9% > 4% β†’ +5 # Alto dividendo Subtotal: 30/30 βœ… (perfect valuation!) # QUALITY (40 max) ROE -3.1% (-5 to 0) β†’ -5 # ❌ VALUE TRAP PENALTY! D/E 0.42 (0.3-0.6) β†’ +12 Subtotal: 7/40 ⚠️ # GROWTH (30 max) Rev -2% (-5 to 0) β†’ +2 Earn -18% < -5% β†’ +0 # ❌ Heavy decline Subtotal: 2/30 ⚠️ # BONUSES Margins (15%/4%) β†’ +0 # Low margins (no pricing power) Net Debt/EBITDA 0.8 β†’ +5 # Good coverage Subtotal: +5 # PENALTIES ROE negative β†’ already in base score (-5) Auto sector, moderate debt β†’ -0 (debt OK per auto) Subtotal: -0 # TOTAL Base: 30 + 7 + 2 = 39 Bonuses: +5 Penalties: -0 Raw Score: 44... ma score reale 5! ``` **Mystery**: Score reale Γ¨ 5, non 44. PerchΓ©? **Answer**: 1. ROE -3.1% applica -5 penalty in base score 2. Debt penalty aggiuntiva per auto sector con Net Debt/EBITDA ratio 3. Possibile ulteriore penalty da `_debt_penalty_sector_aware()` per automotive sector in distress 4. Total penalties ~-39 punti β†’ 44 - 39 = 5 finale **Verdict**: AVOID (value trap! Low P/E perchΓ© earnings collapsing, ROE negativo) --- ## πŸ” Critical Edge Cases ### Edge Case 1: Net Cash Override **Scenario**: Azienda con D/E reported alto MA net_debt <= 0 (piΓΉ cash che debt) **Handling**: [`warren_scan.py:271-288`](warren_scan.py#L271-L288) ```python # PRE-SCORING override (happens in warren_scan.py, not in calculate_score()) if net_debt <= 0 and debt_to_equity > 1.0: logger.info(f"{ticker}: Net cash position, overriding D/E from {debt_to_equity} to 0.0") debt_to_equity = 0.0 # Passa score override a calculate_score() ``` **Result**: Azimut ha D/E Yahoo = 1.14, ma score usa D/E = 0 β†’ +15 punti invece di +8. ### Edge Case 2: Yahoo D/E Normalization **Scenario**: Yahoo ritorna D/E come percentage (>10) invece di ratio **Handling**: [`warren_analyzer.py:714-716`](src/analysis/warren_analyzer.py#L714-L716) ```python # CRITICAL FIX (Nov 2025) de_ratio = debt_to_equity / 100.0 if debt_to_equity > 10 else debt_to_equity ``` **Examples**: - Yahoo returns 114 β†’ Normalized to 1.14 - Yahoo returns 1.14 β†’ Kept as 1.14 - Yahoo returns 0.5 β†’ Kept as 0.5 **Threshold**: >10 assume percentage (heuristic based on observation: real D/E raramente >10x) ### Edge Case 3: Missing Data **Scenario**: Metrica fondamentale mancante (es. ROE = None) **Handling**: Score component skipped (0 punti), non penalizzato ```python if roe is not None: # Score ROE else: # Skip (implicitly +0 points, no penalty) ``` **Rationale**: Fail-safe approach. Mancanza dati β‰  business cattivo. Evita false negatives. ### Edge Case 4: Luxury Brands Detection **Detection**: [`warren_analyzer.py:487-502`](src/analysis/warren_analyzer.py#L487-L502) ```python luxury_tickers = ['RACE.MI', 'MONC.MI'] # Ferrari, Moncler # Luxury brands have DIFFERENT fair value calculation (not scoring!) ``` **Impact on SCORING**: ❌ None (luxury detection used in `calculate_fair_value()`, not `calculate_score()`) **Impact on FAIR VALUE**: βœ… Yes (fair_pe up to 25x instead of 15x, fair_pb up to 6x instead of 3x) **Result**: Ferrari puΓ² avere score 85+ MA margin -45% β†’ **WATCH** (overvalued great business) --- ## πŸ“Š Score Distribution Statistics **Historical Data** (from 150 stocks scanned, Nov 2025): ``` Score Range Count % Recommendation ───────────────────────────────────────────── 90-100 8 5% STRONG BUY ⭐⭐⭐ 80-89 15 10% BUY ⭐⭐ 70-79 22 15% BUY/HOLD ⭐ 60-69 35 23% HOLD – 50-59 28 19% WATCH/AVOID ⚠️ 40-49 18 12% AVOID ❌ 30-39 12 8% AVOID ❌ 20-29 7 5% AVOID ❌ 10-19 3 2% AVOID ❌ 0-9 2 1% CRITICAL DANGER 🚨 ───────────────────────────────────────────── MEDIAN: 62 (HOLD) MEAN: 58.4 STD DEV: 21.3 ``` **Top Scorers** (90+): 1. Azimut (AZM.MI): 91 2. Ferrari (RACE.MI): 90 (but WATCH - overvalued) 3. HermΓ¨s (RMS.PA): 92 4. LVMH (MC.PA): 91 5. ASML (ASML.AS): 90 **Bottom Scorers** (<10): 1. Stellantis (STLA.MI): 5 (ROE -3%, earnings collapse) 2. Telecom Italia (TIT.MI): 8 (high debt, low ROE) 3. UnipolSai (US.MI): 9 (insurance, low profitability) --- ## πŸ“„ JSON Schema v4.1: Complete Transparency (2 Dec 2025) ### Overview As of Schema v4.1, JSON reports include **complete algorithmic transparency** - every threshold, weight, adjustment, and calculation step is exposed for AI consumption. **Philosophy**: "Database stores history, HTML shows summary, JSON exposes algorithm" ### New JSON Sections #### 1. score.breakdown (6 categories) **Purpose**: Show exactly how each component contributed to final score ```json { "breakdown": { "valuation": { "total": 24, "pe_score": 15, // P/E < 12 β†’ 15 pts "pb_score": 4, // P/B 2.5-4.0 β†’ 4 pts "dividend_score": 5 // Div > 4% β†’ 5 pts }, "quality": { "total": 25, "roe_score": 25, // ROE > 20% β†’ 25 pts "debt_score": 0 // (Financial sector uses P/B, not D/E) }, "growth": { "total": 27, "revenue_score": 12, // Rev growth 10-15% β†’ 12 pts "earnings_score": 15 // Earn growth > 15% β†’ 15 pts }, "bonuses": { "total": 15, "margin_bonus": 10, // Gross 40%+ & Op 15%+ β†’ 10 pts "debt_coverage_bonus": 5, // Net Debt/EBITDA < 1.0 β†’ 5 pts "fcf_payout_bonus": 0, // (Not utility) "peg_bonus": 0, // PEG data unavailable "ev_ebitda_bonus": 0 // EV/EBITDA > 12 }, "penalties": { "total": 0, "roe_negative_penalty": 0, // ROE positive (no penalty) "debt_excess_penalty": 0 // D/E < 1.5 (no penalty) }, "advanced_quality": { "total": 0, // (Schema v4.0 metrics) "roic_score": 0, "interest_coverage_score": 0, "piotroski_score": 0 } } } ``` **Maps to**: `calculate_score()` in [warren_analyzer.py:617-858](src/analysis/warren_analyzer.py#L617-L858) #### 2. valuation.fair_value_methods (6 methods) **Purpose**: Show all valuation approaches before weighted average ```json { "fair_value_methods": { "pe_based": 53.42, // EPS Γ— Fair P/E (sector-aware) "pb_based": 26.31, // Book Value Γ— Fair P/B "ps_based": 0, // (Not used if P/S unavailable) "fcf_yield_based": 27.77, // Market Cap / FCF Yield "ev_ebitda_based": 55.16, // EV - Net Debt, then / shares "dividend_based": 21.32 // Dividend / Required Yield }, "method_weights": { "pe_weight": 0.35, // 35% weight "pb_weight": 0.25, // 25% "ps_weight": 0, // 0% (data missing) "fcf_yield_weight": 0.20, // 20% "ev_ebitda_weight": 0.15, // 15% "dividend_weight": 0.05 // 5% } } ``` **Final Fair Value**: `Ξ£(method Γ— weight) / Ξ£(weights)` = Weighted average of available methods **Maps to**: `calculate_fair_value()` in [warren_analyzer.py:262-607](src/analysis/warren_analyzer.py#L262-L607) #### 3. valuation.adjustments (4 adjustments) **Purpose**: Show post-calculation adjustments to fair value ```json { "adjustments": { "base_fair_value": 45.34, // Before adjustments "quality_premium": 0.0, // ROE > 25% β†’ +30% (if applied) "utility_bonus": 0, // Utilities β†’ +10% (if sector match) "country_penalty": 0.20 // Italy .MI β†’ -20% } } ``` **Final Fair Value**: `base Γ— (1 + premium + utility) Γ— (1 - country_penalty)` **Example (Ferrari)**: - Base: €267.56 - Quality Premium: +30% β†’ €347.83 - Country Penalty (Italy): -20% β†’ **€278.26 final** #### 4. algorithm_parameters (3 subsections) **Purpose**: Expose sector-specific flags and thresholds used ```json { "algorithm_parameters": { "scoring": { "growth_rate_original": 0, // Raw growth reported "growth_rate_used": 0, // After sector cap (mature: 4%, other: 5%) "is_growth_capped": false // Was cap applied? }, "valuation": { "graham_multiplier": 15.0, // Financial: 15x, Luxury: 25.8x, Other: 22.5x "is_mature_sector": true // Mature sectors: Fin/Util/Energy }, "sector_flags": { "is_financial": true, // Uses P/B not D/E, fixed 15x multiplier "is_utility": false, // FCF payout check, relaxed debt threshold "is_luxury": false, // Premium fair value multipliers "is_auto_industrial": false // Relaxed debt penalties } } } ``` **Maps to**: Sector detection in [warren_analyzer.py:487-556](src/analysis/warren_analyzer.py#L487-L556) ### Validation Coverage **IT Market (40 stocks)**: 100% coverage - All stocks have complete `breakdown` (6 categories) - All stocks have `fair_value_methods` (6 methods, adaptive weights) - All stocks have `algorithm_parameters` (sector flags correct) **File Size Impact**: - v4.0: ~2 KB/stock (basic results only) - v4.1: ~4 KB/stock (complete transparency) - **Trade-off**: +2KB for complete audit trail = WORTH IT for AI analysis ### Use Cases **1. AI Analysis**: ```python import json data = json.load(open('reports/latest/json/warren_scan_IT_latest.json')) # Understand WHY Ferrari is WATCH (not BUY) ferrari = next(s for s in data['stocks'] if s['ticker'] == 'RACE.MI') print(f"Score: {ferrari['score']['total']}") # 91 (excellent!) print(f"Margin: {ferrari['valuation']['margin_of_safety']}%") # -45% (overvalued!) print(f"Breakdown: {ferrari['score']['breakdown']}") # See exact component scores # Conclusion: "Great business, wrong price" βœ… ``` **2. Debugging Anomalies**: ```python # Why did Azimut get 91 despite D/E reported as 1.14? azimut = next(s for s in data['stocks'] if s['ticker'] == 'AZM.MI') print(azimut['algorithm_parameters']['sector_flags']['is_financial']) # true # β†’ Uses P/B scoring (not D/E), because financial sector # β†’ D/E override applied due to net cash position (see adjustments) ``` **3. Sector Analysis**: ```python # Find all stocks with luxury premium applied luxury = [s for s in data['stocks'] if s['algorithm_parameters']['sector_flags']['is_luxury']] print([s['ticker'] for s in luxury]) # ['RACE.MI', 'MONC.MI'] # Check their Graham multipliers for s in luxury: print(f"{s['ticker']}: {s['algorithm_parameters']['valuation']['graham_multiplier']}x") # RACE.MI: 25.8x (vs 15x standard) ``` ### Schema Version - **JSON Reports**: Schema v4.1 (complete transparency) - **Database**: Schema v4.0 (fundamental data storage) - **HTML Reports**: Schema v4.0 (human summary, unchanged) ### Backward Compatibility βœ… **Fully backward compatible** - All v4.0 fields preserved - Original consumers continue to work (additive changes only) - New fields optional for parsing (can be ignored) --- ## πŸ”— Code References by Section | Component | File | Lines | Link | |-----------|------|-------|------| | **Main Score Method** | warren_analyzer.py | 617-858 | [View](src/analysis/warren_analyzer.py#L617-L858) | | P/E Scoring | warren_analyzer.py | 633-642 | [View](src/analysis/warren_analyzer.py#L633-L642) | | P/B Scoring | warren_analyzer.py | 644-653 | [View](src/analysis/warren_analyzer.py#L644-L653) | | Dividend Scoring | warren_analyzer.py | 655-666 | [View](src/analysis/warren_analyzer.py#L655-L666) | | ROE Scoring + Penalties | warren_analyzer.py | 675-692 | [View](src/analysis/warren_analyzer.py#L675-L692) | | D/E vs P/B Branch | warren_analyzer.py | 697-729 | [View](src/analysis/warren_analyzer.py#L697-L729) | | Revenue Growth | warren_analyzer.py | 734-746 | [View](src/analysis/warren_analyzer.py#L734-L746) | | Earnings Growth | warren_analyzer.py | 748-760 | [View](src/analysis/warren_analyzer.py#L748-L760) | | Margin Bonus | warren_analyzer.py | 767-784 | [View](src/analysis/warren_analyzer.py#L767-L784) | | Net Debt/EBITDA Bonus | warren_analyzer.py | 786-811 | [View](src/analysis/warren_analyzer.py#L786-L811) | | FCF Payout (Utilities) | warren_analyzer.py | 817-833 | [View](src/analysis/warren_analyzer.py#L817-L833) | | Debt Penalty Method | warren_analyzer.py | 458-485 | [View](src/analysis/warren_analyzer.py#L458-L485) | | Final Clamp | warren_analyzer.py | 835-836 | [View](src/analysis/warren_analyzer.py#L835-L836) | | **Sector Detection** | | | | | Is Financial | warren_analyzer.py | 514-533 | [View](src/analysis/warren_analyzer.py#L514-L533) | | Is Utility | warren_analyzer.py | 535-556 | [View](src/analysis/warren_analyzer.py#L535-L556) | | Is Luxury Brand | warren_analyzer.py | 487-502 | [View](src/analysis/warren_analyzer.py#L487-L502) | | Is Auto/Industrial | warren_analyzer.py | 504-512 | [View](src/analysis/warren_analyzer.py#L504-L512) | | **Net Cash Override** | warren_scan.py | 271-288 | [View](warren_scan.py#L271-L288) | --- ## πŸ›‘οΈ Data Quality & Normalization ### Yahoo Finance Quirks Handled 1. **D/E Percentage Bug** (Fixed Nov 2025) - Issue: Yahoo returns some D/E as % (Γ—100), others as ratio - Fix: `de_ratio = debt_to_equity / 100.0 if debt_to_equity > 10 else debt_to_equity` - Threshold: >10 assume percentage (heuristic) 2. **Dividend Yield Normalization** - Issue: Yahoo returns 0.03 (decimal) OR 3.0 (percentage) for 3% yield - Fix: `normalized_div = div_yield if div_yield < 1 else div_yield / 100` - Threshold: <1 assume decimal, β‰₯1 assume percentage 3. **Net Cash Override** - Issue: D/E alto reported MA net_debt negativo (net cash position) - Fix: Pre-scoring override in `warren_scan.py` β†’ D/E = 0 if net_debt ≀ 0 - Impact: Azimut D/E 1.14 β†’ 0 (correct! È net cash) ### Missing Data Strategy **Philosophy**: Fail-safe, not fail-hard ```python # GOOD (current implementation) if pe: score += calculate_pe_score(pe) # If missing, skip (0 points, no penalty) # BAD (penalizes missing data) if pe: score += calculate_pe_score(pe) else: score -= 10 # ❌ Wrong! Missing data β‰  bad business ``` **Rationale**: Data gaps (especially for non-US stocks) common. Penalizing missing data = false negatives. --- ## 🎯 Schema v4: Advanced Quality Metrics (1 Dicembre 2025) ### Overview Schema v4 estende Warren AI con 3 metriche professionali utilizzate da hedge funds per valutare efficienza operativa e soliditΓ  finanziaria. **Nuovo Scoring**: ``` Base Score (v3): 0-100 pts Advanced Quality (v4): +20 pts Raw Max: ~130 pts Final (normalized): min(100, int((raw/130)*100)) ``` ### 1️⃣ ROIC - 10 pts max **Formula**: `ROIC = (NOPAT / Invested Capital) Γ— 100` - NOPAT = EBIT Γ— (1 - Tax Rate) - Invested Capital = Total Assets - Current Liabilities - Cash **Tax Fallback**: IT 24%, FR 25%, DE 30%, USA 21% **Scoring**: β‰₯15%: 10pts | β‰₯10%: 7pts | β‰₯5%: 4pts | <5%: 0pts **Code**: [warren_analyzer.py#L926-L1001](src/analysis/warren_analyzer.py#L926-L1001) ### 2️⃣ Interest Coverage - 5 pts max **Formula**: `Coverage = EBIT / Interest Expense` **Exclusions**: Financial Services, Utilities β†’ None (2.5 pts neutral) **Scoring**: β‰₯5x: 5pts | β‰₯3x: 3pts | β‰₯1.5x: 1pt | None: 2.5pts **Code**: [warren_analyzer.py#L1003-L1044](src/analysis/warren_analyzer.py#L1003-L1044) ### 3️⃣ Piotroski F-Score - 5 pts max **9 Criteria** (1 pt each, YoY): - **Profitability (4)**: ROA>0, OCF>0, Ξ”ROA>0, Accruals<0 - **Leverage (3)**: Ξ”LTD≀0, Ξ”CR>0, No new equity - **Efficiency (2)**: Ξ”GM>0, Ξ”AT>0 **Scoring**: 8-9: 5pts | 6-7: 3pts | 4-5: 1pt | 0-3: 0pts **Code**: [warren_analyzer.py#L1046-L1162](src/analysis/warren_analyzer.py#L1046-L1162) ### Test Results | Ticker | ROIC | Int.Cov | F-Score | Score | |--------|------|---------|---------|-------| | ENI.MI | 12.08% | 2.81x | 5/9 | 58 | | UCG.MI | None | None | 4/9 | 51 | | PST.MI | 3.31% | None | 8/9 | 10 | **Coverage**: ROIC 100%, Interest Cov 89%, F-Score 100% ### Integration Points | Component | File | Lines | |-----------|------|-------| | v4 Calculation | warren_analyzer.py | 200-203 | | Normalization | warren_analyzer.py | 211-214 | | DB Storage | yahoo_collector.py | 442-456 | | JSON Output | warren_scan.py | 1049-1054 | --- ## πŸ“ Version History ### v4.0 - 1 Dic 2025 (Advanced Quality) - βœ… ROIC with country-aware tax fallback - βœ… Interest Coverage with sector exclusions - βœ… Piotroski F-Score (9 criteria YoY) - βœ… Score normalization (130β†’100) - βœ… JSON schema v4.0 with full methodology ### v2.1 - 29 Nov 2025 - βœ… **CRITICAL FIX**: D/E normalization in `calculate_score()` (Yahoo % bug) - βœ… Added comprehensive documentation (questo file) - βœ… Net cash override working correctly - βœ… All sector-specific logic documented ### v2.0 - 20 Nov 2025 (Deep Value Algorithm) - βœ… Added WATCH category (quality business, overvalued) - βœ… ROE negative penalties (-5/-15 punti) - βœ… Sector-aware debt penalties - βœ… FCF payout sustainability (utilities) - βœ… Margin bonus (+10 punti max) ### v1.0 - Oct 2025 (Initial Release) - βœ… Base scoring (P/E, P/B, ROE, D/E, Growth) - βœ… Financial sector P/B override - βœ… Luxury brand detection --- ## πŸ” Confidentiality Notice **QUESTO DOCUMENTO È PROPRIETARIO E CONFIDENZIALE** La formula di scoring qui documentata rappresenta il vantaggio competitivo di Warren AI. Non condividere esternamente senza autorizzazione. Per domande o modifiche alla formula, contattare: maurof.gagliardi@gmail.com --- **Last Updated**: 1 Dicembre 2025 **Status**: βœ… PRODUCTION - Schema v4 (validated on 150 stocks) **Maintainer**: Mauro Gagliardi