Ricerca quantitativa con Python e i feed API di Binance: la pipeline completa

L'errore ambientale del trader algoritmico retail è vivere dentro la piattaforma: idee testate direttamente nel tester di MetaTrader, sui suoi dati, con i suoi tempi. Il salto di qualità è dotarsi di un laboratorio esterno — nel mio caso Python — dove un'ipotesi si interroga in minuti, con la statistica che serve, prima di meritarsi un'implementazione.

Il flusso maturo è un imbuto: ipotesi → laboratorio (ore) → solo se sopravvive: EA e tester (giorni) → solo se sopravvive: shadow/demo (settimane) → produzione. Il laboratorio è il piano dell'imbuto dove morire costa meno; per questo deve essere il più affollato. Questa è la pipeline che ha prodotto la mia ricerca su 210.240 barre — e i suoi pezzi sono riutilizzabili per qualsiasi ipotesi vostra.

1 · I dati: le API di Binance

Binance espone gratuitamente tutto quello che serve alla ricerca su crypto. Tre canali, tre usi diversi:

# Download storico klines — paginato, senza dipendenze esotiche
import requests, pandas as pd, time

def klines(symbol="BTCUSDT", interval="5m", start_ms=None, limit=1000):
    r = requests.get("https://api.binance.com/api/v3/klines",
        params=dict(symbol=symbol, interval=interval,
                    startTime=start_ms, limit=limit), timeout=10)
    r.raise_for_status()
    cols = ["open_time","open","high","low","close","volume","close_time",
            "quote_vol","n_trades","taker_buy_vol","taker_buy_quote","_"]
    df = pd.DataFrame(r.json(), columns=cols).astype(float, errors="ignore")
    return df

frames, t0 = [], int(pd.Timestamp("2024-06-01").timestamp()*1000)
while True:
    df = klines(start_ms=t0)
    if df.empty: break
    frames.append(df); t0 = int(df.close_time.iloc[-1]) + 1
    time.sleep(0.15)          # rispetta i rate limit
data = pd.concat(frames, ignore_index=True)

2 · L'audit: prima dell'ipotesi, sempre

Regola non negoziabile: l'audit dei dati viene prima dell'ipotesi. Un risultato — positivo o negativo — su dati non auditati non è un risultato. Nella mia ricerca, l'audit ha trovato 148.608 timestamp forniti in microsecondi anziché millisecondi: il genere di difetto silenzioso che, non corretto, disallinea le feature e fabbrica edge fantasma.

# Audit minimo: duplicati, gap, coerenza OHLC
assert data.open_time.duplicated().sum() == 0, "duplicati!"
step = data.open_time.diff().dropna()
assert (step == step.mode()[0]).all(), "gap nel feed!"
ok = (data.high >= data[["open","close"]].max(axis=1)) & \
     (data.low  <= data[["open","close"]].min(axis=1))
assert ok.all(), "violazioni OHLC!"

3 · Feature senza look-ahead

Il look-ahead — usare informazione che al momento della decisione non esisteva — è l'errore più subdolo del settore, perché produce backtest meravigliosi e sistemi live che muoiono al primo giorno. La difesa è strutturale: ogni feature è vincolata per costruzione alla barra chiusa. In pandas la parola magica è shift(1):

f = pd.DataFrame(index=data.index)
rng = (data.high - data.low).replace(0, 1e-12)
f["body_ratio"]   = ((data.close - data.open).abs() / rng).shift(1)
f["lower_shadow"] = ((data[["open","close"]].min(axis=1) - data.low) / rng).shift(1)
f["ret_1"]        = data.close.pct_change().shift(1)
f["taker_ratio"]  = (data.taker_buy_vol / data.volume).shift(1)
f["atr_norm"]     = (rng.rolling(96).mean() / data.close).shift(1)
# target: rendimento FORWARD, mai shiftato
horizon = 12   # 1 ora su barre 5m
y = data.close.pct_change(horizon).shift(-horizon)

Se usate l'AI per generare questi script, mettete il vincolo nel prompt, esplicitamente — l'AI introduce look-ahead silenziosi con una frequenza impressionante. Il mio prompt tipo è nell'articolo sul metodo.

4 · Il test: contro il baseline, con i costi, IS/OOS

Tre regole che separano la ricerca dall'autoinganno:

  1. Ogni pattern deve battere il baseline ("sempre long su tutte le barre"), non lo zero: su un asset con drift, anche il nulla sembra un segnale.
  2. I costi dentro dal primo minuto: nel mio caso 6,5 bps di costo round-trip. Un edge lordo di 1,3 bps contro 6,5 di pedaggio è un rapporto segnale/costo di 1:30 — cioè non è un edge.
  3. In-sample / out-of-sample con voto unico: si sviluppa solo sulla prima parte dei dati; gli ultimi 6 mesi restano sigillati e si usano una volta, alla fine, come verdetto. Usarli dieci volte "per controllare" li trasforma in in-sample con cerimonia. Criterio di lettura: non pretendo che l'OOS eguagli l'IS; pretendo che non cambi verdetto. Segnali con +1,5 bps IS che diventano −6,8 OOS sono la firma del rumore.

5 · Triple barrier: etichette che rispettano la volatilità

Etichettare gli esiti con orizzonti fissi ("dov'è il prezzo dopo N barre?") ignora come un trade vive davvero: con uno stop, un target e un tempo massimo. La triple barrier etichetta ogni segnale con barriere dinamiche in unità di ATR — stop a k×ATR, target a m×ATR, timeout a T barre — e misura profit factor e perdita media per configurazione. Con drift nullo le barriere danno PF≈1 al lordo, quindi <1 dopo i costi: la gestione dinamica non crea un edge che non c'è — lo stesso teorema che vale per i vostri trailing stop.

6 · Dal laboratorio alla piattaforma

Solo ciò che sopravvive a tutto questo merita un'implementazione: prototipo Python → specifica formale → MQL5 (con la checklist degli errori) → tester "every tick based on real ticks" → shadow/demo con criterio di promozione scritto prima. Nel mio standard: almeno 30 trade con aspettativa media positiva prima di un euro vero.

Con l'AI, il costo di costruzione del laboratorio è crollato. Gli script della mia ricerca — audit, feature engineering, scan su griglie di orizzonti, triple barrier, report — sono nati in sessioni di ore. Non c'è più nessuna scusa infrastrutturale per testare le idee direttamente sui soldi.
Nota di realismo: questa pipeline non vi troverà un edge. Vi dirà, in fretta e a costo quasi zero, se un'ipotesi NON ce l'ha — che è il 95% del lavoro. La mia ricerca più rigorosa è finita con un "no" su tutta la linea, e l'ho pubblicata per intero: ogni "no" da tre ore di calcolo è un conto non bruciato.