English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Scambio di Dati tra Indicatori: È Facile

Scambio di Dati tra Indicatori: È Facile

MetaTrader 5Esempi | 15 dicembre 2021, 17:04
358 0
Alexey Subbotin
Alexey Subbotin

Introduzione

Il motivo per cui apprezziamo i neofiti è perché sono ostinatamente riluttanti a utilizzare la ricerca, e ci sono molti argomenti come "FAQ", "Per i neofiti" o "Colui che fa una domanda da questa lista, brucerà all'inferno". La loro vera missione è porre domande, come "Come fare...", "È possibile..." e spesso ottenere risposte come "Assolutamente no", "È impossibile".

L'eterna frase "mai dire mai più" ha stimolato scienziati, ingegneri e programmatori a pensare e a creare qualcosa di nuovo e ben dimenticato.

1. Definizione del Problema

Ad esempio, ecco una citazione da uno degli argomenti sul forum della Community MQL4 (tradotto dal russo):

 ...ci sono due indicatori (chiamiamolo A e B). L'indicatore A utilizza i dati direttamente dal grafico dei prezzi come al solito e B utilizza i dati dell'indicatore A. Ecco la domanda: Cosa devo fare per eseguire il calcolo di B in modo dinamico, utilizzando i dati dell'indicatore A (che è già allegato), invece di utilizzare iCustom ("indicatore A", ...), cioè se cambio alcune impostazioni dell'indicatore A, dovrebbe riflettersi nelle modifiche dei parametri dell'indicatore B.

o in altre parole:

...Supponiamo di aver collegato una media mobile a un grafico. Ora come ottenere l'accesso diretto al suo buffer di dati?

e questo:

... Se chiamo un indicatore utilizzando iCustom, si ricarica anche se è stato caricato prima della chiamata. C'è un modo per prevenirlo?

L'elenco di tali domande può essere esteso, vengono poste ancora e ancora, e non solo dai neofiti. In generale, il problema è che MetaTrader non ha alcuna tecnica per accedere ai dati dell'indicatore personalizzato senza l'uso di iCustom (MQL4) o l'uso del legame iCustom + CopyBuffer (MQL5). Ma sarebbe così allettante per il prossimo sviluppo del capolavoro scrivere alcune funzioni nel codice MQL, che otterrà dati o calcolerà qualcosa utilizzando i dati di un grafico specificato.

Non è conveniente lavorare con le funzioni standard sopra menzionate: ad esempio, in MQL4, le copie dell'indicatore vengono create per ogni chiamante quando viene chiamato iCustom; in MQL5 il problema è stato parzialmente risolto utilizzando gli handle e ora i calcoli vengono eseguiti solo una volta per una copia master. Tuttavia non è una panacea: se l'indicatore, a cui ci riferiamo, consuma molte risorse di calcolo, il terminale è virtualmente garantita da una dozzina di secondi ad ogni reinizializzazione.

Ricordo diversi modi offerti per accedere all'origine, inclusi i seguenti:

  • mappatura di file su disco fisico o memoria;
  • utilizzo della memoria condivisa tramite DLL per lo scambio di dati;
  • l'utilizzo di variabili globali del terminale client per lo scambio dei dati e la loro memorizzazione.

E alcuni altri metodi, che sono variazioni di quelli sopra menzionati, così come alcuni modi esotici come socket, mailslot, ecc. (esiste un metodo radicale, che viene spesso utilizzato negli Expert Advisors - il trasferimento dei calcoli degli indicatori direttamente al Codice Expert Advisor, ma questo va ben oltre lo scopo del nostro articolo.

Dal punto di vista dell'autore, c'è qualcosa in tutti questi metodi, ma tutti hanno uno svantaggio comune: i dati vengono prima copiati in un luogo e poi distribuiti ad altri. In primo luogo richiede alcune risorse della CPU e, in secondo luogo, crea un nuovo problema con la rilevanza dei dati trasmessi (non lo considereremo qui).

Quindi, proviamo a definire il problema:

Vogliamo creare un tale ambiente che fornisca l'accesso ai dati degli indicatori allegati a un grafico e che abbia le seguenti proprietà:

  • assenza di copiatura dei dati (e del problema con la sua rilevanza);
  • modifica minima del codice dei metodi disponibili, se è necessario utilizzarli;
  • È preferibile il codice MQL (ovviamente dobbiamo usare DLL, ma useremo solo una dozzina di stringhe di codice C++).

L'autore ha utilizzato C++ Builder per la creazione di DLL e i terminali client MetaTrader 4 e MetaTrader 5. I codici sorgente presentati di seguito sono scritti in MQL5, i codici MQL4 sono allegati all'articolo, verranno discusse le principali differenze tra loro.

2. Array

Innanzitutto, abbiamo bisogno di una teoria, perché lavoreremo con i buffer degli indicatori e dobbiamo sapere come si trovano i loro dati nella memoria. Queste informazioni non sono documentate correttamente.

Gli array dinamici in MQL sono strutture con dimensione variabile, quindi è interessante come MQL risolva il problema della riallocazione dei dati, se la dimensione dell'array è aumentata e non c'è memoria libera subito dopo l'array. Ci sono due modi per risolverlo:

  1. per riallocare nuovi dati nella parte aggiuntiva della memoria disponibile (e salvare gli indirizzi di tutte le parti per questo array, ad esempio utilizzando un elenco referenziato), oppure
  2. per spostare l'intero array nel suo insieme nella nuova parte di memoria, che è sufficiente per allocarlo.

Il primo metodo porta ad alcuni problemi aggiuntivi, perché in questo caso dobbiamo indagare sulle strutture dati create dal compilatore MQL. La seguente considerazione testimonia ancora della seconda variante (che è ancora più lenta): Quando un array dinamico viene passato a una funzione esterna (alla DLL), quest'ultima ottiene il puntatore al primo elemento di dati, gli altri elementi dell'array vengono ordinati e posizionati subito dopo il primo elemento dell'array.

Quando si passa per riferimento, i dati dell'array possono essere modificati, quindi significa che nel primo metodo c'è un problema da copiare l'intero array in un'area di memoria separata per trasferirlo a una funzione esterna, quindi aggiungere il risultato all'array di origine, ovvero eseguire le stesse azioni implicite nel secondo metodo.

Sebbene questa conclusione non sia logicamente rigorosa al 100%, può comunque essere considerata piuttosto affidabile (è dimostrato anche dal corretto funzionamento del nostro prodotto basato su questa idea).

Quindi, supponiamo che le seguenti affermazioni siano corrette:

  • In ogni momento, i dati degli array dinamici si trovano sequenzialmente in memoria uno dopo l'altro; e l'array può essere riposizionato in altre parti della memoria del computer, ma solo nel suo insieme.
  • L'indirizzo del primo elemento dell'array viene passato alla libreria DLL, quando un array dinamico viene passato a una funzione esterna come parametro per riferimento.

E tuttavia prendiamo altre ipotesi: per istanti di tempo intendiamo i momenti di tempo in cui viene chiamata la funzione OnCalculate() (per MQL4 - start()) dell'indicatore corrispondente; inoltre, per semplicità, assumiamo che i nostri buffer di dati abbiano la stessa dimensione e tipo di double [].

3. Sine Qua Non

MQL non supporta i puntatori (ad eccezione dei cosiddetti puntatori a oggetti che non sono puntatori nel suo senso comune), come è stato ripetutamente affermato e confermato dai rappresentanti di MetaQuotes Software. Quindi controlliamo se questo è vero.

Che cos'è un puntatore? Non è solo un identificatore con un asterisco, è l'indirizzo di una cella nella memoria di un computer. E qual è l'indirizzo del cellulare? È il numero progressivo che parte da un certo inizio. E infine, cos'è un numero? È un numero intero che corrisponde a una cella della memoria del computer. E perché non possiamo lavorare con un puntatore come con il numero intero? Sì, possiamo, perché i programmi MQL funzionano perfettamente con i numeri interi!

Ma come convertire un puntatore in un intero? La Dynamic Link Library ci aiuterà, useremo le possibilità di typecasting C++. A causa del fatto che i puntatori C++ sono tipi di dati a quattro byte, nel nostro caso è conveniente usare int come tale tipo di dati a quattro byte.

Il bit di segno non è importante e non lo terremo in considerazione (se è uguale a 1, significa solo che l'intero è negativo), l'importante è che possiamo mantenere invariati tutti i bit del puntatore. Naturalmente, possiamo usare interi senza segno, ma sarà meglio se i codici per MQL5 e MQL4 sono simili, perché MQL4 non fornisce numeri interi senza segno.

Quindi,

extern "C" __declspec(dllexport) int __stdcall GetPtr(double *a)
{
        return((int)a);
}

Wow, ora abbiamo la variabile di tipo lungo con il valore dell'indirizzo iniziale dell'array! Ora dobbiamo imparare a leggere il valore dell'i-esimo elemento dell'array:

extern "C" __declspec(dllexport) double __stdcall GetValue(int pointer,int i)
{
        return(((double*) pointer)[i]);
}

... e scrivi il valore (non è proprio necessario nel nostro caso, comunque ...)

extern "C" __declspec(dllexport) void __stdcall SetValue(int pointer,int i,double value)
{
        ((double*) pointer)[i]=value;
}

Quindi, questo è tutto. Ora possiamo lavorare con i puntatori in MQL.

4. Wrapping

Abbiamo creato il kernel del sistema, ora dobbiamo prepararlo per il comodo utilizzo nei programmi MQL. Tuttavia, fan dell'estetica, per piacere non vi arrabbiate, ci sarà qualcos'altro.

Ci sono miliardi di modi per farlo. Sceglieremo il seguente modo. Ricordiamo che il terminale client ha una funzione speciale per lo scambio di dati tra programmi MQL separati - Global Variables. Il modo più naturale è usarli per memorizzare i puntatori ai buffer degli indicatori, a cui accederemo. Considereremo tali variabili come una tabella di descrittori. Ogni descrittore avrà il nome rappresentato da una stringa del tipo:

string_identifier#buffer_number#symbol#period#buffer_length#indexing_direction#random_number,

e il suo valore sarà uguale alla rappresentazione intera del corrispondente puntatore del buffer nella memoria del computer.

Alcuni dettagli sui campi del descrittore.

  • string_identifier – qualsiasi stringa (ad esempio, il nome dell'indicatore - può essere utilizzata la variabile short_name ecc.); servirà per la ricerca del puntatore necessario, intendiamo che qualche indicatore registrerà i descrittori con lo stesso identificatore e li distinguerà utilizzando il campo:
  • buffer_number – sarà usato per distinguere i buffer;
  • buffer_length – ci serve per controllare i confini, altrimenti potremmo portare al crash del terminale client e di Windows Blue Screen of Death :);
  • simbolo, periodo – simbolo e periodo per specificare la finestra del grafico;
  • ordering_direction – specifica la direzione di ordinamento degli elementi dell'array: 0 – normale, 1 – inversione (il flag AS_SERIES è true);
  • random_number – verrà utilizzato se ci sono più copie di un indicatore anche se con finestre diverse o con diversi set di parametri, attaccate a un terminale client (possono impostare gli stessi valori del primo e del secondo campo, ecco perché dobbiamo distinguere tra in qualche modo) - forse non è la soluzione migliore, ma funziona.

Innanzitutto, abbiamo bisogno di alcune funzioni per registrare il descrittore e per eliminarlo. Dai un'occhiata alla prima stringa della funzione: la chiamata della funzione UnregisterBuffer() è necessaria per eliminare i vecchi descrittori nell'elenco delle variabili globali.

Ad ogni nuova barra la dimensione del buffer sarà aumentata di 1, quindi dobbiamo chiamare RegisterBuffer(). E se la dimensione del buffer è cambiata, il nuovo descrittore verrà creato nella tabella (l'informazione della sua dimensione è contenuta nel suo nome), il vecchio rimarrà nella tabella. Ecco perché è usato.

void RegisterBuffer(double &Buffer[], string name, int mode) export
{
   UnregisterBuffer(Buffer);                    //first delete the variable just in case
   
   int direction=0;
   if(ArrayGetAsSeries(Buffer)) direction=1;    //set the right ordering_direction

   name=name+"#"+mode+"#"+Symbol()+"#"+Period()+"#"+ArraySize(Buffer)+"#"+direction;
   int ptr=GetPtr(Buffer);                      // get the buffer pointer

   if(ptr==0) return;
   
   MathSrand(ptr);                              //it's convenient to use the pointer value instead
                                                //of the current time for the random numbers base 
   while(true)
   {
      int rnd=MathRand();
      if(!GlobalVariableCheck(name+"#"+rnd))    //check for unique name - we assume that 
      {                                         //nobody will use more RAND_MAX buffers :)
         name=name+"#"+rnd;                     
         GlobalVariableSet(name,ptr);           //and write it to the global variable
         break;
      }
   }   
}
void UnregisterBuffer(double &Buffer[]) export
{
   int ptr=GetPtr(Buffer);                      //we will register by the real address of the buffer
   if(ptr==0) return;
   
   int gt=GlobalVariablesTotal();               
   int i;
   for(i=gt-1;i>=0;i--)                         //just look through all global variables
   {                                            //and delete our buffer from all places
      string name=GlobalVariableName(i);        
      if(GlobalVariableGet(name)==ptr)
         GlobalVariableDel(name);
   }      
}

I commenti dettagliati sono qui superflui, ci limitiamo a sottolineare il fatto che la prima funzione crea un nuovo descrittore del formato precedente nell'elenco delle variabili globali; il secondo cerca in tutte le variabili globali e cancella le variabili e cancella i descrittori con il valore uguale al puntatore.

Consideriamo ora il secondo compito: ottenere dati dall'indicatore. Prima di implementare l'accesso diretto ai dati, dobbiamo prima trovare un descrittore corrispondente. Può essere risolto utilizzando la seguente funzione. Il suo algoritmo sarà il seguente: esaminiamo tutte le variabili globali e le controlliamo per la presenza di valori di campo specificati nel descrittore.

Se lo abbiamo trovato, aggiungiamo il suo nome all'array, passato come ultimo parametro. Quindi, come risultato, la funzione restituisce tutti gli indirizzi di memoria che corrispondono alle condizioni di ricerca. Il valore restituito è il numero di descrittori trovati.

int FindBuffers(string name, int mode, string symbol, int period, string &buffers[]) export
{
   int count=0;
   int i;
   bool found;
   string name_i;
   string descriptor[];
   int gt=GlobalVariablesTotal();

   StringTrimLeft(name);                                    //trim string from unnecessary spaces
   StringTrimRight(name);
   
   ArrayResize(buffers,count);                              //reset size to 0

   for(i=gt-1;i>=0;i--)
   {
      found=true;
      name_i=GlobalVariableName(i);
      
      StringExplode(name_i,"#",descriptor);                 //split string to fields
      
      if(StringFind(descriptor[0],name)<0&&name!=NULL) found=false; //check each field for the match
                                                                    //condition
      if(descriptor[1]!=mode&&mode>=0) found=false;
      if(descriptor[2]!=symbol&&symbol!=NULL) found=false;
      if(descriptor[3]!=period&&period>0) found=false;
      
      if(found)
      {
         count++;                                           //conditions met, add it to the list
         ArrayResize(buffers,count);
         buffers[count-1]=name_i;
      }
   }
   
   return(count);
}

Come si vede dal codice della funzione, alcune delle condizioni di ricerca possono essere omesse. Ad esempio, se passiamo il NULLcome nome, il controllo di string_identifier verrà omesso. Lo stesso vale per gli altri campi: mode<0, symbol:=NULL o periodo<=0. Permette di estendere le opzioni di ricerca nella tabella dei descrittori.

Ad esempio, puoi trovare tutti gli indicatori della Media Mobile in tutte le finestre del grafico o solo nei grafici EURUSD con periodo M15 ecc. Nota aggiuntiva: il controllo di string_identifier viene eseguito dalla funzione StringFind() invece del controllo di uguaglianza rigoroso. Viene fatto per fornire la possibilità di ricerca da una parte del descrittore (diciamo quando più indicatori impostano una stringa di tipo "MA(xxx)", la ricerca può essere eseguita per sottostringa "MA" - come risultato troveremo tutti le medie mobili registrate).

Inoltre abbiamo usato la funzione StringExplode(string s, string separator, string &result[]). Divide la stringa specificata in sottostringhe, usando il separatore e scrive i risultati nell'array dei risultati.

void StringExplode(string s, string separator, string &result[])
{
   int i,pos;
   ArrayResize(result,1);
   
   pos=StringFind(s,separator); 
   if(pos<0) {result[0]=s;return;}
   
   for(i=0;;i++)
   {
      pos=StringFind(s,separator); 
      if(pos>=0)
      {
         result[i]=StringSubstr(s,0,pos);
         s=StringSubstr(s,pos+StringLen(separator));
      }
      else break;
      ArrayResize(result,ArraySize(result)+1);
   }
}

Ora, quando abbiamo l'elenco di tutti i descrittori necessari, possiamo ottenere i dati dall'indicatore:

double GetIndicatorValue(string descriptor, int shift) export
{
   int ptr;
   string fields[];
   int size,direction;
   if(GlobalVariableCheck(descriptor)>0)               //check that the descriptor is valid
   {                
      ptr = GlobalVariableGet(descriptor);             //get the pointer value
      if(ptr!=0)
      {
         StringExplode(descriptor,"#",fields);         //split its name to fields
         size = fields[4];                             //we need the current array size
         direction=fields[5];                                 
         if(direction==1) shift=size-1-shift;          //if the ordering_direction is reverse
         if(shift>=0&&shift<size)                      //check for its validity - to prevent crashes
            return(GetValue(MathAbs(ptr),shift));      //ok, return the value
      }   
   } 
   return(EMPTY_VALUE);                                //overwise return empty value 
}

Come vedi, è un wrapping della funzione GetValue() dalla DLL. È necessario verificare la validità del descrittore, i limiti dell'array e prendere in considerazione l'ordine del buffer dell'indicatore. In caso di esito negativo, la funzione restituisce EMPTY_VALUE.

La modifica del buffer dell'indicatore è simile:

bool SetIndicatorValue(string descriptor, int shift, double value) export
{
   int ptr;
   string fields[];
   int size,direction;
   if(GlobalVariableCheck(descriptor)>0)               //check for its validity
   {                
      ptr = GlobalVariableGet(descriptor);             //get descriptor value
      if(ptr!=0)
      {
         StringExplode(descriptor,"#",fields);         //split it to fields
         size = fields[4];                             //we need its size
         direction=fields[5];                                 
         if(direction==1) shift=size-1-shift;          //the case of the inverse ordering
         if(shift>=0&&shift<size)                      //check index to prevent the crash of the client terminal
         {
            SetValue(MathAbs(ptr),shift,value);
            return(true);
         }   
      }   
   }
   return(false);
}

Se tutti i valori sono corretti, chiama la funzione SetValue() dalla DLL. Il valore restituito corrisponde al risultato della modifica: vero in caso di successo e falso in caso di errore.

5. Verificare Come Funziona

Adesso proviamo a verificarlo. Come obiettivo, utilizzeremo l'Average True Range (ATR)dal pacchetto standard e mostreremo cosa dobbiamo modificare nel suo codice per consentire di copiare i suoi valori in un'altra finestra dell'indicatore. Inoltre testeremo la modifica dei dati dei suoi dati del buffer.

Il primo che dobbiamo fare è aggiungere alcune righe di codice alla funzione OnCalculate():

//+------------------------------------------------------------------+

//| Average True Range                                               |
//+------------------------------------------------------------------+
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 &TickVolume[],
                const long &Volume[],
                const int &Spread[])
  {
   if(prev_calculated!=rates_total)
      RegisterBuffer(ExtATRBuffer,"ATR",0);
…

Come vediamo, la nuova registrazione del buffer dovrebbe essere eseguita ogni volta, quando abbiamo nuove barre - questo caso si verifica con il passare del tempo (normalmente) o in caso di download di dati storici aggiuntivi.

Oltre a ciò, dobbiamo eliminare i descrittori dalla tabella al termine dell'operazione dell'indicatore. Ecco perché è necessario aggiungere del codice all’handler dell'evento deinit (ma ricorda che dovrebbe restituire void e avere un parametro di input const int reason):

void OnDeinit(const int reason)
{
   UnregisterBuffer(ExtATRBuffer);
}

Ecco il nostro indicatore modificato nella finestra del grafico del terminale del cliente (Fig.1) e il suo descrittore nell'elenco delle variabili globali (Fig. 2):

 

Fig. 1. Average True Range

Fig. 2. Descrittore creato nell'elenco delle variabili globali del terminale client

Fig. 2. Descrittore creato nell'elenco delle variabili globali del terminale client

Il prossimo passo è ottenere l'accesso ai dati ATR. Creiamo un nuovo indicatore (chiamiamolo test) con il seguente codice:

//+------------------------------------------------------------------+
//|                                                         test.mq5 |
//|                                             Copyright 2009, alsu |
//|                                                 alsufx@gmail.com |
//+------------------------------------------------------------------+
#property copyright "2009, alsu"
#property link      "alsufx@gmail.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1

#include <exchng.mqh>

//---- plot ATRCopy
#property indicator_label1  "ATRCopy"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Red
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- indicator buffers
double ATRCopyBuffer[];

string atr_buffer;
string buffers[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ATRCopyBuffer,INDICATOR_DATA);
//---
   return(0);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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 found=FindBuffers("ATR",0,NULL,0,buffers);   //search for descriptors in global variables
   if(found>0) atr_buffer=buffers[0];               //if we found it, save it to the atr_buffer
   else atr_buffer=NULL;
   int i;
   for(i=prev_calculated;i<rates_total;i++)
   {
      if(atr_buffer==NULL) break;
      ATRCopyBuffer[i]=GetIndicatorValue(atr_buffer,i);  //now it easy to get data
      SetIndicatorValue(atr_buffer,i,i);                 //and easy to record them
   }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Come possiamo notare, non è difficile. Ottieni l'elenco dei descrittori da una sottostringa e leggi i valori del buffer dell'indicatore usando le nostre funzioni. E infine, scriviamo un po' di spazzatura (nel nostro esempio, scriviamo i valori che corrispondono all'indice dell'elemento dell'array).

Ora eseguendo l'indicatore di test (il nostro ATR target dovrebbe ancora essere allegato al grafico). Come vediamo in Fig.3, i valori di ATR sono nella sottofinestra inferiore (nel nostro indicatore di test), vediamo la linea retta al suo posto - infatti, è riempita con i valori corrispondenti agli indici dell'array.

 

Fig. 3. Risultato dell'operazione Test.mq5

Torsione del polso - questo è tutto :)

6. Retrocompatibilità

L'autore ha provato a creare una libreria, scritta in MQL5, che si avvicina al massimo agli standard MQ4. Quindi, ha bisogno di poche modifiche per essere utilizzato in MQL4.

In particolare, dovresti rimuovere la parola chiave export, che appare solo in MQL5, e modificare il codice dove abbiamo usato il typecasting indiretto (MQL4 non è così flessibile). In realtà, l'uso delle funzioni negli indicatori è completamente identico, le differenze sono solo nel metodo della nuova barra e nel controllo dell'aggiunta della cronologia.

Conclusione

Qualche parola sui vantaggi.

Sembra che l'uso del metodo descritto in questo articolo non possa essere limitato solo al suo scopo originale. Oltre alla costruzione di indicatori a cascata ad alta velocità, la libreria può essere applicata con successo agli Expert Advisor, inclusi i suoi casi di test storici. Altre applicazioni sono difficili da immaginare per l'autore, ma penso che i cari utenti saranno felici di lavorare in questa direzione.

Credo che potrebbero essere necessari i seguenti momenti tecnici:

  • miglioramento della tabella dei descrittori (in particolare, indicazione delle finestre);
  • sviluppo della tabella aggiuntiva per l'ordinamento della creazione dei descrittori - può essere importante per la stabilità dei sistemi di negoziazione.

Inoltre sarò lieto di ricevere altri suggerimenti per migliorare la libreria.

Tutti i file necessari sono allegati all'articolo. Vengono presentati i codici per MQL5, MQL4 e il codice sorgente della libreria exchng.dll. I file hanno le stesse posizioni in cui dovrebbero trovarsi nelle cartelle del terminale client.

Disclaimer

I materiali presentati in questo articolo descrivono tecniche di programmazione che con un uso incauto possono causare danni al software in esecuzione sul computer. L'uso dei file allegati dovrebbe avvenire solo a proprio rischio e possono portare a possibili effetti collaterali, sebbene non rivelati fino ad ora.

Ringraziamenti

L'autore ha utilizzato i problemi proposti sul forum di MQL4.Community https://forum.mql4.com e le idee dei suoi utenti: igor.senych, satop, bank , StSpirit, TheXpert, jartmailru, ForexTools, marketeer, IlyaA – scusate se l'elenco è incompleto.

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

File allegati |
cpp.zip (0.36 KB)
mql4.zip (39.29 KB)
mql521x.zip (46.87 KB)

Altri articoli di questo autore

Disegnare le Emissioni di Indicatori in MQL5 Disegnare le Emissioni di Indicatori in MQL5
In questo articolo, prenderemo in considerazione l'emissione di indicatori: un nuovo approccio alla ricerca di mercato. Il calcolo dell'emissione si basa sull'intersezione di diversi indicatori: dopo ogni tick, compaiono sempre più punti con colori e forme differenti. Formano numerosi ammassi come nebulose, nuvole, tracce, righe, archi, ecc. Queste forme aiutano a rilevare le molle e le forze invisibili che influenzano il movimento dei prezzi di mercato.
Come Scambiare i Dati: Una DLL per MQL5 in 10 minuti Come Scambiare i Dati: Una DLL per MQL5 in 10 minuti
Ora, non sono così tanti gli sviluppatori che ricordano come scrivere una semplice DLL e quali sono le caratteristiche speciali dei diversi binding di sistema. Usando diversi esempi, cercherò di mostrare l'intero processo di creazione della semplice DLL in 10 minuti, oltre a discutere alcuni dettagli tecnici della nostra implementazione vincolante. Mostrerò il processo passo dopo passo della creazione di DLL in Visual Studio con esempi di scambio di diversi tipi di variabili (numeri, array, stringhe, ecc.). Inoltre spiegherò come proteggere il tuo terminale client da arresti anomali nelle DLL personalizzate.
Come Esportare Quotazioni da МetaTrader 5 ad Applicazioni .NET Utilizzando i Servizi WCF Come Esportare Quotazioni da МetaTrader 5 ad Applicazioni .NET Utilizzando i Servizi WCF
Vuoi organizzare l'esportazione delle quotazioni da MetaTrader 5 alla tua applicazione? La giunzione MQL5-DLL permette di creare tali soluzioni! Questo articolo ti mostrerà uno dei modi per esportare le quotazioni da MetaTrader 5 ad applicazioni scritte in .NET. Per me è stato più interessante, razionale e facile implementare l'esportazione dei preventivi utilizzando proprio questa piattaforma. Sfortunatamente, la versione 5 non supporta ancora .NET quindi, come ai vecchi tempi, utilizzeremo win32 dll con supporto .NET come interlayer.
L'Istogramma dei Prezzi (Market Profile) e la sua implementazione in MQL5 L'Istogramma dei Prezzi (Market Profile) e la sua implementazione in MQL5
The Market Profile è stato sviluppato dal geniale pensatore Peter Steidlmayer. Ha suggerito di utilizzare la rappresentazione alternativa delle informazioni sui movimenti di mercato "orizzontali" e "verticali" che porta a un insieme completamente diverso di modelli. Ha assunto che ci sia un impulso sottostante del mercato o un modello fondamentale chiamato ciclo di equilibrio e squilibrio. In questo articolo considererò Price Histogram - un modello semplificato di Market Profile e descriverò la sua implementazione in MQL5.