English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Applicazione di un Indicatore ad un Altro

Applicazione di un Indicatore ad un Altro

MetaTrader 5Esempi | 8 dicembre 2021, 17:04
144 0
MetaQuotes
MetaQuotes

Introduzione

Analizziamo un’attività di miglioramento di un indicatore che viene applicato ai valori di un altro indicatore. In questo articolo, continueremo a lavorare con True Strength Index (TSI) che è stato creato e considerato nel precedente articolo "MQL5: Crea il Tuo Indicatore".

Indicatore Personalizzato Basato sui Valori di un Altro Indicatore

Quando si scrive un indicatore che utilizza la forma abbreviata della chiamata di funzione OnCalculate(), potresti non considerare il fatto che un indicatore può essere calcolato non solo dai dati di prezzo, ma anche dai dati di un altro indicatore (indipendentemente dal fatto che sia integrato o uno personalizzato).

Facciamo un semplice esperimento: associa l'indicatore RSI integrato con impostazioni standard a un grafico e trascina l'indicatore personalizzato True_Strength_Index_ver2 .mq5 nella finestra dell'indicatore RSI. Nel tabParametri della finestra visualizzata specifica che l'indicatore deve essere applicato ai Dati dell'Indicatore Precedente (RSI(14)).

Il risultato sarà molto diverso da quello che ci aspettavamo. L’indicatore aggiuntivo della linea del TSI non è comparso nella finestra dell'indicatore RSI e nella Finestra dei Dati noterai che anche i suoi valori sono poco chiari.

Nonostante il fatto che i valori di RSI siano definiti quasi ovunque per tutta la cronologia, i valori del TSI (applicati ai dati RSI) sono o completamente assenti (all'inizio) o sempre pari a -100:

Tale comportamento è causato dal fatto che il valore del parametro begin non viene utilizzato in nessuna parte in OnCalculate() del nostro True_Strength_Index_ver2.mq5. Il parametro begin  consente di specificare il numero di valori vuoti nel parametro di price[] di input. Questi valori vuoti non possono essere utilizzati nel calcolo dei valori degli indicatori. Ricordiamo la definizione della prima forma della chiamata alla funzione OnCalculate().
int OnCalculate (const int rates_total,      // price[] array length
                 const int prev_calculated,  // number of bars calculated after previous call
                 const int begin,            // start index of meaningful data
                 const double& price[]       // array for calculation
   );

Quando si applica l'indicatore ai dati di prezzo specificando una delle costanti di prezzo, il parametro begin è uguale a 0, perché esiste un tipo di prezzo specificato per ogni barra. Pertanto l'array di input price[] ha sempre dati corretti a partire dal suo primo elemento price[0]. Tuttavia, se specifichiamo i dati di un altro indicatore come fonte per i calcoli, esso non è più garantito.

Parametro begin di OnCalculate()

Controlliamo i valori contenuti nell’arrayprice[] se il calcolo viene eseguito utilizzando i dati di un altro indicatore. Per fare ciò, nella funzione OnCalculate() aggiungeremo qualche codice che produrrà i valori che vogliamo controllare. Ora, l'inizio della funzione OnCalculate() appare come:

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,    // price[] array length;
                 const int prev_calculated,// number of available bars after previous call;
                 const int begin,          // start index of meaningful data in price[] array 
                 const double &price[])    // data array, that will be used for calculations;
  {
//--- flag for single output of price[] values
   static bool printed=false;
//--- if begin isn't zero, then there are some values that we shouldn't take into account

   if(begin>0 && !printed)
     {
      //--- let's output them
      Print("Data for calculation begin from index equal to ",begin,
            "   price[] array length =",rates_total);

      //--- let's show the values that we shouldn't take into account for calculation
      for(int i=0;i<=begin;i++)
        {
         Print("i =",i,"  value =",price[i]);
        }
      //--- set printed flag to confirm that we have already logged the values
      printed=true;
     }

Ancora una volta, trasmettiamo la versione modificata del nostro indicatore nella finestra RSI(14) e specifichiamo i dati dell'indicatore precedente per il calcolo. Ora vedremo i valori che non sono tracciati e non dovrebbero essere presi in considerazione per i calcoli, dove vengono utilizzati i valori dell'indicatore RSI(14).


Valori Vuoti nei Buffer degli Indicatori e DBL_MAX

I primi 14 elementi dell’array price[] con indici da 0 a 13 inclusi hanno lo stesso valore pari a 1,797693134862316e+308. Troverai questo numero molto spesso, perché è il valore numerico della costante integrata di EMPTY_VALUE che viene utilizzato per indicare i valori vuoti in un buffer dell'indicatore.

Riempire i valori vuoti con zeri non è una soluzione universale, perché questo valore può essere il risultato del calcolo da parte di altri indicatori. Per questo motivo, tutti gli indicatori integrati del client terminal restituiscono questo numero per i valori vuoti. Il valore 1.797693134862316e+308 è stato scelto perché è il massimo valore possibile di tipo doppio e per comodità si presenta come costante DBL_MAX in MQL5.

Per verificare se un determinato numero di tipo doppio è vuoto o meno, è possibile confrontarlo con le costanti EMPTY_VALUE o DBL_MAX . Entrambe le varianti sono uguali, ma è meglio usare la costante EMPTY_VALUE nel codice per renderlo chiaro.

//+------------------------------------------------------------------+
//| returns true for "empty" values                                  |
//+------------------------------------------------------------------+
bool isEmptyValue(double value_to_check)
  {
//--- if the value is equal DBL_MAX, it has an empty value
   if(value_to_check==EMPTY_VALUE) return(true);
//--- it isn't equal DBL_MAX
   return(false);
  }

Il DBL_MAX è un numero molto grande e l'indicatore RSI intrinsecamente non può restituire tali valori! E solo il quindicesimo elemento dell'array (con indice 14) ha un valore ragionevole pari a 50. Quindi, anche se non sappiamo nulla dell'indicatore come fonte di dati da calcolare, utilizzando il parametro begin possiamo organizzare correttamente l'elaborazione dei dati in questi casi. Per essere più precisi, dobbiamo evitare di utilizzare questi valori vuoti nei nostri calcoli.

Relazione tra il Parametro begin e la Proprietà PLOT_DRAW_BEGIN

Va notato, che c'è una relazione di chiusura tra il parametro begin che viene trasferito alla funzione OnCalculate() e la proprietà PLOT_DRAW_BEGIN che definisce il numero di barre iniziali senza disegno. Se esaminiamo il codice sorgente dell’RSI dal pacchetto standard MetaTrader5, vedremo il seguente codice nella funzione OnInit():
//--- sets first bar from what index will be drawn
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,ExtPeriodRSI);

Ciò significa che il tracciamento grafico con indice 0 inizia solo dalla barra che ha indice uguale a ExtPeriodRSI (è una variabile di input che specifica il periodo dell'indicatore RSI) e non vi è alcun tracciamento delle barre precedenti.

Nel linguaggio MQL5, il calcolo di alcuni indicatori A basato sui dati dell'indicatore B viene sempre eseguita su valori di buffer zero dell'indicatore B. I valori del buffer zero dell'indicatore B vengono trasferiti come parametro di input price[] alla funzione OnCalculate() dell'indicatore A. Intrinsecamente, il buffer zero viene assegnato a zero tracciamento grafico con la funzione SetIndexBuffer(). Pertanto:

Regola di trasferimento della proprietà PLOT_DRAW_BEGIN al parametro begin: Per i calcoli dell'indicatore A personalizzato basati sui dati di altri indicatori (di base) B, il valore del parametro di input begin nella funzione OnCalculate() è sempre uguale al valore della proprietàPLOT_DRAW_BEGIN del tracciamento grafico B zero dell'indicatore di base.

Quindi, se abbiamo creato l'indicatore RSI (indicatore B) con il periodo 14 e, dunque, creato il nostro indicatore personalizzato True Strength Index (Indicatore A) in base ai suoi dati, allora:

  • L'indicatore RSI (14) viene tracciato a partire dalla 14a barra a causa di PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,14);
  • l'array di input price[] nella funzione OnCalculate() contiene i valori del buffer zero dell'indicatore RSI;
  • il valore del parametro di input begin nella funzione OnCalculate() dell'indicatore TSI è ottenuto dal PLOT_DRAW_BEGIN proprietà del tracciamento grafico zero dell'indicatore RSI.

Ricorda che l'indicatore TSI non viene ricavato dall'inizio del grafico, perché il valore dell'indicatore non è determinato per alcune delle prime barre. Il primo indice a barre, che verrà tracciato come linea nell'indicatore TSI, è uguale a r+s-1, dove:

  • r - periodo di primo smoothing esponenziale di array MTMBuffer[] e AbsMTMBuffer[] in corrispondenti array EMA_MTMBuffer[] e EMA_AbsMTMBuffer[];
  • s - periodo del successivo smoothing degli array EMA_MTMBuffer[] e EMA_AbsMTMBuffer[].

Per le barre con indici inferiori a r+s-1 non esistono valori per tracciare l'indicatore TSI. Pertanto, per gli array finali MA2_MTMBuffer[] e EMA2_AbsMTMBuffer[] utilizzati per calcolare l'indicatore TSI, i dati hanno un offset aggiuntivo e iniziano dall'indice r+s-1. Puoi trovare maggiori informazioni nell’articolo "MQL5: Crea il Tuo Indicatore".

C'è una dichiarazione nella funzione OnInit() per disabilitare il disegno delle prime barre r+s-1:

//--- first bar to draw
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,r+s-1);

Poiché l'inizio dei dati di input si è spostato in avanti di barre begin, dobbiamo prenderlo in considerazione e aumentare la posizione iniziale del disegno dei dati con le barre begin nella funzione OnCalculate():

   if(prev_calculated==0)
     { 
      //--- let's increase beginning position of data by begin bars,
      //--- because we use other indicator's data for calculation
      if(begin>0)PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,begin+r+s-1);
     }
Ora stiamo prendendo in considerazione il parametro begin per calcolare i valori dell'indicatore TSI. Inoltre, il parametrobegin verrà trasferito correttamente se qualche altro indicatore utilizzerà i valori del TSI per il calcolo: beginother_indicator=beginour_indicator+r+s-1. Pertanto, possiamo formulare la regola di imporre un indicatore sui valori di un altro indicatore:

Regola degli indicatori che impongono : Se un indicatore personalizzato A viene disegnato partendo dalla posizione N a (prima Na i valori non sono tracciati) e si basa sui dati di altro indicatore B, che viene ricavato dalla posizione Nb, l'indicatore risultante A{B} verrà disegnato a partire dalla posizione Nab=Na+Nb, dove A{B} A{B} significa che l'indicatore A viene calcolato sui valori buffer zero dell'indicatore B.

Pertanto, la TSI (25,13) {RSI (14)} significa che l'indicatore TSI (25,13) è costituito da valori dell'indicatore RSI (14). Come risultato dell'imposizione, ora l'inizio dei dati è (25+ 13-1) + 14 = 51. In altre parole, il disegno dell'indicatore inizierà dalla 52a barra (l'indicizzazione delle barre inizia con 0).

Aggiunta di Valori beginda Utilizzare nei Calcoli degli Indicatori

Ora sappiamo esattamente che i valori significativi dell'array price[] iniziano sempre dalla posizione del modulo, specificati dal parametro begin. Modifichiamo il nostro codice step by step. Innanzitutto, c’è il codice che calcola i valori degli array MTMBuffer[] e AbsMTMBuffer[]. Senza il parametrobegin il riempimento dell’array è iniziato con l'indice 1.

//--- calculate values for mtm and |mtm|
   int start;
   if(prev_calculated==0) start=1;  // start filling MTMBuffer[] and AbsMTMBuffer[] arrays from 1st index 
   else start=prev_calculated-1;    // set start equal to last array index
   for(int i=start;i<rates_total;i++)
     {
      MTMBuffer[i]=price[i]-price[i-1];
      AbsMTMBuffer[i]=fabs(MTMBuffer[i]);
     }

Ora inizieremo dalla posizione (begin + 1) e il codice modificato sarà simile al seguente (le modifiche al codice sono delineate in grassetto):

//--- calculate values for mtm and |mtm|
   int start;
   if(prev_calculated==0) start=begin+1;  // start filling MTMBuffer[] and AbsMTMBuffer[] arrays from begin+1 index 
   else start=prev_calculated-1;           // set start equal to the last array index
   for(int i=start;i<rates_total;i++)
     {
      MTMBuffer[i]=price[i]-price[i-1];
      AbsMTMBuffer[i]=fabs(MTMBuffer[i]);
     }

Poiché i valori da prezzo[0] a prezzo[begin-1] non possono essere utilizzati per i calcoli, iniziamo da prezzo[begin]. I primi valori calcolati per le matrici MTMBuffer[] e AbsMTMBuffer[] appariranno come segue:

      MTMBuffer[begin+1]=price[begin+1]-price[begin];
      AbsMTMBuffer[begin+1]=fabs(MTMBuffer[begin+1]);

Per questo motivo, la variabile startin per ciclo ora ha valore iniziale start=begin+1, invece di 1.

Account per begin per gli Array Dipendenti

Ora arriva lo smoothing esponenziale degli array MTMBuffer[] e AbsMTMBuffer[]. La regola è anche semplice: se la posizione iniziale dell'array di base è aumentata dI barre begin allora la posizione iniziale per tutti gli array dipendenti dovrebbe essere aumentata anche delle barre begin.

//--- calculating the first moving average on arrays
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         1,               // index of the starting element in array 
                         r,               // period of exponential average
                         MTMBuffer,       // source buffer for average
                         EMA_MTMBuffer);  // target buffer
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         1,r,AbsMTMBuffer,EMA_AbsMTMBuffer);

Qui, MTMBuffer [] e AbsMTMBuffer [] sono array di base e i valori calcolati in questi array ora iniziano dall'indice che è maggiore di begin. Quindi, aggiungeremo questo offset alla funzione ExponentialMAOnBuffer().

Ora, questo blocco è simile a questo:

//--- calculating the first moving average on arrays
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+1,        // index of the starting element in array 
                         r,               // period for exponential average
                         MTMBuffer,       // source buffer for average
                         EMA_MTMBuffer);  // target buffer
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+1,r,AbsMTMBuffer,EMA_AbsMTMBuffer);

Come puoi vedere, l'intera modifica è stata quella di adattarsi all'aumento della posizione start dei dati , definito dal parametro begin . Niente complicato. Allo stesso modo, cambieremoil secondo blocco di smoothing.

Prima:

//--- calculating the second moving average on arrays
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         r,s,EMA_MTMBuffer,EMA2_MTMBuffer);
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         r,s,EMA_AbsMTMBuffer,EMA2_AbsMTMBuffer);

Dopo:

//--- calculating the second moving average on arrays
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+r,s,EMA_MTMBuffer,EMA2_MTMBuffer);
   ExponentialMAOnBuffer(rates_total,prev_calculated,
                         begin+r,s,EMA_AbsMTMBuffer,EMA2_AbsMTMBuffer);

Allo stesso modo, cambieremo l'ultimo blocco di calcolo.

Prima:

//--- calculating values of our indicator
   if(prev_calculated==0) start=r+s-1; // set initial index for input arrays
   else start=prev_calculated-1;       // set start equal to last array index
   for(int i=start;i<rates_total;i++)
     {
      TSIBuffer[i]=100*EMA2_MTMBuffer[i]/EMA2_AbsMTMBuffer[i];
     }

Dopo:

//--- calculating values of our indicator
   if(prev_calculated==0) start=begin+r+s-1; // set initial index for input arrays
   else start=prev_calculated-1;              // set start equal to last array index
   for(int i=start;i<rates_total;i++)
     {
      TSIBuffer[i]=100*EMA2_MTMBuffer[i]/EMA2_AbsMTMBuffer[i];
     }

La messa a punto dell'indicatore è finita e ora salterà i primi valori begin vuoti dell'array di input price[] in OnCalculate() e terrà conto dell'offset causato da questa omissione. Tuttavia, dobbiamo ricordare che alcuni altri indicatori possono utilizzare i valori TSI per i calcoli. Per questo motivo, impostiamo i valori vuoti del nostro indicatore su EMPTY_VALUE.

È Necessaria l'Inizializzazione degli Indicatori Buffer?

In MQL5 gli array non vengono inizializzati di default con alcuni valori definiti. Lo stesso vale per gli array che sono specificati dalla funzione SetIndexBuffer() ai buffer indicatori. Se un array è un buffer di indicatori, la sua dimensione dipenderà dal valore del parametro rates_total nella funzione OnCalculate();

Potresti essere tentato di inizializzare tutti i buffer degli indicatori con valori EMPTY_VALUE utilizzando la funzione ArrayInitialize(), ad esempio, contemporaneamente all'inizio di OnCalculate():

//--- if it is the first call of OnCalculate() 
   if(prev_calculated==0)
     {
      ArrayInitialize(TSIBuffer,EMPTY_VALUE);
     }

Ma non è raccomandato per il seguente motivo: Mentre il client terminal lavora, vengono ricevute nuove quotazioni per il simbolo, i cui dati vengono utilizzati per calcolare l'indicatore. Dopo un po’ di tempo, il numero di barre aumenterà, quindi il client terminal riserverà memoria aggiuntiva per i buffer degli indicatori.

Ma i valori dei nuovi elementi di array ("collegati") possono avere qualsiasi valore, poiché durante la riallocazione della memoria per qualsiasi array l'inizializzazione non viene eseguita. L'inizializzazione iniziale può darti una falsa certezza che tutti gli elementi dell’array, che non sono stati definiti in modo esplicito, verranno riempiti con i valori specificati durante l'inizializzazione. Naturalmente non è vero, e non dovresti mai pensare che il valore numerico della variabile o di qualche elemento dell’array verrà inizializzato con il valore per noi necessario.

Dovresti impostare il valore per ogni elemento del buffer dell'indicatore. Se i valori di alcune barre non sono definiti dall'algoritmo dell'indicatore, dovresti impostarli esplicitamente con valore vuoto. Ad esempio, se un valore del buffer dell'indicatore viene calcolato dall'operazione di divisione, in alcuni casi il divisore può essere zero.

Sappiamo che la divisione per zero è un errore di runtime critico in MQL5 e porta alla chiusura immediata di un programma mql5. Invece di evitare la divisione per zero gestendo questo caso speciale nel codice, è necessario impostare il valore per questo elemento buffer. Forse, è meglio usare il valore che abbiamo assegnato come vuoto per questo stile di disegno.

Ad esempio, per alcuni stili di disegno abbiamo definito zero come valore vuoto usando la funzione PlotIndexSetDouble():

   PlotIndexSetDouble(plotting_style_index,PLOT_EMPTY_VALUE,0);   

Quindi, per tutti i valori vuoti del buffer dell'indicatore in questo disegno è necessario definire esplicitamente il valore zero:

   if(divider==0)
      IndicatorBuffer[i]=0;
   else
      IndicatorBuffer[i]=... 

Inoltre, se è stato specificato DRAW_BEGIN per alcuni disegni, tutti gli elementi del buffer dell'indicatore con indici da 0 a DRAW_BEGIN verranno riempiti automaticamente con gli zeri.

Conclusione

Quindi, facciamo un breve riassunto. Ci sono alcune condizioni necessarie affinché l'indicatore sia correttamente calcolato sulla base dei dati di un altro indicatore (e per renderlo adatto all'uso in altri programmi mql5):

  1. I valori vuoti negli indicatori integrati vengono riempiti con i valori della costante EMPTY_VALUE che è esattamente uguale al valore massimo per il tipo doppio (DBL_MAX).
  2. Per informazioni dettagliate sull'indice iniziale dei valori significativi di un indicatore, è necessario analizzare il parametrobegin di input della forma corta di OnCalculate().
  3. Al fine di vietare il disegno della prima N valori per lo stile di disegno specificato, imposta DRAW_BEGIN parametro utilizzando il seguente codice:
    PlotIndexSetInteger(plotting_style_index,PLOT_DRAW_BEGIN,N);
  4. Se DRAW_BEGIN è specificato per alcuni disegni, tutti gli elementi buffer dell'indicatore con gli indici da 0 a DRAW_BEGIN verranno riempiti automaticamente con valori vuoti (l'impostazione predefinita è EMPTY_VALUE).
  5. Nella funzione OnCalculate(), aggiungere un ulteriore offset mediante barre begin per un uso corretto di altri dati dell'indicatore nel tuo indicatore:
    //--- if it's the first call 
       if(prev_calculated==0)
         { 
          //--- increase position of data beginning by begin bars, 
          //--- because of other indicator's data use      
          if(begin>0)PlotIndexSetInteger(plotting_style_index,PLOT_DRAW_BEGIN,begin+N);
         }
    
  6. Puoi specificare il tuo valore vuoto diverso da EMPTY_VALUE nella funzione OnInit() utilizzando il codice seguente:
    PlotIndexSetDouble(plotting_style_index,PLOT_EMPTY_VALUE,your_empty_value);
  7. Non fare affidamento su una inizializzazione una tantum dei buffer degli indicatori utilizzando il seguente codice:
    ArrayInitialize(buffer_number,value);
        
    Dovresti impostare tutti i valori del buffer dell'indicatore per la funzione OnCalculate() in modo esplicito e coerente, compresi i valori vuoti .

Naturalmente, in futuro, quando hai una certa esperienza nella scrittura di indicatori, troverai alcuni casi che esulano dallo scopo di questo articolo, ma spero che in quel momento la tua conoscenza di MQL5 ti permetterà di risolverli.

Tradotto dal russo da MetaQuotes Software Corp.

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

Limitazioni e Verifiche sugli Expert Advisors Limitazioni e Verifiche sugli Expert Advisors
È consentito fare trading con questo simbolo lunedì? Ci sono abbastanza soldi per aprire una posizione? Quanto è grande la perdita se si attiva lo Stop Loss? Come limitare il numero di ordini in sospeso? L'operazione commerciale è stata eseguita nella barra corrente o in quella precedente? Se un robot di trading non può eseguire questo tipo di verifiche, qualsiasi strategia di trading può rivelarsi perdente. Questo articolo mostra gli esempi di verifiche utili su qualsiasi Expert Advisor.
MQL5: Crea il tuo Indicatore MQL5: Crea il tuo Indicatore
Cos'è un indicatore? È un insieme di valori calcolati che vogliamo siano visualizzati sullo schermo in un modo pratico. Gli insiemi di valori sono rappresentati nei programmi, come array. Pertanto, la creazione di un indicatore equivale alla scrittura di un algoritmo che gestisce alcuni array (matrici di prezzo) e registra i risultati della gestione su altri array (valori dell'indicatore). Descrivendo la creazione del True Strength Index, l'autore mostra come scrivere indicatori in MQL5.
L'Ordine di Creazione e Distruzione dell'oggetto in MQL5 L'Ordine di Creazione e Distruzione dell'oggetto in MQL5
Ogni oggetto, sia esso un oggetto personalizzato, un array dinamico o un array di oggetti, viene creato ed eliminato nel programma MQL5 nel suo modo particolare. Spesso, alcuni oggetti fanno parte di altri oggetti e l'ordine di eliminazione dell'oggetto durante la reinizializzazione diventa particolarmente importante. Questo articolo fornisce alcuni esempi che illustrano i meccanismi di utilizzo degli oggetti.
Passi su Nuovi Binari: Indicatori Personalizzati in MQL5 Passi su Nuovi Binari: Indicatori Personalizzati in MQL5
Non elencherò tutte le nuove possibilità e funzionalità del nuovo terminale e del nuovo linguaggio. Sono numerose e alcune novità meritano la trattazione in un articolo a parte. Inoltre non c'è codice qui, scritto con programmazione orientata agli oggetti, è un argomento troppo serio per essere semplicemente menzionato in un contesto come i vantaggi aggiuntivi per gli sviluppatori. In questo articolo, considereremo gli indicatori, la loro struttura, il disegno, i tipi e i loro dettagli di programmazione rispetto a MQL4. Spero che questo articolo sia utile sia per i principianti che per gli sviluppatori esperti, forse alcuni di loro troveranno qualcosa di nuovo.