Array in MQL5: Guida Completa con Esempi Reali per Expert Advisor | Manuel Trotta

Array in MQL5:
La Guida Definitiva con Codice Reale
da Expert Advisor Professionali.

Gli array sono la struttura dati fondamentale in MQL5. Gestire prezzi storici, buffer degli indicatori, risultati di calcoli statistici, serie temporali — tutto passa dagli array. Questa guida copre tutto: statici, dinamici, multidimensionali, ArraySeries, buffer degli indicatori, sorting, ricerca binaria, ottimizzazione della memoria. Con codice reale usato in Oracle v78 UlcerZenith.

1. Array Statici e Dinamici: Le Differenze che Contano

In MQL5 esistono due tipi di array: statici (dimensione fissa definita al momento della dichiarazione) e dinamici (dimensione modificabile a runtime con ArrayResize()). La scelta tra i due non è arbitraria — ha implicazioni dirette sulle performance e sulla stabilità dell’EA.

Array Statici

Un array statico viene dichiarato con la dimensione specificata tra parentesi quadre. La memoria viene allocata al momento della compilazione e rimane fissa per tutta la vita dell’oggetto che lo contiene. Sono ideali quando la dimensione è nota e costante: 5 timeframe, 4 componenti di scoring, 12 mesi dell’anno.

array_statico_dichiarazione.mq5MQL5
// Array statici: dimensione fissa a compile-time
double prezziChiusura[100];      // 100 double, inizializzati a 0.0
int    contatoriSignali[5];      // 5 interi per 5 componenti di scoring
bool   filtriAttivi[8];          // 8 flag booleani
string nomiSymbol[6] = {"EURUSD","USDJPY","XAUUSD","AUDUSD","GBPUSD","NZDUSD"};

// Array statico multidimensionale
double matriceCorrelazione[6][6]; // Matrice 6×6 per correlazioni cross-asset

// Dimensione array: operatore ArraySize()
int n = ArraySize(prezziChiusura); // n = 100
int r = ArrayRange(matriceCorrelazione, 0); // righe = 6
int c = ArrayRange(matriceCorrelazione, 1); // colonne = 6

// Gli array statici NON possono essere ridimensionati
// ArrayResize(prezziChiusura, 200); // ERRORE di compilazione se dichiarati così
💡 Gli array statici dichiarati come membri di una classe o come variabili globali persistono per tutta la sessione MT5. Quelli dichiarati dentro una funzione vengono distrutti quando la funzione ritorna.

Array Dinamici

Un array dinamico viene dichiarato senza specificare la dimensione. È essenzialmente un puntatore a un’area di memoria che può essere riallocata con ArrayResize(). Sono indispensabili quando la dimensione dipende da dati runtime: il numero di barre disponibili, il numero di operazioni in un periodo variabile, la lunghezza di un dataset da calcolare.

array_dinamico_lifecycle.mq5MQL5
// Dichiarazione array dinamico (senza dimensione)
double bufferUlcerIndex[];
double bufferRSI[];
int    indiciSegnali[];

// Allocazione memoria con ArrayResize()
// Secondo parametro opzionale: reserve (pre-alloca memoria extra)
// → evita riallocazioni costose durante cicli di push
int barre = iBars(_Symbol, _Period);
ArrayResize(bufferUlcerIndex, barre);          // Alloca esattamente 'barre' elementi
ArrayResize(bufferRSI, barre, 1000);           // Alloca 'barre' + riserva 1000 elementi
ArrayResize(indiciSegnali, 0);                 // Array vuoto, pronto per crescere

// Aggiunta elemento (pattern "push_back"):
int nuovoIndice = 1234;
int vecchiaDim  = ArraySize(indiciSegnali);
ArrayResize(indiciSegnali, vecchiaDim + 1, 100); // +1 elemento, riserva 100
indiciSegnali[vecchiaDim] = nuovoIndice;

// Liberare la memoria quando non più necessaria
ArrayFree(bufferUlcerIndex);
ArrayFree(bufferRSI);

// Copiare un array dinamico in un altro
double copia[];
ArrayCopy(copia, bufferRSI);          // Copia completa
ArrayCopy(copia, bufferRSI, 0, 0, 50); // Copia solo i primi 50 elementi

// Inizializzare tutti gli elementi a un valore specifico
ArrayInitialize(bufferUlcerIndex, 0.0);
ArrayInitialize(filtriAttivi, false);  // Su array bool
⚠ Il parametro reserve di ArrayResize() è critico per le performance. Se aggiungi elementi uno alla volta in un ciclo senza reserve, MQL5 riallocherà la memoria a ogni chiamata — l’O(n²) ammazzerà la velocità dell'EA. Usa sempre una riserva generosa nelle operazioni di push_back.

2. Accesso agli Elementi: Indicizzazione Diretta e ArraySeries

In MQL5 esiste una distinzione fondamentale che confonde quasi tutti i principianti e molti intermedi: la direzione dell’indicizzazione. Un array normale ha l’elemento 0 come primo elemento inserito (l’elemento più vecchio se parli di prezzi storici). Un array “series” ha l’elemento 0 come l’elemento più recente (l’ultima barra formata).

Questa distinzione è fondamentale perché le funzioni di copia dei prezzi storici (CopyClose(), CopyHigh(), ecc.) producono array series per default, ma le funzioni degli indicatori e i buffer degli indicatori si comportano diversamente. Confondere i due porta a bug subdoli che sono quasi impossibili da debuggare senza capire questo meccanismo.

array_series_direzione.mq5MQL5
double chiusure[];
double chiusureAsc[];

// CopyClose con array dinamico: di default è SERIES (indice 0 = barra più recente)
CopyClose(_Symbol, PERIOD_H4, 0, 100, chiusure);

// chiusure[0] = prezzo chiusura barra H4 più recente (barra 0)
// chiusure[1] = prezzo chiusura barra H4 precedente (barra 1)
// chiusure[99] = prezzo chiusura H4 di 99 barre fa

// Verifica e impostazione manuale della direzione
if(ArrayGetAsSeries(chiusure))
    Print("L'array è in modalità series (0 = più recente)");

// Forza direzione crescente (0 = più vecchio, utile per calcoli statistici)
ArraySetAsSeries(chiusureAsc, false);
CopyClose(_Symbol, PERIOD_H4, 0, 100, chiusureAsc);
// chiusureAsc[0] = barra più vecchia (99 barre fa)
// chiusureAsc[99] = barra più recente

// ATTENZIONE: inversione su array esistente
double temp[];
ArrayCopy(temp, chiusure);
ArraySetAsSeries(temp, false); // Inverte LOGICAMENTE la direzione (non copia i dati)
// Ora temp[0] = vecchio, temp[99] = recente — ma è il PUNTATORE a cambiare, non i dati

// Pattern sicuro per confronto su serie temporali:
// Usa sempre ArrayGetAsSeries() prima di accedere agli elementi in funzioni generiche
void ProcessaArray(double &arr[])
{
    bool isSeries = ArrayGetAsSeries(arr);
    int  n        = ArraySize(arr);
    for(int i = 0; i < n-1; i++)
    {
        // Se series: arr[0] è più recente → confronto con arr[1] è "oggi vs ieri"
        // Se non series: arr[n-1] è più recente → confronto è invertito
        double corrente  = isSeries ? arr[i]   : arr[n-1-i];
        double precedente = isSeries ? arr[i+1] : arr[n-2-i];
        double variazione = corrente - precedente;
        // ... elaborazione
    }
}
💡 Regola pratica Oracle: tutti gli array che rappresentano serie temporali vengono esplicitamente impostati a SERIES=true dopo la copia, indipendentemente da come li hai ricevuti. Aggiunge una riga ma elimina una categoria intera di bug.

3. Array Multidimensionali in MQL5

MQL5 supporta array fino a 4 dimensioni. Gli array multidimensionali in MQL5 sono implementati come array a righe continue in memoria (row-major order), come in C. Questo ha implicazioni importanti per le performance: accedere agli elementi lungo la prima dimensione è più lento che accedere lungo l’ultima (cache miss).

array_multidimensionale_matrice.mq5MQL5
// Matrice di correlazione 6×6 tra 6 coppie forex
double corrMatrix[6][6];

// Calcolo correlazione di Pearson tra due serie
double PearsonCorrelation(const double &x[], const double &y[], int n)
{
    double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0, sumY2 = 0;
    for(int i = 0; i < n; i++)
    {
        sumX  += x[i];
        sumY  += y[i];
        sumXY += x[i] * y[i];
        sumX2 += x[i] * x[i];
        sumY2 += y[i] * y[i];
    }
    double num = n * sumXY - sumX * sumY;
    double den = MathSqrt((n * sumX2 - sumX*sumX) * (n * sumY2 - sumY*sumY));
    if(den == 0.0) return(0.0);
    return(num / den);
}

// Riempimento matrice di correlazione
string simboli[6] = {"EURUSD","USDJPY","XAUUSD","AUDUSD","GBPUSD","NZDUSD"};
int LOOKBACK = 200; // 200 barre H4 = ~33 giorni di trading

void AggiornaMtriceCorrelazione()
{
    double rendimenti[6][];  // Array di array dinamici (jagged array)
    
    for(int i = 0; i < 6; i++)
    {
        double prezzi[];
        int copiati = CopyClose(simboli[i], PERIOD_H4, 0, LOOKBACK+1, prezzi);
        ArrayResize(rendimenti[i], LOOKBACK);
        for(int j = 0; j < LOOKBACK; j++)
            rendimenti[i][j] = MathLog(prezzi[j] / prezzi[j+1]); // log-return
    }
    
    // Riempimento simmetrico della matrice
    for(int i = 0; i < 6; i++)
    {
        corrMatrix[i][i] = 1.0; // Autocorrelazione = 1
        for(int j = i+1; j < 6; j++)
        {
            double r = PearsonCorrelation(rendimenti[i], rendimenti[j], LOOKBACK);
            corrMatrix[i][j] = r;
            corrMatrix[j][i] = r; // Matrice simmetrica
        }
    }
}

// Accesso a un elemento specifico
double corrAUDUSD_NZDUSD = corrMatrix[3][5]; // Indici di AUDUSD e NZDUSD nell'array simboli

// ArrayRange per dimensioni
int righe    = ArrayRange(corrMatrix, 0); // 6
int colonne  = ArrayRange(corrMatrix, 1); // 6

// Dimensione totale
int totale = ArraySize(corrMatrix); // 36 (6×6)
⚠ MQL5 non supporta array “jagged” (array di array con dimensioni diverse) come oggetti di prima classe. Il pattern double arr[6][] non compila. Per array irregolari usa array piatti con offset manuali oppure strutture dinamiche.

4. Array come Buffer degli Indicatori Personalizzati

Questa è forse la funzionalità più importante degli array in MQL5 per chi sviluppa indicatori. I buffer degli indicatori sono array speciali collegati direttamente al motore di rendering del grafico di MT5. Ogni valore che scrivi nel buffer appare automaticamente sul grafico.

indicatore_buffer_setup.mq5MQL5
// File: UlcerIndex_Indicator.mq5
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   3

// Dichiarazione buffer come variabili globali dell'indicatore
double BufferUlcer[];      // Buffer 0: valore Ulcer Index
double BufferSogliaSup[];  // Buffer 1: soglia superiore (livello di allerta)
double BufferSogliaInf[];  // Buffer 2: soglia inferiore (zona di entrata)

//--- Parametri input
input int    InpPeriodo = 14;        // Periodo Ulcer Index
input double InpSogliaAlta = 7.5;   // Soglia superiore
input double InpSogliaBassa = 2.0;  // Soglia inferiore

int OnInit()
{
    // Registrazione dei buffer — DEVE avvenire in OnInit()
    SetIndexBuffer(0, BufferUlcer,     INDICATOR_DATA);  // Dati visualizzati
    SetIndexBuffer(1, BufferSogliaSup, INDICATOR_DATA);
    SetIndexBuffer(2, BufferSogliaInf, INDICATOR_DATA);
    
    // Proprietà visive dei buffer
    PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
    PlotIndexSetInteger(0, PLOT_LINE_COLOR, 0xC8A84B);  // Gold
    PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2);
    PlotIndexSetString(0,  PLOT_LABEL, "Ulcer Index");
    
    PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE);
    PlotIndexSetInteger(1, PLOT_LINE_COLOR, 0xCC2200);  // Red
    PlotIndexSetInteger(1, PLOT_LINE_STYLE, STYLE_DOT);
    PlotIndexSetString(1,  PLOT_LABEL, "Soglia Alta");
    
    PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_LINE);
    PlotIndexSetInteger(2, PLOT_LINE_COLOR, 0x00D084);  // Green
    PlotIndexSetInteger(2, PLOT_LINE_STYLE, STYLE_DOT);
    PlotIndexSetString(2,  PLOT_LABEL, "Soglia Bassa");
    
    // Valore EMPTY per barre non calcolate
    PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
    
    // Numero minimo di barre necessarie prima del calcolo
    IndicatorSetInteger(INDICATOR_DIGITS, 2);
    
    return(INIT_SUCCEEDED);
}

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[])
{
    // rates_total = numero totale di barre disponibili
    // prev_calculated = quante barre erano già state calcolate nella chiamata precedente
    // Ottimizzazione: ricalcola solo le nuove barre
    
    int start = (prev_calculated > InpPeriodo) ? prev_calculated - 1 : InpPeriodo;
    
    for(int i = start; i < rates_total; i++)
    {
        // Calcolo Ulcer Index su InpPeriodo barre precedenti
        double massimoPeriodo = 0.0;
        double sommaScartiQuadratici = 0.0;
        
        for(int j = i - InpPeriodo + 1; j <= i; j++)
        {
            if(close[j] > massimoPeriodo) massimoPeriodo = close[j];
        }
        
        for(int j = i - InpPeriodo + 1; j <= i; j++)
        {
            double drawdown = 0.0;
            if(massimoPeriodo > 0)
                drawdown = (massimoPeriodo - close[j]) / massimoPeriodo * 100.0;
            sommaScartiQuadratici += drawdown * drawdown;
        }
        
        BufferUlcer[i]     = MathSqrt(sommaScartiQuadratici / InpPeriodo);
        BufferSogliaSup[i] = InpSogliaAlta;
        BufferSogliaInf[i] = InpSogliaBassa;
    }
    
    return(rates_total); // Indica quante barre sono state calcolate
}
💡 Il valore di ritorno di OnCalculate() è il parametro che diventerà prev_calculated alla chiamata successiva. Restituire rates_total dice a MT5 che hai calcolato tutto, ottimizzando le chiamate successive.

5. Ordinamento degli Array: ArraySort e Algoritmi Custom

MQL5 fornisce ArraySort() nativo per array monodimensionali. È un quicksort O(n log n) che ordina in senso crescente. Il problema è che ordina solo l’array passato — se hai un array parallelo di indici da mantenere sincronizzato, devi implementare un ordinamento custom.

array_sorting_avanzato.mq5MQL5
// === CASO 1: ArraySort nativo — semplice ma limitato ===
double valori[] = {5.2, 1.8, 9.1, 3.4, 7.6};
ArraySort(valori); // {1.8, 3.4, 5.2, 7.6, 9.1} — crescente, in-place

// Inversione per ordinamento decrescente:
ArraySort(valori);
ArrayReverse(valori); // MQL5 >= build 2361, o implementa manualmente

// === CASO 2: Ordinamento con indici paralleli (ArgSort) ===
// Necessario per trovare "i 5 valori più alti e sapere da quali barre vengono"

void ArgSort(const double &arr[], int &indici[], bool decrescente = false)
{
    int n = ArraySize(arr);
    ArrayResize(indici, n);
    for(int i = 0; i < n; i++) indici[i] = i;  // indici[i] = i inizialmente
    
    // Insertion sort (buono per n < 100, semplice da implementare)
    for(int i = 1; i < n; i++)
    {
        int   keyIdx = indici[i];
        double keyVal = arr[keyIdx];
        int   j = i - 1;
        
        while(j >= 0 && (decrescente ? arr[indici[j]] < keyVal : arr[indici[j]] > keyVal))
        {
            indici[j+1] = indici[j];
            j--;
        }
        indici[j+1] = keyIdx;
    }
}

// Utilizzo:
double scoreComponenti[] = {3.2, 7.8, 1.4, 9.1, 5.5, 4.3, 8.7, 2.1};
int    ordine[];
ArgSort(scoreComponenti, ordine, true); // Ordine decrescente

// ordine[0] = 3 (indice del valore massimo: 9.1)
// ordine[1] = 6 (indice del secondo massimo: 8.7)
// ordine[2] = 1 (indice del terzo massimo: 7.8)
// ... ecc.

Print("Componente con score più alto: indice ", ordine[0],
      " → score = ", scoreComponenti[ordine[0]]);

// === CASO 3: Percentile con array ordinato (usato in Oracle per soglie adattive) ===
double CalcolaPercentile(double &arr[], double percentile)
{
    int n = ArraySize(arr);
    if(n == 0) return(0.0);
    
    double copia[];
    ArrayCopy(copia, arr);
    ArraySort(copia); // Ordina la copia, non l'originale
    
    double posizione = percentile * (n - 1) / 100.0;
    int    idx_basso = (int)MathFloor(posizione);
    int    idx_alto  = (int)MathCeil(posizione);
    double frac      = posizione - idx_basso;
    
    if(idx_alto >= n) return(copia[n-1]);
    return(copia[idx_basso] * (1.0 - frac) + copia[idx_alto] * frac);
}

// Esempio: soglia adattiva all'80° percentile dell'Ulcer Index ultimi 100 barre
double ulcerBuffer[];
CopyBuffer(handleUlcer, 0, 0, 100, ulcerBuffer);
double soglia80 = CalcolaPercentile(ulcerBuffer, 80.0);
// → Entra solo quando UI è nell'80° percentile degli ultimi 100 valori

6. Ricerca Binaria con ArrayBsearch

ArrayBsearch() è la funzione di ricerca binaria nativa di MQL5. Funziona su array già ordinati e ha complessità O(log n) — su 10.000 elementi fa 14 confronti invece di 10.000. È fondamentale quando devi cercare frequentemente in dataset grandi.

array_bsearch_utilizzo.mq5MQL5
// ArrayBsearch — Ricerca binaria su array ordinato

// Prototipo:
// int ArrayBsearch(array[], valore, count=WHOLE_ARRAY, start=0, direction=MODE_ASCEND)
// Ritorna l'indice dell'elemento trovato, o l'indice dell'elemento più vicino

// === CASO 1: Ricerca di un prezzo in una serie di livelli ===
double livelliFibonacci[] = {3180.5, 3195.2, 3210.0, 3225.8, 3240.3, 3255.1, 3270.0};
// Array GIA' ordinato crescente (requisito per Bsearch)

double prezzoCorrente = 3228.4;
int idx = ArrayBsearch(livelliFibonacci, prezzoCorrente, WHOLE_ARRAY, 0, MODE_ASCEND);
// Ritorna l'indice dell'elemento <= prezzoCorrente più vicino
// In questo caso: idx = 3 (livelliFibonacci[3] = 3225.8, il più vicino sotto)

double livelloVicinoBasso = livelliFibonacci[idx];
double livelloVicinoAlto  = (idx+1 < ArraySize(livelliFibonacci)) ?
                             livelliFibonacci[idx+1] : livelliFibonacci[idx];

Print("Prezzo ", prezzoCorrente, " è tra ", livelloVicinoBasso,
      " e ", livelloVicinoAlto);

// === CASO 2: Ricerca in database orario eventi news (usato per filtro news in Oracle) ===
// Database di timestamp news ad alto impatto (ordinati)
datetime newsTimestamps[];  // Caricato da file o aggiornato periodicamente

bool IsNearHighImpactNews(datetime candleTime, int minutiFinestra = 30)
{
    if(ArraySize(newsTimestamps) == 0) return(false);
    
    int idx = ArrayBsearch(newsTimestamps, candleTime, WHOLE_ARRAY, 0, MODE_ASCEND);
    
    // Controlla il news event più vicino (può essere sopra o sotto)
    for(int d = -1; d <= 1; d++)
    {
        int i = idx + d;
        if(i < 0 || i >= ArraySize(newsTimestamps)) continue;
        long diffSeconds = MathAbs((long)(candleTime - newsTimestamps[i]));
        if(diffSeconds <= minutiFinestra * 60)
            return(true);
    }
    return(false);
}

// === CASO 3: Ricerca di una data in serie storica per backtest analysis ===
datetime tempiH4[];
CopyTime(_Symbol, PERIOD_H4, 0, 5000, tempiH4);
ArraySetAsSeries(tempiH4, false); // Ordine crescente per BSearch

// Trova l'indice della barra più vicina a una data specifica
datetime dataCercata = D'2024.01.15 10:00';
int barraPiuVicina = ArrayBsearch(tempiH4, (long)dataCercata,
                                   WHOLE_ARRAY, 0, MODE_ASCEND);
Print("Barra più vicina al ", TimeToString(dataCercata),
      " → barra indice ", barraPiuVicina,
      " (", TimeToString(tempiH4[barraPiuVicina]), ")");
💡 ArrayBsearch() ritorna l'indice dell'elemento ≤ al valore cercato se non trova la corrispondenza esatta. Verifica sempre che il valore all’indice ritornato sia effettivamente quello cercato se ti serve una corrispondenza esatta.

7. Ottimizzazione della Memoria e Performance

In un Expert Advisor che gira 24/5 su VPS con tick frequenti su XAUUSD, la gestione della memoria degli array diventa rilevante. Ecco i pattern di ottimizzazione che Oracle usa internamente:

array_performance_optimization.mq5MQL5
// === OTTIMIZZAZIONE 1: Passaggio per riferimento, mai per valore ===

// SBAGLIATO: copia l'intero array nello stack → O(n) memoria extra, lento
double CalcolaMediaLenta(double arr[], int n)  // arr viene copiato!
{
    double sum = 0;
    for(int i = 0; i < n; i++) sum += arr[i];
    return sum / n;
}

// CORRETTO: passa solo il riferimento → O(1) overhead
double CalcolaMedia(const double &arr[], int n)  // solo il puntatore
{
    double sum = 0;
    for(int i = 0; i < n; i++) sum += arr[i];
    return sum / n;
}

// === OTTIMIZZAZIONE 2: Pre-allocazione con reserve per array che crescono ===

// LENTO: riallocazione a ogni iterazione
double risultati_lento[];
for(int i = 0; i < 10000; i++)
{
    ArrayResize(risultati_lento, i+1);  // 10.000 riallocazioni!
    risultati_lento[i] = MathSqrt(i);
}

// VELOCE: pre-alloca tutto in una volta
double risultati_veloce[];
ArrayResize(risultati_veloce, 10000);  // Una sola allocazione
for(int i = 0; i < 10000; i++)
    risultati_veloce[i] = MathSqrt(i);

// O: usa reserve se la dimensione finale non è nota
double risultati_reserve[];
ArrayResize(risultati_reserve, 0, 10000);  // 0 elementi, 10000 di riserva
// Poi aggiungi con pattern push_back senza riallocazioni

// === OTTIMIZZAZIONE 3: Riciclare array invece di riallocarli ===

// Pattern buono per OnTick() che viene chiamato migliaia di volte:
// Dichiara gli array come variabili globali/statiche, non locali

static double s_bufferCalcolo[500];  // Allocato una volta, riusato ogni tick
static bool   s_inizializzato = false;

void ProcessaTick()
{
    if(!s_inizializzato)
    {
        ArrayInitialize(s_bufferCalcolo, 0.0);
        s_inizializzato = true;
    }
    // Usa s_bufferCalcolo senza allocare/deallocare ogni tick
    CopyClose(_Symbol, PERIOD_H4, 0, 200, s_bufferCalcolo);
    // ...
}

// === OTTIMIZZAZIONE 4: ArrayCopy vs loop manuale ===
double src[], dst[];
ArrayResize(src, 1000);
ArrayResize(dst, 1000);

// LENTO: loop manuale
for(int i = 0; i < 1000; i++) dst[i] = src[i];

// VELOCE: ArrayCopy usa memcpy interno ottimizzato
ArrayCopy(dst, src); // ~10x più veloce per array grandi

8. Errori Comuni e Come Evitarli

Questi sono gli errori più frequenti che vedo nel codice MQL5 dei trader che si avvicinano alla programmazione. Ognuno è stato incontrato almeno una volta in 15 anni di sviluppo su MetaTrader.

ErroreCausaSoluzioneGravità
Accesso fuori boundsarr[ArraySize(arr)] — l'ultimo indice valido è ArraySize-1Usa sempre if(i >= 0 && i < ArraySize(arr))Crash EA
Series vs non-series mixCopi con CopyClose (series) e usi come se fosse crescenteChiama sempre ArraySetAsSeries(arr, true/false) esplicitamenteBug silenzioso
ArrayResize senza reserve in loopRiallocazione O(n²) in cicli push_backUsa il terzo parametro reserve di ArrayResizePerformance
Passaggio per valore di array grandiArray copiato sullo stack a ogni chiamata di funzioneUsa sempre const double &arr[] nei parametriPerformance
ArrayBsearch su array non ordinatoRisultati casuali/errati senza errore esplicitoChiama ArraySort() prima di ArrayBsearch()Bug silenzioso
Dimenticare di registrare i buffer in OnInitSetIndexBuffer non chiamato → buffer non collegato al graficoOgni buffer dichiarato va registrato in OnInitIndicatore vuoto
Modificare un array const per riferimentoCompilazione ok ma comportamento undefinedUsa const solo su array che non modifichiBug intermittente

9. Come Oracle v78 UlcerZenith Usa gli Array Internamente

Oracle v78 è un sistema a 6 componenti di scoring. Ogni componente calcola un valore numerico che contribuisce allo score totale. La gestione di questi calcoli è interamente basata su array ottimizzati. Ecco la struttura semplificata della parte core:

oracle_array_core_structure.mq5MQL5
// Struttura core di Oracle v78 — componenti di scoring
// (versione semplificata per scopo didattico)

#define N_COMPONENTI 6
#define LOOKBACK_MAX 500

// Array globali statici — allocati una volta, riusati ogni tick
static double s_ulcerBuffer[LOOKBACK_MAX];      // Ulcer Index H4
static double s_atrBuffer[LOOKBACK_MAX];         // ATR per normalizzazione
static double s_rsiBuffer[LOOKBACK_MAX];         // RSI filtro momentum
static double s_volumeBuffer[LOOKBACK_MAX];      // Volume relativo
static double s_scoreStorico[LOOKBACK_MAX];      // Score totale ultime N barre
static double s_scoreComponenti[N_COMPONENTI];   // Score attuale per componente

// Handle degli indicatori (calcolati in OnInit)
int g_handleUI, g_handleATR, g_handleRSI;

// Funzione principale di aggiornamento score (chiamata in OnTick sul H4 close)
bool AggiornaScore()
{
    // 1. Copia buffer — array statici, nessuna allocazione
    if(CopyBuffer(g_handleUI,  0, 0, LOOKBACK_MAX, s_ulcerBuffer)  < 0) return false;
    if(CopyBuffer(g_handleATR, 0, 0, LOOKBACK_MAX, s_atrBuffer)    < 0) return false;
    if(CopyBuffer(g_handleRSI, 0, 0, LOOKBACK_MAX, s_rsiBuffer)    < 0) return false;
    
    // Tutti i buffer sono SERIES: [0] = barra più recente
    
    // 2. Calcolo percentile adattivo (Comp. 1: Ulcer Index Percentile)
    double ulcerScore = CalcolaPercentile(s_ulcerBuffer, 80.0); // Vedi funzione sopra
    s_scoreComponenti[0] = NormalizzaRange(s_ulcerBuffer[0], 0, ulcerScore, 0, 10);
    
    // 3. Componente 2: ATR relativo (volatilità normalizzata)
    double atrMedio = CalcolaMedia(s_atrBuffer, 100);
    double atrRatio = (atrMedio > 0) ? s_atrBuffer[0] / atrMedio : 1.0;
    s_scoreComponenti[1] = NormalizzaRange(atrRatio, 0.5, 2.0, 0, 10);
    
    // 4. Componente 3: RSI momentum filter
    s_scoreComponenti[2] = (s_rsiBuffer[0] < 35.0) ? 8.0 :
                            (s_rsiBuffer[0] < 45.0) ? 5.0 :
                            (s_rsiBuffer[0] > 65.0) ? 8.0 : 2.0;
    
    // 5. Score totale = media pesata delle componenti
    double pesi[N_COMPONENTI] = {0.30, 0.20, 0.20, 0.10, 0.10, 0.10};
    double scoreTotale = 0.0;
    for(int i = 0; i < N_COMPONENTI; i++)
        scoreTotale += s_scoreComponenti[i] * pesi[i];
    
    // 6. Push score storico (shift dell'array)
    // NB: per array statico fisso, lo shift è manuale
    for(int i = LOOKBACK_MAX - 1; i > 0; i--)
        s_scoreStorico[i] = s_scoreStorico[i-1];
    s_scoreStorico[0] = scoreTotale;
    
    return true;
}

// Helper: normalizza un valore da range [minIn,maxIn] a [minOut,maxOut]
double NormalizzaRange(double val, double minIn, double maxIn,
                        double minOut, double maxOut)
{
    if(maxIn == minIn) return (minOut + maxOut) / 2.0;
    double norm = (val - minIn) / (maxIn - minIn);
    norm = MathMax(0.0, MathMin(1.0, norm)); // Clamp a [0,1]
    return minOut + norm * (maxOut - minOut);
}
💡 Oracle usa array statici globali invece di array locali per eliminare le allocazioni ricorrenti in OnTick. Su un VPS con 300+ tick/ora su XAUUSD, questa scelta riduce il consumo di CPU del ~40% rispetto alla versione con array locali.

✅ Pattern Riassuntivo: Le 5 Regole degli Array in MQL5 Professionale

1. Passa sempre per riferimento (const double &arr[]) nelle funzioni che non modificano l’array. 2. Imposta esplicitamente la direzione series con ArraySetAsSeries() dopo ogni CopyBuffer/CopyClose. 3. Pre-alloca array che crescono in loop usando il parametro reserve. 4. Usa ArraySort() prima di ArrayBsearch() e verifica sempre il risultato. 5. Dichiara array riutilizzati come static o globali per evitare riallocazioni in OnTick.

⚡ Oracle v78 — 8.293 righe MQL5 — 65.587 trade reali su MT5

Da Questa Guida al Sistema
Che Funziona sui Mercati Reali.

Oracle v78 UlcerZenith è scritto in MQL5 professionale con le tecniche di questa guida. 65.587 trade verificabili su MT5. WR 42,1%, EV +0,684R, MDD 13,1% su 19 anni. La live gratuita mostra il sistema in azione sul mercato reale.

MQL5 Expert Advisor 65.587 trade reali WR 42,1% XAUUSD H4 8.293 righe codice verificato
ENTRA NELLA LIVE GRATUITA →

Scrivi “SERIO” a Rossella su WhatsApp Nessun costo. Rossella risponde entro 2 ore.

Domande Frequenti

Qual è la differenza tra ArraySize e ArrayRange in MQL5? ▼
ArraySize(arr) restituisce il numero totale di elementi in tutte le dimensioni. Per un array double[6][6], ArraySize restituisce 36. ArrayRange(arr, dim) restituisce la dimensione lungo la dimensione specificata (0=prima, 1=seconda). Per double[6][6]: ArrayRange(arr, 0) = 6, ArrayRange(arr, 1) = 6. Usa ArrayRange per array multidimensionali quando ti serve sapere quante righe o colonne hai.
Quando usare un array statico vs dinamico in un EA? ▼
Usa array statico quando: la dimensione è nota a compile-time e non cambierà mai (es. 6 componenti di scoring, 12 mesi). Usa array dinamico quando: la dimensione dipende da parametri input, dal numero di barre disponibili, o da dati runtime. In pratica, per i buffer degli indicatori usa sempre array dinamici perché il numero di barre dipende dall’history disponibile sul broker.
ArrayBsearch restituisce risultati sbagliati: perché? ▼
Quasi certamente l’array non è ordinato. ArrayBsearch richiede che l’array sia ordinato nella direzione specificata (MODE_ASCEND o MODE_DESCEND). Se l’array non è ordinato, i risultati sono imprevedibili ma non viene generato alcun errore. Chiama sempre ArraySort() prima di ArrayBsearch(), o tieni l’array ordinato durante la sua costruzione.
Come copio solo una parte di un array in un altro array? ▼
ArrayCopy(dst, src, dstStart, srcStart, count). I parametri opzionali sono: dstStart (indice di inizio nell’array destinazione, default 0), srcStart (indice di inizio nell’array sorgente, default 0), count (numero di elementi da copiare, default WHOLE_ARRAY). Esempio per copiare gli elementi da 10 a 59 di src in dst: ArrayCopy(dst, src, 0, 10, 50).
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