Descargar MetaTrader 5

Guía para escribir un DLL para MQL en Delphi

21 febrero 2014, 14:29
Andrey Voytenko
4
2 345

Introducción

El mecanismo de escritura de un DLL se tratará con un ejemplo de entorno de desarrollo de Delphi 2009. Esta versión se eligió por el hecho de que, en MQL5, todas las líneas se guardan en formato Unicode. En versiones anteriores de Delphi, el módulo SysUtils no tenía la función para trabajar con líneas en formato Unicode.

Si por cualquier motivo está usando una versión anterior (Delphi 2007 o más antigua), deberá trabajar con líneas en formato ANSI, y para intercambiar datos con MetaTrader 5 deberá producir conversiones directas e inversas a Unicode. Para evitar tantas complicaciones, yo recomiendo desarrollar el módulo DLL para MQL5 en un entorno que no sea más antiguo que Delphi 2009. Puede descargarse una versión de prueba de 30 días de Delphi de la página web oficial http://embarcadero.com.

1. Crear el Proyecto

Para crear el proyecto, deberemos ejecutar el DLL Wizard seleccionándolo en el menú: 'File (Archivo)-> New (Nuevo)-> Other ... (Otro ...)-> DLL Wizard ' Tal y como se muestra en la Figura 1.

Figura 1. Crear un proyecto usando el DLL Wizard

Como resultado, crearemos un proyecto DLL vacío, como se muestra en la Figura 2.


Figura 2. Un proyecto DLL vacío

El objetivo de un comentario largo en el título del proyecto es recordarle que hay una conexión correcta y del uso de un gestor de memoria al trabajar con memoria distribuida dinámicamente. Trataremos esto con más detalle en la sección que trata sobre las cadenas de caracteres.

Antes de empezar a llenar el nuevo DLL con funciones, es importante configurar el proyecto.

Abra la ventana de propiedades del proyecto en el menú: 'Project (Proyectos)-> Options ... (Opciones ...)' o apretando la combinación de teclas 'Shift + Ctrl + F11' .

Para simplificar el proceso de depuración es necesario que el archivo DLL se cree directamente en la carpeta '.. \\MQL5\\Libraries' Trade Terminal MetaTrtader5. Para ello, en la pestaña DelphiCompiler (Compilador Delphi) configure el valor de propiedad correspondiente Output directory (Directorio de salida), tal y como se muestra en la Figura 3. Eliminará la necesidad de copiar constantemente el archivo generado por el DLL de la carpeta del proyecto a la carpeta del terminal.

 Figura 3. Especifique la carpeta para almacenar el archivo DLL resultante

Durante la asamblea, para evitar la unión de módulos BPL sin cuya presencia en la capeta de sistema de Windows el DLL creado no podría funcionar en el futuro, es importante asegurarse de que la pestaña Packages (Paquetes), la flag Build with runtime packages (construir con paquetes de ejecución) se deja sin activar, tal y como se muestra en la Figura 4.

  Figura 4. Exclusión de los módulos BPL de la asamblea

Tras completar la configuración del proyecto, guárdelo en su carpeta de trabajo. El nombre especificado para el proyecto será el nombre futuro del archivo DLL compilado.

2. Añadir los procedimientos y funciones 

Consideremos una situación en la que escribiremos los procedimientos y funciones importados en el módulo DLL en un ejemplo de procedimiento sin parámetros. El anuncio y la transferencia de parámetros se tratarán en la siguiente sección.

Un pequeño comentario. Al escribir procedimientos y funciones en el lenguaje Object Pascal language, el programador tiene la posibilidad de usar las funciones incorporadas de la biblioteca Delphi, y también, por supuesto, los innumerables componentes para este entorno. Por ejemplo, para la ejecución de la misma acción, como por ejemplo provocar la muestra de una ventana modal con un mensaje de texto, puede usar una función API - MessageBox , así como un procedimiento de la biblioteca VCL - ShowMessage.

La segunda opción nos lleva a incluir el módulo Dialogs (Diálogos), y nos da la ventaja de trabajar fácilmente con diálogos estándar de Windows. No obstante, el tamaño del archivo DLL resultante aumentará aproximadamente unos 500 KB. Por tanto, si prefiere crear archivos DLL pequeños que no ocupen mucho espacio en el disco duro, no le recomendaría usar los componentes VCL.

Abajo tiene un proyecto de prueba modelo con explicaciones:

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.
//----------------------------------------------------------+

Todas las funciones exportadas deben anunciarse con el modificador stdcall o cdecl. Si no se especifica ninguno de estos modificadores, Delphi usará el acuerdo fastcall por defecto, que usa principalmente registros de CPU para pasar parámetros. Sin duda, llevará a un error al trabajar con los parámetros pasados en el momento de llamar a las funciones externas DLL.

La sección "begin end" ("comienzo final") contiene un código de inicialización estándar de un controlador de eventos DLL. Se llamará al procedimiento de rellamada DLLEntryPoint al conectarse y desconectarse del proceso que lo llamó. Estos eventos se pueden usar para una gestión de memoria dinámica correcta, distribuida según nuestras necesidades, tal y como se muestra en el ejemplo.

Llamada para 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. Pasar Parámetros a la Función y Valores Devueltos

Antes de tratar el paso de parámetros, analicemos la tabla de correspondencia de datos para MQL5 y Object Pascal.

Tipo de datos para MQL5
Tipo de datos para Object Pascal (Delphi)
Nota
char ShortInt
uchar
Byte
short
SmallInt
ushort
Word

int
Integer
 
uint Cardinal
long Int64
ulong
UInt64

float Single
doble Double

ushort (символ) WideChar
cadena de caracteres PWideChar  
bool Boolean  
datetime TDateTime conversion is required (vea más abajo en esta sección)
color TColor  

Tabla 1. La tabla de correspondencia de datos para MQL5 y Object Pascal

Como puede ver en la tabla, Delphi tiene un análogo completo para todos los tipos de datos a excepción de datetime.

Ahora consideremos dos maneras de pasar parámetros: por valor y por referencia. El formato de la declaración de parámetros para ambas versiones se da en la Tabla 2.

Método de transferencia de parámetros
Anuncio para MQL5
Anuncio para Delphi
Nota 
by value
int func (int a); func (a:Integer): Integer;  correct

int func (int a);
func (var a: Integer): Integer;
 Error: access violation write to <memory address>
by link
int func (int &a);
func (var a: Integer): Integer;
 correct, however lines are transmitted without a modifier var!
  int func (int &a);  func (a: Integer): Integer;  error: instead of the value of the variable, contains the address of the memory cell

 Tabla 2. Métodos de transferencia de parámetros

Consideremos los ejemplos de trabajo con transferencia de parámetros y valores devueltos.

3.1 Convertir fecha y hora

Primero, ocupémonos del tipo de fecha y hora que desea convertir, porque el tipo de datetime se corresponde con TDate Time solo en su tamaño, pero no en formato. Para facilitar la transformación, use Int64 como el tipo de datos recibido, en lugar de TDate Time. Debajo están las funciones para la transformación directa e 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 Trabajar con tipos de datos simples

Examinemos cómo transferir tipos de datos simples poniendo como ejemplos los más comúnmente usados: int, double, bool, and datetime.

Llamada para 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;

Llamada para 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));
Resultado: 
Los valores de las variables i y dt han cambiado - i =  10  d =  2.80000000  b = true dt =  2009.05 .  05  08 :  42 

El valor de d no ha cambiado, puesto que se transfirió por valor. Para evitar que se den cambios en el valor de una variable dentro de una función DLL, se usó un modificador const. en la variable b.

3.3 Trabajar con estructuras y arrays

En varias ocasiones es práctico agrupar los parámetros de diferentes tipos en estructuras, y parámetros de un tipo en arrays. Considere trabajar con todos los parámetros transferidos de la función SetParam del ejemplo anterior, integrándolos en una estructura.

Llamada para 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

Llamada para 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));
Resultado:
Los valores de las variables i,  d  y dt han cambiado i =  10  d =  0.00000000  b = true dt =  2009.05 .  05  12 :  19 

Es necesario señalar una diferencia significativa del resultado del ejemplo anterior. Puesto que la estructura se transfiere a través de una referencia, es imposible proteger los campos seleccionados de ser editados en la función llamada. La tarea de monitorizar la integridad de los datos, en este caso, recae completamente en el programador.

Considere cómo sería trabajar con arrays en un ejemplo de array llenado con una secuencia de números Fibonacci:

Llamada para 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;

Llamada para 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);
Resultado:  
Números Fibonacci 0 1 1 2 3 5 8 13 21 34 55 89

3.4 Trabajar con cadenas de caracteres

Volvamos a la gestión de memoria. En DLL , es posible operar su propio gestor de memoria. No obstante, tanto DLL como los programas que lo llaman a menudo están escritos en diferentes lenguajes de programación y sus propios gestores de memoria, en lugar de un sistema de memoria general, se usan en el trabajo. Por ello, la carga de responsabilidad entera sobre la corrección de la operación de memoria en el cruce entre DLL y la aplicación recae en el programador.

Para trabajar con memoria, es importante cumplir con la regla de oro, que sería algo así como: "Aquellos que distribuyen memoria deben ser los que la liberen". En otras palabras, no debería tratar de liberar la memoria en el código del programa mql5, situado en DLL, y viceversa.

Veamos un ejemplo de gestión de memoria en estilo de llamadas de funciones API de Windows. En nuestro caso, el programa mql5 distribuye memoria para el buffer, un puntero para el buffer transferido a DLL como PWideChar , y el DLL solo llena este buffer con el valor deseado, tal y como se muestra en el siguiente ejemplo:

Llamada para Object Pascal:

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

Llamada para 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);

Resultado:

Tiempo actual: 11: 48:51 

La memoria para el buffer de línea se puede seleccionar en el DLL de varias maneras, tal y como se puede ver en el siguiente ejemplo:

Llamada para 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;
Llamada para MQL5: 
#import "dll_mql5.dll"
    string GetStringBuffer(void);
#import

   printf(GetStringBuffer());

 Resultado: 

Tiempo actual: 19.05.2010

Lo más significativo es que las cuatro opciones funcionan. En las dos primeras opciones, el trabajo con la línea se hace a través de una memoria distribuida globalmente.

En la opción A, la memoria se distribuye independientemente, y en la opción B, el trabajo con la gestión de memoria se asume por el gestor de memoria.

En la opción C, la línea constante no se guarda en la memoria, sino en el segmento de código, de modo que el gestor de memoria no distribuye memoria dinámica para su almacenamiento. La opción C es un error atrevido en programación, ya que la memoria distribuida para la variable local se puede liberar inmediatamente tras salir de la función.

Y aunque el gestor de memoria no libera esta memoria instantáneamente y no hay tiempo para rellenarla con datos innecesarios, recomiendo excluir la última opción.

3.5 Usar los parámetros por defecto

Hablemos ahora sobre el uso de parámetros opcionales. Son muy interesantes, porque sus valores no deben especificarse al llamar procedimientos y funciones. Mientras tanto, se deben describir estrictamente tras todos los parámetros obligatorios en la declaración de procedimientos y funciones, tal y como se muestra en el siguiente ejemplo:

Llamada para 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;
Llamada para MQL5:
#import "dll_mql5.dll"
    string SetOptional(int &a, int b=0);
#import

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

Resultado: 

Llamada con parámetros por defecto

Para facilitar la depuración, el código de los ejemplos anteriores se ha organizado como script, y se puede encontrar en el archivo Testing_DLL.mq5.

4. Posibles errores en la fase de diseño

Error: DLL Loading is not allowed - No se permite la carga de DLL.

Solución: Vaya a la configuración de MetaTrader 5 a través del menú ' Tools-Options' (Herramientas-Opciones) y permita la importación de funciones DLL, tal y como se muestra en la Figura 5.

  Figura 5. Permiso para importar funciones DLL

 Figura 5. Permiso para importar funciones DLL

Error: Cannot find 'function name' in 'DLL name' - No se puede encontrar el 'nombre de función' en el 'nombre DLL'.
Solución: Compruebe si la función de rellamada está especificada en la sección de Exportaciones del proyecto DLL. Si lo está, compruebe que coincide exactamente con el nombre de la función en DLL y en el programa MQL5. ¡Incluyendo mayúsculas y minúsculas!

Error: Access violation write to [memory address] - Violación de acceso escrita a [dirección de memoria]
Solución: Debe comprobar la corrección de la descripción de los parámetros transmitidos (vea la tabla 2). Puesto que generalmente este error se asocia con el procesamiento de líneas, es importante seguir las recomendaciones para trabajar con líneas, explicadas en el capítulo 3.4 de este artículo.

5. Ejemplo de código DLL

Como un ejemplo visual del uso de DLL, consideremos los cálculos de parámetros del canal de regresión, consistente en tres líneas. Para verificar la corrección de la construcción del canal, usaremos el objeto incorporado "Canal regression" ("Regresión de Canal"). El cálculo de la línea aproximada para LS (método de menos cuadrados) se toma de la página web http://alglib.sources.ru/, en la que hay una colección de algoritmos de procesamiento de datos. El código de algoritmos se presenta en varios lenguajes de programación, incluyendo Delphi.

Para calcular los coeficientes de a y b por la línea aproximada y = a + b * x, use el procedimiento descrito en el archivo 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);
  

Para calcular los parámetros del canal, use la función CalcLRChannel.

Llamada para 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;

Llamada para 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);

El código de indicador que usa la función CalcLRChannel para sus cálculos se encuentra en el archivo LR_Channel.mq5 y debajo: 

//+------------------------------------------------------------------+
//|                                                   LR_Channel.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://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;
  }

El resultado del trabajo del indicador es la creación de un canal de regresión azul, tal y como se muestra en la Figura 6. Para verificar la corrección de la construcción del canal, el gráfico muestra un "Regression Canal" del MetaTrader 5 con un arsenal de instrumentos de análisis técnico marcados en rojo.

Como puede ver en la figura, las líneas centrales del canal se juntan. Entretanto, hay una ligera diferencia en la anchura del canal (unos pocos puntos) a causa de los diferentes enfoques en sus cálculos. 

 Figura 6. Comparación de canales de regresión

Figura 6. Comparación de canales de regresión

Conclusión

Este artículo describe las cualidades de la escritura DLL usando una plataforma de desarrollo de aplicaciones Delphi.


Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/96

Archivos adjuntos |
mql5_sources.zip (276.43 KB)
dll_mql5_sources.zip (453.47 KB)
Jorge Fernando De Los Rios De Los Rios

Hello Andrey, his article is excellent, congratulations.

 

I have a doubt. I have RAD Studio 10 Seattle. When I entry to "Project Options for..." (Shift + Ctrl + F11) I find the next:

 

 

 

I do not find the checkButton "Build with runtime packages". I want to request a recomendation about "What must I do about this?". Thank you for your attention.

Jorge Fernando De Los Rios De Los Rios

I had to change a part of the code source.

 

 

 

however, when I run the source code

 

Jorge Fernando De Los Rios De Los Rios
Jorge Fernando De Los Ríos De Los Ríos:

I had to change a part of the code source.

 

 

 

however, when I run the source code

 

sorry, I had not read all the article
Jorge Fernando De Los Rios De Los Rios
Jorge Fernando De Los Ríos De Los Ríos:

Hello Andrey, his article is excellent, congratulations.

 

I have a doubt. I have RAD Studio 10 Seattle. When I entry to "Project Options for..." (Shift + Ctrl + F11) I find the next:

 

 

 

I do not find the checkButton "Build with runtime packages". I want to request a recomendation about "What must I do about this?". Thank you for your attention.

however:

 

Un Ejemplo de Sistema de Trading Basado en un Indicador Heikin-Ashi Un Ejemplo de Sistema de Trading Basado en un Indicador Heikin-Ashi

En este artículo trataremos el uso de un indicador Heikin-Ashi en trading. Basado en este indicador, se considera un sistema de trading simple, y se escribe un Asesor Experto de MQL5. Las operaciones de trading se implementan en las bases de clases de la biblioteca de clase estándar. Los resultados de simulación de la estrategia de trading revisada se basan en el historial, y se obtienen usando el Probador de Estrategias incluido en MetaTrader 5. Podrá encontrarlos en el artículo.

Un Gestor de Órdenes Virtuales para rastrear órdenes dentro del entorno centrado en posiciones de MetaTrader 5 Un Gestor de Órdenes Virtuales para rastrear órdenes dentro del entorno centrado en posiciones de MetaTrader 5

Esta biblioteca de clase se puede añadir al Asesor Experto de MetaTrader 5 para activarla y escribirla con un enfoque centrado en las órdenes, muy similar a MetaTrader 4, en comparación con el enfoque basado en posiciones de MetaTrader 5. Esto se consigue rastreando las órdenes virtuales en el Terminal de Cliente MetaTrader 5, manteniendo a la vez un stop de seguridad para cada posición como protección ante desastres.

Análisis de los patrones de velas Análisis de los patrones de velas

La construcción de un gráfico de velas japonesas y el análisis de los patrones de velas constituye un área fascinante del análisis técnico. La ventaja de las velas es que representan los datos de una forma que le permite hacer un seguimiento de las dinámicas en los datos. En este artículo vamos a analizar los tipos de velas, la clasificación de los patrones de velas y presentar un indicador que puede determinar patrones de velas.

Creación de un Expert Advisor que opera con varios instrumentos Creación de un Expert Advisor que opera con varios instrumentos

El concepto de diversificación de activos en los mercados financieros es bastante antiguo, y siempre ha atraído a los operadores principiantes. En este artículo, el autor propone un enfoque muy simplificado para la implementación de un Expert Advisor multidivisa, para una introducción inicial a este tipo de estrategias de trading.