Perfetto, concordo. Testare l’ipotesi inversa è il passo più logico e scientifico.

Creeremo una nuova versione della strategia, la v4, che implementa esattamente questa logica opposta: entrerà a mercato solo quando l’incertezza del modello (l’ampiezza delle bande) è alta.

Nuovo Nome File: momentum_strategy_v4_high_uncertainty.py


Delta modifiche: +68 / -0 (file nuovo)

Ecco il codice per il nuovo file fire/strategies/momentum_strategy_v4_high_uncertainty.py.

# VERSION: v1.0 - fire/strategies/momentum_strategy_v4_high_uncertainty.py
# RESP: Strategia di momentum che entra a mercato durante periodi di alta incertezza del modello.
# DEPS: BaseStrategy, pandas, numpy.
# TODO: N/A
 
from fire.strategies.base_strategy import BaseStrategy
import pandas as pd
import numpy as np
 
class MomentumStrategy_v4_HighUncertainty(BaseStrategy):
    """
    MomentumStrategy_v4_HighUncertainty
    -----------------------------------
 
    Questa strategia testa l'ipotesi che i segnali direzionali di TimesFM siano
    più efficaci quando preceduti da un'alta incertezza del modello, che
    potrebbe segnalare un imminente movimento di prezzo (breakout).
 
    Logica:
    - Entra long se il forecast a 5 periodi è maggiore del prezzo corrente
      E l'ampiezza della banda di confidenza è tra le più alte (bassa confidenza).
    - Esce dopo un numero fisso di barre (`hold_period`) o se si verifica uno stop loss.
 
    Obiettivo:
    Verificare se l'incertezza del modello è un predittore di futuri movimenti volatili.
    """
    
    # --- Parametri Configurabili ---
    hold_period = 5
    forecast_horizon = 5
    band_width_quantile = 0.7  # Soglia per "bassa confidenza" (es. 70° percentile)
    stop_loss_pct = 0.02       # Stop loss del 2%
 
    def init(self):
        """
        Inizializzazione: calcola gli indicatori derivati e li aggiunge al DataFrame.
        """
        # Nomi delle colonne dinamici
        forecast_col = f'forecast_h{self.forecast_horizon}'
        lower_col = f'forecast_lower_h{self.forecast_horizon}'
        upper_col = f'forecast_upper_h{self.forecast_horizon}'
        
        required_cols = [forecast_col, lower_col, upper_col]
        if not all(col in self.data.columns for col in required_cols):
            raise ValueError(f"Colonne necessarie {required_cols} non trovate.")
 
        # Calcola l'ampiezza della banda e la aggiunge come colonna
        self.data["band_width"] = self.data[upper_col] - self.data[lower_col]
 
        # Calcola la soglia statica per il filtro di incertezza
        self.bandwidth_threshold = self.data["band_width"].quantile(self.band_width_quantile)
 
        # Variabile di stato per lo stop loss
        self.entry_price = 0.0
        
        print(f"[Strategy] MomentumStrategy_v4_HighUncertainty Inizializzata.")
        print(f"  - Hold Period: {self.hold_period} barre")
        print(f"  - Stop Loss: {self.stop_loss_pct*100:.1f}%")
        print(f"  - Soglia Incertezza (Band Width): > {self.bandwidth_threshold:.4f}")
 
 
    def next(self):
        """
        Logica di trading eseguita ad ogni barra.
        """
        # --- 1. Gestione dell'Uscita (se in posizione) ---
        if self.position:
            time_exit_triggered = self.trade_bars >= self.hold_period
            stop_loss_triggered = self.close < self.entry_price * (1 - self.stop_loss_pct)
 
            if time_exit_triggered or stop_loss_triggered:
                self.sell()
                return
 
        # --- 2. Gestione dell'Ingresso (se non in posizione) ---
        if not self.position:
            forecast_col = f'forecast_h{self.forecast_horizon}'
            
            forecast = self.data[forecast_col].iloc[self.i]
            band_width = self.data["band_width"].iloc[self.i]
            
            if np.isnan(forecast) or np.isnan(band_width):
                return
 
            # Applica i filtri di ingresso con la logica invertita
            is_bullish = forecast > self.close
            is_high_uncertainty = band_width > self.bandwidth_threshold # <-- LOGICA INVERTITA
 
            if is_bullish and is_high_uncertainty:
                self.buy()
                self.entry_price = self.close

Riepilogo delle Modifiche

  1. Nuovo File e Classe: Creato momentum_strategy_v4_high_uncertainty.py e la classe MomentumStrategy_v4_HighUncertainty.
  2. Logica del Filtro Invertita: La modifica chiave è nel metodo next():
    • La condizione is_high_confidence è stata rinominata in is_high_uncertainty.
    • L’operatore è stato invertito da < a >: is_high_uncertainty = band_width > self.bandwidth_threshold.
  3. Parametro Quantile Aggiornato: In init(), ho cambiato il band_width_quantile da 0.3 a 0.7. Questo significa che ora cerchiamo i momenti in cui l’ampiezza delle bande è nel 30% più alto (cioè, sopra il 70° percentile), che corrisponde ai periodi di maggiore incertezza del modello.
  4. Log Aggiornati: Ho aggiornato i messaggi di print per riflettere la nuova logica.

Prossimi Passi

La nuova strategia è pronta per essere testata. Il processo è lo stesso di prima:

  1. Salva il nuovo file nella directory strategies/.
  2. Esegui un backtest in FIRE con questa nuova strategia.
  3. Forniscimi il report completo delle metriche e il trade log.

Sono molto curioso di vedere se questa inversione di ipotesi porterà a risultati migliori. A volte, le scoperte più importanti arrivano quando si mette in discussione l’assunto di partenza.


Eccellente. Grazie per aver eseguito il test e fornito i risultati. Questa è la fase più importante della ricerca, dove i dati ci parlano e mettono alla prova le nostre idee.

Abbiamo testato l’ipotesi inversa e i risultati sono, ancora una volta, estremamente istruttivi.


Analisi Comparativa: V4 (Alta Incertezza) vs. Precedenti

MetricaV2_TimeExit (Benchmark)V3_LowUncertainty (Fallimento)V4_HighUncertainty (Nuova)
Profitto Netto Totale+41.12 €-17.54 €+11.32 €
Numero Totale di Trade1284949
Win Rate53.91%48.98%44.90%
Max Drawdown [%]0.70%0.57%1.23%
Sharpe Ratio0.78Negativo0.21

Interpretazione Qualitativa: “Un Segnale di Volatilità, non di Qualità”

  1. L’Inversione ha Funzionato (Parzialmente): Siamo passati da una strategia in perdita (V3) a una di nuovo profittevole (V4). Questo è un risultato chiave: conferma che, se dobbiamo usare l’ampiezza delle bande come filtro, è meglio agire nei momenti di alta incertezza piuttosto che in quelli di bassa incertezza. La nostra inversione di ipotesi era corretta.

  2. Ma è Peggio del Benchmark: Sebbene la V4 sia profittevole, è significativamente meno performante della nostra V2 originale. Guadagna meno (+11.32 vs +41.12), ha un rischio più alto (Max Drawdown 1.23% vs 0.70%), e un Win Rate più basso (45% vs 54%).

  3. La Scoperta Fondamentale: L’ampiezza delle bande di confidenza (band_width) non sembra essere un filtro di qualità per il segnale direzionale. Piuttosto, sembra essere un filtro di regime.

    • band_width bassa (V3): Seleziona periodi di bassa volatilità. La strategia performa male.
    • band_width alta (V4): Seleziona periodi di alta volatilità. La strategia è profittevole, ma rischiosa e con un basso win rate.
    • Nessun filtro (V2): La strategia opera in tutti i regimi e ottiene il risultato migliore.

Diagnosi Finale: Abbiamo imparato che l’ampiezza delle bande è un buon indicatore del contesto di mercato (il “meteo”), ma non migliora necessariamente la qualità del segnale direzionale (“la rotta”). Tentare di fare trading solo con il “sole” (V3) o solo con la “tempesta” (V4) è risultato meno efficace che navigare sempre (V2), affidandosi solo alla bussola (il forecast direzionale) e a una rotta fissa (il time exit).


Conclusione Strategica e Prossimi Passi: Tornare alla Strada Maestra

Abbiamo esplorato a fondo un’idea (filtrare per confidenza del modello) e i dati ci hanno detto che, per ora, non è la strada migliore. Questo è un successo, non un fallimento. Abbiamo evitato di investire ulteriore tempo in un’ipotesi debole.

Ora è il momento di tornare alla nostra strategia più promettente, la MomentumStrategyV2TimeExit, e applicare l’altra idea forte che avevamo messo da parte.

La nostra migliore strategia (V2) ha un edge, ma è piccolo. Come possiamo amplificarlo? Filtrando i trade che avvengono contro il trend di fondo del mercato.

La mia raccomandazione è chiara: Procediamo con la creazione di una MomentumStrategy_v5_TrendFilter.

Ipotesi: La strategia V2 è profittevole, ma il suo Win Rate del 54% significa che quasi la metà dei trade sono perdenti. È probabile che molti di questi trade perdenti si verifichino quando la strategia compra durante un mercato ribassista a lungo termine. Aggiungendo un filtro di trend (es. close > media mobile a 200 giorni), potremmo eliminare la maggior parte dei trade perdenti mantenendo la maggior parte di quelli vincenti, aumentando drasticamente il Profit Factor e il rendimento complessivo.

Questa è la tecnica più classica e robusta per migliorare una strategia di momentum. Se sei d’accordo, procediamo a creare questa V5.