Como adicionar rapidamente paneis de controle a indicadores e conselheiros (EA)
Seu programa MQL4/MQL5 — indicador ou conselheiro — pode ser um dos melhores e realizar completamente as tarefas que lhe são confiadas. Mas, você sempre pode melhorá-lo um pouco. Na maioria dos casos, para qualquer alteração de parâmetros de entrada do programa, o usuário tem de entrar nas respectivas configurações. No entanto, esta etapa pode ser evitada.
Você pode fazer isso criando o seu próprio painel de controle com base nas classes da biblioteca padrão. Isso permitirá alterar as configurações sem reiniciar o programa. Além disso, essa abordagem faz com que seu programa seja mais atraente destacando-se dos concorrentes. Confira exemplos de painéis gráficos no Mercado.
Neste artigo, vou mostrar como adicionar um painel simples ao seu programa MQL4/MQL5. Você vai aprender como ensinar ao programa a ler os parâmetros de entrada e a reagir às alterações dos seus valores.
1. Ligamos o indicador e o painel
1.1. Indicador
O indicador "NewBar.mq5" executa só uma ação: ao aparecer uma barra nova, imprime uma mensagem no diário do EA do terminal. A serguir, você vai encontrar o código do indicador:
//+------------------------------------------------------------------+ //| NewBar.mq5 | //| Copyright 2015, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property description "The indicator identifies a new bar" #property indicator_chart_window #property indicator_plots 0 //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { static datetime prev_time; //--- revert access to array time[] - do it like in timeseries ArraySetAsSeries(time,true); //--- first calculation or number of bars was changed if(prev_calculated==0)// first calculation { prev_time=time[0]; return(rates_total); } //--- if(time[0]>prev_time) Print("New bar!"); //--- prev_time=time[0]; //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Agora vou falar um pouco sobre como funciona o indicador "NewBar.mq5".
Na função OnCalculate(), é declarada a variável estatística "prev_time", nesta variável, é armazenado o tempo de abertura "time[0]". No passo seguinte, é comparado o tempo de abertura "time[0]" com a variável "prev_time", quer dizer, acontece a comparação entre o tempo de abertura "time[0]" no tick atual e o tempo de abertura "time[0]" no tick anterior. No caso de se cumprir a condição
if(time[0]>prev_time)
consideramos a aparição de uma nova barra.
No exemplo a seguir, analisamos em detalhe como o indicador "NewBar.mq5" encontra uma nova barra:
Fig. 1. Processo de detecção da nova barra no indicador
Consideremos 10 ticks em um mercado muito tranqüilo.
Ticks 1-3: o tempo de abertura da barra com o índice "0" (time[0]) é igual ao tempo armazenado na variável estatística prev_time, isto indica que a não existe uma nova barra.
Tick 4: este tick veio para a barra nova. Ao entrar na função OnCalculate(), no time[0], estará o tempo de abertura da barra (2015.12.01 00:02:00), enquanto a variável prev_time ainda armazena o tempo a partir do tick anterior (2015.12.01 00:01:00). Por isso, ao verificar as condições time[0]>prev_time, encontraremos uma nova barra. Antes de sair da OnCalculate(), na variável prev_time, será gravado o tempo a partir do time[0] (2015.12.01 00:02:00).
Ticks 5-8: o tempo de abertura da barra com o índice "0" (time[0]) é igual ao tempo armazenado na variável estatística prev_time, isto indica que a não existe uma nova barra.
Tick 9: isto indica que não existe uma nova barra. Ao entrar na função OnCalculate(), no time[0], estará o tempo de abertura da barra (2015.12.01 00:03:00), enquanto a variável prev_time ainda armazena o tempo a partir do tick anterior (2015.12.01 00:02:00). Por isso, ao verificar as condições time[0]>prev_time, encontraremos uma nova barra. Antes de sair da OnCalculate(), na variável prev_time, será gravado o tempo a partir do time[0] (2015.12.01 00:03:00).
Tick 10: o tempo de abertura da barra com o índice "0" (time[0]) é igual ao tempo armazenado na variável estatística prev_time, isto indica que a não existe uma nova barra.
1.2. Painel
Todos os parâmetros de construção do painel (quantidade, dimensões e coordenadas dos controles) estão concentradas no arquivo de inclusão "PanelDialog.mqh", ele é a classe de implementação do painel.
O painel tem a seguinte forma:
Fig. 2. Painel
A seguir, você vai encontrar o código do arquivo de inclusão "PanelDialog.mqh":
//+------------------------------------------------------------------+ //| PanelDialog.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include <Controls\Dialog.mqh> #include <Controls\CheckGroup.mqh> //+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ //--- indents and gaps #define INDENT_LEFT (11) // indent from left (with allowance for border width) #define INDENT_TOP (11) // indent from top (with allowance for border width) #define INDENT_BOTTOM (11) // indent from bottom (with allowance for border width) //--- for buttons #define BUTTON_WIDTH (100) // size by X coordinate //+------------------------------------------------------------------+ //| Class CControlsDialog | //| Usage: main dialog of the Controls application | //+------------------------------------------------------------------+ class CControlsDialog : public CAppDialog { private: CCheckGroup m_check_group; // CCheckGroup object public: CControlsDialog(void); ~CControlsDialog(void); //--- create virtual bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2); //--- chart event handler virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); protected: //--- create dependent controls bool CreateCheckGroup(void); //--- handlers of the dependent controls events void OnChangeCheckGroup(void); }; //+------------------------------------------------------------------+ //| Event Handling | //+------------------------------------------------------------------+ EVENT_MAP_BEGIN(CControlsDialog) ON_EVENT(ON_CHANGE,m_check_group,OnChangeCheckGroup) EVENT_MAP_END(CAppDialog) //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CControlsDialog::CControlsDialog(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CControlsDialog::~CControlsDialog(void) { } //+------------------------------------------------------------------+ //| Create | //+------------------------------------------------------------------+ bool CControlsDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); //--- create dependent controls if(!CreateCheckGroup()) return(false); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Create the "CheckGroup" element | //+------------------------------------------------------------------+ bool CControlsDialog::CreateCheckGroup(void) { //--- coordinates int x1=INDENT_LEFT; int y1=INDENT_TOP; int x2=x1+BUTTON_WIDTH; int y2=ClientAreaHeight()-INDENT_BOTTOM; //--- create if(!m_check_group.Create(m_chart_id,m_name+"CheckGroup",m_subwin,x1,y1,x2,y2)) return(false); if(!Add(m_check_group)) return(false); m_check_group.Alignment(WND_ALIGN_HEIGHT,0,y1,0,INDENT_BOTTOM); //--- fill out with strings if(!m_check_group.AddItem("Mail",1<<0)) return(false); if(!m_check_group.AddItem("Push",1<<1)) return(false); if(!m_check_group.AddItem("Alert",1<<2)) return(false); Comment(__FUNCTION__+" : Value="+IntegerToString(m_check_group.Value())); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CControlsDialog::OnChangeCheckGroup(void) { Comment(__FUNCTION__+" : Value="+IntegerToString(m_check_group.Value())); } //+------------------------------------------------------------------+
Como você pode ver, na classe de nosso painel, não há métodos de instalação e leitura do estado dos interruptores com fixação independente.
Nosso objetivo: fazer com que o indicador "NewBar.mq5" seja o arquivo básico e adicionar os parâmetros de entrada, por exemplo, usando os métodos ("Mail", "Push" ou "Alert") informar ao usuário que foi encontrada uma barra. Além disso, precisamos adicionar ao arquivo de inclusão "PanelDialog.mqh" os métodos de instalação e de leitura das propriedades do estado dos interruptores com fixação independente "Mail", "Push" e "Alert".
1.3. Alteramos o indicador
Nota: todas as alterações introduzidas serão marcadas com uma cor.
Primeiro, é necessário implementarmos o arquivo de inclusão "PanelDialog.mqh":
#property indicator_chart_window #property indicator_plots 0 #include "PanelDialog.mqh" //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit()
Em seguida, adicionamos os parâmetros de entrada:
#property indicator_chart_window #property indicator_plots 0 #include "PanelDialog.mqh" //--- input parameters input bool bln_mail=false; // Notify by email input bool bln_push=false; // Notify by push input bool bln_alert=true; // Notify by alert //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit()
Compilamos o indicador (F7 no MetaEditor) e verificamos a exibição de parâmetros de entrada no terminal:
Fig. 3. Parâmetros de entrada do indicador
1.4. Alteramos o painel
Precisamos de adicionar ao painel os métodos de instalação e de leitura das propriedades do estado dos interruptores com fixação independente "Mail", "Push" e "Alert".
Adicionamos os novos métodos à classe do painel:
class CControlsDialog : public CAppDialog { private: CCheckGroup m_check_group; // CCheckGroup object public: CControlsDialog(void); ~CControlsDialog(void); //--- create virtual bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2); //--- chart event handler virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- set check for element virtual bool SetCheck(const int idx,const int value); //--- get check for element virtual int GetCheck(const int idx) const; protected: //--- create dependent controls bool CreateCheckGroup(void);
Implementação desses métodos:
//+------------------------------------------------------------------+
//| Set check for element |
//+------------------------------------------------------------------+
bool CControlsDialog::SetCheck(const int idx,const bool check)
{
return(m_check_group.Check(idx,check));
}
//+------------------------------------------------------------------+
//| Get check for element |
//+------------------------------------------------------------------+
int CControlsDialog::GetCheck(const int idx)
{
return(m_check_group.Check(idx));
}
1.5. Fase final da ligação do indicador e o painel
No indicador "NewBar.mq5", no bloque das variáveis globais, anunciamos a variável da classe do nosso painel:
#property indicator_chart_window #property indicator_plots 0 #include "PanelDialog.mqh" //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ CControlsDialog ExtDialog; //--- input parameters input bool bln_mail=false; // Notify by email input bool bln_push=false; // Notify by push input bool bln_alert=true; // Notify by alert
e adicionamos ao final do indicador a função OnChartEvent():
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
ExtDialog.ChartEvent(id,lparam,dparam,sparam);
}
Na função OnInit() do indicador "NewBar.mq5", criamos o painel e pressionamos de modo programático as caixas de verificação de acordo com os parâmetros de entrada:
int OnInit() { //--- indicator buffers mapping //--- create application dialog if(!ExtDialog.Create(0,"Notification",0,50,50,180,160)) return(INIT_FAILED); //--- run application if(!ExtDialog.Run()) return(INIT_FAILED); //--- ExtDialog.SetCheck(0,bln_mail); ExtDialog.SetCheck(1,bln_push); ExtDialog.SetCheck(2,bln_alert); //--- return(INIT_SUCCEEDED); }
Assim, a ligação do indicador e do painel fica concluída. Na classe do painel, implementamos o método de definição de estado da caixa de verificação pressionado/liberado (SetCheck) e o método de obtenção do estado da caixa de verificação (GetCheck).
2. Ligamos o conselheiro e o painel
2.1. Conselheiro
Tomamos a base a partir da entrega padrão ...\MQL5\Experts\Examples\MACD\MACD Sample.mq5.
2.2. Painel
O painel "PanelDialog2.mqh" final terá a seguinte aparência:
Fig. 4. Painel número dois
O que obteremos após ligar conselheiro "MACD Sample.mq5" ao painel "PanelDialog2.mqh"? No timeframe atual, no qual será executado o conselheiro, será possível alterar rapidamente os parâmetros do conselheiro ("Lots", "Trailing Stop Level (in pips)" e outros), bem como os parâmetros de notificação sobre os eventos de negociação do conselheiro ("Mail", "Push", "Alert").
Os parâmetros alterados do conselheiro ("Lots", "Trailing Stop Level (in pips)" e outros) são aplicados após pressionar o botão "Aplicar alterações". A alteração dos parâmetros de notificação sobre os eventos de negociação do conselheiro ("Mail", "Push", "Alert") são aplicados automaticamente, não é preciso pressionar o botão "Aplicar alterações".
2.3. Tanto o conselheiro quanto o painel precisam de comunicação
Fig. 5. Comunicação do conselheiro e do painel
Ao ser executado, o conselheiro deve apresentar seus parâmetros no painel. Após clicar no botão "Aplicar alterações" e os dados terem sido alterados, o painel também deve retornar os parâmetros alterados para o conselheiro, para a sua inicialização com novos parâmetros.
2.4. Primeiro passo. Alterando o conselheiro
Tome o conselheiro a partir da entrega padrão ...\MQL5\Experts\Examples\MACD\MACD Sample.mq5 e cole-o na sua pasta. Por exemplo, é possível criar a pasta "Notification" e nela colar o conselheiro:
Fig. 6. Criação de uma nova pasta
No bloque das variáveis globais do conselheiro (não as confunda com as variáveis globais do terminal), anunciamos novas variáveis que podem responder pelo modo de envio de notificações sobre as ações de negociação do conselheiro. Note-se que estas variáveis e outras variáveis externas têm o prefixo "Inp":
#include <Trade\PositionInfo.mqh> #include <Trade\AccountInfo.mqh> //--- input parameters input bool InpMail=false; // Notify by email input bool InpPush=false; // Notify by push input bool InpAlert=true; // Notify by alert //--- input double InpLots =0.1; // Lots input int InpTakeProfit =50; // Take Profit (in pips)
Abaixo adicionamos duplicados de todas as variáveis externas do conselheiro. Os duplicados terão o prefixo "Ext":
input int InpMACDCloseLevel=2; // MACD close level (in pips) input int InpMATrendPeriod =26; // MA trend period //--- ext variables bool ExtMail; bool ExtPush; bool ExtAlert; double ExtLots; int ExtTakeProfit; int ExtTrailingStop; int ExtMACDOpenLevel; int ExtMACDCloseLevel; int ExtMATrendPeriod; //--- int ExtTimeOut=10; // time out in seconds between trade operations //+------------------------------------------------------------------+ //| MACD Sample expert class | //+------------------------------------------------------------------+
Na função OnInit(), definimos a cópia de valores a partir das variáveis externas para os valores das variáveis-duplicados:
//| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(void) { ExtMail=InpMail; ExtPush=InpPush; ExtAlert=InpAlert; ExtLots=InpLots; ExtTakeProfit=InpTakeProfit; ExtTrailingStop=InpTrailingStop; ExtMACDOpenLevel=InpMACDOpenLevel; ExtMACDCloseLevel=InpMACDCloseLevel; ExtMATrendPeriod=InpMATrendPeriod; //--- create all necessary objects if(!ExtExpert.Init())
Nesta fase, nas funções do conselheiro CSampleExpert::InitIndicators, CSampleExpert::InitCheckParameters e CSampleExpert::Init usam-se variáveis externas do conselheiro com o prefixo "Inp". Nestas funções, precisamos de substituir as variáveis externas pelos seus duplicados (nossos duplicados tem o prefixo "Ext"). Eu sugiro fazer isto de uma forma bastante original:
Após a substituição, é necessário verificar a validade da ação, isto é, executar a compilação do arquivo. Não deve haver nenhum erro.
2.5. Passo dois. Alterando o painel
O painel mostrado na Fig. 4 é o projeto. Nele ainda não existe nem a função para "contato" com o conselheiro nem a função de processamento de dados de entrada. Copie o arquivo do projeto do painel "PanelDialog2Original.mqh" e cole na pasta "Notification".
Adicionamos à classe do painel as variáveis internas nas quais mais tarde vamos armazenar o estado de todos os dados introduzidos. Observe a variável "mModification", sobre ela discutiremos no ponto 2.7.
private: //--- get check for element virtual int GetCheck(const int idx); //--- bool mMail; bool mPush; bool mAlert_; double mLots; // Lots int mTakeProfit; // Take Profit (in pips) int mTrailingStop; // Trailing Stop Level (in pips) int mMACDOpenLevel; // MACD open level (in pips) int mMACDCloseLevel; // MACD close level (in pips) int mMATrendPeriod; // MA trend period //--- bool mModification; // Values have changed }; //+------------------------------------------------------------------+ //| Event Handling |
Inicializamos as variáveis internas no construtor de classe do painel abaixo:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CControlsDialog::CControlsDialog(void) : mMail(false), mPush(false), mAlert_(true), mLots(0.1), mTakeProfit(50), mTrailingStop(30), mMACDOpenLevel(3), mMACDCloseLevel(2), mMATrendPeriod(26), mModification(false) { } //+------------------------------------------------------------------+ //| Destructor |
Adicionamos à função CControlsDialog::Create a instalação do grupo de elementos-interruptor de acordo com as variáveis internas:
if(!CreateButtonOK()) return(false); //--- SetCheck(0,mMail); SetCheck(1,mPush); SetCheck(2,mAlert_); //--- succeed return(true); }
2.6. Passo três. Introduzindo alterações no conselheiro
Até agora o conselheiro e o painel têm sido dois arquivos distintos e independentes. Ligamo-los e anunciamos a variável da classe do nosso painel "ExtDialog":
#include <Trade\PositionInfo.mqh> #include <Trade\AccountInfo.mqh> #include "PanelDialog2Original.mqh" //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ CControlsDialog ExtDialog; //--- input parameters input bool InpMail=false; // Notify by email input bool InpPush=false; // Notify by push
Para o painel funcionar e se tornar visível, é necessário criá-lo e executá-lo. Também é necessário adicionar a função OnChartEvent(), ela é o manipulador de eventos ChartEvent, e a função OnDeinit(). A função OnInit() no conselheiro adota a seguinte forma:
int OnInit(void) { ExtMail=InpMail; ExtPush=InpPush; ExtAlert=InpAlert; ExtLots=InpLots; ExtTakeProfit=InpTakeProfit; ExtTrailingStop=InpTrailingStop; ExtMACDOpenLevel=InpMACDOpenLevel; ExtMACDCloseLevel=InpMACDCloseLevel; ExtMATrendPeriod=InpMATrendPeriod; //--- create all necessary objects if(!ExtExpert.Init()) return(INIT_FAILED); //--- create application dialog if(!ExtDialog.Create(0,"Notification",0,100,100,360,380)) return(INIT_FAILED); //--- run application if(!ExtDialog.Run()) return(INIT_FAILED); //--- succeed return(INIT_SUCCEEDED); }
Na função OnDeinit(), destruímos o nosso painel, enquanto criamos a função OnDeinit() após a função OnInit():
//--- succeed return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Comment(""); //--- destroy dialog ExtDialog.Destroy(reason); } //+------------------------------------------------------------------+ //| Expert new tick handling function | //+------------------------------------------------------------------+ void OnTick(void)
Adicionamos a função OnChartEvent() ao final do conselheiro (após a função OnTick):
//--- change limit time by timeout in seconds if processed if(ExtExpert.Processing()) limit_time=TimeCurrent()+ExtTimeOut; } } } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ExtDialog.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
Agora é possível compilar o conselheiro e verificá-lo no gráfico. O conselheiro será executado junto com o painel:
Fig. 7. Conselheiro junto com o painel
2.7. Passo quatro. Introduzindo alterações no painel. A grande integração
Primeiramente, o conselheiro se executa, a seguir, o usuário define seus parâmetros de entrada, e só depois disto é que o painel se executa. É por esta razão que, no painel, é necessário prever as funções para troca de dados entre ele e conselheiro.
Adicionamos o método Initialization(), ele aceita os parâmetros e inicializa, com esses parâmetros, as variáveis internas do painel. Anúncio:
virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- initialization virtual bool Initialization(const bool Mail,const bool Push,const bool Alert_, const double Lots,const int TakeProfit, const int TrailingStop,const int MACDOpenLevel, const int MACDCloseLevel,const int MATrendPeriod); protected: //--- create dependent controls bool CreateCheckGroup(void);
Corpo do método (colocamo-lo antes da função CControlsDialog::GetCheck):
//+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ bool CControlsDialog::Initialization(const bool Mail,const bool Push,const bool Alert_, const double Lots,const int TakeProfit, const int TrailingStop,const int MACDOpenLevel, const int MACDCloseLevel,const int MATrendPeriod) { mMail=Mail; mPush=Push; mAlert_=Alert_; mLots=Lots; mTakeProfit=TakeProfit; mTrailingStop=TrailingStop; mMACDOpenLevel=MACDOpenLevel; mMACDCloseLevel=MACDCloseLevel; mMATrendPeriod=MATrendPeriod; //--- return(true); } //+------------------------------------------------------------------+ //| Get check for element | //+------------------------------------------------------------------+ int CControlsDialog::GetCheck(const int idx)
Como as variáveis internas do painel foram inicializadas pelos dados, agora é necessário preencher controles do painel, isto é, a caixa de edição Como temos seis caixas de edição, vou dar um exemplo baseado em m_edit1. A cadeia de caracteres na qual foi atribuído o texto estava assim:
... if(!m_edit1.Text("Edit1")) ...
e tornou-se o seguinte:
... if(!m_edit1.Text(DoubleToString(mLots,2))) ...
Assim, cada caixa de edição tem sua própria variável interna.
O seguinte método — GetValues() — responde pelo retorno dos valores das variáveis internas:
virtual bool Initialization(const bool Mail,const bool Push,const bool Alert_, const double Lots,const int TakeProfit, const int TrailingStop,const int MACDOpenLevel, const int MACDCloseLevel,const int MATrendPeriod); //--- get values virtual void GetValues(bool &Mail,bool &Push,bool &Alert_, double &Lots,int &TakeProfit, int &TrailingStop,int &MACDOpenLevel, int &MACDCloseLevel,int &MATrendPeriod); protected: //--- create dependent controls bool CreateCheckGroup(void);
Colocamos seu corpo após CControlsDialog::Initialization()):
//+------------------------------------------------------------------+ //| Get values | //+------------------------------------------------------------------+ void CControlsDialog::GetValues(bool &Mail,bool &Push,bool &Alert_, double &Lots,int &TakeProfit, int &TrailingStop,int &MACDOpenLevel, int &MACDCloseLevel,int &MATrendPeriod) { Mail=mMail; Push=mPush; Alert_=mAlert_; Lots=mLots; TakeProfit=mTakeProfit; TrailingStop=mTrailingStop; MACDOpenLevel=mMACDOpenLevel; MACDCloseLevel=mMACDCloseLevel; MATrendPeriod=mMATrendPeriod; } //+------------------------------------------------------------------+ //| Get check for element | //+------------------------------------------------------------------+ int CControlsDialog::GetCheck(const int idx)
Como é precisamente o painel quem responde pelo envio de quaisquer ações de negociação do conselheiro, nele é necessário um método especial que responda por isto. Declaramo-lo:
virtual void GetValues(bool &Mail,bool &Push,bool &Alert_, double &Lots,int &TakeProfit, int &TrailingStop,int &MACDOpenLevel, int &MACDCloseLevel,int &MATrendPeriod); //--- send notifications virtual void Notifications(const string text); protected: //--- create dependent controls bool CreateCheckGroup(void);
Colocamos seu corpo após CControlsDialog::GetValues()):
//+------------------------------------------------------------------+ //| Send notifications | //+------------------------------------------------------------------+ void CControlsDialog::Notifications(const string text) { int i=m_check_group.ControlsTotal(); if(GetCheck(0)) SendMail(" ",text); if(GetCheck(1)) SendNotification(text); if(GetCheck(2)) Alert(text); } //+------------------------------------------------------------------+ //| Get check for element | //+------------------------------------------------------------------+ int CControlsDialog::GetCheck(const int idx)
Para lembrar se as alterações no painel foram efetuadas, foi introduzida a variável bandeira "mModification" (ela foi referida no ponto 2.5.).
virtual void Notifications(const string text); //--- virtual bool Modification(void) const { return(mModification); } virtual void Modification(bool value) { mModification=value; } protected: //--- create dependent controls bool CreateCheckGroup(void);
Vamos controlar as alterações no "CControlsDialog::OnClickButtonOK", isto é, o manipulador de evento de pressão no botão "Aplicar alterações":
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CControlsDialog::OnClickButtonOK(void) { //--- verifying changes if(m_check_group.Check(0)!=mMail) mModification=true; if(m_check_group.Check(1)!=mPush) mModification=true; if(m_check_group.Check(2)!=mAlert_) mModification=true; if(StringToDouble(m_edit1.Text())!=mLots) { mLots=StringToDouble(m_edit1.Text()); mModification=true; } if(StringToInteger(m_edit2.Text())!=mTakeProfit) { mTakeProfit=(int)StringToDouble(m_edit2.Text()); mModification=true; } if(StringToInteger(m_edit3.Text())!=mTrailingStop) { mTrailingStop=(int)StringToDouble(m_edit3.Text()); mModification=true; } if(StringToInteger(m_edit4.Text())!=mMACDOpenLevel) { mMACDOpenLevel=(int)StringToDouble(m_edit4.Text()); mModification=true; } if(StringToInteger(m_edit5.Text())!=mMACDCloseLevel) { mMACDCloseLevel=(int)StringToDouble(m_edit5.Text()); mModification=true; } if(StringToInteger(m_edit6.Text())!=mMATrendPeriod) { mMATrendPeriod=(int)StringToDouble(m_edit6.Text()); mModification=true; } }
Além disso, o painel verifica os dados inseridos nos manipuladores:
void OnChangeCheckGroup(void); void OnChangeEdit1(void); void OnChangeEdit2(void); void OnChangeEdit3(void); void OnChangeEdit4(void); void OnChangeEdit5(void); void OnChangeEdit6(void); void OnClickButtonOK(void);
Omitirei sua descrição.
2.8. Passo cinco. Introduzindo alterações no conselheiro. Última edição
Atualmente, o painel não funciona no testador de estratégias, por isso você precisa de se proteger introduzindo uma variável interna, nomeadamente, a bandeira "bool_tester".
//--- int ExtTimeOut=10; // time out in seconds between trade operations bool bool_tester=false; // true - mode tester //+------------------------------------------------------------------+ //| MACD Sample expert class | //+------------------------------------------------------------------+ class CSampleExpert
Introduzimos alterações na função OnInit(), para nos proteger da execução no testador de estratégias, e, antes da visualização do painel, analisamos seus parâmetros:
//--- create all necessary objects if(!ExtExpert.Init()) return(INIT_FAILED); //--- if(!MQLInfoInteger(MQL_TESTER)) { bool_tester=false; //--- ExtDialog.Initialization(ExtMail,ExtPush,ExtAlert, ExtLots,ExtTakeProfit,ExtTrailingStop, ExtMACDOpenLevel,ExtMACDCloseLevel,ExtMATrendPeriod); //--- create application dialog if(!ExtDialog.Create(0,"Notification",0,100,100,360,380)) return(INIT_FAILED); //--- run application if(!ExtDialog.Run()) return(INIT_FAILED); } else bool_tester=true; //--- secceed return(INIT_SUCCEEDED); }
Na função OnChartEvent(), verificamos se houveram alterações nos parâmetros do painel. No caso de existirem alterações, será necessário executar a inicialização do conselheiro com novos parâmetros:
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ExtDialog.ChartEvent(id,lparam,dparam,sparam); // Perguntamos à variável bool, no painel, se foram alterados os parâmetros // Se os parâmetros estiverem alterados, perguntaremos aos parâmetros do painel e chamamos // CSampleExpert::Init(void) if(ExtDialog.Modification()) { ExtDialog.GetValues(ExtMail,ExtPush,ExtAlert, ExtLots,ExtTakeProfit,ExtTrailingStop, ExtMACDOpenLevel,ExtMACDCloseLevel,ExtMATrendPeriod); if(ExtExpert.Init()) { ExtDialog.Modification(false); Print("Parâmetros alterados, ",ExtLots,", ",ExtTakeProfit,", ",ExtTrailingStop,", ", ExtMACDOpenLevel,", ",ExtMACDCloseLevel,", ",ExtMATrendPeriod); } else { ExtDialog.Modification(false); Print("Erro de alteração de parâmetros"); } } } //+------------------------------------------------------------------+
Conclusão
Ligar o painel e o indicador acabou por ser fácil. Para fazer isso, é necessário, por um lado, na classe do painel, implementar toda a funcionalidade (dimensões e colocação dos controles, reação aos eventos) e, por outro lado, no indicador, declarar a variável da classe de nosso painel e adicionar a função OnChartEvent().
Por sua vez, ligar o conselheiro ao painel mais complexo se tornou uma tarefa mais séria, principalmente pela necessidade de "contato" do conselheiro e do painel. A complexidade da tarefa depende, em grande medida, de quão pronto está o painel para a ligação. Em outras palavras, quanto maior for o número inicial de funções implementadas e de recursos de integração com outros programas, no painel, mais fácil será ligá-lo com outro programa (indicador ou conselheiro).
Ao artigo foram anexados os arquivos:
- NewBarOriginal.mq5 — arquivo inicial do indicador.
- PanelDialogOriginal.mqh — arquivo inicial do painel.
- NewBar.mq5 — arquivo alterado do indicador.
- PanelDialog.mqh — arquivo alterado do painel.
- PanelDialog2Original.mqh — arquivo inicial do segundo painel.
- PanelDialog2.mqh — arquivo alterado do segundo painel.
- MACD Sample.mq5 — arquivo alterado do conselheiro.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2171
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso