Modulo 5 — Trading algoritmico · Lezione 20 di 23 · 7 min
Dall'idea alle regole: scrivere una specifica testabile
Come trasformare un concetto discrezionale in una specifica formale: definizioni misurabili, pseudocodice, gestione dell'ordine degli eventi, parametri, casi limite e test unitari.
Il passaggio più difficile del trading algoritmico non è programmare: è decidere esattamente cosa vuoi programmare. Un concetto discrezionale ("uno spostamento di forza dopo uno sweep") deve diventare una specifica formale senza ambiguità. In questa lezione impari a scriverla, a evitare gli errori di logica più comuni e a testarla prima ancora di fare un backtest.
Dall'ipotesi al documento di specifica
Parti da un'ipotesi in una frase, poi la sviluppi in un documento con cinque sezioni:
- Ipotesi: "Dopo lo sweep del minimo asiatico durante la killzone di Londra, se compare un MSS rialzista con displacement e il prezzo ritorna nel FVG creato, il prezzo tende a raggiungere il massimo asiatico prima di riprendere lo stop".
- Definizioni: ogni termine come formula.
- Regole di ingresso, uscita, gestione, rischio.
- Parametri con valori di default e intervalli ammessi.
- Assunzioni di esecuzione (costi, slippage, ordine degli eventi).
Definizioni misurabili
Ogni concetto si traduce in condizioni numeriche sulle candele:
| Concetto | Definizione formale (esempio) |
|---|---|
| ATR | Media mobile a 14 periodi del True Range |
| Swing high (n=3) | high[i] è il massimo tra le candele i−3…i+3; noto alla candela i+3 |
| Range asiatico | min e max di low/high tra 00:00 e 06:00 UTC; noto dalle 06:00 |
| Sweep del minimo L | low < L e close ≥ L e (L − low) ≥ 0,2 × ATR |
| Displacement | abs(close − open) > 1,5 × ATR(14) |
| MSS rialzista | close > ultimo swing high confermato e displacement |
| FVG rialzista | low[i] − high[i−2] > 0,3 × ATR (zona [high[i−2], low[i]]) |
| Ritorno nel FVG | low ≤ CE = (zona_bassa + zona_alta) / 2 entro 20 candele dall'MSS |
Lo pseudocodice
Prima di scrivere codice, scrivi la logica come macchina a stati. Ogni giorno ricomincia da zero:
STATO = "inattivo"
per ogni candela chiusa t del giorno:
se STATO == "inattivo":
se sweep_del_minimo(t) e t in killzone:
STATO = "attesa_MSS"; memorizza minimo_sweep
se STATO == "attesa_MSS":
se low(t) < minimo_sweep o candele_trascorse > 12: STATO = "inattivo"
se MSS_rialzista(t) e esiste FVG valido nelle ultime 3 candele:
STATO = "attesa_ritorno"; memorizza zona_FVG
se STATO == "attesa_ritorno":
se low(t) < minimo_sweep o candele_trascorse > 20: STATO = "inattivo"
se low(t) <= CE(zona_FVG) e candela_t rialzista:
segnale LONG (ingresso all'apertura della candela t+1)
stop = minimo_sweep - margine ; target = ingresso + RR * rischio
STATO = "inattivo"Vantaggi della macchina a stati: ogni condizione è esplicita, l'ordine degli eventi è chiaro, ed è facile scrivere test che verifichino una transizione alla volta.
L'ordine degli eventi dentro una candela
Un problema spinoso: una candela ha un massimo e un minimo, ma non sai quale è avvenuto prima. Se nella stessa candela il prezzo tocca sia lo stop sia il target, cosa è successo? Regole prudenti:
- Stop e target nella stessa candela → vince lo stop (ipotesi pessimistica: il backtest non dovrebbe essere ottimista).
- Ingresso e stop nella stessa candela (la candela di ingresso tocca subito lo stop): conta come perdita.
- Se disponi di dati a risoluzione inferiore (M1 dentro una M5), puoi ricostruire l'ordine reale: più preciso ma più pesante.
Regole "senza look-ahead": una checklist
Prima di ogni backtest, per ogni feature chiediti: a quale candela questa informazione è davvero nota?
- Uno swing con n candele per lato è noto solo n candele dopo il massimo/minimo.
- Un FVG è noto alla chiusura della terza candela.
- Il range asiatico è noto alla fine della sessione asiatica.
- Il massimo/minimo del giorno è noto solo a fine giornata (nel corso del giorno hai solo il massimo finora).
- Un indicatore calcolato sulla candela in formazione cambia finché la candela non chiude: usa solo candele chiuse.
- L'ingresso avviene all'apertura della candela successiva al segnale, con spread.
Parametri: pochi, con valori di default
| Parametro | Default | Intervallo per il test di robustezza |
|---|---|---|
| n (swing) | 3 | 2-5 |
| displacement | 1,5 × ATR | 1,2-1,8 |
| ampiezza minima FVG | 0,3 × ATR | 0,2-0,5 |
| ombra minima sweep | 0,2 × ATR | 0,1-0,3 |
| max candele MSS | 12 | 8-16 |
| RR target | 2,0 | 1,5-3,0 |
Non ottimizzare cercando il valore "migliore": usa l'intervallo per verificare la robustezza (il risultato deve restare simile).
Casi limite da decidere prima
- Weekend e gap: cosa succede se il segnale è generato il venerdì sera?
- Cambio d'ora: definisci tutto in UTC.
- Dati mancanti: una candela mancante può creare un falso FVG.
- Segnali multipli sovrapposti: una sola posizione alla volta? Se sì, cosa fai con un segnale che arriva a posizione aperta?
- Stop troppo largo o troppo stretto: filtra i trade con rischio in pip oltre un massimo (per esempio > 3 × ATR) o sotto un minimo.
- Spread anomalo: ignora i segnali quando lo spread supera una soglia.
Testare la specifica: i test unitari
Prima del backtest, verifica che ogni funzione faccia ciò che dici con piccoli casi costruiti a mano. Esempio per l'FVG:
import pandas as pd
from lib_structure import find_fvg
def test_fvg_rialzista():
# C1: high 1.1000 | C2: candela forte | C3: low 1.1012 -> FVG rialzista [1.1000, 1.1012]
df = pd.DataFrame({
"open": [1.0990, 1.1002, 1.1015],
"high": [1.1000, 1.1030, 1.1030],
"low": [1.0985, 1.1001, 1.1012],
"close": [1.0998, 1.1028, 1.1025],
})
fvg = find_fvg(df)
assert bool(fvg["bull"].iloc[2]) is True
assert abs(fvg["bull_low"].iloc[2] - 1.1000) < 1e-9
assert abs(fvg["bull_high"].iloc[2] - 1.1012) < 1e-9
def test_nessun_fvg_se_i_range_si_sovrappongono():
df = pd.DataFrame({
"open": [1.1000, 1.1005, 1.1008],
"high": [1.1010, 1.1020, 1.1022],
"low": [1.0995, 1.1000, 1.1005],
"close": [1.1005, 1.1015, 1.1018],
})
assert not find_fvg(df)["bull"].iloc[2]Anche per gli swing scrivi un test: una sequenza di massimi nota in cui sai esattamente quali indici devono essere riconosciuti, e verifica che la conferma avvenga n candele dopo. Questi test ti proteggono quando modifichi il codice.
Errori di specifica tipici
- Regole ambigue che ciascuno interpreta a modo suo.
- Condizioni che dipendono dal futuro senza accorgersene.
- Troppi parametri o valori "ritoccati" sui dati.
- Dimenticare i costi o assumere esecuzioni perfette.
- Non gestire i casi limite (gap, dati mancanti).
- Non versionare la specifica: quando cambi una regola, devi poter confrontare i risultati con la versione precedente.
In sintesi
- Scrivi un documento di specifica: ipotesi, definizioni, regole, parametri, assunzioni di esecuzione.
- Traduci ogni concetto in formule sulle candele e la logica in una macchina a stati.
- Per ogni informazione chiediti quando è davvero nota (niente look-ahead) e usa solo candele chiuse.
- Ordine degli eventi in una candela: in dubbio vince lo stop.
- Usa pochi parametri con default e testa le funzioni con casi costruiti a mano.
Esercizio pratico
- Scrivi la specifica completa del modello sweep-MSS-FVG in una pagina (definizioni, stati, parametri).
- Per ogni definizione indica a quale candela l'informazione diventa nota.
- Scrivi tre test unitari (swing, FVG, sweep) con dati costruiti a mano.
Metti alla prova quello che hai imparato
1. Perché usare una macchina a stati per lo pseudocodice?
2. Se nella stessa candela toccano stop e target, cosa si assume?
3. Quando è noto uno swing con n candele per lato?
4. A cosa servono i test unitari?
Hai una domanda o vuoi condividere il tuo esercizio?
Scrivi nella community, oppure entra nella sala segnali gratuita su Telegram.
Contenuto educativo, non è consulenza finanziaria. Il trading comporta rischi. ICT è un termine riferito ai materiali di Michael Huddleston: questo corso è indipendente e non affiliato.