bias di forward looking backtest
Il bias di forward-looking infetta i backtest quando dati futuri filtrano nelle decisioni. Scopri come same bar, sopravvivenza e dati rivisti gonfiano i rendimenti, e come individuarlo.
Il bias di forward-looking è l'uso, all'interno di un backtest, di informazioni che nessuno poteva possedere nel momento in cui il trade simulato è stato eseguito. La curva del capitale sale, le statistiche sembrano eccellenti, ma nulla di tutto ciò sopravvive al contatto con un ordine reale. Questa pagina definisce il bias, illustra tre meccanismi che lo producono e misura ciascuno di essi su dati di mercato reali.
Cos'è il bias di forward-looking
Un backtest è un'affermazione su una decisione: a questo timestamp, con queste informazioni, la regola avrebbe fatto questo. L'affermazione è valida solo quando ogni input letto dalla regola esisteva già, nella sua forma finale, prima di quel timestamp. Il bias di forward-looking è qualsiasi violazione di questa condizione, ed è silenzioso. Niente, in un foglio di calcolo o in uno script, si lamenta quando una riga restituisce un valore timbrato dopo la decisione che alimenta.
Tre violazioni coprono la maggior parte di ciò che si incontra nella pratica. Una decisione che legge una barra di prezzo all'interno della quale opera anche. Un universo composto dai nomi che sono ancora quotati oggi. Un campo dati che è stato rivisto dopo la data che porta.
Meccanismo uno: decidere su una barra che si opera anche
Iniziamo con una regola che sembra innocua. Compra sulla forza, ovvero nelle sessioni in cui il fondo chiude al di sopra dell'apertura. Se codificata con noncuranza, il test entra all'apertura di quella sessione ed esce alla chiusura di quella sessione, mentre la condizione su cui fa lo screening è il prezzo di chiusura della stessa barra. Nessuno conosce un prezzo di chiusura alla campana di apertura.
Il pannello seguente esegue entrambe le versioni su SPY, il tracker dell'S&P 500, su dieci anni solari. La prima colonna è quella impossibile. La seconda ritarda la condizione identica di una singola sessione, che è il prima possibile in cui un account finanziato potrebbe agire su di essa.
L'esatto SQL dietro ogni numero
WITH bars AS (
SELECT date,
toFloat64(open) AS o,
toFloat64(close) AS c,
toFloat64(close) > toFloat64(open) AS up_day
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2015-12-01')
AND date <= toDate('2025-12-31')
),
lagged AS (
SELECT date, o, c, up_day,
any(up_day) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prior_up
FROM bars
)
SELECT toYear(date) AS year,
countIf(up_day) AS signal_days,
round(100 * avgIf(c / o - 1, up_day), 3) AS same_bar_avg_pct,
round(100 * avgIf(c / o - 1, prior_up), 3) AS lagged_avg_pct,
round(100 * (avgIf(c / o - 1, up_day) - avgIf(c / o - 1, prior_up)), 3) AS gap_pp
FROM lagged
WHERE date >= toDate('2016-01-01')
GROUP BY year
ORDER BY yearLa separazione non è sottile. In 2016 la versione con la stessa barra ha mediato 0.447% per sessione qualificante, mentre la versione ritardata ha mediato -0.008%, uno spread di 0.455 punti percentuali al giorno. In 2025 le due leggono 0.636% e -0.03%. Con circa 140 sessioni qualificanti in un anno, uno spread di mezzo punto percentuale per sessione si compone in una curva del capitale che nessun conto reale ha mai stampato.
L'intero vantaggio è definitorio. Selezionare sessioni che hanno chiuso al di sopra della loro apertura, e poi misurare l'apertura-chiusura su quelle stesse sessioni, misura una quantità garantita positiva. La regola ha scoperto il proprio filtro.
Le varianti di questo si nascondono sempre nei dati giornalieri: una media mobile che include la chiusura di oggi e viene utilizzata per fare trading oggi, un segnale calcolato su prezzi già aggiustati per uno split che non era ancora avvenuto, uno stop intraday piazzato a un massimo di sessione che non si era ancora stampato. La forma comune è un timestamp decisionale che si trova prima di uno dei suoi stessi input.
Meccanismo due: un universo tratto dai sopravvissuti di oggi
Il secondo meccanismo non tocca mai la regola. Risiede nell'elenco dei nomi che la regola è autorizzata a vedere.
Preleva un elenco di ticker da un fornitore di dati attuale, eseguilo indietro di dieci anni, e il campione esclude silenziosamente ogni azienda che è fallita, è stata acquisita o è stata delistata lungo il percorso. Alla regola non sono mai stati offerti i perdenti. Il pannello misura l'entità di questa omissione: ogni nome quotato negli Stati Uniti che ha scambiato almeno 200 sessioni sopra $1 in un dato anno solare, la quota di quella coorte che sta ancora stampando prezzi a luglio 2026, e il rendimento mediano dell'anno solare dell'intera coorte accanto al rendimento mediano dei soli sopravvissuti.
L'esatto SQL dietro ogni numero
WITH recent AS (
SELECT DISTINCT ticker
FROM global_markets.stocks_daily_aggs
WHERE date >= toDate('2026-06-15')
AND date <= toDate('2026-07-28')
),
per_name AS (
SELECT toYear(date) AS year,
ticker,
argMin(toFloat64(close), date) AS first_close,
argMax(toFloat64(close), date) AS last_close,
count() AS sessions
FROM global_markets.stocks_daily_aggs
WHERE date >= toDate('2015-01-01')
AND date <= toDate('2024-12-31')
AND close > 1
GROUP BY year, ticker
HAVING sessions >= 200
)
SELECT year,
uniqExact(ticker) AS names_trading,
round(100 * uniqExactIf(ticker, ticker IN (SELECT ticker FROM recent)) / uniqExact(ticker), 1) AS still_listed_pct,
round(100 * quantileDeterministic(0.5)(last_close / first_close - 1, cityHash64(ticker)), 2) AS median_return_all_pct,
round(100 * quantileDeterministicIf(0.5)(last_close / first_close - 1, cityHash64(ticker), ticker IN (SELECT ticker FROM recent)), 2) AS median_return_survivors_pct,
round(100 * (quantileDeterministicIf(0.5)(last_close / first_close - 1, cityHash64(ticker), ticker IN (SELECT ticker FROM recent))
- quantileDeterministic(0.5)(last_close / first_close - 1, cityHash64(ticker))), 2) AS survivor_gap_pp
FROM per_name
GROUP BY year
ORDER BY yearDegli 7063 nomi che hanno superato la barriera in 2015, 52.5% erano ancora in negoziazione a luglio 2026. Per la coorte 2024 la cifra è 87.5%, in gran parte una questione di tempo trascorso: un nome del 2024 ha avuto due anni per scomparire e un nome del 2015 ne ha avuti undici.
Le colonne dei rendimenti portano il bias stesso. In 2017 il nome mediano ha reso 7.51% mentre il sopravvissuto mediano ha reso 11.32%, un divario di 3.81 punti percentuali regalati. Il divario è positivo in ciascuno degli 10 anni rappresentati. Una strategia testata sui sopravvissuti eredita questo vantaggio prima ancora di prendere una singola decisione, e le regole che fanno screening per la debolezza ne ereditano più della mediana, poiché i nomi che sono scomparsi si trovano in modo sproporzionato in quel gruppo.
La correzione è un universo point-in-time: l'elenco dei nomi così com'era a ciascuna data di ribilanciamento, compresi i delisting, con una posizione delistata chiusa al suo ultimo prezzo stampato anziché rimossa dalla registrazione.
Meccanismo tre: dati che sono stati rivisti dopo il loro timestamp
I prezzi vengono timbrati una volta. I fondamentali no. Una cifra trimestrale porta la data di fine trimestre, viene modificata da un deposito successivo, e un database che memorizza solo la versione corrente servirà volentieri la cifra modificata con la data originale. Una regola che fa screening su quella cifra sta leggendo una correzione che ha impiegato mesi per arrivare.
Lo stesso schema attraversa gli elenchi dei membri degli indici ricostruiti con i costituenti attuali, le storie delle stime degli analisti sovrascritte con il consenso finale, e gli aggiustamenti per azioni societarie applicati a ritroso su un'intera serie di prezzi. In ogni caso, il record memorizzato è un'istantanea al presente che indossa una data al passato.
Due pratiche lo contengono. Usa una fonte che mantiene le annate, ovvero il valore così com'era noto a ciascuna data, piuttosto che il valore così com'è noto ora. Laddove le annate non siano disponibili, applica un ritardo di rendicontazione sufficientemente generoso da coprire quello reale, e tratta la lunghezza di quel ritardo come un parametro che vale la pena testare, piuttosto che un dettaglio da indovinare.
Che aspetto ha la curva quando il futuro filtra
Il bias di forward-looking ha una firma: una performance che si colloca molto al di sopra di ciò che il mercato sottostante ha offerto. Un punto di riferimento utile è il soffitto del senno di poi, la crescita che una previsione perfetta con un giorno di anticipo avrebbe prodotto su un singolo strumento.
L'esatto SQL dietro ogni numero
WITH bars AS (
SELECT date,
toFloat64(close) AS c,
any(toFloat64(close)) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_c
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2015-12-01')
AND date <= toDate('2025-12-31')
),
rets AS (
SELECT toYear(date) AS year,
c / prev_c - 1 AS r
FROM bars
WHERE date >= toDate('2016-01-01')
AND prev_c > 0
)
SELECT year,
count() AS sessions,
round(100 * (exp(sum(log(1 + r))) - 1), 1) AS buy_and_hold_pct,
round(100 * (exp(sumIf(log(1 + r), r < 0.02)) - 1), 1) AS without_big_up_days_pct,
round(exp(sum(log(1 + abs(r)))), 1) AS hindsight_ceiling_multiple
FROM rets
GROUP BY year
ORDER BY yearIl soffitto è enorme. In 2020, la perfetta previsione a un giorno su SPY ha moltiplicato il capitale 27.7 volte in 253 sessioni, contro 16.2% per la semplice detenzione del fondo. Nell'anno più calmo dei dieci, 2017, la stessa previsione perfetta ha restituito 2.2 volte il capitale.
La colonna centrale mostra quanto sia concentrato quel soffitto. Rimuovi le sessioni che hanno guadagnato più del 2%, e 2020 passa da 16.2% a -44.9%. Una perdita che vale solo una manciata di quelle sessioni all'anno sposta un backtest molto lontano. Una curva con drawdown poco profondi, un alto tasso di successo, e i suoi giorni migliori raggruppati nelle sessioni più violente del mercato merita un audit.
La calibrazione conta più di qualsiasi singola soglia. Una regola che rivendica un liscio 40% annuo per un decennio sta rivendicando una grande frazione del soffitto del senno di poi, e la domanda onesta è quale input le ha detto cosa stava per accadere.
Come testare un backtest per il bias di forward-looking
- Ritarda tutto di una barra. Sposta ogni segnale di un intero periodo più avanti e riesegui. I veri vantaggi decadono leggermente. Le perdite collassano, spesso a zero.
- Perturba il futuro. Prendi i dati dopo ogni timestamp decisionale, sostituiscili con rumore o un rimescolamento, e riesegui la logica decisionale. Ogni decisione dovrebbe risultare identica. Qualsiasi decisione che si muove ha letto qualcosa che non poteva sapere.
- Tratta i timestamp come dati. Per ogni input, memorizza il momento in cui un valore è diventato disponibile accanto al momento che descrive. Una join che non confronta mai queste due colonne non può far rispettare la regola.
- Ricostruisci l'universo alla data. Includi nomi delistati e acquisiti, chiudili al loro ultimo prezzo stampato, ed esegui la stessa regola su entrambi gli universi per quantificare il contributo della sopravvivenza.
- Misura rispetto al soffitto. Confronta il rendimento della strategia con una cifra di buy and hold e una cifra di perfetta previsione per la stessa finestra. La distanza dal soffitto è un contesto che un solo Sharpe ratio non fornisce.
Il dimensionamento della posizione è a valle di tutto questo. Un tasso di vincita gonfiato alimenta una dimensione della scommessa gonfiata, che è dove il dimensionamento della posizione con il criterio di Kelly trasforma un errore contabile in un drawdown. La stessa disciplina si applica prima che il capitale reale si muova: il paper trading prima del denaro reale testa i meccanismi degli ordini, e gli stack automatizzati portano lo stesso identico pericolo alla velocità della macchina, trattato in sistemi di trading AI multi agente. Gli effetti che sopravvivono a un attento test point-in-time, come l'anomalia della bassa volatilità, sono interessanti proprio per questo motivo.
FAQ
Cos'è il bias di forward-looking nel backtesting?
È l'uso, in un trade simulato, di dati che non erano disponibili quando il trade sarebbe stato piazzato. Le forme comuni sono: decidere su una barra di prezzo all'interno della quale il trade viene anche eseguito, fare screening su un universo di aziende che sono ancora quotate oggi, e leggere una cifra finanziaria che è stata rivista dopo la data ad essa associata.
Come si può rilevare il bias di forward-looking?
Ritarda ogni segnale di un intero periodo e riesegui: una perdita di solito collassa mentre un vero vantaggio decade dolcemente. Un test più forte sostituisce tutti i dati dopo ogni timestamp decisionale con rumore e conferma che ogni decisione rimane invariata.
Il bias di sopravvivenza è la stessa cosa del bias di forward-looking?
Sono distinti ma viaggiano insieme. Il bias di sopravvivenza è un campione costruito dai nomi che sono durati, e sapere quali nomi sono durati è di per sé un'informazione dal futuro, quindi un universo di soli sopravvissuti è una perdita di forward-looking situata nei dati piuttosto che nella regola.
Perché i backtest con bias di forward-looking sembrano così buoni?
La perdita seleziona i risultati piuttosto che prevederli. Nel pannello sopra, lo screening delle sessioni sul loro stesso prezzo di chiusura ha prodotto un guadagno medio per sessione di circa 0.455 punti percentuali al di sopra della versione ritardata in 2016, un vantaggio che è definitorio piuttosto che guadagnato.
Cosa sono i dati point-in-time?
I dati point-in-time memorizzano ogni valore così com'era noto a ciascuna data storica, inclusa la cifra originale e ogni revisione successiva, insieme all'universo dei titoli così com'era quel giorno. È il riferimento di cui un backtest ha bisogno per dimostrare che i suoi input esistevano prima delle sue decisioni.
Ogni cifra sopra è una query memorizzata e versionata su barre giornaliere reali. Espandi il SQL di qualsiasi pannello, o esegui il test di ritardo sulla tua regola sul terminale Strasmore.