English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Guida alla scrittura di una DLL per MQL5 in Delphi

Guida alla scrittura di una DLL per MQL5 in Delphi

MetaTrader 5Esempi | 16 dicembre 2021, 10:32
119 0
Andriy Voitenko
Andriy Voitenko

Introduzione

Il meccanismo di scrittura di una DLL verrà considerato utilizzando un esempio dell'ambiente di sviluppo di Delphi 2009. Questa versione è stata scelta poiché in MQL5 tutte le righe sono memorizzate in formato Unicode. Nelle versioni precedenti di Delphi, al modulo SysUtils manca la funzione per lavorare con le righe in formato Unicode.

Se, per qualsiasi motivo, stai utilizzando una versione precedente (Delphi 2007 e precedenti), devi lavorare con righe in formato ANSI e, per scambiare dati con MetaTrader 5, devi produrre conversioni dirette e inverse in Unicode . Per evitare queste complicazioni, consiglio di sviluppare il modulo DLL per MQL5 in un ambiente non più vecchio di Delphi 2009. Dal sito ufficiale http://embarcadero.com puoi scaricare una versione di prova gratuita di Delphi per 30 giorni.

1. Creazione del Progetto

Per creare il progetto dobbiamo eseguire una DLL Wizard scegliendo la voce di menu: 'File -> Nuovo -> Altro... -> DLL Wizard' Come mostrato nella Figura 1.

Figura 1. Creazione di un progetto utilizzando una DLL Wizard

Di conseguenza, creeremo un progetto DLL vuoto, come mostrato nella Figura 2.


Figura 2. Un progetto DLL vuoto

Il significato di un commento lungo nel titolo del progetto è quello di ricordare una connessione corretta e l'uso di un gestore di memoria quando si lavora con la memoria allocata dinamicamente. Questo argomento verrà trattato in maniera più dettagliata nella sezione relativa alle stringhe.

Prima di iniziare a riempire la nuova DLL con le funzioni, è importante configurare il progetto.

Aprire la finestra delle proprietà del progetto dal menu: 'Progetto -> Opzioni...' o tramite la tastiera 'Shift + Ctrl + F11' .

Per semplificare il processo di debug, è necessario che il file DLL venga creato direttamente nella cartella '.. \\MQL5\\Libraries' Trade Terminal MetaTrtader5. Per fare ciò, nella scheda DelphiCompiler impostare il valore della proprietà corrispondente Output directory , come mostrato nella Figura 3. Ciò elimina la necessità di copiare costantemente il file generato dalla DLL dalla cartella del progetto nella cartella del terminale.

Figure 3. Specificare la cartella in cui memorizzare il file DLL risultante

Per evitare l'aggancio di moduli BPL durante l'assemblaggio, senza la cui presenza nella cartella di sistema di Windows la DLL creata non funzionerà in futuro, è importante verificare che nella scheda Packages, il flag Build con i pacchetti runtime sia deselezionato, come mostrato nella Figura 4.

Figure 4. Esclusione dei moduli BPL dall'assemblaggio

Dopo aver completato la configurazione del progetto, salvalo nella tua cartella di lavoro, il nome specificato del progetto sarà il nome futuro del file DLL compilato.

2. Aggiunta di procedure e funzioni 

Consideriamo la situazione generale quando si scrivono le procedure e le funzioni esportate nel modulo DLL, su un esempio di una procedura senza parametri. La comunicazione e la trasmissione dei parametri saranno discusse nella sezione successiva.

Una piccola digressione. Durante la scrittura delle procedure e delle funzioni nel linguaggio Object Pascal, il programmatore ha la possibilità di utilizzare le funzioni integrate della libreria Delphi, per non parlare degli svariati componenti sviluppati per questo ambiente. Ad esempio, per eseguire la stessa azione, come ad esempio aprire la visualizzazione di una finestra modale con un messaggio di testo, è possibile utilizzare come funzione API - MessageBox nonché una procedura della libreria VCL - ShowMessage.

La seconda opzione porta all'inclusione del modulo Dialogs e offre il vantaggio di lavorare facilmente con le finestre di dialogo standard di Windows. Tuttavia, la dimensione del file DLL risultante aumenterà di circa 500 KB. Pertanto, se preferisci creare piccoli file DLL, che non occupano molto spazio su disco, ti sconsiglio di utilizzare i componenti VCL.

Di seguito, viene riportato un esempio di progetto di prova con relative spiegazioni:

library dll_mql5;

uses
  Windows, // necessary for the work of the MessageBox function
  Dialogs; // necessary for the work of the ShowMessage procedure from the Dialogs module

var   Buffer: PWideChar;
//------------------------------------------------------+
procedure MsgBox(); stdcall; // 
//to avoid errors, use the stdcall (or cdecl) for the exported functions
//------------------------------------------------------+
begin
    {1} MessageBox(0,'Hello World!','terminal', MB_OK);
    {2} ShowMessage('Hello World!');// alternative to the MessageBox function 
end;

//----------------------------------------------------------+
exports
//----------------------------------------------------------+
  {A} MsgBox,
  {B} MsgBox name 'MessageBox';// renaming of the exported function


//----------------------------------------------------------+
procedure DLLEntryPoint(dwReason: DWord); // event handler
//----------------------------------------------------------+
begin
    case dwReason of

      DLL_PROCESS_ATTACH: // DLL attached to the process;
          // allocate memory
          Buffer:=AllocMem(BUFFER_SIZE);

      DLL_PROCESS_DETACH: // DLL detached from the process;
          // release memory
          FreeMem(Buffer);

    end;
end;

//----------------------------------------------------------+
begin
    DllProc := @DLLEntryPoint; //Assign event handler
    DLLEntryPoint(DLL_PROCESS_ATTACH);
end.
//----------------------------------------------------------+

Tutte le funzioni esportate devono essere annunciate con il modificatore stdcall o cdecl. Se nessuno di questi modificatori viene specificato, Delphi utilizza l'accordo fastcall predefinito che utilizza principalmente i registri della CPU per trasmettere i parametri piuttosto che lo stack. Porterà senza dubbio a un errore nel lavorare con i parametri trasmessi, nella fase di calling up delle funzioni esterne DLL.

La sezione "begin end" contiene un codice di inizializzazione standard di un gestore di eventi DLL. La procedura di callback DLLEntryPoint verrà chiamata durante la connessione e la disconnessione dal processo che l'ha chiamata. Questi eventi possono essere utilizzati per la corretta gestione della memoria dinamica, allocata per le nostre esigenze, come mostrato nell'esempio.

Chiamata per MQL5:

#import "dll_mql5.dll"
    void MsgBox(void);
    void MessageBox(void);
#import

// Call of procedure
   MsgBox();
// If the names of the function coincide with the names of MQL5 standard library function
// use the DLL name when calling the function
   dll_mql5::MessageBox();

3. Trasmissione dei Parametri alla Funzione e Valori Restituiti

Prima di considerare la trasmissione dei parametri, analizziamo la tabella di corrispondenza dei dati per MQL5 ed Object Pascal.

Tipo di dati per MQL5
Tipo di dati per Object Pascal (Delphi)
Nota
char ShortInt
uchar
Byte
short
SmallInt
ushort
Word

int
Integer
 
uint Cardinal
long Int64
ulong
UInt64

float Single
double Double

ushort (символ) WideChar
string PWideChar  
bool Boolean  
datetime TDateTime è richiesta la conversione (vedi sotto in questa sezione)
color TColor  

Tabella 1. La tabella di corrispondenza dei dati per MQL5 e Object Pascal

Come puoi vedere dalla tabella, per tutti i tipi di dati diversi da datetime, Delphi ha un equivalente completo.

Consideriamo ora due modi per passare i parametri: per valore e per riferimento. Il formato della dichiarazione dei parametri per entrambe le versioni viene riportato nella Tabella 2.

Metodo di trasmissione dei parametri
Annuncio per MQL5
Annuncio per Delphi
Nota 
per valore
int funzione (int a); func (a:Integer): Integer;  corretto

int funzione (int a);
funzione (var a: Integer): Integer;
 Errore: violazione di accesso scrivere su <memory address>
per collegamento
int funzione (int &a);
funzione (var a: Integer): Integer;
 corretto, tuttavia le linee vengono trasmesse senza un modificatore var!
  int funzione (int &a);  func (a: Integer): Integer;  errore: al posto del valore della variabile, contiene l'indirizzo della cella di memoria

Tabella 2. Metodi di trasferimento dei parametri

Consideriamo ora gli esempi di utilizzo dei parametri trasferiti e dei valori restituiti.

3.1 Conversione di data e ora

Innanzitutto, ci occupiamo del tipo di data e ora che si desidera convertire, poiché il tipo datetime corrisponde a TDateTime solo nelle sue dimensioni ma non nel formato. Per facilitare la trasformazione, utilizzare Int64 come tipo di dati ricevuti, invece di TDateTime. Di seguito, vengono riportate le funzioni per la trasformazione diretta ed inversa:

uses 
    SysUtils,  // used for the constant UnixDateDelta 
    DateUtils; // used for the function IncSecon, DateTimeToUnix

//----------------------------------------------------------+
Function MQL5_Time_To_TDateTime(dt: Int64): TDateTime;
//----------------------------------------------------------+
begin
      Result:= IncSecond(UnixDateDelta, dt);
end;

//----------------------------------------------------------+
Function TDateTime_To_MQL5_Time(dt: TDateTime):Int64;
//----------------------------------------------------------+
begin
      Result:= DateTimeToUnix(dt);
end;

3.2 Lavorare con tipi di dati semplici

Analizziamo come trasmettere tipi di dati semplici, sull'esempio di quelli più comunemente usati: int, double, bool e datetime.

Chiamata per Object Pascal:

//----------------------------------------------------------+
function SetParam(var i: Integer; d: Double; const b: Boolean; var dt: Int64): PWideChar; stdcall;
//----------------------------------------------------------+
begin
  if (b) then d:=0;                   // the value of the variable d is not changed in the calling program
  i:= 10;                             // assign a new value for i
  dt:= TDateTime_To_MQL5_Time(Now()); // assign the current time for dt
  Result:= 'value of variables i and dt are changed';
end;

Chiamata per MQL5:

#import "dll_mql5.dll"
    string SetParam(int &i, double d, bool b, datetime &dt);
#import

// initialization of variables
   int i = 5;
   double d = 2.8;
   bool b = true;
   datetime dt= D'05.05.2010 08:31:27';
// calling the function
   s=SetParam(i,d,b,dt);
// output of results
   printf("%s i=%s d=%s b=%s dt=%s",s,IntegerToString(i),DoubleToString(d),b?"true":"false",TimeToString(dt));
Risultato: 
The values of variables i and dt are changed i =  10  d =  2.80000000  b = true dt =  2009.05 .  05  08 :  42 

Il valore di d non è cambiato da quando è stato trasferito per valore. Per evitare che si verifichino cambiamenti del valore di una variabile, all'interno di una funzione DLL, un modificatore const. è stato utilizzato sulla variabile b.

3.3 Lavorare con strutture e array

In diverse occasioni, è utile raggruppare i parametri di tipi diversi in strutture e i parametri di un tipo in array. Supponiamo di lavorare con tutti i parametri trasferiti della funzione SetParam, dall'esempio precedente, integrandoli in una struttura.

Chiamata per Object Pascal: 

type
StructData = packed record
    i: Integer;
    d: Double;
    b: Boolean;
    dt: Int64;
  end;

//----------------------------------------------------------+
function SetStruct(var data: StructData): PWideChar; stdcall;
//----------------------------------------------------------+
begin
  if (data.b) then data.d:=0;
  data.i:= 10;                                 // assign a new value for i
  data.dt:= TDateTime_To_MQL5_Time(Now()); // assign the current time for dt
  Result:= 'The values of variables i, d and dt are changed';
end

Chiamata per MQL5:  

struct STRUCT_DATA
  {
   int i;
   double d;
   bool b;
   datetime dt;
  };

#import "dll_mql5.dll"
    string SetStruct(STRUCT_DATA &data);
#import

   STRUCT_DATA data;
   
   data.i = 5;
   data.d = 2.8;
   data.b = true;
   data.dt = D'05.05.2010 08:31:27';
   s = SetStruct(data);
   printf("%s i=%s d=%s b=%s dt=%s", s, IntegerToString(data.i),DoubleToString(data.d), 
             data.b?"true":"false",TimeToString(data.dt));
Risultato:
The values of variables i,  d  and dt are changed i =  10  d =  0.00000000  b = true dt =  2009.05 .  05  12 :  19 

È necessario notare una differenza significativa rispetto al risultato dell'esempio precedente. Poiché la struttura viene trasferita attraverso un riferimento, non è possibile proteggere i campi selezionati dalla modifica nella funzione chiamata. Il compito di monitorare l'integrità dei dati, in questo caso, spetta interamente al programmatore.

Supponiamo di lavorare con gli array, su un esempio di riempimento dell'array con la sequenza dei numeri di Fibonacci:

Chiamata per Object Pascal:

//----------------------------------------------------------+
function SetArray(var arr: IntegerArray; const len: Cardinal): PWideChar; stdcall;
//----------------------------------------------------------+
var i:Integer;
begin
  Result:='Fibonacci numbers:';
  if (len < 3) then exit;
  arr[0]:= 0;
  arr[1]:= 1;
  for i := 2 to len-1 do
    arr[i]:= arr[i-1] + arr[i-2];
end;

Chiamata per MQL5: 

#import "dll_mql5.dll"
    string SetArray(int &arr[],int len);
#import
   
   int arr[12];
   int len = ArraySize(arr);
// passing the array by reference to be filled by data in DLL
   s = SetArray(arr,len);
//output of result
   for(int i=0; i<len; i++) s = s + " " + IntegerToString(arr[i]);
   printf(s);
Risultato:  
Fibonacci numbers 0 1 1 2 3 5 8 13 21 34 55 89

3.4 Lavorare con lo storage

Torniamo alla gestione della memoria. Nella DLL è possibile utilizzare il proprio gestore di memoria. Tuttavia, poiché laDLL e il programma che la chiama, sono spesso scritti in linguaggi di programmazione diversi e nel lavoro vengono utilizzati i propri gestori di memoria, piuttosto che la memoria di sistema generale, l'intero onere della responsabilità per il corretto funzionamento della memoria nella DLL di giunzione e dell'applicazione, ricade sul programmatore.

Per lavorare con la memoria, è importante rispettare la regola d'oro, secondo la quale: "Coloro che hanno allocato la memoria, devono essere quelli che la liberano." Cioè non dovresti provare a rilasciare la memoria nel codice mql 5-program, allocato nella DLL, e viceversa.

Consideriamo un esempio di gestione della memoria in uno stile di chiamate di funzioni API di Windows. Nel nostro caso il programma mql5 alloca memoria per il buffer, un puntatore al buffer trasferito alla DLL come PWideChar e la DLL riempie solo questo buffer con il valore desiderato, come mostrato nel seguente esempio:

Chiamata per Object Pascal:

//----------------------------------------------------------+
procedure SetString(const str:PWideChar) stdcall;
//----------------------------------------------------------+
begin
  StrCat(str,'Current time:');
  strCat(str, PWideChar(TimeToStr(Now)));
end;

Chiamata per MQL5: 

#import "dll_mql5.dll"
    void SetString(string &a);
#import

// the string must be initialized before the use
// the size of the buffer must be initially larger or equal to the string length
   StringInit(s,255,0); 
//passing the buffer reference to DLL 
   SetString(s);
// output of result 
   printf(s);

Risultato:

Current Time: 11: 48:51 

La memoria per il buffer di linea può essere selezionata nella DLL in diversi modi, come si può vedere dall'esempio seguente:

Chiamata per Object Pascal: 

//----------------------------------------------------------+
function GetStringBuffer():PWideChar; stdcall;
//----------------------------------------------------------+
var StrLocal: WideString;
begin
     // working through the dynamically allocated memory buffer
     StrPCopy(Buffer, WideFormat('Current date and time: %s', [DateTimeToStr(Now)]));
     // working through the global varialble of WideString type
     StrGlobal:=WideFormat('Current time: %s', [TimeToStr(Time)]);
     // working through the local varialble of WideString type
     StrLocal:= WideFormat('Current data: %s', [DateToStr(Date)]);

{A}  Result := Buffer;

{B}  Result := PWideChar(StrGlobal);
     // it's equal to the following
     Result := @StrGlobal[1];

{С}  Result := 'Return of the line stored in the code section';

     // pointer to the memory, that can be released when exit from the function
{D}  Result := @StrLocal[1];
end;
Chiamata per MQL5: 
#import "dll_mql5.dll"
    string GetStringBuffer(void);
#import

   printf(GetStringBuffer());

 Risultato: 

Current Date: 19.05.2010

L’aspetto significativo è che tutte e quattro le opzioni funzionano. Nelle prime due opzioni, il lavoro con la riga viene eseguito tramite una memoria allocata globalmente.

Nell'opzione A, la memoria è allocata in modo indipendente e nell'opzione B, il lavoro con la gestione della memoria è assunto dal gestore della memoria.

Nell'opzione C, la costante di riga non viene memorizzata nella memoria ma nel segmento di codice; quindi il gestore della memoria non alloca memoria dinamica per la sua memorizzazione. L'opzione D è un grave errore di programmazione, perché la memoria, allocata per la variabile locale, può essere rilasciata immediatamente dopo l'uscita dalla funzione.

E sebbene il gestore della memoria non rilasci questa memoria in maniera istantanea e non vi sia tempo per riempirsi di spazzatura, consiglio di escludere quest'ultima opzione dall'uso.

3.5 Utilizzo dei parametri di default

Parliamo dell'uso dei parametri opzionali. Sono interessanti perché i loro valori non devono essere specificati quando si richiamano procedure e funzioni. Nel frattempo, devono essere descritti, rigorosamente dopo tutti i parametri obbligatori nella dichiarazione di procedure e funzioni, come mostrato nel seguente esempio:

Chiamata per Object Pascal: 

//----------------------------------------------------------+
function SetOptional(var a:Integer; b:Integer=0):PWideChar; stdcall;
//----------------------------------------------------------+
begin
    if (b=0) then Result:='Call with default parameters'
    else          Result:='Call without default parameters';
end;
Chiamata per MQL5:
#import "dll_mql5.dll"
    string SetOptional(int &a, int b=0);
#import

  i = 1;
  s = SetOptional(i); // second parameter is optional
  printf(s);

Risultato: 

Call with default parameters

Per facilitare il debug, il codice degli esempi precedenti è organizzato come script, che si trova nel file Testing_DLL.mq5.

4. Possibili errori in fase di progettazione

Errore: Il caricamento della DLL non è consentito.

Soluzione: Vai alle impostazioni di MetaTrader 5 attraverso il menu 'Strumenti-Opzioni' e consenti l'importazione della funzione DLL, come mostrato nella Figura 5.

 Figura 5. Permesso di importare funzioni DLL

Figura 5. Permesso di importare funzioni DLL

Errore: Impossibile trovare "nome funzione" in "nome DLL".
Soluzione: Verificare se la funzione di callback è specificata nella sezione Esportazioni del progetto DLL. Se lo è, dovresti controllare la corrispondenza completa del nome della funzione nella DLL e nel programma mql5, considerando che è sensibile ai caratteri!

Errore: Violazione di accesso scrivere a [memory address]
Soluzione: È necessario verificare la correttezza della descrizione dei parametri trasmessi (vedi tabella 2). Poiché di solito questo errore è associato all'elaborazione delle righe, è importante seguire le raccomandazioni per lavorare con le righe illustrate al paragrafo 3.4 di questo articolo.

5. Esempio di codice DLL

Come esempio visivo dell'uso della DLL, si consideri il calcolo dei parametri del canale di regressione, costituito da tre righe. Per verificare la correttezza della costruzione del canale, utilizzeremo l'oggetto integrato "Canale di regressione". Il calcolo della linea di approssimazione per LS (metodo dei minimi quadrati) è tratto dal sito http://alglib.sources.ru/, dove è presente una raccolta di algoritmi per l'elaborazione dei dati. Il codice degli algoritmi è presentato in diversi linguaggi di programmazione, incluso Delphi.

Per calcolare i coefficienti di a e b mediante la retta approssimante y = a + b * x, utilizzare la procedura descritta nel file LRLine linreg.pas.

 procedure  LRLine (  const  XY: TReal2DArray;  / / Two-dimensional array of real numbers for X and Y coordinates 
                           N : AlglibInteger;  // number of points
                     var Info : AlglibInteger; // conversion status
                       var  A: Double;  / / Coefficients of the approximating line 
                        var  B: Double);
  

Per calcolare i parametri del canale, utilizzare la funzione CalcLRChannel.

Chiamata per Object Pascal:  

//----------------------------------------------------------+
function CalcLRChannel(var rates: DoubleArray; const len: Integer;
                          var A, B, max: Double):Integer; stdcall;
//----------------------------------------------------------+
var arr: TReal2DArray;
    info: Integer;
    value: Double;
begin

    SetLength(arr,len,2);
    // copy the data to a two-dimensional array
    for info:= 0 to len - 1 do
    begin
      arr[info,0]:= rates[info,0];
      arr[info,1]:= rates[info,1];
    end;

    // calculation of linear regression coefficients
    LRLine(arr, len, info, A,  B);

    // find the maximal deviation from the approximation line found
    // and determine the width of the channel 
    max:= rates[0,1] - A;
    for info := 1 to len - 1 do
    begin
      value:= Abs(rates[info,1]- (A + B*info));
      if (value > max) then max := value;
    end;

    Result:=0;
end;

Chiamata per MQL5:

#import "dll_mql5.dll"
    int CalcLRChannel(double &rates[][2],int len,double &A,double &B,double &max);
#import

   double arr[][2], //data array for processing in the ALGLIB format
              a, b,  // Coefficients of the approximating line  
              max; // maximum deviation from the approximating line is equal to half the width of the channel
   
   int len = period; //number of points for calculation
   ArrayResize(arr,len);

// copying the history to a two-dimensional array
   int j=0;
   for(int i=rates_total-1; i>=rates_total-len; i--)
     {
      arr[j][0] = j;
      arr[j][1] = close[i];
      j++;
     }

// calculation of channel parameters
   CalcLRChannel(arr,len,a,b,max);

Il codice dell'indicatore, che utilizza la funzione CalcLRChannel per i calcoli, si trova nel file LR_Channel.mq5 e di seguito: 

//+------------------------------------------------------------------+
//|                                                   LR_Channel.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

#include <Charts\Chart.mqh>
#include <ChartObjects\ChartObjectsChannels.mqh>

#import "dll_mql5.dll"
int CalcLRChannel(double &rates[][2],int len,double &A,double &B,double &max);
#import

input int period=75;

CChart               *chart;
CChartObjectChannel  *line_up,*line_dn,*line_md;
double                arr[][2];
//+------------------------------------------------------------------+
int OnInit()
//+------------------------------------------------------------------+
  {

   if((chart=new CChart)==NULL)
     {printf("Chart not created"); return(false);}

   chart.Attach();
   if(chart.ChartId()==0)
     {printf("Chart not opened");return(false);}

   if((line_up=new CChartObjectChannel)==NULL)
     {printf("Channel not created"); return(false);}

   if((line_dn=new CChartObjectChannel)==NULL)
     {printf("Channel not created"); return(false);}

   if((line_md=new CChartObjectChannel)==NULL)
     {printf("Channel not created"); return(false);}

   return(0);
  }
//+------------------------------------------------------------------+
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[])
//+------------------------------------------------------------------+
  {

   double a,b,max;
   static double save_max;
   int len=period;

   ArrayResize(arr,len);

// copying of history to a two-dimensional array
   int j=0;
   for(int i=rates_total-1; i>=rates_total-len; i--)
     {
      arr[j][0] = j;
      arr[j][1] = close[i];
      j++;
     }

// procedure of calculating the channel parameters
   CalcLRChannel(arr,len,a,b,max);

// if the width of the channel has changed
   if(max!=save_max)
     {
      save_max=max;

      // Delete the channel
      line_md.Delete();
      line_up.Delete();
      line_dn.Delete();

      // Creating a channel with new coordinates
      line_md.Create(chart.ChartId(),"LR_Md_Line",0, time[rates_total-1],     a, time[rates_total-len], a+b*(len-1)    );
      line_up.Create(chart.ChartId(),"LR_Up_Line",0, time[rates_total-1], a+max, time[rates_total-len], a+b*(len-1)+max);
      line_dn.Create(chart.ChartId(),"LR_Dn_Line",0, time[rates_total-1], a-max, time[rates_total-len], a+b*(len-1)-max);

      // assigning the color of channel lines     
      line_up.Color(RoyalBlue);
      line_dn.Color(RoyalBlue);
      line_md.Color(RoyalBlue);

      // assigning the line width
      line_up.Width(2);
      line_dn.Width(2);
      line_md.Width(2);
     }

   return(len);
  }
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
//+------------------------------------------------------------------+
  {
// Deleting the created objects
   chart.Detach();

   delete line_dn;
   delete line_up;
   delete line_md;
   delete chart;
  }

Il risultato del lavoro dell'indicatore è la creazione di un canale di regressione blu, come mostrato nella Figura 6. Per verificare la correttezza della costruzione del canale, il grafico mostra un "Canale di regressione", dall'arsenale di strumenti di analisi tecnica MetaTrader 5, contrassegnato in rosso.

Come si può vedere nella figura, le linee centrali del canale si fondono insieme. Nel frattempo, vi è una leggera differenza della larghezza del canale (pochi punti), dovuta ai diversi approcci nel suo calcolo. 

Figura 6. Confronto dei canali di regressione

Figura 6. Confronto dei canali di regressione

Conclusione

Questo articolo descrive le funzionalità di scrittura di una DLL, utilizzando una piattaforma di sviluppo di applicazioni Delphi.

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

File allegati |
mql5_sources.zip (276.43 KB)
dll_mql5_sources.zip (453.47 KB)
Analizzare i Modelli di Candele Analizzare i Modelli di Candele
La costruzione del grafico a candele giapponesi e l'analisi dei modelli di candele costituiscono un'area straordinaria dell’analisi tecnica. Il vantaggio dei modelli di candele è che rappresentano i dati in modo tale da poter tenere traccia delle dinamiche all'interno dei dati. In questo articolo analizziamo i tipi di candele, la classificazione dei modelli di candele e presentiamo un indicatore in grado di determinare queste ultime.
Un Esempio di un Sistema di Trading Basato su un Indicatore Heiken-Ashi Un Esempio di un Sistema di Trading Basato su un Indicatore Heiken-Ashi
In questo articolo esaminiamo la questione dell'utilizzo di un indicatore Heiken-Ashi nel trading. Sulla base di questo indicatore, viene considerato un semplice sistema di trading e viene scritto un Expert Advisor MQL5. Le operazioni di trading sono implementate sulla base delle classi della libreria di classi Standard. I risultati dello Strategy Tester di trading recensito, si basano sulla cronologia e sono ottenuti utilizzando lo Strategy Tester MetaTrader 5 integrato, sono forniti nell'articolo.
Creazione di un Expert Advisor, che fa Trading su una Serie di Strumenti Creazione di un Expert Advisor, che fa Trading su una Serie di Strumenti
Il concetto di diversificazione delle attività sui mercati finanziari è tranquillo e ha sempre attratto i trader principianti. In questo articolo, l'autore propone un approccio estremamente semplice alla costruzione di un Expert Advisor multi-valuta, per una prima introduzione a questa direzione delle strategie di trading.
Un Gestore degli Ordini Virtuale per tenere traccia degli ordini all'interno dell'ambiente MetaTrader 5 incentrato sulla posizione Un Gestore degli Ordini Virtuale per tenere traccia degli ordini all'interno dell'ambiente MetaTrader 5 incentrato sulla posizione
Questa libreria di classi può essere aggiunta a un MetaTrader 5 Expert Advisor per consentirne la scrittura con un approccio incentrato sull'ordine sostanzialmente simile a MetaTrader 4, rispetto all'approccio basato sulla posizione di MetaTrader 5. Lo fa tenendo traccia degli ordini virtuali sul client terminal MetaTrader 5, mantenendo un broker protettivo per ogni posizione per la protezione dai disastri.