#!/usr/bin/env python3 """ SCRIPT DI TEST DA ESEGUIRE NEL TUO AMBIENTE LOCALE Questo script verifica se yfinance può fornire i dati necessari per implementare ROIC, Interest Coverage e Piotroski F-Score sui titoli italiani del FTSE MIB. ISTRUZIONI: 1. pip install yfinance 2. python test_yfinance_italia.py 3. Leggi il report finale per decidere se Fase 1 è fattibile a costo zero NOTA: I miei test in ambiente Claude falliscono per restrizioni di rete, ma nel tuo ambiente locale yfinance dovrebbe funzionare normalmente. """ import yfinance as yf import pandas as pd from datetime import datetime def test_comprehensive_data(ticker_symbol): """Test completo per verificare disponibilità dati per ROIC, Interest Coverage, F-Score""" print(f"\n{'='*80}") print(f"Testing: {ticker_symbol}") print(f"{'='*80}") ticker = yf.Ticker(ticker_symbol) report = { 'ticker': ticker_symbol, 'roic_feasible': False, 'interest_coverage_feasible': False, 'fscore_feasible': False, 'details': {} } # Test 1: Balance Sheet print("\n1. BALANCE SHEET:") try: bs = ticker.balance_sheet if not bs.empty: years = len(bs.columns) print(f" ✅ Disponibile - {years} periodi") print(f" Colonne: {list(bs.columns[:3])}") # Cerca campi critici per ROIC required_bs_fields = { 'Total Assets': False, 'Total Debt': False, 'Long Term Debt': False, 'Total Liabilities Net Minority Interest': False, 'Stockholders Equity': False, 'Cash And Cash Equivalents': False, 'Current Assets': False, 'Current Liabilities': False } for field in required_bs_fields: for idx in bs.index: if field.lower().replace(' ', '') in str(idx).lower().replace(' ', ''): required_bs_fields[field] = True print(f" ✅ {field}: trovato come '{idx}'") break report['details']['balance_sheet'] = { 'available': True, 'years': years, 'fields': required_bs_fields } # Check ROIC components has_assets = required_bs_fields.get('Total Assets', False) has_equity = required_bs_fields.get('Stockholders Equity', False) has_debt = required_bs_fields.get('Total Debt', False) or required_bs_fields.get('Long Term Debt', False) has_cash = required_bs_fields.get('Cash And Cash Equivalents', False) if has_assets and has_equity and (has_debt or has_cash): report['roic_feasible'] = True print(" ✅ ROIC: dati sufficienti nel Balance Sheet") else: print(f" ⚠️ ROIC: mancano dati (Assets:{has_assets}, Equity:{has_equity}, Debt:{has_debt}, Cash:{has_cash})") else: print(" ❌ Balance Sheet vuoto") report['details']['balance_sheet'] = {'available': False} except Exception as e: print(f" ❌ Errore: {e}") report['details']['balance_sheet'] = {'available': False, 'error': str(e)} # Test 2: Income Statement print("\n2. INCOME STATEMENT:") try: inc = ticker.financials if not inc.empty: years = len(inc.columns) print(f" ✅ Disponibile - {years} periodi") # Cerca campi critici required_inc_fields = { 'EBIT': False, 'Operating Income': False, 'Interest Expense': False, 'Tax Provision': False, 'Tax Rate For Calcs': False, 'Gross Profit': False, 'Total Revenue': False, 'Net Income': False } for field in required_inc_fields: for idx in inc.index: if field.lower().replace(' ', '') in str(idx).lower().replace(' ', ''): required_inc_fields[field] = True print(f" ✅ {field}: trovato come '{idx}'") break report['details']['income_stmt'] = { 'available': True, 'years': years, 'fields': required_inc_fields } # Check Interest Coverage components has_ebit = required_inc_fields.get('EBIT', False) or required_inc_fields.get('Operating Income', False) has_interest = required_inc_fields.get('Interest Expense', False) if has_ebit and has_interest: report['interest_coverage_feasible'] = True print(" ✅ INTEREST COVERAGE: dati sufficienti") else: print(f" ⚠️ INTEREST COVERAGE: mancano dati (EBIT:{has_ebit}, Interest:{has_interest})") # Check ROIC tax component has_tax = required_inc_fields.get('Tax Provision', False) or required_inc_fields.get('Tax Rate For Calcs', False) if has_ebit and has_tax and report['roic_feasible']: print(" ✅ ROIC: anche NOPAT calcolabile (EBIT + Tax disponibili)") else: print(" ❌ Income Statement vuoto") report['details']['income_stmt'] = {'available': False} except Exception as e: print(f" ❌ Errore: {e}") report['details']['income_stmt'] = {'available': False, 'error': str(e)} # Test 3: Cash Flow print("\n3. CASH FLOW STATEMENT:") try: cf = ticker.cashflow if not cf.empty: years = len(cf.columns) print(f" ✅ Disponibile - {years} periodi") required_cf_fields = { 'Operating Cash Flow': False, 'Free Cash Flow': False, 'Capital Expenditure': False } for field in required_cf_fields: for idx in cf.index: if field.lower().replace(' ', '') in str(idx).lower().replace(' ', ''): required_cf_fields[field] = True print(f" ✅ {field}: trovato come '{idx}'") break report['details']['cashflow'] = { 'available': True, 'years': years, 'fields': required_cf_fields } else: print(" ❌ Cash Flow vuoto") report['details']['cashflow'] = {'available': False} except Exception as e: print(f" ❌ Errore: {e}") report['details']['cashflow'] = {'available': False, 'error': str(e)} # Test 4: Piotroski F-Score feasibility print("\n4. PIOTROSKI F-SCORE FEASIBILITY:") bs_ok = report['details'].get('balance_sheet', {}).get('available', False) inc_ok = report['details'].get('income_stmt', {}).get('available', False) cf_ok = report['details'].get('cashflow', {}).get('available', False) if bs_ok and inc_ok and cf_ok: bs_years = report['details']['balance_sheet'].get('years', 0) inc_years = report['details']['income_stmt'].get('years', 0) cf_years = report['details']['cashflow'].get('years', 0) min_years = min(bs_years, inc_years, cf_years) if min_years >= 2: report['fscore_feasible'] = True print(f" ✅ F-Score calcolabile (storico {min_years} anni disponibile)") else: print(f" ⚠️ F-Score parziale (solo {min_years} anni, servono almeno 2)") else: print(f" ❌ F-Score non calcolabile (mancano statement)") return report def main(): print("="*80) print("TEST DISPONIBILITÀ DATI YFINANCE PER FASE 1") print("Verifica recupero dati per ROIC, Interest Coverage, Piotroski F-Score") print("="*80) print(f"\nData test: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") # Test su campione esteso FTSE MIB - Prova del 9 test_tickers = { 'Large Cap': ['ENI.MI', 'ENEL.MI', 'ISP.MI'], 'Settore Finanziario': ['UCG.MI', 'BAMI.MI', 'PST.MI'], 'CRITICAL DANGER (high debt)': ['BC.MI', 'AMP.MI', 'IG.MI', 'STLAM.MI'], 'Small/Mid Cap': ['CPR.MI', 'RACE.MI', 'TIT.MI'], 'Utilities/Industrials': ['A2A.MI', 'TEN.MI', 'PRY.MI'], 'Original Small Cap': ['DIA.MI', 'AZM.MI'] } all_reports = [] for category, tickers in test_tickers.items(): print(f"\n\n{'='*80}") print(f"CATEGORIA: {category}") print(f"{'='*80}") for ticker in tickers: report = test_comprehensive_data(ticker) all_reports.append(report) # Summary finale print("\n\n" + "="*80) print("SUMMARY FINALE") print("="*80) total = len(all_reports) roic_count = sum(1 for r in all_reports if r['roic_feasible']) interest_count = sum(1 for r in all_reports if r['interest_coverage_feasible']) fscore_count = sum(1 for r in all_reports if r['fscore_feasible']) print(f"\nTotale titoli testati: {total}") print(f"\n📊 METRICHE IMPLEMENTABILI:") print(f" ROIC: {roic_count}/{total} titoli ({roic_count/total*100:.1f}%)") print(f" Interest Coverage: {interest_count}/{total} titoli ({interest_count/total*100:.1f}%)") print(f" Piotroski F-Score: {fscore_count}/{total} titoli ({fscore_count/total*100:.1f}%)") print("\n" + "="*80) print("VERDICT FINALE") print("="*80) if roic_count >= total * 0.7: print("\n✅ FASE 1 FATTIBILE A COSTO ZERO") print(f" yfinance fornisce dati sufficienti per implementare:") if roic_count >= total * 0.8: print(f" ✅ ROIC ({roic_count/total*100:.0f}% copertura)") if interest_count >= total * 0.8: print(f" ✅ Interest Coverage ({interest_count/total*100:.0f}% copertura)") if fscore_count >= total * 0.7: print(f" ✅ Piotroski F-Score ({fscore_count/total*100:.0f}% copertura)") elif fscore_count >= total * 0.5: print(f" ⚠️ Piotroski F-Score parziale ({fscore_count/total*100:.0f}% copertura)") else: print(f" ❌ Piotroski F-Score non implementabile (solo {fscore_count/total*100:.0f}% copertura)") print("\n RACCOMANDAZIONE: Procedi con implementazione Fase 1") print(" Gestisci titoli con dati mancanti con fallback logic") elif roic_count >= total * 0.5: print("\n⚠️ FASE 1 FATTIBILE CON LIMITAZIONI") print(f" yfinance fornisce dati per ~{roic_count/total*100:.0f}% dei titoli") print("\n RACCOMANDAZIONE: Implementa con gestione robusta dei fallback") print(" Considera upgrade a EODHD (€60/mese) se necessiti 100% coverage") else: print("\n❌ FASE 1 NON FATTIBILE A COSTO ZERO") print(f" yfinance coverage insufficiente: {roic_count}/{total} titoli") print("\n RACCOMANDAZIONE: Passa direttamente a EODHD (€60/mese)") print(" - Coverage 95%+ su mercati europei") print(" - 30+ anni di storico garantito") print(" - API stabile e documentata") print("\n" + "="*80) print("\n💡 NOTA IMPORTANTE:") print(" Se questo test mostra 0% coverage, il problema è probabilmente") print(" la rete/firewall locale. yfinance richiede accesso libero a") print(" query2.finance.yahoo.com per funzionare.") print("\n Prova ad eseguire da una rete diversa o VPN se vedi 0% coverage.") print("="*80) if __name__ == '__main__': main()