English Русский Deutsch 日本語 Türkçe
preview
Architettura basata sugli eventi in MQL5: Come convertire un Expert Advisor in un sistema di trading a tutti gli effetti

Architettura basata sugli eventi in MQL5: Come convertire un Expert Advisor in un sistema di trading a tutti gli effetti

MetaTrader 5Esempi |
14 6
MetaQuotes
MetaQuotes

Introduzione

Quando si sviluppa un Expert Advisor (EA) in MQL5, molti iniziano con la soluzione più ovvia: inseriscono tutta la logica nel metodo OnTick. È davvero più facile iniziare in questo modo. Ma questo approccio ha un costo nascosto. Con la crescita del progetto, le regole di trading, la verifica delle condizioni, l'elaborazione degli ordini, l'aggiornamento dei dati, l'interfaccia, i calcoli e la registrazione vengono tutti combinati in un unico gestore. Di conseguenza, il codice cresce fino a diventare ingestibile e di fatto, è tenuto insieme solo dalla pura fortuna. Qualsiasi cambiamento in un punto inizia a influenzare parti completamente diverse del sistema. Si corregge il pannello visivo e improvvisamente lo scenario di trading smette di funzionare correttamente. Si modifica il filtro di entrata - un errore viene visualizzato durante il controllo in background. Un simile EA si trasforma rapidamente in un monolite fragile, dove la complessità cresce più velocemente della fiducia dello sviluppatore.

MetaTrader 5 è molto più di un semplice flusso di quotazioni. Si basa su eventi. Il terminale riceve costantemente tick, segnali del timer, azioni dell'utente, cambiamenti dello stato di trading ed eventi relativi alla profondità del mercato. Questi messaggi devono essere gestiti separatamente. Per raggiungere questo obiettivo, MQL5 prevede diversi gestori di eventi, ognuno con la propria area di responsabilità. OnTick è responsabile degli aggiornamenti di mercato. OnTimer — per attività periodiche e in background. OnChartEvent — per la reazione all'interfaccia grafica e alle azioni dell'utente. Quando la logica viene distribuita in base al suo scopo, il codice smette di essere disordinato. Inizia ad assomigliare a un sistema ingegneristico ben strutturato, in cui ogni modulo svolge il proprio compito e non interferisce con quelli vicini.

Questa progettazione è particolarmente importante quando l'EA va oltre un singolo simbolo e inizia a svolgere più funzioni simultaneamente. Dobbiamo monitorare il mercato, gestire l'interfaccia, rispondere alla pressione dei pulsanti, sincronizzare lo stato interno, trasmettere segnali tra i componenti e a volte, persino gestire più strumenti contemporaneamente. A questo punto, l'architettura degli eventi non appare più come una bella teoria. Diventa una necessità pratica. Se spostiamo il lavoro in background su OnTimer, mentre la risposta alle azioni dell'utente viene gestita da OnChartEvent, il circuito di trading principale viene liberato da carichi inutili. Ciò rende il comportamento del sistema più prevedibile e semplifica notevolmente la manutenzione.

In tale architettura, eventi e servizi personalizzati rivestono un ruolo separato. Gli eventi personalizzati ci permettono di organizzare un canale interno di messaggistica tra i moduli, mentre i servizi ci consentono di spostare la logica ausiliaria al di fuori di un grafico specifico. Non si tratta più solo di un Expert Advisor (EA), ma di un sistema di componenti in grado di scambiare comandi e segnali senza mescolare tutto in un'unica funzione. Ciò consente di realizzare soluzioni a livello di applicazione di trading con pannelli di controllo, analisi in background, scambio tra moduli e una chiara separazione dei ruoli.

Inoltre, l'approccio basato sugli eventi migliora significativamente la testabilità. Quando la logica è distribuita tra diversi gestori di eventi, è più facile testarla per componenti. Possiamo analizzare separatamente la risposta al timer, agli eventi dell'interfaccia utente e alle transazioni di trading. Ciò è particolarmente importante in compiti in cui un errore può costare tempo e denaro. Quanto migliore è la struttura, tanto più facile è controllare il comportamento del sistema e individuare rapidamente le cause degli errori.

In questo articolo analizzeremo come passare dal modello tutto in OnTick verso un'architettura più matura basata sugli eventi. Analizziamo i ruoli dei gestori di eventi predefiniti e degli eventi personalizzati, nonché dei servizi non collegati a un grafico. Analizziamo più da vicino gli errori tipici che compromettono l'architettura ancor prima che inizi il lavoro vero e proprio. L'idea principale è semplice: quando MQL5 viene utilizzato per lo scopo per cui è stato progettato, ci permette di costruire non solo robot di trading, ma anche sistemi applicativi completi.

Eventi MQL5



Eventi predefiniti

Gli eventi predefiniti in MQL5 costituiscono il framework su cui si basa l'intera logica del programma. Non si tratta semplicemente di un insieme di funzioni di gestione, ma di un modello di risposta rigorosamente definito ai cambiamenti nell'ambiente. E prima lo sviluppatore smette di percepirli come un'aggiunta a OnTick, prima il codice inizia ad acquisire un'architettura.

Il ciclo di vita di qualsiasi programma inizia con OnInit e termina con OnDeinit. Si tratta di una sorta di punti di ingresso e di uscita. OnInit getta le basi: i timer vengono impostati tramite EventSetTimer, vengono creati gli oggetti grafici e vengono inizializzate le strutture interne e i componenti esterni. È inoltre comodo avviare i meccanismi di servizio e preparare l'ambiente in questa sede.

int OnInit()
  {
   EventSetTimer(30); // poll every 30 seconds
   return(INIT_SUCCEEDED);
  }

OnDeinit, a sua volta, richiede disciplina. Tutto ciò che è stato creato deve essere rilasciato correttamente: gli oggetti vengono eliminati, il timer viene disabilitato tramite EventKillTimer, le risorse vengono chiuse. Ignorare questa simmetria è un errore classico che causa la contaminazione dell'ambiente di runtime e la comparsa di piccoli glitch.

void OnDeinit(const int reason)
  {
   EventKillTimer(); // disable the timer when unloading
  }

OnTick è tradizionalmente considerato il cuore dell'EA. Ciò è parzialmente vero. Il suo scopo è quello di reagire alle variazioni di mercato di uno specifico simbolo. Qui si calcolano i segnali, si verificano le condizioni di ingresso e si gestiscono le posizioni. Tuttavia, il punto fondamentale è che la presenza di OnTick non è obbligatoria. Se la logica viene spostata su altri eventi, l'EA può funzionare anche senza di essa. Inoltre, in architetture mature, OnTick diventa spesso un router leggero piuttosto che il centro dell'universo.

OnTimer è uno strumento che molti sottovalutano. Il terminale genera questi eventi a intervalli specifici, consentendo di separare le attività in background da quelle di mercato. Raccolta di statistiche, aggiornamento delle cache, interrogazione di diversi simboli, ricalcolo degli indicatori — tutto ciò rientra logicamente nel timer. Questo approccio riduce il carico di lavoro di OnTick e rende il comportamento del sistema più stabile, soprattutto durante i periodi di elevata volatilità. Ricordate una regola semplice ma importante: il timer non solo deve essere impostato in OnInit, ma deve anche essere necessariamente rimosso in OnDeinit.

OnChartEvent apre le porte al mondo delle applicazioni interattive. Questo è un gestore per gli eventi dell'interfaccia grafica: clic del mouse, pressione dei tasti, interazioni con gli oggetti e soprattutto, eventi personalizzati. Al suo interno sono integrati pannelli di controllo, pulsanti e interruttori di modalità. È qui che viene implementata la risposta ai segnali esterni provenienti da altri programmi. Nel contesto dell'architettura basata sugli eventi, questo non è più un semplice gestore dell'interfaccia utente, ma un vero e proprio canale di comunicazione.

OnTradeTransaction è un punto di controllo dello stato delle transazioni. A differenza degli approcci semplificati in cui viene controllato solo il risultato di un'operazione, qui è disponibile l'intera struttura MqlTradeTransaction. Ciò consente un tracciamento preciso del ciclo di vita dell'ordine: dall'invio all'esecuzione e alle successive modifiche. Questo livello di dettaglio è particolarmente importante negli scenari complessi di gestione delle posizioni, dove non solo l'obiettivo, ma anche il percorso per raggiungerlo è fondamentale.

OnBookEvent viene utilizzato meno frequentemente, ma è insostituibile in determinate attività. Reagisce ai cambiamenti nella profondità del mercato e si attiva tramite MarketBookAdd. Questo è già il territorio di strategie più raffinate, dove non conta solo il prezzo, ma anche la struttura della liquidità. Negli approcci ad alta frequenza o nell'analisi dei movimenti di micro-mercato, questo gestore diventa una fonte chiave di segnali.

Il concetto fondamentale è semplice: ogni tipo di programma in MQL5 riceve il proprio insieme di eventi, ed è attraverso di essi che si definisce il suo comportamento. Cercare di ignorare questo modello e ridurre tutto a un singolo gestore porta inevitabilmente alla complessità. Al contrario, la corretta distribuzione della logica tra gli eventi crea un sistema chiaro, prevedibile e scalabile — il fondamento stesso, senza il quale è impossibile progredire.



Eventi personalizzati

Gli eventi personalizzati in MQL5 sono uno degli strumenti più pratici per la creazione di architetture di eventi. Grazie al loro contributo, il programma non è più isolato. Acquisisce la capacità di trasmettere segnali tra le sue parti con la stessa naturalezza con cui i nodi di un singolo sistema si scambiano messaggi di servizio. Questo risultato si ottiene tramite la funzione EventChartCustom, che ci permette di inviare programmaticamente un evento personalizzato a un grafico specificato e di gestirlo successivamente in OnChartEvent. Si tratta di un canale interno di messaggistica. È particolarmente utile quando un singolo programma deve rispondere al mercato, coordinare il funzionamento dell'interfaccia, dei moduli di servizio e della logica di trading.

Il vantaggio principale di questo approccio è che aiuta a separare le responsabilità tra i moduli. Una componente può essere coinvolta nell'analisi di mercato. L'altra - nella visualizzazione dello stato del pannello. La terza - nell'esecuzione delle operazioni di trading. La quarta - nei calcoli eseguiti in background. Se proviamo a collegare tutto questo con chiamate dirette e variabili globali condivise, il codice inizierà rapidamente a disgregarsi. La manutenzione diventerà come riparare un meccanismo in cui ogni ingranaggio è ingranato con tutti gli altri. Gli eventi personalizzati evitano questa confusione: il modulo non interferisce con la logica interna di altri, ma si limita a inviare un segnale preciso al punto in cui deve essere ricevuto.

La capacità di trasmissione di un evento mirato merita di essere menzionata separatamente. In EventChartCustom, possiamo specificare non solo il grafico corrente, ma anche l'ID del grafico di un'altra finestra. Ciò apre la strada alla comunicazione tra diverse istanze di EA, indicatori e servizi. Questo meccanismo è particolarmente utile nei sistemi multi-simbolo e multi-grafico. Una componente raccoglie o genera informazioni, mentre l'altra le riceve ed esegue un'azione. In questo caso, l'evento personalizzato diventa l'anello di congiunzione che unisce parti disparate in un'unica architettura.

Inoltre, l'idea stessa di scambio di eventi è particolarmente evidente nella fase di inizializzazione. A volte, è necessario sovraccaricare OnInit con azioni preparatorie: creazione di oggetti interni, caricamento dei dati, creazione di cache, configurazione dell'interfaccia, avvio dei servizi. Se tutto ciò viene fatto immediatamente durante l'inizializzazione, il metodo diventa pesante e inizia a rallentare l'avvio del programma. Inoltre, se l'inizializzazione richiede troppo tempo, si rischia di incorrere in un limite di tempo o semplicemente di ottenere un avvio lento e macchinoso. Ha molto più senso lasciare solo la preparazione minima in OnInit e creare immediatamente un evento personalizzato che sposti la parte più complessa a un gestore separato. Il programma inizia quindi rapidamente e la preparazione principale viene effettuata in modalità evento.

Questo è importante anche perché in MQL5, il funzionamento a lungo termine di una funzione blocca la gestione di altri eventi. Richiamare in sequenza diverse piccole funzioni in sequenza non risolve il problema: finché la catena non è completa, nulla può intervenire nel flusso degli eventi. Ciò è particolarmente evidente nei programmi dotati di interfaccia utente. L'utente interagisce con il pannello e attende una risposta, ma il programma non risponde perché sta eseguendo uno script di grandi dimensioni. Si ha la sensazione di un blocco, sebbene in realtà il sistema semplicemente non abbia il tempo di trasferire il controllo al gestore degli eventi. È proprio per questo motivo che il modello a eventi è architettonicamente necessario in questo contesto. Consente di delegare l'orchestrazione a un gestore di eventi che esegue i passaggi in sequenza senza bloccare l'interfaccia o rendere il programma non responsivo.

Questa flessibilità ha uno svantaggio: gli eventi personalizzati non possono essere inviati senza un sistema. È necessario un protocollo chiaro: cosa significano gli ID, dove viene memorizzato il codice del messaggio, come determinare se un evento appartiene all'applicazione. Solitamente, per questo scopo vengono impostati intervalli di ID personalizzati; utilizzare etichette chiare nei parametri di tipo stringa e assicurarsi che l'evento sia effettivamente destinato a questo modulo. Altrimenti, potremmo facilmente ricevere un segnale non correlato che manderebbe in tilt la logica a livello architetturale.

Esiste un'altra regola che viene spesso sottovalutata: non creare eventi troppo spesso, a meno che non ci sia una reale necessità. La coda degli eventi del terminale non gradisce il rumore superfluo. Se gli eventi vengono inviati con troppa frequenza, la coda dei messaggi si sovraccarica e l'elaborazione del segnale si degrada. È preferibile trasmettere solo gli stati significativi: il fatto che i dati siano pronti, la comparsa di un segnale, un comando per aggiornare l'interfaccia, la conferma dell'esecuzione di un'azione. In questo modo gli eventi funzioneranno come promemoria ordinati, anziché come un flusso caotico di telegrammi.

Ecco perché gli eventi personalizzati sono particolarmente preziosi in un sistema maturo. Connettono i moduli senza accoppiamenti rigidi, consentono di costruire protocolli di interazione chiari e trasformano MQL5 in un ambiente per lo sviluppo basato sugli eventi a tutti gli effetti.



Servizi

In MQL5, il servizio è un servizio terminale in background. Nessun grafico, nessun vincolo di simbolo, nessuna attesa di tick. Dopo l'avvio, funziona in autonomia, come un assistente impeccabile che non ha bisogno di essere sollecitato a fare ciò che deve fare.

Tutta la logica risiede nella funzione OnStart. Nella maggior parte dei casi, all'interno del sistema si organizza un ciclo: controllo dello stato, esecuzione dell'attività, pausa tramite Sleep seguito dalla fase successiva. Questo ritmo è semplice e affidabile. Non richiede eventi di mercato e non dipende dall'attività dei grafici. Il servizio si regola secondo i propri ritmi.

I vantaggi pratici diventano subito evidenti. Se avete bisogno di scaricare regolarmente dati da una fonte esterna, il servizio è in grado di gestirlo. Avete bisogno di generare dei tick per un simbolo personalizzato? Il servizio può fare anche questo. È necessario inviare segnali o aggiornare lo stato del sistema ogni pochi secondi? Anche in questo caso, è compito del servizio. Tutto ciò che si ripete, tutto ciò che non dovrebbe dipendere dalle fluttuazioni del mercato, dovrebbe logicamente essere portato qui.

C'è un altro dettaglio che rende il servizio particolarmente conveniente. Se non lo arresti manualmente, si avvierà automaticamente al successivo avvio del terminale. Questo lo trasforma in uno strumento per le operazioni di routine: controllo dello stato all'avvio, preparazione dei dati, sincronizzazione - tutto ciò può essere fatto senza l'intervento dell'utente. Una volta configurato, il servizio funziona ogni giorno con la massima precisione.

In architettura, questo appare molto positivo. L'EA sul grafico reagisce al mercato. Allo stesso tempo, l'interfaccia è orientata all'utente. Nel frattempo, il servizio svolge un lavoro silenzioso e regolare in background. Nessuna complicazione, nessun sovraccarico di OnTick. Questa divisione dei ruoli allevia lo stress inutile all'interno del sistema e lo rende più stabile. Quando ogni parte svolge il proprio compito, l'intera struttura inizia a funzionare in modo sensibilmente più pulito.



Comunicazione tra programmi

Quando una soluzione di trading è composta da più componenti, è necessario un meccanismo di messaggistica. Un modulo può raccogliere dati. Il secondo li mostra sul pannello. Il terzo prende una decisione di trading. Il quarto gestisce la logica di background. E se ognuno di loro iniziasse a vivere nel proprio universo, il risultato non sarebbe un'architettura, ma un groviglio di flag, variabili globali e controlli casuali. Ecco perché la struttura basata sugli eventi funziona particolarmente bene in questo contesto.

La versione più pulita dell'architettura basata sugli eventi si ha quando gli indicatori diventano la fonte dei segnali, sebbene non nel modo convenzionale (richiesta ad un buffer - valore ricevuto), ma come veri e propri generatori di eventi.

L'idea è semplice. Ciascun indicatore è incapsulato in un leggero adattatore. Internamente, svolge una sola funzione: monitora la presenza di un segnale e se la condizione è soddisfatta, invia un evento personalizzato. È tutto pronto. Niente continue richieste dall'esterno, nessun disturbo inutile.

int OnCalculate(const int32_t rates_total,
                const int32_t prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int32_t &spread[])
  {
   if(prev_calculated==rates_total)
     return prev_calculated;
//---
   if(BarsCalculated(handle) < rates_total)
      return(prev_calculated);
   vector<double> sig, main;
   if(!main.CopyIndicatorBuffer(handle, 0, 1, 2) ||
      !sig.CopyIndicatorBuffer(handle, 1, 1, 2))
      return(prev_calculated);
   if(sig[0] > main[0] && sig[1] <= main[1])
     {
      if(!EventChartCustom(ChartID(), BuyID, MagicNumber, main[0], IndComment))
         return(prev_calculated);
     }
   if(sig[0] < main[0] && sig[1] >= main[1])
     {
      if(!EventChartCustom(ChartID(), SellID, MagicNumber, main[0], IndComment))
         return(prev_calculated);
     }
//--- return value of prev_calculated for the next call
   return(rates_total);
  }

Ad esempio, prendiamo due indicatori classici - MACD e RSI. Ciascuno di loro lavora in modo indipendente, secondo il proprio timeframe. Si attivano solo su una nuova barra. MACD verifica l'intersezione dell'istogramma e della linea del segnale. Per l'RSI si considera l'attraversamento dei livelli di ipercomprato e ipervenduto Quando si verifica un segnale, generano eventi personalizzati predefiniti. Questo è un punto fondamentale: l'indicatore non memorizza un segnale nel buffer, bensì registra un evento.

A questo punto entra in gioco l'EA, ma con un ruolo completamente diverso. Non rileva gli indicatori a ogni tick. Non ripercorre la cronologia, non sincronizza i buffer, non cerca di indovinare se c'è stato un segnale. Si limita ad ascoltare gli eventi in OnChartEvent.

void OnChartEvent(const int32_t id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   if(lparam != MagicNumber)
      return;
   switch(id - CHARTEVENT_CUSTOM)
     {
      case MACD1Buy:
         indSignals[0] = 1;
         break;
      case MACD1Sell:
         indSignals[0] = -1;
         break;
      case MACD2Buy:
         indSignals[1] = 1;
         CloseSellSignal = true;
         CloseBuySignal = false;
         break;
      case MACD2Sell:
         indSignals[1] = -1;
         CloseBuySignal = true;
         CloseSellSignal = false;
         break;
      case RSI1CrossOverBoughtDown:
         indSignals[2] = -1;
         break;
      case RSI1CrossOverSoldUp:
         indSignals[2] = 1;
         break;
      case RSI2CrossOverBoughtDown:
         indSignals[3] = -1;
         CloseBuySignal = true;
         CloseSellSignal = false;
         break;
      case RSI2CrossOverSoldUp:
         indSignals[3] = 1;
         CloseSellSignal = true;
         CloseBuySignal = false;
         break;
     }
   BuySignal = true;
   SellSignal = true;
   for(uint i = 0; i < indSignals.Size() && (BuySignal || SellSignal); i++)
     {
      BuySignal = BuySignal && (indSignals[i] == 1);
      SellSignal = SellSignal && (indSignals[i] == -1);
     }
  }

L'EA registra gli eventi in entrata. Se non arriva nulla, non fa nulla.

Ciò modifica drasticamente la natura del carico. Nella struttura classica con l’interrogazione continua degli indicatori, la stessa cosa accade ad ogni tick - richiesta di buffer, controllo dei valori. A volte, potrebbe essere utile anche ripercorrere la cronologia per non perdere il segnale. Ciò diventa particolarmente difficile in presenza di diversi indicatori e intervalli temporali differenti. C'è sempre un intervallo di tempo tra i segnali e dobbiamo costantemente ricontrollare per coglierli.

Il modello a eventi elimina semplicemente questi sforzi superflui. Ogni segnale è un evento già pronto. Non c'è bisogno di cercarlo, ricalcolarlo o ricostruirlo retroattivamente. Si manifesta nel momento dell'emersione.

Il risultato è un diagramma chiaro e veloce:

  • l'indicatore è responsabile solo del proprio calcolo e del proprio segnale;
  • un evento registra il momento del cambiamento di stato;
  • L'EA aggrega i segnali e prende una decisione.

Ciò è particolarmente evidente nelle strategie multi-indicatore. Ad esempio, un segnale arriva da M5, l'altro da M30. Nel modello classico, è necessario sincronizzare i dati, considerare i ritardi e controllare lo storico. Qui tutto è più semplice: ogni indicatore ha segnalato il proprio stato al momento giusto, l'EA salva lo stato e attende che si verifichi l'insieme completo delle condizioni.

In termini di prestazioni, ciò offre un vantaggio notevole. Non ci sono continui riferimenti agli indicatori, né controlli a vuoto ad ogni tick. OnTick diventa facile e veloce, il che significa che la reazione al mercato è più rapida. Nel trading, questa non è solo una teoria, ma un vantaggio concreto: meno ritardi, logica più chiara, esecuzione più precisa.

void OnTick()
  {
//---
   if(BuySignal)
     {
      cSymbol.Refresh();
      cSymbol.RefreshRates();
      if(!cTrade.Buy(InpLot, cSymbol.Name(), cSymbol.Ask(),
                     cSymbol.Ask() - SL * cSymbol.Point(),
                     cSymbol.Ask() + TP * cSymbol.Point(), "Event Example"))
        {
         PrintFormat("Error open Buy position: %d", GetLastError());
         return;
        }
      BuySignal = false;
      ArrayFill(indSignals, 0, indSignals.Size(), 0);
     }
//---
   if(SellSignal)
     {
      cSymbol.Refresh();
      cSymbol.RefreshRates();
      if(!cTrade.Sell(InpLot, cSymbol.Name(), cSymbol.Bid(),
                      cSymbol.Bid() + SL * cSymbol.Point(),
                      cSymbol.Bid() - TP * cSymbol.Point(), "Event Example"))
        {
         PrintFormat("Error open Sell position: %d", GetLastError());
         return;
        }
      SellSignal = false;
      ArrayFill(indSignals, 0, indSignals.Size(), 0);
     }
//---
   if(CloseBuySignal)
     {
      if(cPosition.SelectByMagic(cSymbol.Name(), MagicNumber) &&
         cPosition.PositionType() == POSITION_TYPE_BUY)
        {
         if(!cTrade.PositionClose(cPosition.Ticket()))
           {
            PrintFormat("Error close Buy position: %d", GetLastError());
            return;
           }
        }
     }
//---
   if(CloseSellSignal)
     {
      if(cPosition.SelectByMagic(cSymbol.Name(), MagicNumber) &&
         cPosition.PositionType() == POSITION_TYPE_SELL)
        {
         if(!cTrade.PositionClose(cPosition.Ticket()))
           {
            PrintFormat("Error close Sell position: %d", GetLastError());
            return;
           }
        }
     }
//---
  }

Altrettanto importante è che il codice diventi più trasparente. Il segnale non è più un valore del buffer da interpretare, ma si trasforma in un evento chiaro: accaduto - registrato - gestito. Questo è esattamente il caso in cui l'architettura migliora direttamente sia la leggibilità che il comportamento del sistema.

L'esecuzione dell'EA nel tester di strategie di MetaTrader 5 ha mostrato un quadro interessante. Abbiamo un sistema con un carattere ben definito: genera profitti in modo attivo, ma al tempo stesso mantiene i rischi entro limiti ragionevoli.

Risultati dei test Risultati dei test

Con un deposito di 1000 USD, il risultato è stato di 2427,52 USD, ovvero un +143% in 3 anni. Il risultato è positivo, soprattutto considerando il moderato drawdown. La curva dell’equity appare regolare: la crescita è a gradini, ci sono delle correzioni, ma senza cali prolungati. Il drawdown massimo è di circa il 12-13%, mentre il Recovery Factor è di circa 4,6 - il sistema è in grado di riprendersi dai drawdown senza rimanervi bloccato. Questo è un importante segnale di sostenibilità.

Il meccanismo convenzionale di calcolo del profitto sta funzionando. Complessivamente, 18 trade, con una percentuale di successo del 50%. Ma il profitto medio è quasi quattro volte superiore alla perdita media. Ciò ha permesso a Profit Factor di raggiungere il livello di 3,9. La strategia genera profitto non dalla frequenza, ma dalla qualità dei segnali e dal mantenimento delle posizioni. Ma qui sta un limite. Il numero ridotto di transazioni rende le statistiche fragili.

La curva dell’equity è piuttosto regolare, senza bruschi salti. Ciò conferma indirettamente che i segnali non sono casuali.

Ed ecco un punto architettonico importante. Il modello basato sugli eventi elimina la necessità di interrogare costantemente gli indicatori. L'EA reagisce solo al segnale. Meno operazioni superflue significano tempi di risposta più rapidi. Nel tester si tratta solo di una sottigliezza, ma nel trading reale diventa un vantaggio.

Il risultato è semplice. Quello che abbiamo qui è un modello di segnale con un buon rapporto rischio/rendimento e un'architettura ben progettata. Ma per ora si tratta di un prototipo. Sono necessari test fuori campione e statistiche più approfondite.



Errori tipici

Quando un'architettura diventa basata sugli eventi, il codice diventa più pulito e più veloce. Ma allo stesso tempo, cambia anche la natura degli errori. Non rimangono più in superficie, ma si manifestano nelle connessioni tra i gestori di eventi e nella disciplina del lavoro con le risorse.

Il primo e più urgente problema è il solito sovraccarico di OnTick. Anche dopo aver familiarizzato con il modello ad eventi, si è tentati di lasciare parte della logica lì, per ogni evenienza. Il risultato è un sistema ibrido: ci sono timer ed eventi, ma la maggior parte del lavoro viene comunque svolta da un unico metodo. Questo compromesso ci riporta rapidamente al punto di partenza: codice pesante e mal tenuto. L'esperienza dimostra che, se abbiamo già suddiviso i ruoli, dobbiamo portare a termine il compito.

Il livello successivo di errori è legato alla gestione delle risorse. Il modello a eventi coinvolge più entità: timer, oggetti, sottoscrizioni, strutture ausiliarie. E se non vengono gestiti con attenzione, il sistema comincia a fare rumore. EventKillTimer dimenticato, elementi grafici non rimossi, variabili globali rimaste - tutto ciò non manda in crash il programma immediatamente, ma ne riduce gradualmente la prevedibilità. Di conseguenza, ci troviamo di fronte a una situazione classica: l'errore compare in un punto diverso da quello in cui è stato commesso.

void OnDeinit(const int reason)
  {
//---
   for(uint i = 0; i < handles.Size(); i++)
      if(handles[i] != INVALID_HANDLE)
         IndicatorRelease(handles[i]);
  }

La natura stessa degli eventi richiede un'attenzione particolare. I gestori di eventi vengono eseguiti in sequenza e un'operazione lunga all'interno di un evento blocca le altre. Ciò è particolarmente evidente nell'interfaccia: l'utente interagisce con il pannello, ma il programma non risponde. È impegnato in calcoli. Anche in questo caso, l'idea chiave dell'architettura è evidente: suddividere l'esecuzione in fasi e non tenere il thread occupato più a lungo del necessario.

Quando in un sistema sono presenti più sorgenti di eventi (indicatori, un pannello, un servizio), la disciplina dell'identificazione diventa fondamentale. Se non introduciamo convenzioni chiare per i nomi degli oggetti e i codici degli eventi, inizia la confusione: i segnali si sovrappongono e i gestori reagiscono agli eventi sbagliati. Una semplice regola con prefissi e intervalli di ID può sembrare quasi banale, ma è ciò che mantiene il sistema in ordine quando inizia a crescere.

#define  MACD1Buy                   1
#define  MACD1Sell                  2
#define  MACD2Buy                   3
#define  MACD2Sell                  4
#define  RSI1CrossOverBoughtUp      5
#define  RSI1CrossOverBoughtDown    6
#define  RSI1CrossOverSoldUp        7
#define  RSI1CrossOverSoldDown      8
#define  RSI2CrossOverBoughtUp      9
#define  RSI2CrossOverBoughtDown    10
#define  RSI2CrossOverSoldUp        11
#define  RSI2CrossOverSoldDown      12

E infine, il momento più delicato è l'avvio del programma. In un modello basato sugli eventi, gli eventi possono arrivare quasi immediatamente, anche prima che l'inizializzazione sia completamente ultimata. Se le strutture non sono pronte a questo punto, è facile finire per accedere a dati vuoti o a uno stato errato. Pertanto, l'inizializzazione deve essere completata rigorosamente prima che inizi la gestione, oppure – il che è spesso più conveniente – essere suddivisa in fasi con controllo della prontezza tramite gli stessi eventi personalizzati.

Di conseguenza, è chiaro che gli eventi di per sé non rendono un sistema affidabile. Forniscono solo gli strumenti. L'affidabilità arriva quando questi strumenti sono accompagnati da disciplina ingegneristica. E se esiste, l'architettura basata sugli eventi cessa di essere solo una tecnica conveniente - diventa la base di un sistema di trading stabile e prevedibile.



Conclusioni

Quando la logica del programma è basata sugli eventi, la piattaforma cessa di essere semplicemente un ambiente per gli EA e diventa la base per soluzioni applicative complete. In questo modello, la programmazione assomiglia già allo sviluppo di una classica applicazione desktop: compare un'interfaccia con pulsanti e pannelli, vengono avviati servizi in background e vengono creati gestori separati per i diversi tipi di eventi. Ogni cosa è al suo posto, ed è per questo che il sistema risulta sensibilmente più stabile in fase di manutenzione.

L'approccio basato sugli eventi è particolarmente utile quando un Expert Advisor (EA) tradizionale non è più in grado di gestire il ruolo di un singolo file per tutte le occasioni. Consente di realizzare assistenti multi-simbolo, pannelli di controllo flessibili e controllori complessi che rispondono a cambiamenti semantici di stato. È più facile aggiungere nuove funzionalità a questa architettura: basta introdurre un altro evento o un altro gestore senza dover riscrivere l'intero codice.

Questo è il principale vantaggio dell'architettura basata sugli eventi: fornisce ordine senza perdere flessibilità. Una corretta separazione tra gestori di eventi, servizi e canali di comunicazione consente la creazione di sistemi di trading affidabili, scalabili e realmente dinamici. È proprio in questo ambito che MQL5 si espande oltre il formato tradizionale degli Expert Advisor (EA), affermandosi come ambiente per applicazioni di trading complesse, dove non è importante solo la risposta al mercato, ma anche la solida organizzazione ingegneristica dell'intero programma.


Programmi utilizzati nell'articolo

# Nome Tipo Descrizione
1 EventExample.mq5 Expert Advisor Gestione eventi dell’EA
2 EventMACD.mq5 Indicatore Indicatore MACD con generazione di eventi all'incrocio delle linee
3 EventRSI.mq5 Indicatore Indicatore RSI con generazione di eventi quando i livelli si incrociano

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

File allegati |
MQL5.zip (5.12 KB)
Ultimi commenti | Vai alla discussione (6)
fxsaber
fxsaber | 5 mag 2026 a 15:00
//+------------------------------------------------------------------+
//| Funzione di trading                                                   |
//+------------------------------------------------------------------+
void OnTrade()
  {
   Sleep(0);
//---
  }
//+------------------------------------------------------------------+
//| Funzione TradeTransaction                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
  {
   Sleep(0);
//---
  }
A che scopo?
fxsaber
fxsaber | 5 mag 2026 a 15:02
            if(!cTrade.PositionClose(cPosition.Ticket()))
              {
               PrintFormat("Error close Sell position: %d", GetLastError());
               return;
              }
A quanto pare, cTrade contiene un'istruzione di uscita in caso di errore.
fxsaber
fxsaber | 5 mag 2026 a 15:33
fxsaber #:
Lavorare con gli indicatori senza buffer in un sistema automatizzato: probabilmente può servire da esempio.


Nell'esempio viene effettuato un aggiornamento rigoroso dei dati del simbolo (sarebbe opportuno utilizzare MQL_TESTER).

void OnTick()
  {
//---
   if(BuySignal)
     {
      cSymbol.Refresh();
      cSymbol.RefreshRates();


Tuttavia, non viene verificata l’attualità del segnale calcolato tramite gli eventi e i tick. E questo è un problema reale.


È possibile attenuarla tramite OrderSend asincroni, ma non risolverla. Pertanto, anche in un esempio come questo, è necessario trasmettere additionally nell’evento ChartEvent i dati del tick in cui si è verificata la generazione dell’evento.

Chacha Ian Maroa
Chacha Ian Maroa | 11 mag 2026 a 12:46
Fantastico, il tipo di servizio dei programmi MQL5 è davvero sottovalutato.
Picazzo Research and Development
Picazzo Research and Development | 11 mag 2026 a 14:27
Ottimo esempio, grazie.
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.
Dalla CPU alla GPU in MQL5: Un framework OpenCL pratico per accelerare la ricerca, le ottimizzazioni e il riconoscimento dei pattern. Dalla CPU alla GPU in MQL5: Un framework OpenCL pratico per accelerare la ricerca, le ottimizzazioni e il riconoscimento dei pattern.
Scopri come creare un percorso di migrazione pratico da CPU a GPU in MQL5 utilizzando OpenCL. Ci concentreremo sull'inizializzazione del contesto, sull'organizzazione del buffer, sui batch di grandi dimensioni, sull'avvio del kernel e sulla minimizzazione degli scambi di dati. Verranno inoltre esaminati gli errori più comuni e i modi per eliminarli. Un esempio con i pattern di candele illustra il vantaggio pratico di questo approccio.
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.
MetaTrader 5 e il calendario economico di MQL5: Come Trasformare le Notizie in un Sistema di Trading Riproducibile MetaTrader 5 e il calendario economico di MQL5: Come Trasformare le Notizie in un Sistema di Trading Riproducibile
L'articolo presenta un approccio sistematico al trading di notizie in MetaTrader 5 utilizzando il calendario economico integrato: struttura dei dati, funzioni API, regole di sincronizzazione temporale e filtraggio degli eventi. Vengono descritti metodi di caching e aggiornamento incrementale senza sovraccaricare il server. L'articolo fornisce anche un meccanismo funzionante per esportare la cronologia in una risorsa .EX5 per test deterministici utilizzando lo stesso algoritmo.