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:

  1. 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".
  2. Definizioni: ogni termine come formula.
  3. Regole di ingresso, uscita, gestione, rischio.
  4. Parametri con valori di default e intervalli ammessi.
  5. Assunzioni di esecuzione (costi, slippage, ordine degli eventi).

Definizioni misurabili

Ogni concetto si traduce in condizioni numeriche sulle candele:

ConcettoDefinizione formale (esempio)
ATRMedia 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 asiaticomin e max di low/high tra 00:00 e 06:00 UTC; noto dalle 06:00
Sweep del minimo Llow < L e close ≥ L e (L − low) ≥ 0,2 × ATR
Displacementabs(close − open) > 1,5 × ATR(14)
MSS rialzistaclose > ultimo swing high confermato e displacement
FVG rialzistalow[i] − high[i−2] > 0,3 × ATR (zona [high[i−2], low[i]])
Ritorno nel FVGlow ≤ 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

ParametroDefaultIntervallo per il test di robustezza
n (swing)32-5
displacement1,5 × ATR1,2-1,8
ampiezza minima FVG0,3 × ATR0,2-0,5
ombra minima sweep0,2 × ATR0,1-0,3
max candele MSS128-16
RR target2,01,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

  1. Scrivi la specifica completa del modello sweep-MSS-FVG in una pagina (definizioni, stati, parametri).
  2. Per ogni definizione indica a quale candela l'informazione diventa nota.
  3. 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.