Report di Audit: Analisi del Bug di Visualizzazione nel Backtest del “Forecast Lab”

A: Kimi e Gruppo di Lavoro “FIRE” Da: [Il tuo nome] e Assistente AI Data: 11 Novembre 2025 Oggetto: Dossier investigativo sul malfunzionamento dei grafici di backtest a seguito del refactoring delle dipendenze.

1. Descrizione Dettagliata del Problema (La “Scena del Crimine”)

A seguito del successo della Fase 1 (stabilizzazione delle dipendenze), abbiamo identificato un bug di regressione critico nell’interfaccia dell’applicazione.

  • Azione Scatenante: L’utente naviga nella tab “Forecast Lab” “Model Backtester”, configura un backtest (es. con il modello TIMESFM) e preme il pulsante “Run Backtest”.
  • Comportamento Atteso: L’applicazione dovrebbe visualizzare un grafico interattivo che mostra:
    1. La serie storica reale del ticker selezionato (es. BC.MI).
    2. Le previsioni generate dal modello.
    3. Un grafico secondario con le metriche di errore (es. MAE).
  • Comportamento Osservato (Il Bug): L’applicazione visualizza un grafico visivamente corrotto, come mostrato nell’immagine allegata. Nello specifico:
    • La linea dei “Dati Reali” è una retta diagonale perfetta che parte da zero.
    • Le linee del “Forecast” sono piatte e costanti.
    • Il grafico dell’errore “MAE Over Time” è anch’esso una linea retta quasi perfetta.
  • Indizio Cruciale: Questo comportamento non era presente prima del refactoring delle dipendenze. Questo suggerisce fortemente che il problema non sia un errore nella logica di business, ma un effetto collaterale inatteso causato dall’aggiornamento di una delle librerie chiave (es. pandas, plotly).

Allegato 1: Evidenza visiva del bug

2. Metodologia di Indagine

Per identificare la causa radice, abbiamo adottato un approccio “follow the data”, mappando l’intero flusso di informazioni dal click dell’utente fino al rendering del grafico. Abbiamo trattato ogni componente software lungo questo percorso come un potenziale “colpevole” e lo abbiamo analizzato per trovare indizi.

3. Analisi dei Componenti (“I Sospettati”)

Abbiamo esaminato la catena di file responsabili della funzionalità, dal più profondo (il worker) al più esterno (l’orchestratore).

Sospettato #1: forecast_backtest_worker.py

  • Ruolo: Il “motore” che esegue i calcoli di backtest.
  • Motivo del Sospetto: Potrebbe generare dati corrotti.
  • Indizi Raccolti: Il codice del worker prende in input una pd.Series (full_series) e lavora correttamente su di essa. Utilizza .iloc per lo slicing e .to_numpy() per passare i dati ai modelli AI. Questa logica è corretta, ma è vulnerabile: se la full_series che riceve in input ha un indice numerico invece che un indice di date, tutto l’output (forecast, errori) erediterà questa corruzione.
  • Verdetto: Scagionato (come causa primaria). Il worker è una “vittima”: se riceve dati spazzatura, produce risultati spazzatura.

Sospettato #2: forecast_backtest_widget.py

  • Ruolo: La UI che contiene il pulsante “Run Backtest”.
  • Motivo del Sospetto: Potrebbe raccogliere parametri errati o manipolare i dati.
  • Indizi Raccolti: L’analisi del codice mostra che questo widget si limita a raccogliere i parametri dalla UI (nome del modello, ticker, date) e a emettere un segnale (backtest_requested) con questi parametri. Non carica né manipola direttamente i dati della serie storica.
  • Verdetto: Scagionato.

Sospettato #3: forecast_results_widget.py

  • Ruolo: La UI che riceve i risultati finali e li visualizza.
  • Motivo del Sospetto: Potrebbe interpretare male i dati ricevuti prima di passarli al componente grafico.
  • Indizi Raccolti: Il metodo _plot_forecast_vs_actuals contiene la riga fig.add_trace(go.Scatter(x=full_series.index, y=full_series)). Questa è la “pistola fumante” a livello di visualizzazione: conferma che per ottenere una retta diagonale, sia l’indice (x) che i valori (y) della full_series devono essere sequenze lineari di interi (es. [0, 1, 2, ...]). Il widget disegna fedelmente i dati corrotti che riceve.
  • Verdetto: Scagionato (come causa primaria). È il “testimone oculare” che ci mostra la natura della corruzione dei dati.

Sospettato #4: forecast_lab_widget.py

  • Ruolo: Il contenitore a schede che ospita i due widget precedenti.
  • Motivo del Sospetto: Potrebbe contenere la logica di orchestrazione che collega i componenti.
  • Indizi Raccolti: Il codice rivela che questo widget è un semplice contenitore di layout. Non ha logica di business, non si connette a segnali e non gestisce dati.
  • Verdetto: Scagionato.

Sospettato #5: main_window.py

  • Ruolo: L’orchestratore centrale dell’intera applicazione.
  • Motivo del Sospetto: È il candidato più probabile per ospitare la logica che collega il segnale backtest_requested al caricamento dei dati e all’avvio del worker.
  • Indizi Raccolti: L’analisi di questo file ha rivelato il punto esatto di fallimento. Il metodo on_forecast_backtest_requested contiene la seguente sequenza:
    1. Riceve i parametri dalla UI.
    2. Chiama self.data_manager.fetch_data(...) per ottenere un DataFrame (df).
    3. Estrae la serie con full_series = df['Close'].
    4. Passa full_series al ForecastBacktestWorker.

4. Conclusione dell’Audit: Identificazione della Causa Radice

L’indagine ha dimostrato che tutti i componenti della feature “Forecast Lab” funzionano come previsto. Il punto di rottura si trova a un livello superiore, in main_window.py.

La causa più probabile del bug è un cambiamento nel comportamento di pandas (o di una delle librerie di dati sottostanti) dopo il refactoring. La funzione data_manager.fetch_data ora restituisce un DataFrame il cui indice è un RangeIndex di default (0, 1, 2, …), mentre la colonna delle date è una colonna di dati normale.

Il codice in main_window.py non ne tiene conto. Quando esegue full_series = df['Close'], la nuova pd.Series eredita il RangeIndex del DataFrame. Questa serie corrotta (con valori di prezzo ma indice numerico) viene poi propagata lungo tutta la catena di elaborazione, portando alla fine a un grafico che plotta valori contro posizioni intere, risultando in una linea retta.

In sintesi, l’audit ha identificato un unico punto di fallimento: la mancata garanzia di un DatetimeIndex nel DataFrame prima della sua elaborazione nel metodo on_forecast_backtest_requested di main_window.py.