English Русский 한국어 Türkçe
preview
Dal “Best Pass” a soluzioni robuste: Esplorazione della superficie di ottimizzazione in MetaTrader 5

Dal “Best Pass” a soluzioni robuste: Esplorazione della superficie di ottimizzazione in MetaTrader 5

MetaTrader 5 — Tester |
20 0
MetaQuotes
MetaQuotes

Introduzione

L'ottimizzazione delle strategie di trading è da tempo una componente standard dello sviluppo di sistemi di trading algoritmico. MetaTrader 5 offre potenti strumenti per questo scopo: un tester multi-thread, il calcolo distribuito e criteri definiti dall'utente. I risultati dell'ottimizzazione possono essere visualizzati come dipendenze unidimensionali, bidimensionali e tridimensionali delle metriche di performance rispetto ai parametri dell'EA. A prima vista, questo è più che sufficiente: si avvia l'ottimizzazione, i risultati vengono ordinati in base al Profit Factor o a un altro criterio di sintesi, dopodiché viene selezionata il miglior passaggio. Tuttavia, è proprio qui che inizia una delle illusioni più comuni del trading algoritmico.

Il problema è che l'ottimizzazione si concentra quasi sempre sulla ricerca del risultato massimo su un singolo passaggio. Il tester mostra il picco della superficie dei parametri - il Profit Factor più elevato, il miglior Sharpe Ratio o il profitto massimo. Ma il mercato raramente premia i sistemi basati su singoli estremi. Al contrario, tali picchi spesso si rivelano essere il risultato di una coincidenza casuale dei parametri con la struttura locale dei dati storici. Basta una minima variazione nel periodo dell'indicatore, nel valore dello stop loss o nella modalità di mercato, e un risultato promettente comincia a sgretolarsi rapidamente.

Ecco perché un elevato Profit Factor di per sé non garantisce nulla. Due strategie possono generare gli stessi rendimenti, ma differire sostanzialmente in termini di robustezza. La prima mantiene risultati accettabili nell'intero intervallo dei parametri adiacenti. La seconda esiste solo sotto forma di un picco strettissimo sulla superficie di ottimizzazione, circondato da configurazioni caotiche e non redditizie. Formalmente, entrambi i sistemi sono redditizi. In pratica, solo uno di loro ha la possibilità di sopravvivere nel mercato reale.

Gli strumenti di visualizzazione integrati di MetaTrader 5 mostrano chiaramente la dipendenza delle metriche finali dai singoli parametri di ottimizzazione. Il tester mostra i valori delle metriche e la distribuzione dei risultati. Tuttavia, offre scarso aiuto nella valutazione delle configurazioni adiacenti: tasso di degrado, presenza di plateau stabili, sensibilità ai parametri e limiti di sovra-ottimizzazione. In altre parole, il valore massimo della funzione non è più importante quanto la geometria dell'intera superficie di ottimizzazione.

È qui che i frame di ottimizzazione acquisiscono un valore speciale. Questo meccanismo consente di memorizzare e analizzare dati utente arbitrari dopo ogni passaggio di ottimizzazione. Anziché utilizzare diversi indicatori predefiniti, lo sviluppatore ha la possibilità di creare la propria telemetria di ricerca: trasmettere metriche aggiuntive, valutare la stabilità delle configurazioni adiacenti, analizzare aree di parametri locali e definire criteri focalizzati non sulla redditività estrema, ma sulla stabilità del comportamento del sistema. A questo punto, l'ottimizzazione cessa di essere la ricerca di un bel numero e si trasforma in una vera e propria ricerca.

In questo articolo esamineremo l'applicazione pratica dei frame di ottimizzazione in MetaTrader 5, impareremo come salvare le nostre metriche e analizzare i risultati, nonché come costruire criteri di stabilità personalizzati basati sull'intera superficie di ottimizzazione. L'obiettivo principale sarà quello di individuare aree di parametri stabili, in grado di mantenere un comportamento prevedibile anche in seguito a cambiamenti delle condizioni di mercato.

Ottimizzazione senza illusione



I frame di ottimizzazione come sistema di telemetria per la ricerca

Nonostante tutte le funzionalità del tester integrato, l'ottimizzazione standard in MetaTrader 5 rimane per sua natura una procedura arida. L'agente esegue i calcoli. Il tester registra il risultato. Alla fine, l'utente visualizza una serie di cifre già pronte: Profit Factor, Sharpe Ratio, drawdown, profitto e altre metriche riassuntive. Questo è sufficiente per la selezione iniziale. Ma per un'analisi seria, non è sufficiente. La sommità è visibile, ma il terreno in sé rimane in gran parte invisibile.

È qui che entrano in gioco i Frame di Ottimizzazione. Non si tratta semplicemente di un altro modo per salvare il risultato di un passaggio. Si tratta di un canale completo per la trasmissione dei dati di ricerca tra gli agenti di test e il codice di analisi. Attraverso i frame, possiamo trasmettere tutto ciò che ci aiuta a comprendere più a fondo il comportamento della strategia: statistiche aggiuntive, parametri intermedi, stato del modello, caratteristiche della curva dell’equity, valutazione della stabilità e qualsiasi altra metrica personalizzata.

Il funzionamento di questo meccanismo appare del tutto naturale. Al termine di ogni passaggio, l'esperto trasmette i dati tramite FrameAdd() da OnTester(). Questo attiva l'evento TesterPass nel terminale, che a sua volta chiama OnTesterPass(). All'interno di questo gestore, è possibile analizzare immediatamente il frame in arrivo: leggere sequenzialmente i dati tramite FrameFirst() e FrameNext() e se necessario, filtrare le voci richieste. In pratica, questo dà la sensazione di un flusso di risultati in tempo reale: il passaggio è completato, il frame è arrivato, i dati possono ora essere analizzati.

Ma questo flusso ha una caratteristica importante. I frame potrebbero arrivare non una alla volta, ma a lotti, e talvolta con un ritardo percettibile. Ciò è particolarmente evidente nell'ottimizzazione distribuita, quando alcuni calcoli vengono eseguiti da agenti remoti. Ecco perché non tutti i TesterPass vengono elaborati esattamente nel momento del completamento del passaggio successivo. È per questo motivo che OnTesterDeinit() rimane l'ultima fase affidabile per lavorare con i risultati. Se alcuni frame sono arrivati in un secondo momento, possono essere letti al termine dell'ottimizzazione, scorrendo nuovamente l'intero array di dati dall'inizio tramite FrameFirst() e, se necessario, ordinati o filtrati tramite FrameFilter().

Di conseguenza, l'ottimizzazione non è più solo una serie di passaggi isolati. Si trasforma in un sistema per l'accumulo di dati di telemetria relativi alla ricerca sull'intera superficie dei parametri. Ciò cambia radicalmente la logica stessa del lavoro. Ora lo sviluppatore vede non solo il valore finale del criterio, ma anche il contesto in cui è stato ottenuto. Insieme al Profit Factor, possiamo memorizzare ulteriori metriche: concentrazione del profitto, uniformità delle transazioni, stabilità nel tempo e caratteristiche dell’equity. Separatamente, è possibile salvare gli indicatori di degrado sui parametri adiacenti e la sensibilità al regime di mercato.

Pertanto, ogni passaggio inizia ad assomigliare a un vero e proprio batch diagnostico. E più l'ottimizzazione è lunga o complessa, maggiore è il valore di questo approccio. In realtà, il vero valore dei frame di ottimizzazione risiede nella post analisi dell'intera superficie di ottimizzazione. Anziché analizzare i singoli punti, diventa possibile studiare lo spazio dei parametri nel suo complesso: ricercare aree stabili, misurare il tasso di degrado dei risultati, valutare la sensibilità di una strategia e costruire i propri criteri di stabilità basati su configurazioni vicine.

Per questo motivo, in questo approccio l'ottimizzazione diventa uno studio di una superficie multidimensionale, in cui non sono importanti solo i valori assoluti delle metriche, ma anche la forma della loro distribuzione, la densità delle zone stabili e il comportamento dei parametri vicini. Qui il vincitore non è più colui che ha la cima isolata più alta, ma colui che ha una superficie più robusta e affidabile.



Una strategia semplice come oggetto di ricerca

Dopo aver familiarizzato con i frame di ottimizzazione, sorge spontanea una domanda: Cosa dovremmo esattamente utilizzare come oggetto di questa analisi? Intuitivamente, vorremmo adottare un sistema di trading complesso - un modello multilivello, un insieme di filtri, una cascata di indicatori, una gestione adattiva delle posizioni. Ma la strategia complessa sta iniziando a rubare la scena. Anziché esplorare l'ottimizzazione, l'articolo si trasforma gradualmente in una discussione sul sistema di trading stesso. E questa è una storia completamente differente.

Nel nostro lavoro, abbiamo bisogno dell'approccio opposto. La strategia dovrebbe essere la più semplice possibile, quasi banale. Dovrebbe essere talmente trasparente che il lettore possa facilmente comprendere la fonte di ogni segnale e ogni variazione nei risultati. In questo caso, la purezza dell'esperimento di ricerca è più importante della magia della profittabilità.

Pertanto, come oggetto di ricerca utilizziamo un modello estremamente semplice costruito attorno a due idee ben note: l'intersezione delle medie mobili e la modalità RSI. La prima parte del sistema è responsabile del momento dell'ingresso. Il secondo serve per filtrare lo stato del mercato. In questo caso, i segnali stessi non vengono generati tramite il polling diretto degli indicatori dall'EA, ma tramite un modello di eventi . Gli indicatori generano segnali in modo indipendente e li trasmettono tramite eventi utente.

Questo approccio risolve diversi problemi contemporaneamente. La strategia rimane estremamente compatta e di facile lettura. La logica del segnale risulta essere ben scomposta: gli indicatori sono responsabili solo della generazione degli eventi, mentre l'EA è responsabile solo dell'elaborazione degli stati e della gestione delle posizioni. L'architettura basata sugli eventi si adatta particolarmente bene alla natura di ricerca dell'articolo, poiché consente una facile raccolta di dati di telemetria diagnostica sul comportamento del sistema.

Alla base della logica c'è un'idea semplice. L'intersezione dell’EMA viene utilizzata come potenziale punto di ingresso.

//--- chek signal
   if(fast_buff[1] > slow_buff[1] &&
      fast_buff[0] <= slow_buff[0])
      SendSignalEvent(EVENT_EMA_CROSS, DIR_BUY, 0.0, "EMA CROSS");
   if(fast_buff[1] < slow_buff[1] &&
      fast_buff[0] >= slow_buff[0])
      SendSignalEvent(EVENT_EMA_CROSS, DIR_SELL, 0.0, "EMA CROSS");

Tuttavia, il segnale di crossover di per sé non apre una posizione. Innanzitutto, il sistema verifica la modalità RSI corrente.

Allo stesso tempo, RSI genera un evento solo al momento del cambio di modalità, ovvero quando vengono superati i livelli specificati.

//--- chek signal
   ENUM_SIGNAL_DIRECTION new_regime = current_regime;
   if(rsi_buff[0] <= UpperLevel &&
      rsi_buff[1] > UpperLevel)
      new_regime = DIR_BUY;
   if(rsi_buff[0] >= LowerLevel &&
      rsi_buff[1] < LowerLevel)
      new_regime = DIR_SELL;
   if(rsi_buff[1] >= LowerLevel &&
      rsi_buff[1] <= UpperLevel)
      new_regime = DIR_NONE;
//--- send signal
   if(new_regime != current_regime)
     {
      current_regime = new_regime;
      SendSignalEvent(EVENT_RSI_REGIME, current_regime,
                      rsi_buff[1], "RSI REGIME");
     }

Grazie a ciò, è possibile ridurre il rumore in modo significativo. L'EA memorizza lo stato attuale dell'RSI sotto forma di flag interno e lo utilizza come contesto di mercato. Se i segnali coincidono, l'evento viene considerato sincronizzato e l'EA apre un trade.

void OnChartEvent(const int32_t id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   if(id-CHARTEVENT_CUSTOM == EVENT_RSI_REGIME)
     {
      rsi_regime = (ENUM_SIGNAL_DIRECTION)lparam;
      rsi_regime_changes++;
      CloseConflictingPosition();
     }
   if(id-CHARTEVENT_CUSTOM == EVENT_EMA_CROSS)
     {
      ema_events++;
      ProcessEntry((int)lparam);
     }
  }

Questo è un punto importante per tutte le successive ottimizzazioni. Costruiamo deliberatamente un sistema basato sulla coerenza di fonti di informazione indipendenti.

Anche la gestione del rischio è volutamente mantenuta semplice. Lo Stop Loss e il Take Profit vengono calcolati tramite moltiplicatori ATR all'apertura di una posizione. Il valore ATR viene utilizzato solo come stima della volatilità attuale e non è coinvolto nel modello degli eventi. Questo ci permette di mantenere l'architettura il più semplice possibile: gli eventi sono responsabili della direzione, mentre ATR definisce la scala di rischio.

void ProcessEntry(const int signal)
  {
   if(PositionSelect(_Symbol))
      return;
   if(signal == DIR_BUY &&
      rsi_regime == DIR_BUY)
     {
      if(!atr_buff.CopyIndicatorBuffer(atr_handle, 0, 1, 2))
         return;
      double atr = atr_buff[0];
      double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      synchronized_entries++;
      double sl = NormalizeDouble(bid - atr * iSLmult,
                                  _Digits);
      double tp = NormalizeDouble(bid + atr * iTPmult,
                                  _Digits);
      trade.Buy(iLots, _Symbol, 0, sl, tp);
     }
   if(signal == DIR_SELL &&
      rsi_regime == DIR_SELL)
     {
      if(!atr_buff.CopyIndicatorBuffer(atr_handle, 0, 1, 2))
         return;
      double atr = atr_buff[0];
      double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      synchronized_entries++;
      double sl = NormalizeDouble(ask + atr * iSLmult,
                                  _Digits);
      double tp = NormalizeDouble(ask - atr * iTPmult,
                                  _Digits);
      trade.Sell(iLots, _Symbol, 0, sl, tp);
     }
  }

Da un punto di vista pratico, una strategia di questo tipo è interessante proprio per la sua prevedibilità. Non ci sono logiche complesse, dipendenze nascoste o filtri pesanti. Qualsiasi variazione nei risultati dell'ottimizzazione è quasi direttamente correlata a una variazione dei parametri del modello. Ciò significa che la superficie di ottimizzazione diventa molto più leggibile e adatta all'analisi esplorativa.



Metriche personalizzate e analisi di alto livello

Il tester di MetaTrader 5 è in grado di calcolare numerosi indicatori integrati: profitto, Profit Factor, Recovery Factor, Sharpe Ratio, drawdown, profitto atteso e altre caratteristiche del singolo passaggio. Questo è più che sufficiente per valutare una singola configurazione. Tuttavia, tali indicatori descrivono un solo punto nello spazio dei parametri. Rispondono alla domanda su quanto successo abbia avuto un determinato passaggio. Ma non dice quasi nulla su come si comporta la strategia in prossimità di questo punto.

È qui che inizia il divertimento. MetaTrader 5 offre diverse opzioni di ottimizzazione e ordina i risultati in base al valore migliore del criterio selezionato. La tabella dei risultati mostra anche altri indicatori di performance, quindi il quadro appare piuttosto completo. Tuttavia, la visualizzazione viene creata solo per il criterio selezionato e solo in base alla sua dipendenza sui parametri ottimizzati. In altre parole, lo sviluppatore vede la superficie di una metrica, ma non ne vede la stabilità nelle vicinanze dell'area rilevata. Ma questa è una questione fondamentalmente diversa.

Visualizzazione dei risultati dell'ottimizzazione nel terminale

Due configurazioni possono apparire molto simili nel report finale, ma comportarsi in modo completamente diverso. La prima ottiene un elevato Profit Factor, tuttavia, questo si mantiene su un picco ristretto, che crolla al minimo cambiamento dei parametri. La seconda mostra un risultato più modesto, ma mantiene valori simili su un intervallo più ampio. Un tester standard sceglierà quasi sempre la prima configurazione come la migliore. Dal punto di vista della ricerca, però, la seconda si rivela spesso più apprezzabile perché il suo comportamento appare stabile.

Pertanto, iniziamo a raccogliere non solo statistiche integrate, ma anche le nostre metriche diagnostiche già a livello di singolo passaggio. FrameAdd ci permette di trasmettere qualsiasi dato telemetrico aggiuntivo che ci aiuti a comprendere il comportamento interno. Nel nostro caso, il numero di eventi di attraversamento EMA, il numero di cambi di modalità RSI e di ingressi sincronizzati, nonché il tasso di conversione del segnale, il profitto per segnale e il criterio di stabilità definito dall'utente vengono salvati insieme ai dati di base. Questo è un tentativo di capire esattamente come funziona il sistema internamente.

double OnTester()
  {
//---
   double profit_per_signal = 0.0;
   double trigger_conversion_ratio = 0.0;
//---
   double profit = TesterStatistics(STAT_PROFIT);
   double pf = TesterStatistics(STAT_PROFIT_FACTOR);
   if(ema_events > 0)
     {
      trigger_conversion_ratio =
         (double)synchronized_entries / (double)ema_events;
     }
   if(synchronized_entries > 0)
     {
      profit_per_signal =
         profit / synchronized_entries;
     }
   double ret =
      CalculateCustomCriterion(pf,
                               trigger_conversion_ratio,
                               profit_per_signal
                              );
//---
   double telemetry[12];
   telemetry[0]  = profit;
   telemetry[1]  = pf;
   telemetry[2]  = ema_events;
   telemetry[3]  = rsi_regime_changes;
   telemetry[4]  = synchronized_entries;
   telemetry[5]  = trigger_conversion_ratio;
   telemetry[6]  = profit_per_signal;
   telemetry[7]  = ret;
   telemetry[8]  = iFastEMA;
   telemetry[9]  = iSlowEMA;
   telemetry[10] = iSLmult;
   telemetry[11] = iTPmult;
//---
   FrameAdd("Telemetry", 0, ret, telemetry);
//---
   return(ret);
  }

Il tasso di conversione dei segnali trigger_conversion_ratio è particolarmente utile. Mostra quale quota dei trigger EMA si traduce effettivamente in un'operazione di trading. Se il valore è elevato, il segnale di base e il filtro di modalità si comportano in modo coordinato. Se il valore è basso, il sistema è troppo selettivo, troppo rumoroso oppure la logica dei parametri non funziona correttamente. In ogni caso, questa metrica non si riferisce al profitto in sé, bensì alla qualità dell'interazione tra i componenti del modello.

Un altra metrica è il profitto per segnale (profit_per_signal). È simile al profitto medio per trade, ma non coincide con esso. Nel nostro modello, un trade viene aperto solo dopo che i segnali EMA e RSI sono concordanti, ma la posizione aperta non viene chiusa automaticamente in caso di incrocio inverso dell'EMA. Inoltre, finché la posizione rimane attiva, i segnali successivi possono essere semplicemente ignorati. Pertanto, il numero di segnali sincronizzati e il numero di cicli di trading effettivamente eseguiti non sempre coincidono esattamente. In questo senso, il profitto per segnale riflette l'efficacia con cui il sistema monetizza i punti di ingresso disponibili, tenendo conto dei segnali persi.

Qui avviene la transizione al livello successivo. Quando i frame di ottimizzazione iniziano ad accumulare dati di telemetria sull'intera superficie dei parametri, diventa possibile analizzare non solo i singoli passaggi, ma anche il loro comportamento reciproco. E questo è molto più importante di un semplice elenco dei migliori risultati. Non ci interessa solo dove si trova il massimo, ma anche come si comportano le configurazioni vicine, con quale rapidità il risultato si degrada con una piccola variazione dei parametri e se esiste un plateau stabile intorno al punto trovato.

Per questo motivo i dati esportati tramite frame vengono salvati in un file CSV.

void OnTesterDeinit()
  {
   ArrayResize(frames, 0, 100);
   FrameFirst();
   int pos = 0;
   ulong pass = 0;
   string name = "";
   long id = 0;
   double value = 0;
   double data[];
   SFrameData item;
//---
   while(FrameNext(pass, name, id, value, data))
     {
      if(ArraySize(data) < 12)
         continue;
      item.pass                 = id;
      item.profit               = data[0];
      item.pf                   = data[1];
      item.ema_events           = data[2];
      item.regime_changes       = data[3];
      item.synchronized_entries = data[4];
      item.conversion_ratio     = data[5];
      item.profit_per_signal    = data[6];
      item.custom_criterion     = data[7];
      item.fast_ema             = data[8];
      item.slow_ema             = data[9];
      item.sl_mult              = data[10];
      item.tp_mult              = data[11];
      pos = ArraySize(frames);
      if(ArrayResize(frames, pos + 1, (pos + 49) / 50) < pos + 1)
         return;
      frames[pos] = item;
     }
   ExportCSV();
//---
  }

Questo formato è pratico perché consente un'ulteriore analisi utilizzando una vasta gamma di strumenti a disposizione del lettore: dai fogli di calcolo ai pacchetti di analisi specializzati. Nel nostro lavoro, abbiamo utilizzato Python per questo scopo. MetaTrader 5 fornisce l'integrazione necessaria. Questo ci permette di costruire uno studio di superficie dei parametri: smussare i valori, valutare la stabilità locale, osservare la distribuzione dei risultati e confrontare le aree adiacenti.

Per i test e le successive analisi, abbiamo utilizzato i dati storici relativi a EUR/USD su timeframe H1 per il periodo compreso tra il 1° gennaio 2020 e il 1° gennaio 2026. Questo intervallo è abbastanza lungo da permettere l'emergere di una varietà di modelli di mercato: periodi di calma, movimenti impulsivi, tendenze prolungate e periodi di maggiore rumore. Ciò è importante non solo per ottenere un risultato plausibile, ma anche per garantire che la superficie di ottimizzazione non appaia artificialmente liscia.

Periodo di ottimizzazione

Nella prima fase, l'EA viene ottimizzato in base a quattro parametri: EMA veloce, EMA lenta, moltiplicatore dello stop loss e moltiplicatore del take profit. Questi quattro parametri definiscono la struttura di base della strategia e ne determinano la reazione al mercato. Per ampliare l'intervallo attivo di generazione degli eventi e prevenire un filtraggio eccessivamente ristretto, i limiti dell’RSI sono stati spostati a 40 e 60. In questa configurazione, il filtro di modalità diventa più sensibile e il modello degli eventi risulta meno vincolato. Ciò consente all'EA di reagire più spesso agli stati di mercato osservati e quindi di generare una superficie più espressiva.

Una volta completata l'ottimizzazione, i risultati vengono salvati in un file CSV e inviati aPython per l’elaborazione. Qui, per ogni coppia di parametri, viene costruita una superficie bidimensionale tramite pivot_table, dopodiché viene applicato un filtro di smoothing gaussiano. Ciò si traduce in una superficie dei criteri più calma e uniforme — la versione di ricerca — in cui i picchi casuali diventano notevolmente meno pronunciati.

Superficie grezza e superficie smussata del criterio

Nella stessa funzione, la stabilità locale viene calcolata come rapporto tra il valore medio dei parametri vicini e la sua deviazione standard.

Visualizzazione della stabilità locale

Viene quindi creata una mappa del gradiente per mostrare dove la superficie cambia bruscamente e dove rimane relativamente piatta.

Mappa del gradiente

Questi grafici ci permettono di vedere se nello spazio dei parametri è presente un ampio plateau o solo un picco stretto e fragile.

Un livello di analisi separato fornisce un grafico a coordinate parallele per tutti i parametri contemporaneamente. Prima della sua costruzione, i parametri vengono normalizzati e solo i passaggi migliori vengono evidenziati dal criterio selezionato sulle Coordinate Parallele. Questo è utile quando è necessario visualizzare non solo un singolo punto di successo, ma anche la sua posizione rispetto ad altre configurazioni. Lo script stampa il 5% dei migliori risultati secondo il criterio, in modo che l'immagine visiva sia immediatamente accompagnata da un supporto numerico.

Coordinate parallele dei parametri da ottimizzare

Di conseguenza, Python funge da livello di analisi successivo. MetaTrader 5 fornisce la superficie di ottimizzazione, mentre Python ne elimina il rumore, mostra la stabilità locale, confronta le aree adiacenti e aiuta a distinguere una sezione di parametri realmente stabile da un picco casuale e appariscente. L'ottimizzazione cessa di essere una questione di ordinamento dei singoli passaggi in base a un singolo numero. Si tratta di uno studio di una superficie multidimensionale, in cui non è importante solo il valore del criterio, ma anche il comportamento dell'intera area circostante. Questo ci offre l'opportunità di esaminare la strategia più a fondo, considerandola come un sistema la cui stabilità è determinata dalla forma della superficie di ottimizzazione.



Dalla ricerca del massimo a quella di un plateau stabile

L'obiettivo principale dell'ottimizzazione esplorativa non è massimizzare il risultato, bensì trovare intervalli di parametri in cui la strategia rimanga stabile anche con piccole variazioni di configurazione.

Questa differenza è fondamentale. Un singolo picco elevato sulla superficie di ottimizzazione appare quasi sempre allettante. In pratica, però, tali estremi si rivelano spesso il risultato della volatilità del mercato, delle caratteristiche di un particolare periodo storico o semplicemente dell'overfitting del modello. Nei test sembrano convincenti, ma sul mercato reale la situazione potrebbe essere diversa.

Ecco perché, dopo aver caricato i risultati su Python, non analizziamo i singoli passaggi, ma l'intera superficie dei parametri nel suo complesso.

È innanzitutto utile confrontare le superfici grezze e omogenee del criterio di ottimizzazione. La superficie iniziale appare variegata: sono presenti numerose esplosioni locali, massimi netti e picchi singoli. Questa è una tipica immagine di ottimizzazione del rumore, in cui coincidenze casuali vengono facilmente mascherate da soluzioni di successo. Dopo l'operazione di “smoothing”, la maggior parte di questi valori anomali scompare e emergono strutture più stabili, anziché casuali.

È qui che diventa chiaro che non tutte le vette più belle sono affidabili. Se dopo lo smoothing la superficie si disintegra e perde la sua forma, allora gli estremi riscontrati sono stati più una casualità che uno schema. Nel nostro caso la situazione è diversa: rimangono due creste distinte attorno a SlowEMA negli intervalli 120–150 e 220–240. Questo è già un segnale importante che indica che non si tratta di un picco isolato, ma di un modello statisticamente stabile. Inoltre, l'optimum in questo caso non assomiglia a un ago appuntito, ma a un pettine largo. E questo è in realtà un buon segno. Le strategie solide raramente nascono in un unico luogo. Nella maggior parte dei casi, i valori rimangono su un plateau, dove i valori adiacenti forniscono risultati simili.

Dopodiché, ha senso dare un'occhiata alla mappa di calore del gradiente. Questo grafico mostra la rapidità con cui cambia la funzione obiettivo quando i parametri EMA vengono modificati. Qui si notano aree di maggiore sensibilità, principalmente con SlowEMA negli intervalli 70–90 e 200–220. In questi intervalli, anche una piccola modifica delle impostazioni può cambiare drasticamente il risultato. E questo è già un chiaro segnale di un aumentato rischio di sovra-ottimizzazione.

Il gradiente stesso presenta un carattere spiccatamente verticale. Ciò significa che SlowEMA influenza il risultato in modo significativamente maggiore rispetto a FastEMA. La logica alla base di questo ragionamento è piuttosto naturale: la media mobile lenta definisce il regime di mercato, mentre quella veloce è maggiormente responsabile del punto di ingresso e dell'adeguamento locale del comportamento del sistema. Al contrario, le aree intorno a SlowEMA negli intervalli 160–190 e 30–50 mostrano un gradiente basso. È però importante non sopravvalutare questo fatto: Raw Surface e Smoothed Surface mostrano chiaramente che si tratta piuttosto di aree a bassa o quasi nulla redditività, piuttosto che di aree realmente produttive. Lo smussamento della superficie non rende la strategia valida.

Il grafico delle Coordinate Parallele completa il quadro delineato per il 5% delle migliori configurazioni in base ai criteri dell'utente. E qui la cosa fondamentale è chiara: le soluzioni sostenibili non convergono in un unico punto magico. Sono distribuite tra diverse combinazioni funzionanti. Questo è un segnale molto importante. Un singolo massimo ristretto può sembrare impressionante, ma di solito è associato a un'eccessiva ottimizzazione. Al contrario, i cluster distribuiti indicano che il sistema può operare in diverse modalità.

È particolarmente evidente che le configurazioni migliori tendono ad avere valori FastEMA più elevati. Ciò ha senso: valori più uniformi riducono il rumore e rendono i segnali più puliti. Allo stesso tempo, SlowEMA forma diversi cluster, che possono indicare differenti modalità di funzionamento delle strategie di mercato. I parametri SLMult e TPMult sono distribuiti in modo più uniforme. Ciò suggerisce che la gestione delle posizioni ha un impatto minore sul risultato finale rispetto alla struttura stessa dei segnali EMA. In altre parole, il fulcro del sistema risiede nella logica di ingresso, mentre la gestione dei trade svolge un ruolo secondario.

Il passo successivo, logicamente, non viene compiuto sull'intera griglia iniziale di parametri, bensì sull'area che ha mostrato segni di funzionamento stabili. Dopo la prima fase, restringiamo l'analisi di FastEMA, SlowEMA, SLMult e TPMult alle aree in cui la superficie appare come un ampio plateau. Successivamente, al modello vengono aggiunti altri due parametri: i limiti inferiore e superiore dell’RSI, che in questa fase vengono ottimizzati come parte della struttura complessiva di ingresso. Questo ci permette di evitare di concentrarci sull'intero array parametrico contemporaneamente, ma di procedere dalla configurazione stabile trovata alla sua messa a punto più precisa.

È qui che il forward test torna utile. In MetaTrader 5, durante l'ottimizzazione forward, il periodo specificato per lo studio viene automaticamente suddiviso in due parti: la prima viene utilizzata per l'ottimizzazione e la seconda per il forward test. Inoltre, non tutte le esecuzioni vengono avviate nel periodo di “forward”, ma solo le migliori: con un'enumerazione completa dei parametri, viene selezionato il 10% delle migliori, e con un algoritmo genetico il 25%. I risultati dell'ottimizzazione e del forward possono quindi essere confrontati in schede di test separate. Questo è comodo per verificare la qualità dell’adattamento. Questo metodo può essere utilizzato anche per verificare se il gruppo di parametri selezionato rimane stabile al di fuori della sezione relativa allo storico di addestramento.

La prima fase ha già dimostrato che la strategia non deve necessariamente basarsi su un unico, netto successo. Il compito ora è verificare se la zona dei parametri rilevata è in grado di resistere al passaggio a una modalità di test più rigorosa. Un forward test aiuta a separare una regione di parametri stabili da una che appare positiva solo nel periodo di dati passato.

Ottimizzazione forward

Nella seconda fase, il compito cambia. Ora ci interessa verificare se la regione dei parametri individuata rimane stabile dopo il trasferimento a nuovi dati. Per questo motivo, il forward testing diventa la logica continuazione dell'analisi di un plateau stabile. Stiamo parlando di verificare la fattibilità della configurazione trovata su una sezione di dati indipendente. Ciò è particolarmente importante per il nostro modello EMA con filtro in modalità RSI. Tali sistemi spesso dimostrano risultati molto interessanti durante il periodo di ottimizzazione, ma perdono sensibilmente qualità dopo aver superato il set di addestramento. È il forward test che ci permette di distinguere le decisioni realmente stabili dai parametri che coincidono per caso con le caratteristiche di uno specifico regime di mercato.

La tabella di ottimizzazione forward mostra immediatamente che i valori migliori del criterio utente nella fase forward non devono necessariamente coincidere con i valori migliori nella fase di ottimizzazione.

Risultati dei test forward

Nel nostro caso, in cima alla tabella vediamo passaggi con un criterio di ottimizzazione molto elevato, ma un'analisi più attenta rivela che alcune di queste decisioni si basano su un numero estremamente ridotto di trade.

Lo squilibrio dei livelli dell’RSI nella parte superiore della tabella è particolarmente indicativo. Il limite inferiore è fissato intorno a 40-45, mentre il limite superiore rientra nell'intervallo 75-90. Ciò significa che il modello inizia effettivamente a tenere conto del regime di mercato globale prevalentemente ascendente e si sposta sensibilmente verso una tendenza unidirezionale. Questo modello può mostrare risultati molto positivi in un segmento in cui il mercato supporta realmente il movimento direzionale, ma quando la fase cambia, perde facilmente una parte significativa della sua efficacia. In altre parole, il rischio di overfitting è già evidente: la strategia è fin troppo adatta alle caratteristiche specifiche del periodo storico.

In questo contesto, la linea del passaggio 244 appare molto più interessante. Non fornisce un valore estremo del criterio, ma appare notevolmente più equilibrato e quindi probabilmente più robusto. Per questa configurazione sono stati utilizzati i seguenti parametri:

  • iFastEMA = 25,
  • iSlowEMA = 135,
  • iUpperLevel = 60,
  • iLowerLevel = 40,
  • iTPmult = 5,
  • iSLmult = 4.

Questa combinazione non mira più a sfruttare al massimo un singolo segmento di mercato favorevole, ma piuttosto a formare una struttura di ingresso più equilibrata e controllata. Di conseguenza, la strategia mostra un profitto di 134.16 USD, un Profit Factor di 4.08, un Drawdown del 7.05% e 5 trade. Per questo periodo, questo sembra un profilo del tutto fattibile e al tempo stesso prudente.

Il grafico del saldo e dell’equity per questo insieme di parametri confermano lo stesso quadro.

Grafico del saldo
Grafico a passaggio singolo per due periodi: ottimizzazione e test forward.
 

Durante la fase di ottimizzazione, durata 72 mesi, la strategia ha completato 259 trade e ha mostrato un profitto del 141.16% con un drawdown massimo del saldo del 63.57% e un Profit Factor di 1.25.

Durante il test forward di 3 mesi, sono stati effettuati 5 trade: il profitto è stato del 13.42%, il drawdown del 3.91%, mentre il Profit Factor è stato di 4.08.

Partendo da un deposito di 1000 dollari, la strategia inizialmente mostra una crescita fino a raggiungere un livello di circa 1700 dollari, dopodiché subisce una correzione significativa, scendendo a un minimo locale di circa 620 dollari nel settembre 2022. A ciò fa seguito una lunga fase di ripresa e una costante tendenza al rialzo che si conclude a un livello di circa 2400 dollari al termine del periodo di ottimizzazione. Nel segmento del test forward si nota anche una tendenza all'aumento del saldo, il che avvalora ulteriormente la conclusione circa la fattibilità pratica della configurazione scelta.

Pertanto, l'ottimizzazione forward ci ha permesso di filtrare soluzioni eccessivamente aggressive e statisticamente fragili, comprese configurazioni con PF superiore a 8, che, a un esame più attento, risultano essere adatte al periodo di test. In questo contesto, il passaggio 244 appare l'opzione più equilibrata: combina una redditività accettabile, un drawdown moderato e una stabilità sufficiente su una sezione dati indipendente. Sono proprio queste soluzioni a rivestire il maggiore interesse nell'ottimizzazione esplorativa, non perché producano il picco più eclatante, ma perché resistono meglio alla prova del tempo e alle mutevoli condizioni di mercato.



Conclusioni

In questo articolo, abbiamo volutamente spostato l'attenzione dalla ricerca del massimo profitto allo studio della robustezza di una strategia di trading. Questo approccio cambia la logica stessa dell'ottimizzazione. L'attenzione non è rivolta ad un singolo passaggio riuscito, ma all'intera superficie dei parametri: dove la strategia si dimostra solida, dove inizia a perdere la sua forma e dove si trasforma in una cima sottile e fragile. Ecco perché i frame di ottimizzazione sono particolarmente utili. Ci permettono di esaminare la struttura dei risultati, il loro comportamento in configurazioni vicine e come la qualità del modello cambia con una piccola variazione dei parametri. In questo senso, i frame diventano uno strumento per la diagnostica ingegneristica.

Da qui la conclusione principale del lavoro:

L'ottimizzazione forte non mira a massimizzare il profitto, ma a massimizzare la comprensione di cosa sia stato effettivamente ottimizzato.

Ed è per questo motivo che, per questo articolo, non è stata scelta la strategia più redditizia, bensì una estremamente semplice e trasparente. In questo contesto, ciò rappresenta un vantaggio metodologico. Avevamo bisogno di un ambiente di test pulito, in grado di dimostrare il processo stesso: dall'ottimizzazione di base alla selezione di un plateau stabile per i test forward e all'analisi dei risultati al di fuori dell'area di addestramento. Questa scelta ci permette di dimostrare l'aspetto principale senza inutili complicazioni: le funzionalità di ottimizzazione di MetaTrader 5 si rivelano particolarmente chiare quando lo sviluppatore inizia a cercare un'area stabile in grado di resistere a un cambiamento del regime di mercato.


Programmi utilizzati nell'articolo

# Nome Tipo Descrizione
1 Expert.mq5 Expert Advisor L'EA per la strategia ottimizzata nell'articolo
2 CrossEMA.mq5 Indicatore Indicatore che genera eventi all’incrocio delleEMA.
3 RSI.mq5 Indicatore RSI con generazione di eventi al superamento dei livelli
4 postanalysis.py Script Script di post-analisi in Python
5 defines.mqh Code Base Libreria di sostituzioni macro e metodi comuni

Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/22578

File allegati |
MQL5.zip (10.25 KB)
Arriva il Nuovo MetaTrader 5 e MQL5 Arriva il Nuovo MetaTrader 5 e MQL5
Questa è solo una panoramica di MetaTrader 5. Non posso descrivere tutte le nuove funzionalità del sistema per un periodo di tempo così breve: i test sono iniziati il 09.09.2009. Questa è una data simbolica e sono sicuro che sarà un numero fortunato. Sono passati alcuni giorni da quando ho ricevuto la versione beta del terminale MetaTrader 5 e MQL5. Non sono riuscito a provare tutte le sue funzionalità, ma sono già sorpreso.
Dalle matrici ai modelli: Come costruire una pipeline di machine learning in MQL5 ed esportarla nel formato ONNX Dalle matrici ai modelli: Come costruire una pipeline di machine learning in MQL5 ed esportarla nel formato ONNX
L'articolo descrive la configurazione di una pipeline di machine learning coordinata in MetaTrader 5 con separazione dei ruoli: Python addestra ed esporta il modello in ONNX, MQL5 riproduce la normalizzazione e la PCA tramite matrice/vettore ed esegue l'inferenza. Questo approccio rende stabili e verificabili i dati di input del modello, e lo strategy tester di MetaTrader 5 fornisce metriche per analizzare il comportamento del sistema.
Utilizza i canali MQL5.community e le chat di gruppo Utilizza i canali MQL5.community e le chat di gruppo
Il sito web MQL5.com riunisce trader di tutto il mondo. Gli utenti pubblicano articoli, condividono codici gratuiti, vendono prodotti nel Market, offrono servizi da freelance e copiano segnali di trading. Puoi comunicare con loro sul Forum, nelle chat dei trader e nei canali MetaTrader.
La potenza di MetaTrader 5: Dal debug passo-passo alla protezione dei file EX5 in un ambiente integrato La potenza di MetaTrader 5: Dal debug passo-passo alla protezione dei file EX5 in un ambiente integrato
Questo articolo esamina un approccio completo allo sviluppo di algoritmi di trading: dalla configurazione del progetto e del debug della logica, alla protezione del prodotto finito. Esploreremo gli strumenti integrati di MetaEditor, tra cui il debug passo-passo tramite tick reali, la profilazione delle prestazioni e l'integrazione diretta con le DLL C++ per velocizzare i calcoli. L'articolo spiega anche come proteggere la proprietà intellettuale utilizzando MQL5 Cloud Protector. L'applicazione delle tecniche descritte trasformerà lo sviluppo degli Expert Advisor da una ricerca caotica di soluzioni in un processo sistematico, riducendo significativamente il tempo necessario per sviluppare una strategia.