English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Promova seus projetos de desenvolvimento utilizando bibliotecas EX5

Promova seus projetos de desenvolvimento utilizando bibliotecas EX5

MetaTrader 5Exemplos | 20 fevereiro 2014, 15:13
1 849 0
---
---

Introdução

Um leitor sofisticado não precisa de uma explicação sobre o propósito de esconder implementações de função e de classe em bibliotecas. Aqueles de vocês que estão ativamente a procura de novas ideias talvez queiram saber que a ocultação dos detalhes de implementação de classes/funções em um arquivo .ex5 vai permitir que você compartilhe seus algoritmos experientes com outros desenvolvedores, defina projetos comuns e promova-os na Internet.

E enquanto a equipe MetaQuotes não mede esforços para viabilizar a possibilidade de herança direta de classes de biblioteca ex5, vamos implementá-la agora.

Índice

1. Funções Importar e Exportar
2. Exportação da implementação escondida de uma classe
3. Inicialização de variáveis em arquivo .ex5
4. Herança de classes de exportação
5. Publicação de bibliotecas ex5


1. Funções Importar e Exportar

Este é um método de base subjacente à exportação de classes. Há três coisas importantes a serem levadas em consideração para suas funções estarem disponíveis nos outros programas:

  1. O arquivo a ser criado deve ter a extensão .mq5 (não .mqh) para ser compilado em arquivo .ex5;
  2. O arquivo deve conter diretivas de pré-processador de biblioteca #property;
  3. A palavra-chave "export" (exportar) deverá ser colocada após os cabeçalhos das funções exportadas requeridas
Example 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(с);
}

Após a compilação deste arquivo, você obterá o arquivo library.ex5 de onde libfunc possa então ser utilizada em um outro programa.

O processo de importação de funções é também muito simples. é executado utilizando a diretiva de pré-processador #import.

Example 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);
}

Mantenha em mente que o compilador procurará por arquivos .ex5 na pasta MQL5\Libraries. Então se library.ex5 não for localizado na pasta, você terá que especificar o nome do caminho relativo.

Ex.:

#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 seu uso futuro, funções podem ser importadas não apenas no arquivo de destino .mq5 mas também em arquivos .mqh.

Além de ilustrar a aplicação prática, deixe-nos usar alguns gráficos.

Vamos criar uma biblioteca de funções para exportar. Estas funções exibirão objetos gráficos, como Button (botão), Edit (editar), Label (etiqueta) e Rectangle Label (etiqueta retângulo) em um gráfico, excluir os objetos a partir do gráfico e redefinir os parâmetros de cor do gráfico.

Isso pode ser mostrado através de esquema conforme a seguir:

Esquema de exportação de método de classe

O arquivo completo Graph.mq5 pode ser encontrado no final do artigo. Aqui daremos apenas um exemplo modelo da função de desenho Editar.

//+------------------------------------------------------------------+
//| 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,"");
  }

A importação das funções requeridas e seu uso serão implementados no arquivo de destino Spiro.mq5:

Example 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); 
}

Seguindo a execução do Expert Advisor, os objetos aparecerão no gráfico:

Exemplo de uso de objetos de biblioteca

Como pode ser visto, o processo de exportação e importação de funções não é nada difícil, mas certifique-se de ler sobre certas limitações na Ajuda: exportação, importação.


2. Exportação da implementação escondida de uma classe

Visto que as classes no MQL5 não podem ser exportadas diretamente até o momento, teremos de recorrer a um método um tanto sofisticado. é baseado em funções de polimorfismo e virtuais. Por falar nisso, não é a classe em si que é retornada a partir do módulo ex5, mas um objeto criado pela mesma. Vamos chamá-lo de objeto de implementação oculto.

A essência do método é dividir a classe necessária em duas, para que a declaração de funções e variáveis ​seja aberta para acesso público e seus detalhes de implementação fiquem ocultos em um arquivo.ex5 fechado.

Isso pode ser simplesmente exemplificado conforme a seguir. Há a classe CSpiro que gostaríamos de compartilhar com outros desenvolvedores sem revelar detalhes de implementação. Vamos supor que ela contenha funções de variáveis, construtoras, destruidoras e de trabalho.

Para exportar a classe, devemos fazer o seguinte:

  • Criar um clone da classe descendente CSpiro. Vamos chamá-lo ISpiro (a primeira letra C é substituída por um I, como derivado da palavra "interface")
  • Deixe todas as variáveis​ e funções fictícias na classe inicial CSpiro.
  • Os detalhes da implementação da função deve formar uma nova classe ISpiro.
  • Adicione a isso a função de exportação que irá criar uma instância da ISpiro fechada.
  • Observação! Todas as funções necessárias deverão ter o prefixo virtual

Como resultado, temos dois arquivos:

Example 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) { };
  };

Por favor, observe que todas as classes de função são declaradas com a palavra-chave virtual.

//--- 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 pode ser visto, a classe escondida foi implementada no arquivo .mq5 e contém o comando de pré-processador #property library. Assim, todas as regras estabelecidas na seção anterior foram observadas.

Observe também o operador de resolução de escopo para a função SetPoint. Ele é tanto declarado na biblioteca Graph quanto na classe CSpiro. Para que o compilador chame a função desejada, especificamos explicitamente usando a ação :: e damos o nome do arquivo.

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

Podemos agora incluir o arquivo de cabeçalho e importar a sua implementação em nosso Expert Advisor resultante.

Isso pode ser mostrado através de esquema conforme a seguir:

Esquema para trabalhar com métodos de classes de biblioteca

Example 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, você será capaz de alterar os parâmetros de objeto no gráfico e traçar o gráfico do objeto:



Parâmetros de objeto gráfico


3. Inicialização de variáveis em arquivo .ex5

é frequente o caso em que a sua ISuperClass utiliza variáveis do arquivo incluso globals.mqh. Estas variáveis podem ser inclusas em um modo semelhante para serem usadas em outros arquivos.

Ex.:

Example 6. Public include file

//--- globals.mqh

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

A única instância do objeto _trade é inicializada em seu programa, mas ela é usada na classe escondida ISuperClass.

Para este propósito, um ponteiro para o objeto que você criou deve ser passado a partir da classe ISuperClass para o arquivo .ex5.

é facilmente feito quando o objeto é recebido do arquivo .ex5, conforme abaixo:

Example 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

Assim, todas as variáveis necessárias são inicializadas após o recebimento do objeto em seu módulo.

Na verdade, pode haver um grande número de variáveis globais públicas, que podem ser de diferentes tipos. Para aqueles que não estão dispostos a alterar o cabeçalho da função iSuperClass o tempo todo, é melhor que criem uma classe especial agregando todas as variáveis e funções globais para trabalhar com ela.

Example 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; 
O arquivo ISuperClass.mq5 será implementado da seguinte forma:
Example 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

A chamada de função será agora transformada em uma forma simplificada e o que é mais importante, extensível.

Example 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. Herança de classes de exportação

Você já deve ter entendido que esta forma de exportar objetos implica que a herança simples e direta esteja fora de questão. A exportação do objeto de implementação oculto sugere que o objeto em si seja a última conexão da cadeia de herança e o que pode ser usado em última análise.

No caso geral, você pode criar uma "simulação" da herança escrevendo uma classe intermediária adicional. E aqui, precisaremos naturalmente de polimorfismo e virtualidade.

Example 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); }; 
};

O único problema aqui é o acesso a variáveis ​de CSuperClass. Como pode ser visto, elas não estão presentes na declaração da descendente e estão localizadas na variável _base. Normalmente, não afeta a capacidade de utilização, desde que exista uma classe de cabeçalho SuperClass.mqh.

Naturalmente, se você estiver focado principalmente em funções experientes, você não tem que criar um padrão adaptador de ISuperClass com relação a elas com antecedência. Bastará exportar estas funções experientes e deixar que os desenvolvedores externos criem suas próprias classes de padrões adaptadores que então serão fáceis de herdar.

Assim, ao preparar seus desenvolvimentos para outros desenvolvedores, você deve cuidar para criar um conjunto de funções de exportação necessárias, arquivos e classes .mqh e .ex5:
  1. Exportação de funções independentes de classe
  2. Arquivos de cabeçalho .mqh e suas implementações .ex5
  3. Inicialização de variáveis nos arquivos .ex5


5. Publicação de bibliotecas ex5

Em novembro de 2011, a MetaQuotes começou a fornecer acesso a um armazenamento de arquivos. Mais sobre isso pode ser encontrado no anúncio.

Este armazenamento de arquivos permite que você armazene seus desenvolvimentos e, o que é mais importante, fornece acesso a isso para outros desenvolvedores. Esta ferramenta permitirá que você publique facilmente novas versões dos seus arquivos para garantir acesso rápido a eles pelos desenvolvedores que possam estar utilizando esses arquivos.

Além do mais, o website da empresa fornece a oportunidade de oferecer-lhe suas próprias bibliotecas no Mercado em uma base comercial sem custo nenhum.


Conclusão

Você já sabe como criar bibliotecas ex5 com a exportação de suas funções ou objetos de classe e pode aplicar seus conhecimentos na prática. Todos estes recursos permitem estabelecer uma cooperação mais estreita com outros desenvolvedores: trabalhar em projetos comuns, promovê-los no Mercado ou fornecer acesso às funções de biblioteca ex5.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/362

Arquivos anexados |
spiro.zip (3.91 KB)
A última cruzada A última cruzada
Veja seu terminal de negociação. Quais meios de apresentação de preço você pode ver? Barras, candlesticks, linhas. Estamos buscando tempo e preços onde temos apenas lucro com os preços. Devemos dar atenção aos preços ao analisarmos o mercado? Este artigo propõe um algorítimo e um script para um gráfico de ponto e figura ("jogo da velha") é dada consideração a vários padrões de preço em que o uso prático é destacado nas recomendações fornecidas.
Código MQL5 de segurança: proteção de senha, geradores de chaves, limites de tempo, licenças remotas e técnicas de codificação de chave de licença de EA avançadas Código MQL5 de segurança: proteção de senha, geradores de chaves, limites de tempo, licenças remotas e técnicas de codificação de chave de licença de EA avançadas
A maioria dos desenvolvedores precisa ter seu código protegido. Este artigo apresentará alguns meios diferentes para proteger o software MQL5 - ele apresenta métodos para fornecer recursos de licenciamento para Scripts do MQL5, Exper Advisors e Indicadores. Ele cobre a proteção de senha, geradores de chave, licença de conta, avaliação de limite de tempo e proteção remota usando chamadas MQL5-RPC.
AutoElliottWaveMaker - Ferramenta do MetaTrader 5 para análise semi-automática de Ondas de Elliott AutoElliottWaveMaker - Ferramenta do MetaTrader 5 para análise semi-automática de Ondas de Elliott
O artigo fornece uma análise de AutoElliottWaveMaker - o primeiro desenvolvimento para a análise Elliot Wave no MetaTrader 5 que representa uma combinação de rotulagem de onda manual e automática. A ferramenta de análise de onda é escrita exclusivamente no MQL5 e não inclui bibliotecas dll externas. Esta é mais uma prova de que programas sofisticados e interessantes podem (e devem) ser desenvolvidos no MQL5.
Trademinator 3: ascensão das máquinas comerciais Trademinator 3: ascensão das máquinas comerciais
No artigo "Dr. Tradelove..." criamos um Exper Advisor, que otimiza parâmetros independentemente do sistema de negociação pré-selecionado. Além disso, decidimos criar um Expert Advisor que não apenas otimizasse parâmetros de um sistema de negócio destacando o EA, mas também selecione o melhor dos vários sistemas de negócio. Vamos ver o que pode resultar disso...