Perfetto, procediamo.
Questa è un’evoluzione logica e molto promettente. Stiamo prendendo la nostra strategia migliore (V2) e le stiamo applicando un filtro classico e robusto per migliorarne la selettività. L’ipotesi è solida: se il nostro “edge” di breve termine funziona, dovrebbe funzionare ancora meglio quando è allineato con la marea del mercato a lungo termine.
Creeremo la nuova strategia, MomentumStrategy_v5_TrendFilter, in un nuovo file per mantenere la nostra cronologia di ricerca.
Delta modifiche: +62 / -0 (file nuovo)
Ecco il codice per il nuovo file fire/strategies/momentum_strategy_v5_trend_filter.py.
# VERSION: v1.0 - fire/strategies/momentum_strategy_v5_trend_filter.py
# RESP: Strategia di momentum che filtra i segnali di forecast in base al trend di lungo periodo.
# DEPS: BaseStrategy, pandas, numpy.
# TODO: N/A
from fire.strategies.base_strategy import BaseStrategy
import pandas as pd
import numpy as np
class MomentumStrategy_v5_TrendFilter(BaseStrategy):
"""
MomentumStrategy_v5_TrendFilter
-----------------------------------
Questa strategia migliora la V2 aggiungendo un filtro di trend a lungo termine.
L'ipotesi è di migliorare il Win Rate e il Profit Factor evitando i segnali
di acquisto durante i mercati ribassisti.
Logica di Ingresso:
- Entra long se il forecast a 5 periodi è maggiore del prezzo corrente
E il prezzo corrente è al di sopra della sua media mobile semplice a 200 periodi.
Logica di Uscita:
- Esce dopo un numero fisso di barre (`hold_period`).
"""
# --- Parametri Configurabili ---
hold_period = 5
forecast_horizon = 5
trend_filter_period = 200 # Periodo per la media mobile del trend
def init(self):
"""
Inizializzazione: calcola gli indicatori necessari.
"""
forecast_col = f'forecast_h{self.forecast_horizon}'
if forecast_col not in self.data.columns:
raise ValueError(f"La colonna '{forecast_col}' non è presente nei dati.")
# --- MODIFICA: Calcolo del filtro di trend ---
# Utilizziamo il sistema di indicatori integrato per calcolare la SMA
# e la aggiungiamo come una nuova colonna al nostro DataFrame.
try:
sma_series = self.indicators.SMA(period=self.trend_filter_period)
self.data[f'sma_{self.trend_filter_period}'] = sma_series
except AttributeError:
# Fallback se l'indicatore SMA non fosse disponibile
raise ImportError("Indicatore 'SMA' non trovato. Assicurarsi che sia registrato nell'Analytics Core.")
# --- FINE MODIFICA ---
print(f"[Strategy] MomentumStrategy_v5_TrendFilter Inizializzata.")
print(f" - Hold Period: {self.hold_period} barre")
print(f" - Filtro Trend: SMA({self.trend_filter_period})")
def next(self):
"""
Logica di trading eseguita ad ogni barra.
"""
# --- 1. Gestione dell'Uscita ---
if self.position:
if self.trade_bars >= self.hold_period:
self.sell()
return
# --- 2. Gestione dell'Ingresso ---
if not self.position:
forecast_col = f'forecast_h{self.forecast_horizon}'
sma_col = f'sma_{self.trend_filter_period}'
# Recupera i valori per la barra corrente
forecast = self.data[forecast_col].iloc[self.i]
sma_value = self.data[sma_col].iloc[self.i]
if np.isnan(forecast) or np.isnan(sma_value):
return
# --- MODIFICA: Aggiunta del filtro di trend alla condizione di ingresso ---
is_bullish_forecast = forecast > self.close
is_uptrend = self.close > sma_value
if is_bullish_forecast and is_uptrend:
self.buy()
# --- FINE MODIFICA ---Riepilogo delle Modifiche
- Nuovo File e Classe: Creato
momentum_strategy_v5_trend_filter.pycon la classeMomentumStrategy_v5_TrendFilter. - Calcolo del Filtro di Trend: In
init(), ho usato il nostro sistema di indicatori (self.indicators.SMA(...)) per calcolare la media mobile a 200 periodi. Il risultato, unapandas.Series, viene aggiunto come una nuova colonna al DataFrameself.data. Questo mantiene la nostra architettura a pre-calcolo pulita e coerente. - Nuova Logica di Ingresso: Nel metodo
next(), la condizione di ingresso ora è un doppio filtro:is_bullish_forecast: La previsione di TimesFM deve essere rialzista (il nostro “edge” a breve termine).is_uptrend: Il prezzo deve essere sopra la sua media mobile a 200 periodi (il “vento a favore” a lungo termine). Un’operazione di acquisto viene eseguita solo se entrambe le condizioni sono vere.
- Logica di Uscita Invariata: La logica di uscita rimane il nostro robusto
time exitdi 5 periodi, poiché si è già dimostrata efficace nel gestire il rischio.
Prossimi Passi
La strategia è pronta per il test finale di questo ciclo di ricerca. Il processo è quello che ormai conosciamo bene:
- Salva il nuovo file nella directory
strategies/. - Esegui un backtest in FIRE con la strategia
MomentumStrategy_v5_TrendFilter. - Forniscimi il report completo delle metriche e il trade log.
Sono estremamente curioso di vedere i risultati. Se la nostra ipotesi è corretta, dovremmo vedere un miglioramento significativo del Win Rate e, di conseguenza, della profittabilità complessiva.
Perfetto, grazie per il feedback. “Nessun trade” è di per sé un risultato estremamente significativo.
Questo risultato inatteso ci dice che c’è un problema, ma non necessariamente un bug nel codice. È più probabile che sia un problema nella logica della strategia o nei dati. Analizziamo le possibili cause, partendo dalla più probabile.
Analisi delle Possibili Cause
Causa 1 (Molto Probabile): Filtro Troppo Restrittivo / Condizioni Mai Soddisfatte
Il nostro segnale di ingresso ora richiede che due condizioni siano vere contemporaneamente:
forecast > close(segnale a breve termine)close > SMA(200)(segnale a lungo termine)
Il fatto che non ci siano trade significa che non c’è mai stata una singola candela nell’intero backtest in cui entrambe queste condizioni fossero vere.
- Perché potrebbe succedere?
- Decorelazione dei segnali: È possibile che il segnale di forecast a breve termine di TimesFM tenda a diventare rialzista proprio quando il prezzo è sceso temporaneamente sotto la sua media a 200 giorni (ad esempio, durante un pullback in un trend rialzista). In questo caso, i due segnali sarebbero quasi mutuamente esclusivi. Il forecast dice “compra il dip”, ma il filtro di trend dice “non comprare finché non risale sopra la media”.
- Periodo di Test Sfortunato: Se gran parte del periodo di backtest era in un mercato ribassista (
close < SMA(200)), allora il filtro di trend avrebbe bloccato la maggior parte delle opportunità, e in quei pochi periodi di rialzo, forse il segnale del forecast non si è mai attivato.
Causa 2 (Meno Probabile, ma da Verificare): Problema nei Dati Pre-calcolati
Potrebbe esserci un problema nel modo in cui calcoliamo o accediamo ai dati, che porta una delle condizioni ad essere sempre False.
- SMA(200) Piena di
NaN: La media mobile a 200 periodi ha bisogno di 200 candele per “scaldarsi”. Le prime 199 candele del backtest avranno un valoreNaNper la SMA. Il nostro codice gestisce questo (if np.isnan(sma_value): return), ma se il backtest fosse su dati molto brevi, questo potrebbe essere un fattore. Dato il periodo che stiamo usando, è improbabile che sia questo il problema principale. - Problema nell’Accesso ai Dati: Un errore di battitura nel nome della colonna o un errore nel calcolo dell’indicatore. Il nostro
try-exceptininit()dovrebbe aver catturato problemi di calcolo, ma vale la pena controllare.
Diagnosi e Come Verificare
La mia ipotesi principale è la Causa 1. I nostri filtri sono troppo “severi” e non si allineano mai.
Per validarlo, dobbiamo “debuggare” la strategia. Non possiamo mettere un breakpoint, ma possiamo usare i log per “vedere” cosa pensa la strategia a ogni candela.
Piano d’Azione: Creare una Versione di Debug della Strategia
Propongo di modificare temporaneamente la strategia V5 per aggiungere dei log che ci mostrino lo stato di ogni filtro a ogni candela. Questo ci darà la prova definitiva del perché non viene mai eseguito un trade.
Modifiche Proposte:
- Creeremo una
MomentumStrategy_v5_TrendFilter_DEBUG. - Nel metodo
next(), prima di controllare le condizioni di ingresso, stamperemo lo stato di ogni filtro. Ad esempio:print(f"Tick {self.i}: Forecast > Close? {is_bullish_forecast}. Close > SMA(200)? {is_uptrend}.") - Eseguiremo il backtest con questa versione di debug. I log ci diranno se entrambe le condizioni non sono mai
Truecontemporaneamente.
Questo approccio è scientifico e ci darà i dati necessari per decidere come procedere (es. allentare i filtri, cambiare il tipo di filtro, etc.).
Se sei d’accordo, procedo a scrivere la versione di debug della strategia.