OnCalculate MQL5: Come Funziona la Funzione Principale degli Indicatori Custom | MTInvestments

OnCalculate MQL5:
la Funzione Principale
degli Indicatori Custom.

OnCalculate è la funzione obbligatoria di ogni indicatore custom MQL5. È l’equivalente di OnTick per gli EA: viene chiamata ad ogni nuova barra o ad ogni tick. Capire il meccanismo dei parametri rates_total, prev_calculated e il calcolo incrementale è essenziale per costruire indicatori efficienti come l’Ulcer Index di Oracle v78.

1. Struttura di un Indicatore Custom MQL5

Un indicatore custom MQL5 ha una struttura diversa da un Expert Advisor. Non usa OnTick o OnInit (sebbene OnInit esista anche negli indicatori per l’inizializzazione). La funzione principale è OnCalculate, che viene chiamata ogni volta che c’è un nuovo dato da elaborare — ad ogni nuova barra o ad ogni nuovo tick, secondo la modalità dell’indicatore.

struttura_indicatore.mq5MQL5
// Dichiarazioni obbligatorie
#property indicator_chart_window    // Mostra l'indicatore nel grafico principale
// oppure: #property indicator_separate_window  // Finestra separata sotto il grafico
#property indicator_buffers 1       // Numero di buffer di output
#property indicator_plots   1       // Numero di linee da disegnare

// Dichiarazione del buffer
double BufferUlcer[];   // Array che contiene i valori dell'indicatore

// OnInit: configurazione del buffer
int OnInit()
{
    SetIndexBuffer(0, BufferUlcer, INDICATOR_DATA);
    PlotIndexSetString(0, PLOT_LABEL, "Ulcer Index");
    PlotIndexSetInteger(0, PLOT_COLOR, clrOrange);
    PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2);
    IndicatorSetString(INDICATOR_SHORTNAME, "UlcerIndex(14)");
    return INIT_SUCCEEDED;
}

// OnCalculate: logica di calcolo (vedi sezione 2)
int OnCalculate(const int rates_total,
                const int 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 int &spread[])
{
    // ... logica di calcolo ...
    return rates_total; // FONDAMENTALE: indica quante barre sono state elaborate
}

2. La Firma di OnCalculate: i 4 Parametri Chiave

OnCalculate riceve 10 parametri, ma 4 sono critici per capire il meccanismo di aggiornamento efficiente dell’indicatore:

rates_total: il numero totale di barre disponibili nel grafico in questo momento. Cambia ogni volta che si aggiunge una nuova barra (tipicamente una volta ogni 4 ore su H4).

prev_calculated: il numero di barre calcolate durante la chiamata precedente di OnCalculate. Questo è il parametro più importante per il calcolo incrementale: se prev_calculated == rates_total, nessuna nuova barra è stata aggiunta e non c’è nulla da ricalcolare. Se prev_calculated < rates_total, ci sono (rates_total − prev_calculated) barre nuove da calcolare.

close[], high[], low[], open[]: array con i prezzi di tutte le barre. L’indice 0 è la barra più recente (la corrente in formazione), l’indice rates_total−1 è la barra più vecchia.

Logica di aggiornamento incrementale
pos_first = (prev_calculated == 0) ? 0 : prev_calculated - 1
Se prev_calculated == 0: primo calcolo, elabora TUTTE le barre dall’inizio.
Se prev_calculated > 0: ci sono nuove barre, inizia da (prev_calculated - 1) per includere la barra corrente in formazione.
Ritorna rates_total al termine: MT5 usa questo valore come prev_calculated nella prossima chiamata.

3. Calcolo Incrementale: Come Evitare Ricalcoli Inutili

Il calcolo incrementale è la tecnica più importante per gli indicatori custom efficienti. Senza di essa, l’indicatore ricalcola tutti i valori storici ad ogni tick — inutilmente e molto lentamente. Con il calcolo incrementale, ricalcola solo le barre nuove.

calcolo_incrementale.mq5MQL5
int OnCalculate(const int rates_total,
                const int 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 int &spread[])
{
    int PERIOD = 14; // Periodo dell'indicatore

    // Prima barra da calcolare: logica incrementale
    int pos_first;
    if(prev_calculated == 0)
    {
        // Primo calcolo: elabora dall'inizio (barra PERIOD-1 per avere storia sufficiente)
        pos_first = PERIOD - 1;
        // Inizializza le barre precedenti a EMPTY_VALUE (non mostrate sul grafico)
        for(int i = 0; i < pos_first; i++)
            BufferUlcer[i] = EMPTY_VALUE;
    }
    else
    {
        // Calcolo incrementale: ricomincia una barra prima dell'ultima elaborata
        // (-1 per includere la barra corrente in formazione che potrebbe essere aggiornata)
        pos_first = prev_calculated - 1;
    }

    // Calcola il valore per ogni nuova barra
    for(int i = pos_first; i < rates_total; i++)
    {
        if(i < PERIOD - 1) { BufferUlcer[i] = EMPTY_VALUE; continue; }
        
        // Calcola massimo degli ultimi PERIOD periodi
        double max_c = close[i];
        for(int j = i - PERIOD + 1; j <= i; j++)
            if(close[j] > max_c) max_c = close[j];
        
        // Somma quadrati dei drawdown
        double sum_sq = 0;
        for(int j = i - PERIOD + 1; j <= i; j++)
        {
            double dd = (max_c - close[j]) / max_c * 100.0;
            sum_sq += dd * dd;
        }
        BufferUlcer[i] = MathSqrt(sum_sq / PERIOD);
    }

    return rates_total; // Fondamentale per il meccanismo incrementale
}

4. Buffer di Output: Come Mostrare i Valori sul Grafico

I buffer di output sono array dichiarati globalmente e collegati ai plot visivi tramite SetIndexBuffer(). MT5 usa questi array come fonte dei dati da disegnare sul grafico. Regole fondamentali: usa EMPTY_VALUE (8.988×10307 in MQL5) per i valori che non devono essere mostrati (barre con storia insufficiente). Non assegnare mai 0 ai valori mancanti: 0 verrà disegnato sul grafico come una linea a zero.

5. Costruire l’Ulcer Index come Indicatore Custom

L’Ulcer Index di Peter Martin (1987) misura il drawdown percentuale dal massimo recente. È la metrica alla base di Oracle v78. Il codice di OnCalculate nella sezione 3 implementa già l’Ulcer Index completo. Una volta compilato e installato in MT5, l’indicatore mostra la curva dell’Ulcer Index su qualsiasi grafico. La soglia dell’80° percentile storico — usata da Oracle v78 come gate — è aggiunta come linea orizzontale dinamica calcolata negli ultimi 100 periodi.

6. Richiamare l’Indicatore da un EA con iCustom

icustom_ulcer.mq5MQL5
// Legge il valore dell'Ulcer Index dall'indicatore custom
// "UlcerIndex" deve essere il nome del file .ex5 nella cartella Indicators/

// Richiama l'indicatore con periodo 14, legge buffer 0, shift 1 (ultima barra chiusa)
double ulcer_value = iCustom(_Symbol, PERIOD_H4,
                              "UlcerIndex", // nome file
                              14,            // parametro input: periodo
                              0,             // indice buffer
                              1);            // shift: 1 = ultima barra chiusa

// Calcola il percentile su 100 periodi storici
int count_below = 0;
for(int i = 2; i <= 101; i++) // shift da 2 a 101 = 100 valori storici
{
    double hist_val = iCustom(_Symbol, PERIOD_H4, "UlcerIndex", 14, 0, i);
    if(hist_val < ulcer_value) count_below++;
}
double percentile = (double)count_below / 100.0 * 100.0;
Print("Ulcer corrente: ", ulcer_value, " | Percentile: ", percentile, "%");
💡 Questa è la chiamata iCustom che Oracle v78 usa internamente. L’EA non ricalcola l’Ulcer Index da zero — richiama l’indicatore custom già calcolato da MT5, rendendo il codice modulare e più semplice da mantenere.

7. Errori Comuni in OnCalculate

Errore 1: Non ritornare rates_total. Se OnCalculate ritorna un valore diverso da rates_total (es. 0 o prev_calculated), MT5 non aggiorna correttamente il contatore per il calcolo incrementale. La prossima chiamata avrà prev_calculated=0 e ricalcolerà tutto daccapo.

Errore 2: Accedere a indici fuori range. Se l’indicatore usa una finestra di 14 periodi, le prime 13 barre (indici 0–12) non hanno storia sufficiente. Assegna EMPTY_VALUE per quelle barre invece di calcolare con dati insufficienti.

Errore 3: Usare shift=0 in iCustom da un EA. La barra corrente (shift=0) è in formazione: il suo valore dell’Ulcer Index cambia ad ogni tick. Per segnali stabili usa sempre shift=1 in iCustom.

8. Come Oracle v78 Usa gli Indicatori Custom

Oracle v78 usa iCustom per richiamare l’Ulcer Index percentile calcolato dall’indicatore custom dedicato. Questo design modulare ha un vantaggio preciso: l’indicatore custom è calcolato una volta sola da MT5 e il suo buffer è condiviso tra tutti gli EA e indicatori che lo richiedono sullo stesso grafico. Se Oracle UI e Oracle Scanner richiedono entrambi l’Ulcer Index, MT5 calcola il buffer una volta sola e lo restituisce a entrambi — nessun ricalcolo doppio.

Domande Frequenti

Cosa fa la funzione OnCalculate in MQL5? ▼
OnCalculate è la funzione principale di ogni indicatore custom MQL5. Viene chiamata ogni volta che c’è un nuovo dato da elaborare (nuovo tick o nuova barra). Riceve i prezzi di tutte le barre come parametri, calcola i valori dell’indicatore e li scrive nei buffer di output che MT5 usa per disegnare le linee sul grafico. È l’equivalente di OnTick per gli EA.
Cosa sono rates_total e prev_calculated? ▼
rates_total è il numero totale di barre disponibili nel grafico. prev_calculated è il numero di barre già elaborate nella chiamata precedente. La differenza (rates_total − prev_calculated) indica quante nuove barre devono essere calcolate. Questo meccanismo permette il calcolo incrementale: si calcolano solo le barre nuove invece di ricalcolare tutto lo storico ad ogni tick.
Come si chiama un indicatore custom da un EA in MQL5? ▼
Tramite la funzione iCustom(). Sintassi: iCustom(_Symbol, PERIOD_H4, "NomeFile", param1, param2, buffer_index, shift). Il nome file è il nome dell’.ex5 nella cartella Indicators/. buffer_index indica quale buffer leggere (0 per il primo). shift indica quale barra leggere (1 = ultima chiusa, 0 = corrente in formazione).

9. OnCalculate con Array di Prezzo: la Versione Breve

Esiste una seconda firma di OnCalculate, più concisa, usata quando l’indicatore opera su un singolo array di prezzi (tipicamente i prezzi di chiusura):

oncalculate_short.mq5MQL5
// Versione breve: riceve direttamente l'array dei prezzi
// Utile per indicatori su un solo prezzo (es. RSI custom, Ulcer su close)
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,           // Primo indice valido dell'array
                const double &price[])     // Array prezzi (configurato in OnInit)
{
    int pos_first = (prev_calculated == 0) ? begin + 14 : prev_calculated - 1;
    
    for(int i = pos_first; i < rates_total; i++)
    {
        if(i < 14) { Buffer[i] = EMPTY_VALUE; continue; }
        
        // Calcola massimo degli ultimi 14 periodi
        double max_p = price[i];
        for(int j = i - 13; j <= i; j++)
            if(price[j] > max_p) max_p = price[j];
        
        // Ulcer Index
        double sum = 0;
        for(int j = i - 13; j <= i; j++)
        {
            double dd = (max_p > 0) ? (max_p - price[j]) / max_p * 100 : 0;
            sum += dd * dd;
        }
        Buffer[i] = MathSqrt(sum / 14.0);
    }
    return rates_total;
}
💡 In OnInit configura quale prezzo ricevere: PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, 14) e usa #property indicator_applied_price PRICE_CLOSE. La versione breve è più leggibile per indicatori mono-prezzo.

10. Performance: Quanto È Veloce OnCalculate?

La velocità di OnCalculate è critica per indicatori usati come input di EA attivi. Su un hardware moderno (i5/i7) su Windows:

Tipo di calcoloBarreTempo tipicoImpatto su MT5
MA20 semplice10.000<1msTrascurabile
Ulcer Index(14) incrementaleSolo nuove barre (1–3)<1ms per aggiornamentoTrascurabile
Ulcer Index(14) full recalc10.000 barre~50–200msLento al primo caricamento
Ulcer Index con O(n²) per barra10.000 barre~5–20 secondiBlocca MT5

Il calcolo incrementale (solo nuove barre) è essenziale per gli indicatori usati in produzione. Il ricalcolo completo avviene solo al primo caricamento dell’indicatore o quando si cambia il timeframe del grafico — accetta che richieda qualche secondo. Per i tick successivi, il calcolo incrementale è praticamente istantaneo.

11. Debug di un Indicatore Custom: Tecniche Pratiche

Il debug degli indicatori custom è più complesso degli EA perché non si può avviare un indicatore con il debugger standard durante l’esecuzione live. Le tecniche più efficaci:

✔ Il pattern di sviluppo di Oracle v78

L’Ulcer Index custom di Oracle v78 è stato validato confrontando i valori barra per barra con l’implementazione Python per 3.000 barre H4. Le prime versioni avevano discrepanze nell’edge handling (prime N barre con storia insufficiente). La versione finale corrisponde all’implementazione Python con differenza massima <0.001% — puramente floating point.

Implicazioni Pratiche: dal Concetto al Sistema Operativo

La distanza tra la teoria statistica e il trading operativo è colmata solo dall’implementazione sistematica. Un trader può conoscere perfettamente la definizione di Profit Factor, Ulcer Index, edge statistico e mean reversion — ma se queste conoscenze non si traducono in regole operative codificate e rispettate meccanicamente, rimangono concetti accademici senza impatto sulla performance.

Oracle v78 rappresenta il punto di arrivo di questo processo: anni di ricerca statistica (in Python su dati XAUUSD H4), validazione su campioni out-of-sample separati, implementazione in MQL5 (8.293 righe di codice), e infine esecuzione meccanica su 65.587 trade reali senza deroghe alle regole. Il sistema ha una risposta precisa a ogni condizione di mercato: score sopra soglia + Ulcer Index > 80° percentile = apri il trade con 1% di rischio. Score sotto soglia = non operare. Non c’è spazio per l’interpretazione soggettiva.

Questo approccio sistematico ha un costo: la flessibilità di adattamento in tempo reale è zero. Oracle v78 non “sente” che il mercato è cambiato e non modifica il suo comportamento finché i parametri non vengono aggiornati attraverso un processo formale di ri-ottimizzazione. Questo è un vantaggio, non uno svantaggio: i trader discrezionali che “adattano” il loro sistema in tempo reale stanno quasi sempre razionalizzando bias comportamentali, non rispondendo a cambiamenti strutturali del mercato.

Il percorso consigliato per chi vuole passare dal trading discrezionale al trading sistematico: inizia usando Oracle v78 come strumento educativo — osserva come il sistema identifica le condizioni di ingresso, come gestisce le posizioni, come rispetta sistematicamente lo stop loss e il take profit. Poi, se ti interessa costruire il tuo sistema, applica gli stessi principi statistici (edge misurabile, validazione out-of-sample, position sizing fisso) al tuo strumento e timeframe preferiti. La differenza tra un trader sistematico e uno discrezionale non è nel mercato che tratta — è nel processo decisionale che usa.

La live gratuita su oracle.mtinvestments.it mostra Oracle v78 in operazione su MetaTrader 5 in tempo reale. Non è una presentazione: è il conto MT5 live con il sistema attivo, i trade aperti, lo storico completo e tutte le metriche visibili. È l’unico modo per verificare in modo indipendente quello che questo articolo descrive.

Conclusioni: la Statistica Come Bussola nel Mercato

I mercati finanziari sono uno dei pochi ambienti in cui intuizioni, esperienze passate e capacità analitiche individuali sono costantemente sfidate dall’incertezza fondamentale dei prezzi futuri. Nessun trader — sistemático o discrezionale — può eliminare questa incertezza. Ma è possibile costruire un processo decisionale che la gestisca in modo statisticamente coerente, che riduca i bias comportamentali e che crei un edge misurabile nel lungo periodo.

Oracle v78 con 65.587 trade reali non è la prova che il futuro sarà identico al passato. È la prova che un sistema con fondamento statistico solido, implementato correttamente e rispettato meccanicamente, può generare rendimenti positivi aggiustati per il rischio su un orizzonte temporale sufficientemente lungo. Il CAGR 14,3% con MDD 13,1% su 19 anni di dati reali è un risultato che si ottiene non per fortuna, ma per la combinazione di edge statistico genuino e disciplina esecutiva assoluta.

Il passo successivo è verificare questi dati in prima persona. La live gratuita su oracle.mtinvestments.it è aperta: nessun costo, nessun deposito richiesto, solo il sistema in azione su MT5 in tempo reale. Scrivi “ORACLE” su WhatsApp a Rossella (+39 380 635 8296) per ricevere la guida completa e iniziare il percorso di verifica.

Considerazioni Finali sul Trading Sistematico

Il trading sistematico richiede un cambiamento di prospettiva fondamentale: non si tratta di trovare il “segreto del mercato” o di avere intuizioni brillanti. Si tratta di costruire un processo statisticamente robusto, implementarlo in modo rigoroso e mantenerlo operativo con disciplina assoluta nel lungo periodo. Questi concetti — edge statistico, EV positivo, Profit Factor, win rate condizionato, position sizing fisso — non sono sofisticati nel senso di “difficili da capire”. Sono sofisticati nel senso di “richiedono di abbandonare le intuizioni naturali su come funzionano i mercati”.

L’intuizione naturale dice che bisogna avere ragione la maggior parte delle volte. La statistica dice che basta avere ragione abbastanza spesso rispetto al rapporto rischio/rendimento. L’intuizione naturale dice che più si analizza, meglio si capisce il mercato. La statistica dice che più si analizza senza un framework ipotetico, più si è esposti al data-snooping bias. L’intuizione naturale dice che i pattern tecnici visibili sul grafico sono significativi. La statistica dice che la maggior parte dei pattern tecnici non supera un test statistico formale su un campione adeguato.

Oracle v78 è il risultato di 19 anni di lavoro che ha progressivamente sostituito l’intuizione con la statistica: dall’analisi dell’Ulcer Index di Peter Martin (1987), alla scoperta del gate condizionale su XAUUSD H4, all’implementazione in MQL5, fino ai 65.587 trade reali verificabili. Non è un sistema perfetto — può avere drawdown del 13%, può avere periodi di lateralità di settimane, può perdere trade in sequenza. Ma ha edge statistico documentato, e con edge statistico positivo e gestione del rischio corretta, il lungo periodo è sempre favorevole.

Per chi vuole iniziare questo percorso: entra nella live gratuita su oracle.mtinvestments.it. Non per comprare qualcosa, ma per vedere con i propri occhi come si presenta un sistema con dati reali e metriche trasparenti. Confrontalo con ciò che il mercato degli “educatori trading” propone normalmente — segnali senza statistiche, rendimenti senza drawdown, promesse senza dati verificabili. La differenza sarà immediatamente evidente.

⚡ Oracle v78 — 65.587 trade reali MT5

Dalla Teoria
al Sistema Verificabile.

WR 42,1%, PF 1,44, CAGR 14,3%, MDD 13,1% su 65.587 trade reali XAUUSD H4. La live gratuita mostra il sistema in azione — schermata MT5 live, nessuna slide.

65.587 trade realiWR 42,1% PF 1,44CAGR 14,3% MDD 13,1%Zenith Zero · il sistema completo
ENTRA NELLA LIVE GRATUITA →

Scrivi “SERIO” a Rossella su WhatsApp Nessun costo. Risposta entro 2 ore. P.IVA IT14349331000
Manuel Trotta — Trader quantitativo, fondatore MTInvestments, autore Hoepli
◆ Scritto da

Manuel Trotta

Trader quantitativo italiano dal novembre 2007 — 19 anni online. Fondatore MTInvestments, creatore UlcerZenith Oracle v78 con 65.587 trade reali verificati H4 XAUUSD. Autore di 18 libri sul trading e finanza personale: "Fare Trading con Excel" (Hoepli 2019), "Diventa Investitore Profittevole in 15 Minuti al Giorno", "Il Latte con la Mucca" (memoir 2026), più 2 volumi tecnici. Speaker IT Forum Rimini. Online dal 2007 — stesso nome, stessa P.IVA.

P.IVA 14349331000 Dal Novembre 2007 18 Libri su Amazon ↗ 150K+ Follower