Cómo añadir rápidamente un panel de control a un indicador o asesor
Para qué es necesario un panel gráfico
Su programa - indicador o asesor - MQL4/MQL5 puede ser uno de los mejores y ejecutar eficazmente todas las tareas que se le encarguen. Pero siempre hay forma de mejorarlo un poco. Normalmente, en el 99% de los casos, para realizar cualquier cambio en los parámetros de entrada de un programa, el usuario tiene que entrar en sus ajustes. ¿Quiere arreglárselas sin hacer esto?
Es posible hacerlo creando su propio panel de control basado en las clases de la Biblioteca estándar. Esto le permitirá cambiar los ajustes sin reiniciar el programa. Además, este enfoque revitalizará el programa, distinguiéndolo de otros de forma positiva. Podrá ver ejemplos de paneles gráficos en el Mercado.
En este artículo le enseñaré a añadir sin ayuda de nadie un sencillo panel a un programa MQL4/MQL5. Usted ya sabe cómo enseñar a un programa a leer los parámetros de entrada y a reaccionar a los cambios de sus valores.
1. Cómo unir un indicador y un panel
1.1. Indicador
El indicador "NewBar.mq5" ejecuta una acción: al aparecer una nueva barra, realiza una nueva entrada en el registro de expertos del terminal. El código del indicador se muestra más abajo:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Hablemos con un poco más de detalle sobre el funcionamiento del indicador "NewBar.mq5".
En la función OnCalculate() se declara la variable estática "prev_time", en esta variable se guarda la hora de apertura "time[0]". En la siguiente pasada se compara la hora de apertura"time[0]" con la variable "prev_time", es decir, tiene lugar la comparación de la hora de apertura "time[0]" en el tick actual con la hora de apertura "time[0]" en el tick anterior. En caso de que se cumpla la condición
if(time[0]>prev_time)
consideraremos que se ha detectado una nueva barra.
En el siguiente ejemplo analizaremos con detalle cómo el indicador "NewBar.mq5" detecta una nueva barra:
Fig. 1. Proceso de detección de una nueva barra en el indicador
Estudiaremos 10 ticks en un mercado muy tranquilo.
Ticks del 1 al 3 inclusive: la hora de apertura de la barra con el índice "0" (time[0]) es igual a la hora que se ha guardado en la variable estática prev_time, entonces significa que no hay barras nuevas.
Tick №4: este tick ha llegado en la nueva barra. Al entrar en la función OnCalculate() en time[0] estará la hora de apertura de la barra (2015.12.01 00:02:00), y la variable prev_time todavía guarda la hora del tick anterior (2015.12.01 00:01:00). Por eso, al comprobar las condiciones de time[0]>prev_time detectamos una nueva barra. Antes de salir de OnCalculate(), en la variable prev_time se guardará la hora time[0] (2015.12.01 00:02:00).
Ticks del 5 al 8 inclusive: la hora de apertura de la barra con el índice "0" (time[0]) es igual a la hora que se ha guardado en la variable estática prev_time, entonces significa que no hay barras nuevas.
Tick №9: este tick ha llegado en la nueva barra. Al entrar en la función OnCalculate() en time[0] estará la hora de apertura de la barra (2015.12.01 00:03:00), y la variable prev_time todavía guarda la hora del tick anterior (2015.12.01 00:02:00). Por eso, al comprobar las condiciones de time[0]>prev_time detectamos una nueva barra. Antes de salir de OnCalculate(), en la variable prev_time se guardará la hora time[0] (2015.12.01 00:03:00).
Ticks 10: la hora de apertura de la barra con el índice "0" (time[0]) es igual a la hora que se ha guardado en la variable estática prev_time, entonces significa que no hay barras nuevas.
1.2. Panel
Todas los parámetros de construcción del panel: la cantidad, las dimensiones y las coordenadas de los elementos de control se concentran en el archivo de inclusión "PanelDialog.mqh". El archivo "PanelDialog.mqh" es la clase de implementación del panel.
El panel tiene el aspecto siguiente:
Fig. 2. Panel
El código del archivo de inclusión "PanelDialog.mqh" se muestra más abajo:
//+------------------------------------------------------------------+ //| 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 puede ver en la clase de nuestro panel no hay métodos para establecer y leer las propiedades del estado de los interruptores con fijación independiente.
Nuestra tarea será: convertir el indicador "NewBar.mq5" en el archivo principal y añadir los parámetros de entrada, por ejemplo, con la ayuda de qué métodos ("Mail", "Push" o "Alert") comunicaremos al usuario que se ha encontrado una nueva barra. Además, deberemos añadir en el archivo de inclusión "PanelDialog.mqh" los métodos para establecer y leer las propiedades del estado de los interruptores con fijación independiente "Mail", "Push" y "Alert".
1.3. Cambiar el indicador
Advertencia: todos los cambios introducidos se destacarán a color.
Primero debemos conectar el archivo de inclusión "PanelDialog.mqh":
#property indicator_chart_window #property indicator_plots 0 #include "PanelDialog.mqh" //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit()
A continuación, añadimos los 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 el indicador (F7 en MetaEditor) y comprobamos la representación de los parámetros de entrada en el terminal:
Fig. 3. Parámetros de entrada del indicador
1.4. Cambiar el panel
En el panel se deben añadir métodos para establecer y leer las propiedades del estado de los interruptores con fijación independiente "Mail", "Push" y "Alert".
Añadimos a la clase del panel los nuevos métodos:
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);
Implementación de estos 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. Etapa final de la unión del indicador y el panel
En el indicador "NewBar.mq5", en la zona donde se declaran las variables globales, declaramos la variable de clase de nuestro panel:
#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
y añadimos al final del indicador la función OnChartEvent():
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
ExtDialog.ChartEvent(id,lparam,dparam,sparam);
}
En la función OnInit() del indicador "NewBar.mq5", creamos el panel y pulsamos de forma programática las casillas de verificación de acuerdo con los 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); }
Con esto damos por terminada la unión del indicador y el panel. En la clase del panel hemos implementado un método para establecer el estado de la casilla de verificación pulsado/sin pulsar (SetCheck) y un método para obtener el estado de la casilla de verificación (GetCheck).
2. Conectar el asesor y el panel
2.1. Asesor
Se ha tomado como base un asesor del paquete estándar ...\MQL5\Experts\Examples\MACD\MACD Sample.mq5.
2.2. Panel
Aspecto del panel "PanelDialog2.mqh" después de introducir los cambios definitivos:
Fig. 4. Panel número dos
¿Qué obtenemos tras unir el asesor "MACD Sample.mq5" y el panel "PanelDialog2.mqh"? En el marco temporal actual, en el que funcionará el asesor, se pueden cambiar de forma operativa los parámetros del asesor ("Lots", "Trailing Stop Level (in pips)" y otros), así como los parámetros de notificación sobre los eventos comerciales del asesor ("Mail", "Push", "Alert").
Los parámetros modificados del asesor ("Lots", "Trailing Stop Level (in pips)" y otros) se aplican después de pulsar el botón "Aplicar cambios".". El cambio de los parámetros de notificación sobre los eventos comerciales del asesor ("Mail", "Push", "Alert") se aplica de forma automática, no es necesario pulsar el botón "Aplicar cambios".
2.3. El asesor y el panel pueden comunicarse
Fig. 5. Comunicación del asesor y el panel
Al producirse el inicio, el asesor debe transmitir sus parámetros al panel. El panel, después de clicar en el botón "Aplicar cambios", y si los datos han sido modificados, también deberá retornar los parámetros modificados al asesor para que este se inicialice con los nuevos parámetros.
2.4. Primer paso. Introducir los cambios en el asesor.
El plan de acción es el siguiente: tome el asesor del paquete estándar ...\MQL5\Experts\Examples\MACD\MACD Sample.mq5 y cópielo en su carpeta. Por ejemplo, se puede crear la carpeta "Notification" y copiamos en ella el asesor:
Fig. 6. Crear una nueva carpeta
En la zona de las variables globales del asesor (no las confunda con las variables globales del terminal) declaramos las nuevas variables, que serán responsables del modo de envío de las notificaciones y de las acciones comerciales del asesor. Preste atención al hecho de que las variables globales, al igual que otras variables externas, tienen el prefijo "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)
Un poco más abajo añadimos los duplicados de todas las variables externas del asesor. Los duplicados tendrán el prefijo "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 | //+------------------------------------------------------------------+
En OnInit() escribimos la copia de los valores de las variables externas del asesor en los valores de las variables-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())
En esta etapa, en las funciones del asesor CSampleExpert::InitIndicators, CSampleExpert::InitCheckParameters y CSampleExpert::Init se usan las variables externas del asesor con el prefijo "Inp". Necesitamos sustituir en estas funciones las variables externas por sus duplicados (los duplicados tienen en nuestro caso el prefijo "Ext"). Propongo hacer esto con un método bastante original:
Después de realizar la sustitución, se deberá comprobar que las acciones sean correctas, hay que compilar el archivo. No debería haber errores.
2.5. Segundo paso. Introducir los cambios en el panel.
El panel representado en la fig. 4, es una especie de plantilla. En ella todavía no hay funciones para la "comunicación" con el asesor, ni funciones de procesamiento de los datos introducidos. El archivo de la plantilla "PanelDialog2Original.mqh" deberá copiarlo también en "Notification".
Añadimos a la clase del panel las variables internas, en las que guardaremos el estado de todos los datos introducidos. Preste atención a la variable "mModification", se hablará sobre ella en el punto 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 |
Inmediatamente después inicializamos las variables internas en el constructor de clase del panel:
//+------------------------------------------------------------------+ //| 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 |
A la función CControlsDialog::Create le añadimos la instalación de un grupo de elementos interruptores de acuerdo con las variables internas:
if(!CreateButtonOK()) return(false); //--- SetCheck(0,mMail); SetCheck(1,mPush); SetCheck(2,mAlert_); //--- succeed return(true); }
2.6. Tercer paso. Introducir los cambios en el asesor.
Hasta ahora, el asesor y el panel eran dos archivos independientes el uno del otro. Los uniremos y declararemos la variable de clase de nuestro panel "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 que el panel funcione y sea visible, hay que crearlo e iniciarlo para su ejecución. También es necesario añadir la función OnChartEvent(), que es el manejador de eventos ChartEvent, y la función OnDeinit(). En el asesor, OnInit() tendrá el aspecto que sigue:
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); }
En OnDeinit() eliminamos nuestro panel, y la propia función OnDeinit() la escribiremos justo después de 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)
La función OnChartEvent() la añadiremos al final del asesor (después de la función 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); } //+------------------------------------------------------------------+
Ahora ya podemos compilar el asesor y comprobarlo en el gráfico. El asesor se iniciará junto con el panel:
Fig. 7. Asesor junto con el panel
2.7. Tercer paso. Introducir las cambios en el panel. La gran integración
Primero se inicia el asesor, después el usuario establece sus parámetros de entrada, y solo después de ello se inicia el panel. Precisamente por este motivo, en el panel se debe contemplar la función de intercambio de datos entre él y su asesor.
Añadimos el método Initialization() , este método recibe los parámetros e inicializa con estos las variables internas del panel. Declaración:
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);
El propio cuerpo del método (lo ponemos delante de 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)
Puesto que las variables internas del panel son analizadas por los datos, ahora tenemos que rellenar correctamente los elementos de control del panel, los campos de búsqueda. Como tenemos seis campos de búsqueda, pondré un ejemplo basado en m_edit1. La línea a la que se adjudicaba el texto era de esta forma:
... if(!m_edit1.Text("Edit1")) ...
y ahora es así:
... if(!m_edit1.Text(DoubleToString(mLots,2))) ...
De esta forma a cada campo de búsqueda le corresponde su variable interna.
El siguiente método, GetValues() es responsable del retorno de los valores de las variables 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);
El cuerpo del mismo lo ponemos después de 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)
Dado que precisamente el panel es el responsable del envío de mensajes en respuesta a cualquier acción comercial del asesor, entonces necesitaremos un método especial que será responsable de ello. Lo declaramos:
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);
El cuerpo lo pegaremos después de 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 recordar si se han realizado cambios en los parámetros del panel, se ha introducido la variable interna - bandera "mModification" se habló de ella anteriormente, en el punto 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);
El control de los cambios lo vamos a realizar en "CControlsDialog::OnClickButtonOK", el manejador de la pulsación del botón "Aplicar cambios":
//+------------------------------------------------------------------+ //| 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; } }
Asimismo, el panel comprueba los datos introducidos en los manejadores:
void OnChangeCheckGroup(void); void OnChangeEdit1(void); void OnChangeEdit2(void); void OnChangeEdit3(void); void OnChangeEdit4(void); void OnChangeEdit5(void); void OnChangeEdit6(void); void OnClickButtonOK(void);
Voy a omitir su descripción.
2.8. Quinto paso. Introducir los cambios en el asesor. Última edición
En este momento, el panel no funciona en el simulador de estrategias, por eso hay que protegerse, introduciendo una variable interna, precisamente la bandera "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
Introducimos los cambios en OnInit(), nos protegemos del inicio del simulador de estrategias, asimismo, antes de visualizar el panel, inicializamos sus 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); }
En OnChartEvent() comprobamos si han sido cambiados los parámetros en el panel. Si los parámetros han sido modificados, es necesario inicializar el asesor con los parámetros nuevos:
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ExtDialog.ChartEvent(id,lparam,dparam,sparam); // Luego preguntamos a la variable bool en el panel si han cambiado los parámetros // Si los parámetros han cambiado, preguntamos los parámetros del panel y llamamos // 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 modificados, ",ExtLots,", ",ExtTakeProfit,", ",ExtTrailingStop,", ", ExtMACDOpenLevel,", ",ExtMACDCloseLevel,", ",ExtMATrendPeriod); } else { ExtDialog.Modification(false); Print("Error en el cambio de los parámetros"); } } } //+------------------------------------------------------------------+
Conclusión
Unir un asesor y un panel ha resultado ser muy sencillo. Para ello hay que implementar en la clase del panel todo el funcional (dimensiones y ubicación de los elementos de control, reacciones a los eventos), declarar en el indicador la variable de clase de nuestro panel y añadir la función OnChartEvent().
Unir el asesor y un panel más complejo ha resultado más complicado, principalmente debido a la necesidad de "comunicación" entre el asesor y el panel. La laboriosidad de la tarea depende en gran medida de lo preparado que esté el propio panel para la conexión. En otras palabras, cuanto mejor implementadas estén en el panel desde un primer momento las funciones y capacidades de integración con otros programas, más sencillo será conectar ese panel con otro programa (indicador o asesor).
El artículo se presenta con los archivos adjuntos:
- NewBarOriginal.mq5 — archivo inicial del indicador.
- PanelDialogOriginal.mqh — archivo inicial del panel.
- NewBar.mq5 — archivo modificado del indicador.
- PanelDialog.mqh — archivo modificado del panel.
- PanelDialog2Original.mqh — archivo inicial del segundo panel.
- PanelDialog2.mqh — archivo modificado del segundo panel.
- MACD Sample.mq5 — archivo modificado del asesor.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/2171
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso