English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Promocione sus proyectos de desarrollo usando las librerías EX5

Promocione sus proyectos de desarrollo usando las librerías EX5

MetaTrader 5Ejemplos | 13 marzo 2014, 12:31
912 0
---
---

Introducción

Un lector avezado no necesita una explicación sobre la finalidad de ocultar las implementaciones de funciones y clases en librerías. Quienes busquen activamente nuevas ideas puede que quieran saber que ocultar los detalles de la implementación de las clases/funciones en un archivo .ex5 les permitirá compartir algoritmos propios con otros desarrolladores, iniciar proyectos y promocionarlos en la Web.

Y mientras el equipo de MetaQuotes no escatima esfuerzos para tener la posibilidad de heredar directamente las clases de las librerías ex5, vamos a implementarlas ahora.

Tabla de contenidos

1. Exportar e importar funciones
2. Exportar la implementación oculta de una clase
3. Inicialización de variables en un archivo .ex5
4. Herencia de clases de exportación
5. Publicación de librerías ex5


1. Exportar e importar funciones

Este es un método básico que subyace a la exportación de clases. Hay tres cosas clave que deben tenerse en cuenta para que nuestras funciones estén disponibles en otros programas:

  1. El archivo a crear debe tener la extensión .mq5 (no .mqh) para ser compilado a un archivo .ex5;
  2. El archivo debe contener la directiva del preprocesador de la librería #property.
  3. La palabra clave "exportar" debe ponerse después de los encabezados de las funciones exportadas correspondientes.
Ejemplo 1. Let us create a function to be used in other programs

//--- library.mq5
#property library
int libfunc (int a, int b) export
{
  int c=a+b;
  Print("a+b="+string(с));
  return(с);
}

Después de compilar este archivo, obtendremos el archivo library.ex5 del que libfunc puede usarse en otro programa.

El proceso de importar las funciones es también muy simple. Se realiza usando la directiva de preprocesador #import.

Ejemplo 2. We will use the export function libfunc() in our script

//--- uses.mq5
#import "library.ex5"
  int libfunc(int a, int b);
#import

void OnStart()
{
  libfunc(1, 2);
}

Tenga en cuenta que el compilador buscará los archivos .ex5 en la carpeta MQL5\Libraries. Posteriormente, si el archivo ibrary.ex5 no está ubicado en esa carpeta, tendremos que especificar el nombre de la ruta respectiva.

P. ej.

#import "..\Include\MyLib\library.ex5" // the file is located in the MQL5\Include\MyLib folder
#import "..\Experts\library.ex5" // the file is located in the MQL5\Experts\ folder

Para un uso en el futuro, las funciones pueden ser importadas no solo en el archivo de destino .mq5 sino también en los archivos .mqh.

Para ilustrar la aplicación práctica, vamos a usar algunos gráficos.

Vamos a crear una librería de funciones para exportar. Estas funciones mostrarán los objetos gráficos como botones, editar, etiqueta y etiqueta de rectángulo en un gráfico, borrarán los objetos del gráfico y restablecerán los parámetros de color del mismo.

Esto puede mostrarse esquemáticamente de la siguiente forma:

Esquema de exportación del método de la clase

Puede descargar todo el archivo Graph.mq5 al final del artículo. Por tanto, solo daremos un ejemplo de plantilla de la función dibujo Edit.

//+------------------------------------------------------------------+
//| SetEdit                                                          |
//+------------------------------------------------------------------+
void SetEdit(long achart,string name,int wnd,string text,color txtclr,color bgclr,color brdclr,
             int x,int y,int dx,int dy,int corn=0,int fontsize=8,string font="Tahoma",bool ro=false) export
  {
   ObjectCreate(achart,name,OBJ_EDIT,wnd,0,0);
   ObjectSetInteger(achart,name,OBJPROP_CORNER,corn);
   ObjectSetString(achart,name,OBJPROP_TEXT,text);
   ObjectSetInteger(achart,name,OBJPROP_COLOR,txtclr);
   ObjectSetInteger(achart,name,OBJPROP_BGCOLOR,bgclr);
   ObjectSetInteger(achart,name,OBJPROP_BORDER_COLOR,brdclr);
   ObjectSetInteger(achart,name,OBJPROP_FONTSIZE,fontsize);
   ObjectSetString(achart,name,OBJPROP_FONT,font);
   ObjectSetInteger(achart,name,OBJPROP_XDISTANCE,x);
   ObjectSetInteger(achart,name,OBJPROP_YDISTANCE,y);
   ObjectSetInteger(achart,name,OBJPROP_XSIZE,dx);
   ObjectSetInteger(achart,name,OBJPROP_YSIZE,dy);
   ObjectSetInteger(achart,name,OBJPROP_SELECTABLE,false);
   ObjectSetInteger(achart,name,OBJPROP_READONLY,ro);
   ObjectSetInteger(achart,name,OBJPROP_BORDER_TYPE,0);
   ObjectSetString(achart,name,OBJPROP_TOOLTIP,"");
  }

La importación de las funciones necesarias y su uso se implementará en el archivo de destino Spiro.mq5:

Ejemplo 3. Using imported functions

//--- Spiro.mq5 – the target file of the Expert Advisor

//--- importing some graphics functions
#import "Graph.ex5" 
  void SetLabel(long achart, string name, int wnd, string text, color clr, 
               int x, int y, int corn=0, int fontsize=8, string font="Tahoma");
  void SetEdit(long achart, string name, int wnd, string text, color txtclr, color bgclr, color brdclr, 
                 int x, int y, int dx, int dy, int corn=0, int fontsize=8, string font="Tahoma", bool ro=false);
  void SetButton(long achart, string name, int wnd, string text, color txtclr, color bgclr, 
                int x, int y, int dx, int dy, int corn=0, int fontsize=8, string font="Tahoma", bool state=false);
  void HideChart(long achart, color BackClr);
#import

//--- prefix for chart objects
string sID; 
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

void OnInit()
{
  HideChart(0, clrWhite);
  sID="spiro.";
  DrawParam();
}
//+------------------------------------------------------------------+
//| DrawParam                                                        |
//+------------------------------------------------------------------+
void DrawParam()
{
  color bgclr=clrWhite, clr=clrBlack;
//--- bigger radius    
  SetLabel(0, sID+"stR.", 0, "R", clr, 10, 10+3);
  SetEdit(0, sID+"R.", 0, "100", clr, bgclr, clr, 40, 10, 50, 20);
//--- smaller radius   
  SetLabel(0, sID+"str.", 0, "r", clr, 10, 35+3);
  SetEdit(0, sID+"r.", 0, "30", clr, bgclr, clr, 40, 35, 50, 20);
//--- distance to the center
  SetLabel(0, sID+"stD.", 0, "D", clr, 10, 60+3);
  SetEdit(0, sID+"D.", 0, "40", clr, bgclr, clr, 40, 60, 50, 20);
//--- drawing accuracy
  SetLabel(0, sID+"stA.", 0, "Alfa", clr, 10, 85+3); 
  SetEdit(0, sID+"A.", 0, "0.04", clr, bgclr, clr, 40, 85, 50, 20);
//--- drawing accuracy
  SetLabel(0, sID+"stN.", 0, "Rotor", clr, 10, 110+3); 
  SetEdit(0, sID+"N.", 0, "10", clr, bgclr, clr, 40, 110, 50, 20);
//--- draw button
  SetButton(0, sID+"draw.", 0, "DRAW", bgclr, clr, 39, 135, 51, 20); 
}

Después de la ejecución del asesor experto, los objetos aparecerán en el gráfico:

Ejemplo del uso de los objetos de la librería

Como puede ver, el proceso de exportación e importación de funciones no es en absoluto difícil, pero debe asegurarse de que lee sobre algunas limitaciones en la ayuda: exportar, importar.


2. Exportar la implementación oculta de una clase

Como las clases en MQL5 no se pueden exportar directamente por ahora, tendremos que recurrir a algún método creativo. Este se basa en las funciones polymorphism y virtual. De hecho, no es la clase en sí la que es devuelta desde el módulo ex5 sino el objeto creado por la misma. Vamos a llamarlo objeto de la implementación oculto.

La esencia del método es dividir la clase necesaria en dos, de forma que la declaración de funciones y variables esté accesible al público y la información sobre su implementación esté oculta en un archivo .ex5 cerrado.

Esto puede mostrarse esquemáticamente de la siguiente forma: Tenemos la clase CSpiro que nos gustaría compartir con otros desarrolladores sin revelar la información sobre la implementación. Supongamos que contiene variables, un constructor, destructor y funciones.

Para exportar la clase debemos hacer lo siguiente:

  • Creamos una copia de la clase CSpiro hija. Vamos a llamarla ISpiro (la primera letra C se sustituye por I, según deriva de la palabra "interfaz")
  • Dejamos todas las variables y funciones dummy en la clase inicial CSpiro.
  • La información sobre la implementación de la función formará una nueva clase ISpiro.
  • La añadimos a la función de exportación que creará una instancia de la función ISpiro cerrada.
  • ¡Cuidado! Todas las funciones necesarias deben tener el prefijo virtual.

Como resultado tenemos dos archivos:

Ejemplo 4. Hiding of the class implementation in the ex5 module

//--- Spiro.mqh – public file, the so called header file

//+------------------------------------------------------------------+
//| Class CSpiro                                                     |
//| Spirograph draw class                                            |
//+------------------------------------------------------------------+
class CSpiro
  {
public:
   //--- prefix of the chart objects
   string            m_sID;
   //--- offset of the chart center
   int               m_x0,m_y0;
   //--- color of the line
   color             m_clr;
   //--- chart parameters
   double            m_R,m_r,m_D,m_dAlfa,m_nRotate;

public:
   //--- constructor
                     CSpiro() { };
   //--- destructor
                    ~CSpiro() { };
   virtual void Init(int ax0,int ay0,color aclr,string asID) { };
   virtual void SetData(double aR,double ar,double aD,double adAlpha,double anRotate) { };

public:
   virtual void DrawSpiro() { };
   virtual void SetPoint(int x,int y) { };
  };

//--- ISpiro.mq5 – hidden implementation file

#include "Spiro.mqh"

//--- importing some functions
#import "..\Experts\Spiro\Graph.ex5"
void SetPoint(long achart,string name,int awnd,int ax,int ay,color aclr);
void ObjectsDeleteAll2(long achart=0,int wnd=-1,int type=-1,string pref="",string excl="");
#import

CSpiro *iSpiro() export { return(new ISpiro); }
//+------------------------------------------------------------------+
//| Сlass ISpiro                                                     |
//| Spirograph draw class                                            |
//+------------------------------------------------------------------+
class ISpiro : public CSpiro
  {
public:
                     ISpiro() { m_x0=0; m_y0=0; };
                    ~ISpiro() { ObjectsDeleteAll(0,0,-1); };
   virtual void      Init(int ax0,int ay0,color aclr,string asID);
   virtual void      SetData(double aR,double ar,double aD,double adAlpha,double anRotate);

public:
   virtual void      DrawSpiro();
   virtual void      SetPoint(int x,int y);
  };
//+------------------------------------------------------------------+
//| Init                                                             |
//+------------------------------------------------------------------+
void ISpiro::Init(int ax0,int ay0,color aclr,string asID)
  {
   m_x0=ax0;
   m_y0=ay0;
   m_clr=aclr;
   m_sID=asID;
   m_R=0; 
   m_r=0; 
   m_D=0;
  }
//+------------------------------------------------------------------+
//| SetData                                                          |
//+------------------------------------------------------------------+
void ISpiro::SetData(double aR,double ar,double aD,double adAlpha,double anRotate)
  {
   m_R=aR; m_r=ar; m_D=aD; m_dAlfa=adAlpha; m_nRotate=anRotate;
  }
//+------------------------------------------------------------------+
//| DrawSpiro                                                        |
//+------------------------------------------------------------------+
void ISpiro::DrawSpiro()
  {
   if(m_r<=0) { Print("Error! r==0"); return; }
   if(m_D<=0) { Print("Error! D==0"); return; }
   if(m_dAlfa==0) { Print("Error! Alpha==0"); return; }
   ObjectsDeleteAll2(0,0,-1,m_sID+"pnt.");
   int n=0; double a=0;
   while(a<m_nRotate*2*3.1415926)
     {
      double x=(m_R-m_r)*MathCos(a)+m_D*MathCos((m_R-m_r)/m_r*a);
      double y=(m_R-m_r)*MathSin(a)-m_D*MathSin((m_R-m_r)/m_r*a);
      SetPoint(int(m_x0+x),int(m_y0+y));
      a+=m_dAlfa;
     }
   ChartRedraw(0);
  }
//+------------------------------------------------------------------+
//| SetPoint                                                         |
//+------------------------------------------------------------------+
void ISpiro::SetPoint(int x,int y)
  {
   Graph::SetPoint(0,m_sID+"pnt."+string(x)+"."+string(y),0,x,y,m_clr);
  }
//+------------------------------------------------------------------+

Como puede ver, la clase oculta ha sido implementada en un archivo .mq5 y contiene la librería #property del comando del preprocesador. Por tanto, se han observado todas las reglas que se establecen en la sección anterior.

También observe el operador scope resolution para la función SetPoint. Se declara tanto en la librería Graph como en la clase CSpiro. Para que el compilador pueda llamar a la función necesaria, lo especificamos explícitamente usando la acción :: y dando el nombre del archivo.

  Graph::SetPoint(0, m_sID+"pnt."+string(x)+"."+string(y), 0, x, y, m_clr);

Ahora podemos incluir el archivo cabecera e importar su implementación en nuestro asesor experto resultante.

Esto puede mostrarse esquemáticamente de la siguiente forma:

Esquema para el trabajo con métodos de las clases de la librería

Ejemplo 5. Using export objects

//--- Spiro.mq5 - the target file of the Expert Advisor

//--- importing some functions
#import "Graph.ex5" 
  void SetLabel(long achart, string name, int wnd, string text, color clr,
               int x, int y, int corn=0, int fontsize=8, string font="Tahoma");
  void SetEdit(long achart, string name, int wnd, string text, color txtclr, color bgclr, color brdclr, 
              int x, int y, int dx, int dy, int corn=0, int fontsize=8, string font="Tahoma", bool ro=false);
  void SetButton(long achart, string name, int wnd, string text, color txtclr, color bgclr, 
                int x, int y, int dx, int dy, int corn=0, int fontsize=8, string font="Tahoma", bool state=false);
  void HideChart(long achart, color BackClr);
#import

//--- including the chart class
#include <Spiro.mqh> 

//--- importing the object
#import "ISpiro.ex5"
  CSpiro *iSpiro();
#import

//--- object instance
CSpiro *spiro; 
//--- prefix for chart objects
string sID; 
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
void OnInit()
{
  HideChart(0, clrWhite);
  sID="spiro.";
  DrawParam();
//--- object instance created
  spiro=iSpiro(); 
//--- initializing the drawing
  spiro.Init(250, 200, clrBlack, sID);
//--- setting the calculation parameters
  spiro.SetData(100, 30, 40, 0.04, 10);
//--- drawing
  spiro.DrawSpiro(); 
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
  delete spiro; // deleting the object
}

Como resultado, podremos cambiar los parámetros del objeto en el gráfico y dibujar el gráfico del objeto



Parámetros de los objetos gráficos


3. Inicialización de variables en un archivo .ex5

Suele ocurrir que ISuperClass utilice variables del archivo globals.mqh . Estas variables pueden incluirse de forma similar para su uso en otros archivos.

P. ej.

Ejemplo 6. Public include file

//--- globals.mqh

#include <Trade\Trade.mqh>
//--- instance of the trade function object
extern CTrade *_trade; 

La única instancia del objeto _trade se inicializa en nuestro programa, aunque no obstante se usa en la clase oculta ISuperClass.

Para esta finalidad el puntero hacia el objeto que hayamos creado debe pasarse desde la clase ISuperClass al archivo .ex5.

Es más fácil hacerlo cuando el objeto se recibe desde el archivo .ex5, como se muestra a continuación:

Ejemplo 7. Initialization of variables upon creation of the object

//--- ISuperClass.mq5 –hidden implementation file

#property library
CSuperClass *iSuperClass(CTrade *atrade) export
{
//--- saving the pointer
   _trade=atrade; 
//--- returning the object of the hidden implementation of ISuperClass of the open CSuperClass class
  return(new ISuperClass); 
}
//... the remaining code

De esta forma, todas las variables se inicializan a la recepción del objeto en su módulo.

De hecho, puede haber un montón de variables globales públicas de distintos tipos. Aquellos que no deseen cambiar el encabezado de la funciónISuperClass todo el tiempo, deben crear una clase aggregating especial para que todas las variables y funciones trabajen con ella.

Ejemplo 8. Public include file

//--- globals.mqh
#include <Trade\Trade.mqh>

//--- trade "object"
extern CTrade *_trade; 
//--- name of the Expert Advisor of the system
extern string _eaname; 

//+------------------------------------------------------------------+
//| class __extern                                                   |
//+------------------------------------------------------------------+
class __extern // all extern parameters for passing between the ex5 modules are accumulated here
{
public:
//--- the list of all public global variables to be passed
//--- trade "object"
  CTrade *trade; 
//--- name of the Expert Advisor of the system
  string eaname; 
    
public:
  __extern() { };
  ~__extern() { };

//--- it is called when passing the parameters into the .ex5 file
  void Get() { trade=_trade; eaname=_eaname; };  // getting the variables

 //--- it is called in the .ex5 file
  void Set() { _trade=trade; _eaname=eaname; };  // setting the variables
                                                       
};
//--- getting the variables and pointer for passing the object into the .ex5 file
__extern *_GetExt() { _ext.Get(); return(GetPointer(_ext)); } 

//--- the only instance for operation
extern __extern _ext; 
El archivo ISuperClass.mq5 se implementará de la siguiente forma:
Ejemplo 9.

//--- ISuperClass.mq5 –hidden implementation file

#property library
CSuperClass *iSuperClass(__extern *aext) export
{
//--- taking in all the parameters
  aext.Set();
//--- returning the object
  return(new ISuperClass); 
}
//--- ... the remaining code

La llamada a la función se transformará en otra forma simplificada y, lo que es más importante, ampliable.

Ejemplo 10. Using export objects in the presence of public global variables

//--- including global variables (usually located in SuperClass.mqh)
#include "globals.mqh"    

//--- including the public header class
#include "SuperClass.mqh" 
//--- getting the hidden implementation object
#import "ISuperClass.ex5"
  CSuperClass *iSuperClass();
#import

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
//--- creating the hidden implementation object providing for the passing of all parameters
  CSuperClass *sc=iSuperClass(_GetExt()); 
  //--- ... the remaining code
}


4. Herencia de clases de exportación

Ya debe haber comprendido que esta forma de exportar objetos implica que la herencia simple y directa está fuera de duda. La exportación del objeto de la implementación oculta sugiere que el propio objeto es el último enlace de la cadena de herencia y es además el que puede usarse en última instancia.

En el caso general podemos crear una "emulación" de la herencia escribiendo una clase intermedia adicional. Y aquí necesitaremos, por supuesto, el polimorfismo y la virtualidad.

Ejemplo 11. Emulation of inheritance of hidden classes

//--- including the public header class
#include "SuperClass.mqh" 

//--- getting the hidden implementation object
#import "ISuperClass.ex5"
  CSuperClass *iSuperClass();
#import

class _CSuperClass
{
public:
//--- instance of the hidden implementation object
  CSuperClass *_base;
public:
//--- constructor
  _CSuperClass() {  _base=iSuperClass(_GetExt()); };
//--- destructor
  ~_CSuperClass() { delete _base; };
//--- further followed by all functions of the base CSuperClass class
//--- working function called from the hidden implementation object
  virtual int func(int a, int b) { _base.func(a,b); }; 
};

La única cuestión aquí es el acceso a las variables de CSuperClass. Como puede ver, no están presentes en la declaración de la hija y se ubican en la variable _base. Normalmente no afecta a la usabilidad siempre que haya una clase de encabezado SuperClass.mqh.

Naturalmente, si estamos centrados principalmente en funciones de conocimientos técnicos, tenemos que crear una envolvente de ISuperClass en relación a las mismas por adelantado. Será suficiente exportar estas funciones de conocimientos técnicos y dejar a los desarrolladores externos crear sus propias clases envolventes que serán luego fáciles de heredar.

De esta forma, al preparar nuestros desarrollos para otros programadores, debemos procurar crear todo un conjunto de funciones de exportación, .mph y archivos .ex5 y clases:
  1. Exportación de las funciones independientes de la clase
  2. Archivos .mqh de encabezado y sus implementaciones .ex5
  3. Inicialización de variables en un archivo .ex5


5. Publicación de librerías ex5

En noviembre de 2011 MetaQuotes comenzó a proporcionar acceso al repositorio de archivos. Puede encontrar más información al respecto en el anuncio.

Este repositorio nos permite almacenar nuestros desarrollos y, lo que es más importante, proporcionar acceso para otros desarrolladores. Esta herramienta nos permitirá publicar fácilmente nuevas versiones de nuestros archivos para garantizar un acceso rápido a ellos a los desarrolladores que puedan usarlos.

Además, la página web de la empresa da la oportunidad a los usuarios de ofrecer sus propias librerías de funciones en el Mercado comercialmente y sin coste alguno.


Conclusión

Ya sabe cómo crear librerías ex5 exportando sus funciones u objetos de clases y puede poner en práctica sus conocimientos. Todos estos recursos permitirán establecer una cooperación con otros desarrolladores: para trabajar en proyectos comunes, promocionarlos en el Mercado o proporcionar acceso a las funciones de la librería ex5.


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

Archivos adjuntos |
spiro.zip (3.91 KB)
La última cruzada La última cruzada
Eche un vistazo a su terminal de cliente. ¿Qué sistema de presentación del precio puede ver? Barras, velas, líneas. Perseguimos el tiempo y los precios mientras que solo obtenemos beneficio a partir de los precios. ¿Debemos prestar atención solo a los precios cuando analizamos el mercado? Este artículo propone un algoritmo y un script para el trazado de punto y forma ("ceros y cruces"). Se consideran varios patrones de precio cuyo uso práctico se muestra mediante las recomendaciones que se incluyen.
Proteger el código MQL5 Protección con contraseña, generadores de claves, límites de tiempo, licencias remotas y técnicas de encriptación de claves de licencia de asesores expertos avanzadas Proteger el código MQL5 Protección con contraseña, generadores de claves, límites de tiempo, licencias remotas y técnicas de encriptación de claves de licencia de asesores expertos avanzadas
La mayoría de desarrolladores necesitan tener su código protegido. Este artículo presenta diferentes formas de proteger el software MQL5 mediante métodos que permiten disponer de licencias para scripts de MQL5, Expert Advisors e indicadores. Se incluye la protección mediante contraseñas, los generadores de claves, las licencias de cuentas, las pruebas de evaluación y la protección remota mediante llamadas MQL5-RPC.
AutoElliottWaveMaker: la herramienta de MetaTrader 5 para el análisis semiautomático de los ondas de Elliott AutoElliottWaveMaker: la herramienta de MetaTrader 5 para el análisis semiautomático de los ondas de Elliott
Este artículo hace una revisión de AutoElliottWaveMaker, el primer desarrollo para el análisis de ondas de Elliott en Meta Trader 5 que representa una combinación de etiquetado de ondas manual y automático. La herramienta de análisis de onda está escrita exclusivamente en MQL5 y no incluye librerías externas dll. Esta es otra prueba de que en MQL5 pueden (y deben) desarrollarse programas sofisticados e interesantes.
Trademinator 3: el auge de las máquinas de trading Trademinator 3: el auge de las máquinas de trading
En el artículo "Dr. Tradelove..." creamos un Expert Advisor que optimiza independientemente los parámetros del sistema de trading preseleccionado. Además, decidimos crear un Expert Advisor que no solo pudiera optimizar los parámetros de un sistema de trading subyacente al EA, sino también elegir el mejor de varios sistemas de trading. Vamos a ver qué sale de esto...