1- # run_weekly_report.py
1+ # run_weekly_report.py - Versione Semplificata
22"""
3- Entry Point: Report Settimanale Portfolio
4- =========================================
3+ Entry Point: Report Settimanale Portfolio (VERSIONE SEMPLIFICATA)
4+ ================================================================
55
6- Genera report settimanale completo usando il RiskManager refactored.
7- Schedulato per esecuzione automatica ogni venerdì.
6+ Workflow semplice e diretto:
7+ 1. Genera segnali da tutte le strategie disponibili
8+ 2. Applica risk management per validare/raffinare i segnali
9+ 3. Apre Google Sheet esistente nel drive
10+ 4. Pulisce e scrive N+1 fogli:
11+ - 1 foglio per strategia con segnali validati
12+ - 1 foglio con snapshot portfolio della settimana
13+ 5. Fine!
814
9- NUOVO WORKFLOW:
10- 1. Carica portfolio "demo"
11- 2. Genera segnali da tutte le strategie (moving_average, rsi, breakout)
12- 3. Valida segnali con risk manager (position sizing, cash limits, concentration)
13- 4. Scrive Google Sheets organizzati:
14- - Portfolio_Overview: Metriche aggregate + storico
15- - Active_Positions: Dettaglio posizioni correnti
16- - Strategy_*: Segnali per ogni strategia con validazione
17- - Execution_Summary: Dashboard per decisioni weekend
18-
19- OUTPUT: Google Sheet aggiornato pronto per review weekend
15+ Niente classi complesse, solo funzioni dirette.
2016"""
2117
2218import logging
2319import os
2420import sys
25- from datetime import datetime
26-
27- # Assicura che la cartella logs/ esista
28- os .makedirs ("logs" , exist_ok = True )
21+ from datetime import datetime , timedelta
22+ import pandas as pd
2923
3024# Setup logging
25+ os .makedirs ("logs" , exist_ok = True )
3126logging .basicConfig (
3227 level = logging .INFO ,
3328 format = "%(asctime)s [%(levelname)s] %(name)s: %(message)s" ,
3429 handlers = [
3530 logging .FileHandler ("logs/run_weekly_report.log" , encoding = 'utf-8' ),
36- logging .StreamHandler (sys .stdout ) # Aggiungi anche output console
31+ logging .StreamHandler (sys .stdout )
3732 ]
3833)
3934
4035logger = logging .getLogger (__name__ )
4136
4237
4338def main ():
44- """Entry point principale per generazione report settimanale."""
45- pass
46- #start_time = datetime.now()
47- #logger.info("=" * 60)
48- #logger.info("🚀 AVVIO GENERAZIONE REPORT SETTIMANALE")
49- #logger.info("=" * 60)
50- #logger.info(f"Timestamp: {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
51- #
52- #try:
53- # # Import dopo setup logging per catturare eventuali errori di import
54- # from scripts.reports import generate_weekly_report
55- #
56- # # Genera report per portfolio demo
57- # result = generate_weekly_report(
58- # portfolio_name="demo",
59- # date=None # Usa data corrente
60- # )
61- #
62- # if result['success']:
63- # # SUCCESS
64- # duration = datetime.now() - start_time
65- # logger.info("=" * 60)
66- # logger.info("✅ REPORT GENERATO CON SUCCESSO!")
67- # logger.info("=" * 60)
68- # logger.info(f"Portfolio: {result['portfolio_name']}")
69- # logger.info(f"Data: {result['date']}")
70- # logger.info(f"Fogli scritti: {', '.join(result['sheets_written'])}")
71- # logger.info(f"Segnali totali: {result['total_signals']}")
72- # logger.info(f"Ordini approvati: {result['approved_orders']}")
73- # logger.info(f"Valore portfolio: €{result['portfolio_summary']['Total_Value']:,.2f}")
74- # logger.info(f"Cash disponibile: €{result['portfolio_summary']['Cash_Balance']:,.2f}")
75- # logger.info(f"Posizioni attive: {result['portfolio_summary']['Positions_Count']}")
76- # logger.info(f"Durata esecuzione: {duration.total_seconds():.1f} secondi")
77- # logger.info("=" * 60)
78- # logger.info("📊 REPORT PRONTO PER REVIEW WEEKEND")
79- # logger.info("=" * 60)
80- #
81- # else:
82- # # ERROR
83- # logger.error("=" * 60)
84- # logger.error("❌ ERRORE NELLA GENERAZIONE DEL REPORT")
85- # logger.error("=" * 60)
86- # logger.error(f"Errore: {result.get('error', 'Errore sconosciuto')}")
87- # logger.error("=" * 60)
88- # raise Exception(f"Report generation failed: {result.get('error')}")
89- #
90- #except ImportError as e:
91- # logger.error(f"❌ Errore import moduli: {e}")
92- # logger.error("Verificare che tutti i moduli siano disponibili")
93- # raise
94- #
95- #except Exception as e:
96- # logger.exception(f"❌ Errore critico durante la generazione del report: {e}")
97- # raise
98- #
39+ """Entry point per generazione report settimanale semplificato."""
40+ start_time = datetime .now ()
41+ today = start_time .strftime ("%Y-%m-%d" )
42+
43+ logger .info ("=" * 60 )
44+ logger .info ("🚀 AVVIO REPORT SETTIMANALE SEMPLIFICATO" )
45+ logger .info ("=" * 60 )
46+
47+ try :
48+ # Import dopo logging setup
49+ from scripts import database , portfolio , strategies , risk_manager , google_services
50+ from scripts import config
51+
52+ # 1. CARICA PORTFOLIO
53+ logger .info ("📊 Caricamento portfolio 'demo'..." )
54+ pf = portfolio .Portfolio ("demo" , today )
55+ logger .info (f"Portfolio caricato: valore={ pf .get_total_value ():,.2f} €, cash={ pf .get_cash_balance ():,.2f} €" )
56+
57+ # 2. CARICA DATI UNIVERSE (ultima settimana)
58+ week_ago = (datetime .now () - timedelta (days = 7 )).strftime ("%Y-%m-%d" )
59+ logger .info (f"📈 Caricamento dati universe dal { week_ago } al { today } ..." )
60+ df = database .get_universe_data (start_date = week_ago , end_date = today )
61+ logger .info (f"Dati caricati: { len (df )} righe, { df ['ticker' ].nunique ()} ticker" )
62+
63+ # 3. GENERA SEGNALI PER OGNI STRATEGIA
64+ strategies_list = [
65+ ("Moving_Average" , strategies .moving_average_crossover ),
66+ ("RSI_Strategy" , strategies .rsi_strategy ),
67+ ("Breakout_Strategy" , strategies .breakout_strategy )
68+ ]
69+
70+ signals_data = {}
71+ for strategy_name , strategy_fn in strategies_list :
72+ logger .info (f"🎯 Generazione segnali: { strategy_name } " )
73+
74+ # Genera e raffina segnali
75+ signals = risk_manager .generate_signals (strategy_fn , df , today , pf )
76+
77+ # Converti in DataFrame per Google Sheets
78+ signals_df = _convert_signals_to_dataframe (signals , strategy_name )
79+ signals_data [strategy_name ] = signals_df
80+
81+ buy_count = len (signals .get ("BUY" , {}))
82+ sell_count = len (signals .get ("SELL" , {}))
83+ hold_count = len (signals .get ("HOLD" , {}))
84+ logger .info (f"Segnali { strategy_name } : BUY={ buy_count } , SELL={ sell_count } , HOLD={ hold_count } " )
85+
86+ # 4. GENERA SNAPSHOT PORTFOLIO SETTIMANALE
87+ logger .info ("📸 Generazione snapshot portfolio settimanale..." )
88+ portfolio_df = _generate_portfolio_snapshots (pf , week_ago , today )
89+
90+ # 5. SCRIVI GOOGLE SHEETS
91+ logger .info ("📝 Scrittura Google Sheets..." )
92+ sheet_url = _write_to_google_sheets (signals_data , portfolio_df , today )
93+
94+ # SUCCESS
95+ duration = datetime .now () - start_time
96+ logger .info ("=" * 60 )
97+ logger .info ("✅ REPORT GENERATO CON SUCCESSO!" )
98+ logger .info ("=" * 60 )
99+ logger .info (f"Durata: { duration .total_seconds ():.1f} secondi" )
100+ logger .info (f"Google Sheet: { sheet_url } " )
101+ logger .info ("📊 REPORT PRONTO PER REVIEW!" )
102+ logger .info ("=" * 60 )
103+
104+ except Exception as e :
105+ logger .exception (f"❌ Errore critico: { e } " )
106+ raise
107+
108+
109+ def _convert_signals_to_dataframe (signals : dict , strategy_name : str ) -> pd .DataFrame :
110+ """Converte dict segnali in DataFrame per Google Sheets."""
111+ rows = []
112+
113+ # BUY signals
114+ for ticker , data in signals .get ("BUY" , {}).items ():
115+ rows .append ({
116+ "Ticker" : ticker ,
117+ "Signal" : "BUY" ,
118+ "Strategy" : strategy_name ,
119+ "Size" : data .get ("size" , 0 ),
120+ "Price" : data .get ("price" , 0 ),
121+ "Stop_Loss" : data .get ("stop" , 0 ),
122+ "Risk_Amount" : data .get ("risk" , 0 ),
123+ "Notes" : f"Size: { data .get ('size' , 0 )} shares"
124+ })
125+
126+ # SELL signals
127+ for ticker , data in signals .get ("SELL" , {}).items ():
128+ rows .append ({
129+ "Ticker" : ticker ,
130+ "Signal" : "SELL" ,
131+ "Strategy" : strategy_name ,
132+ "Size" : data .get ("quantity" , 0 ),
133+ "Price" : data .get ("price" , 0 ),
134+ "Stop_Loss" : "" ,
135+ "Risk_Amount" : "" ,
136+ "Notes" : data .get ("reason" , "Strategy sell" )
137+ })
138+
139+ # HOLD signals
140+ for ticker , data in signals .get ("HOLD" , {}).items ():
141+ rows .append ({
142+ "Ticker" : ticker ,
143+ "Signal" : "HOLD" ,
144+ "Strategy" : strategy_name ,
145+ "Size" : "" ,
146+ "Price" : "" ,
147+ "Stop_Loss" : "" ,
148+ "Risk_Amount" : "" ,
149+ "Notes" : data .get ("reason" , "Keep position" )
150+ })
151+
152+ return pd .DataFrame (rows )
153+
154+
155+ def _generate_portfolio_snapshots (pf , start_date : str , end_date : str ) -> pd .DataFrame :
156+ """Genera snapshots portfolio per la settimana."""
157+ from scripts import database
158+
159+ # Query snapshot della settimana
160+ query = """
161+ SELECT date, total_value, cash_balance, positions_count,
162+ total_return_pct, max_drawdown_pct, volatility_pct, sharpe_ratio
163+ FROM portfolio_snapshots
164+ WHERE portfolio_name = %s AND date BETWEEN %s AND %s
165+ ORDER BY date
166+ """
167+
168+ rows , columns = database .execute_query (query , (pf .name , start_date , end_date ))
169+ df = pd .DataFrame (rows , columns = columns )
170+
171+ # Converti date in stringhe per Google Sheets
172+ if not df .empty and 'date' in df .columns :
173+ df ['date' ] = df ['date' ].astype (str )
174+
175+ return df
176+
177+
178+ def _write_to_google_sheets (signals_data : dict , portfolio_df : pd .DataFrame , date : str ) -> str :
179+ """Scrive tutti i dati su Google Sheets e ritorna URL."""
180+ from scripts import google_services , config
181+
182+ client = google_services .get_gsheet_client ()
183+
184+ # Apri/crea spreadsheet per la settimana
185+ sheet_name = f"Weekly_Report_{ date } "
186+
187+ try :
188+ # Prova ad aprire sheet esistente nella cartella
189+ # (questo richiede di implementare una funzione per cercare nella cartella)
190+ # Per ora creiamo sempre nuovo sheet
191+ spreadsheet = client .create (sheet_name )
192+
193+ # Sposta nella cartella corretta se definita
194+ if hasattr (config , 'WEEKLY_REPORTS_FOLDER_ID' ):
195+ # Codice per spostare il file nella cartella...
196+ pass
197+
198+ except Exception as e :
199+ logger .warning (f"Creazione nuovo sheet fallita: { e } " )
200+ # Fallback: usa sheet di test
201+ spreadsheet = client .open_by_key (config .TEST_SHEET_ID )
202+
203+ # Pulisci tutti i worksheet esistenti (tranne il primo)
204+ worksheets = spreadsheet .worksheets ()
205+ for ws in worksheets [1 :]: # Mantieni il primo
206+ spreadsheet .del_worksheet (ws )
207+
208+ # Rinomina il primo worksheet
209+ main_ws = worksheets [0 ]
210+ main_ws .update_title ("Portfolio_Snapshot" )
211+
212+ # Scrivi snapshot portfolio
213+ if not portfolio_df .empty :
214+ main_ws .update ([portfolio_df .columns .tolist ()] + portfolio_df .values .tolist ())
215+
216+ # Crea worksheet per ogni strategia
217+ for strategy_name , signals_df in signals_data .items ():
218+ ws = spreadsheet .add_worksheet (title = strategy_name , rows = 100 , cols = 10 )
219+ if not signals_df .empty :
220+ ws .update ([signals_df .columns .tolist ()] + signals_df .values .tolist ())
221+
222+ logger .info (f"Google Sheet scritto: { spreadsheet .title } " )
223+ return spreadsheet .url
224+
99225
100226if __name__ == "__main__" :
101227 main ()
0 commit comments