#!/usr/bin/env python3 """ Test script to verify enhanced JSON report structure with complete scoring breakdown. Tests with a single stock (RACE.MI - Ferrari) to validate new fields. """ import sys import json from pathlib import Path from datetime import datetime sys.path.insert(0, str(Path(__file__).parent)) from src.database.db_manager import DatabaseManager from src.analysis.warren_analyzer import WarrenAnalyzer from warren_scan import get_stock_data def test_json_enhanced(): """Test that JSON report includes complete scoring breakdown""" print("=" * 80) print("TEST: Enhanced JSON Report Structure (Schema v4.1)") print("=" * 80) db = DatabaseManager() analyzer = WarrenAnalyzer() # Test with RACE.MI (Ferrari) ticker = "RACE.MI" print(f"\nšŸŽÆ Testing with: {ticker} (Ferrari)") print("-" * 80) try: # Get stock data and analyze stock_data, warnings = get_stock_data(db, ticker) result = analyzer.analyze(stock_data) print(f"\nāœ… Analysis complete:") print(f" • Score: {result['score']}/100") print(f" • Rating: {result['valutazione']}") # Construct stock_entry as done in generate_json_report() stock_entry = { "ticker": ticker, "name": result['name'], "sector": result.get('sector', 'N/A'), "score": { "total": result['score'], "rating": result['valutazione'], # Add raw score before normalization "raw_score": ( result.get('score_breakdown', {}).get('valuation', {}).get('total', 0) + result.get('score_breakdown', {}).get('quality', {}).get('total', 0) + result.get('score_breakdown', {}).get('growth', {}).get('total', 0) + result.get('score_breakdown', {}).get('bonuses', {}).get('total', 0) - result.get('score_breakdown', {}).get('penalties', {}).get('total', 0) + result.get('score_breakdown', {}).get('advanced_quality', {}).get('total', 0) ), # Add complete breakdown "breakdown": { "valuation": { "total": result.get('score_breakdown', {}).get('valuation', {}).get('total', 0), "pe_score": result.get('score_breakdown', {}).get('valuation', {}).get('pe', 0), "pb_score": result.get('score_breakdown', {}).get('valuation', {}).get('pb', 0), "dividend_score": result.get('score_breakdown', {}).get('valuation', {}).get('dividend', 0) }, "quality": { "total": result.get('score_breakdown', {}).get('quality', {}).get('total', 0), "roe_score": result.get('score_breakdown', {}).get('quality', {}).get('roe', 0), "debt_score": result.get('score_breakdown', {}).get('quality', {}).get('debt', 0) }, "growth": { "total": result.get('score_breakdown', {}).get('growth', {}).get('total', 0), "revenue_score": result.get('score_breakdown', {}).get('growth', {}).get('revenue', 0), "earnings_score": result.get('score_breakdown', {}).get('growth', {}).get('earnings', 0) }, "bonuses": { "total": result.get('score_breakdown', {}).get('bonuses', {}).get('total', 0), "margin_bonus": result.get('score_breakdown', {}).get('bonuses', {}).get('margins', 0), "debt_coverage_bonus": result.get('score_breakdown', {}).get('bonuses', {}).get('debt_coverage', 0), "fcf_payout_bonus": result.get('score_breakdown', {}).get('bonuses', {}).get('fcf_payout', 0), "peg_bonus": result.get('score_breakdown', {}).get('bonuses', {}).get('peg', 0), "ev_ebitda_bonus": result.get('score_breakdown', {}).get('bonuses', {}).get('ev_ebitda', 0) }, "penalties": { "total": result.get('score_breakdown', {}).get('penalties', {}).get('total', 0), "roe_negative_penalty": result.get('score_breakdown', {}).get('penalties', {}).get('roe_negative', 0), "debt_excess_penalty": result.get('score_breakdown', {}).get('penalties', {}).get('debt_excess', 0) }, "advanced_quality": { "total": result.get('score_breakdown', {}).get('advanced_quality', {}).get('total', 0), "roic_score": result.get('score_breakdown', {}).get('advanced_quality', {}).get('roic', 0), "interest_coverage_score": result.get('score_breakdown', {}).get('advanced_quality', {}).get('interest_coverage', 0), "piotroski_score": result.get('score_breakdown', {}).get('advanced_quality', {}).get('piotroski', 0) } } }, "valuation": { "current_price": result['current_price'], "fair_value": result['fair_value'], "margin_of_safety": result['margin_of_safety'], "is_undervalued": result['margin_of_safety'] > 0, # Add fair value methods breakdown "fair_value_methods": { "pe_based": result.get('fair_value_methods', {}).get('pe', 0), "pb_based": result.get('fair_value_methods', {}).get('pb', 0), "ps_based": result.get('fair_value_methods', {}).get('ps', 0), "fcf_yield_based": result.get('fair_value_methods', {}).get('fcf_yield', 0), "ev_ebitda_based": result.get('fair_value_methods', {}).get('ev_ebitda', 0), "dividend_based": result.get('fair_value_methods', {}).get('dividend', 0) }, # Add method weights "method_weights": { "pe_weight": result.get('fair_value_method_weights', {}).get('pe', 0), "pb_weight": result.get('fair_value_method_weights', {}).get('pb', 0), "ps_weight": result.get('fair_value_method_weights', {}).get('ps', 0), "fcf_yield_weight": result.get('fair_value_method_weights', {}).get('fcf_yield', 0), "ev_ebitda_weight": result.get('fair_value_method_weights', {}).get('ev_ebitda', 0), "dividend_weight": result.get('fair_value_method_weights', {}).get('dividend', 0) }, # Add adjustments applied "adjustments": { "base_fair_value": result.get('fair_value_adjustments', {}).get('base_fair_value', 0), "quality_premium": result.get('fair_value_adjustments', {}).get('quality_premium', 0), "utility_bonus": result.get('fair_value_adjustments', {}).get('utility_bonus', 0), "country_penalty": result.get('fair_value_adjustments', {}).get('country_penalty', 0) } }, # Add algorithm parameters used "algorithm_parameters": { "scoring": { "growth_rate_original": result.get('score_parameters', {}).get('growth_rate_original', 0), "growth_rate_used": result.get('score_parameters', {}).get('growth_rate_used', 0), "is_growth_capped": result.get('score_parameters', {}).get('is_growth_capped', False) }, "valuation": { "graham_multiplier": result.get('fair_value_parameters', {}).get('graham_multiplier', 0), "is_mature_sector": result.get('fair_value_parameters', {}).get('is_mature_sector', False) }, "sector_flags": { "is_financial": ( result.get('score_parameters', {}).get('is_financial', False) or result.get('fair_value_parameters', {}).get('is_financial', False) ), "is_utility": ( result.get('score_parameters', {}).get('is_utility', False) or result.get('fair_value_parameters', {}).get('is_utility', False) ), "is_luxury": result.get('fair_value_parameters', {}).get('is_luxury', False), "is_auto_industrial": result.get('score_parameters', {}).get('is_auto_industrial', False) } }, "reasoning": result['ragionamento'] } # Verify all new sections present print("\nšŸ” Verifying new sections...") # Check score.breakdown if 'breakdown' in stock_entry['score']: print("āœ… score.breakdown present") breakdown = stock_entry['score']['breakdown'] print(f" • Valuation total: {breakdown['valuation']['total']}") print(f" • Quality total: {breakdown['quality']['total']}") print(f" • Growth total: {breakdown['growth']['total']}") print(f" • Bonuses total: {breakdown['bonuses']['total']}") print(f" • Penalties total: {breakdown['penalties']['total']}") else: print("āŒ score.breakdown MISSING") return False # Check valuation.fair_value_methods if 'fair_value_methods' in stock_entry['valuation']: print("āœ… valuation.fair_value_methods present") methods = stock_entry['valuation']['fair_value_methods'] print(f" • P/E based: €{methods['pe_based']:.2f}") print(f" • P/B based: €{methods['pb_based']:.2f}") print(f" • FCF Yield based: €{methods['fcf_yield_based']:.2f}") print(f" • EV/EBITDA based: €{methods['ev_ebitda_based']:.2f}") else: print("āŒ valuation.fair_value_methods MISSING") return False # Check algorithm_parameters if 'algorithm_parameters' in stock_entry: print("āœ… algorithm_parameters present") params = stock_entry['algorithm_parameters'] print(f" • Growth rate used: {params['scoring']['growth_rate_used']:.2f}%") print(f" • Graham multiplier: {params['valuation']['graham_multiplier']:.2f}x") print(f" • is_luxury: {params['sector_flags']['is_luxury']}") else: print("āŒ algorithm_parameters MISSING") return False # Write test JSON file test_json = { "test_stock": stock_entry, "schema_version": "4.1", "generated_at": datetime.now().isoformat() } with open('test_json_enhanced_output.json', 'w', encoding='utf-8') as f: json.dump(test_json, f, indent=2, ensure_ascii=False) print("\nšŸ“„ Test JSON written to: test_json_enhanced_output.json") print(f" File size: {Path('test_json_enhanced_output.json').stat().st_size} bytes") print("\n" + "=" * 80) print("āœ… ENHANCED JSON STRUCTURE TEST PASSED") print("=" * 80) return True except Exception as e: print(f"\nāŒ Error during test: {e}") import traceback traceback.print_exc() return False if __name__ == "__main__": success = test_json_enhanced() sys.exit(0 if success else 1)