
Как быстро добавить панель управления к индикатору и советнику
Зачем нужна графическая панель
Ваша MQL4/MQL5-программа — индикатор или советник — может быть одной из лучших и полностью выполнять возложенные на нее задачи. Но вы всегда можете немного улучшить ее. Как правило, в 99% случаев для любого изменения входных параметров программы пользователю нужно заходить в ее настройки. Хотите обойтись без этого?
Сделать это можно, создав собственную панель управления на базе классов Стандартной библиотеки. Это позволит менять настройки без перезапуска программы. К тому же, такой подход оживляет программу и выгодно выделяет ее среди других. Посмотреть примеры графических панелей вы можете в Маркете.
В этой статье я покажу, как самому добавить простую панель к вашей MQL4/MQL5-программе. Вы узнаете, как научить программу читать входные параметры и реагировать на изменение их значений.
1. Соединяем индикатор и панель
1.1. Индикатор
Индикатор "NewBar.mq5" выполняет одно действие: при появлении нового бара печатает сообщение в журнал экспертов терминала. Код индикатора представлен ниже:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Расскажу немного подробнее о том, как работает индикатор "NewBar.mq5".
В функции OnCalculate() объявляется статическая переменная "prev_time" — в этой переменной сохраняется время открытия "time[0]". В следующем заходе сравнивается время открытия "time[0]" с переменной "prev_time" — то есть происходит сравнение времени открытия "time[0]" на текущем тике и время открытия "time[0]" на предыдущем тике. В случае выполнения условия
if(time[0]>prev_time)
считаем, что обнаружен новый бар.
На следующем примере детально разберем, как индикатор "NewBar.mq5" обнаруживает новый бар:
Рис. 1. Процесс обнаружения нового бара в индикаторе
Рассмотрим 10 тиков на очень спокойном рынке.
Тики с 1 по 3 включительно: время открытия бара с индексом "0" (time[0]) равно времени, которое сохранено в статической переменной prev_time, а значит, нового бара нет.
Тик №4: этот тик пришел на новом баре. При входе в функцию OnCalculate() в time[0] будет время открытия бара (2015.12.01 00:02:00), а переменная prev_time еще хранит время из предыдущего тика (2015.12.01 00:01:00). Поэтому при проверке условия time[0]>prev_time мы обнаружим новый бар. Перед выходом из OnCalculate() в переменной prev_time будет записано время из time[0] (2015.12.01 00:02:00).
Тики с 5 по 8 включительно: время открытия бара с индексом "0" (time[0]) равно времени, которое сохранено в статической переменной prev_time, а значит, нового бара нет.
Тик №9: этот тик пришел на новом баре. При входе в функцию OnCalculate() в time[0] будет время открытия бара (2015.12.01 00:03:00), а переменная prev_time еще хранит время из предыдущего тика (2015.12.01 00:02:00). Поэтому при проверке условия time[0]>prev_time мы обнаружим новый бар. Перед выходом из OnCalculate() в переменной prev_time будет записано время из time[0] (2015.12.01 00:03:00).
Тик 10: время открытия бара с индексом "0" (time[0]) равно времени, которое сохранено в статической переменной prev_time, а значит, нового бара нет.
1.2. Панель
Все параметры построения панели: количество, размеры и координаты элементов управления сосредоточены в одном включаемом файле "PanelDialog.mqh". Файл "PanelDialog.mqh" — это класс реализации панели.
Панель имеет такой вид:
Рис. 2. Панель
Код включаемого файла "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())); } //+------------------------------------------------------------------+
Как видите, в классе нашей панели нет методов установки и чтения свойств состояния переключателей с независимой фиксацией.
Наша задача: сделать индикатор "NewBar.mq5" основным файлом и добавить входные параметры — например, с помощью каких методов ("Mail", "Push" или "Alert") сообщать пользователю о том, что найден новый бар. Кроме того, мы должны добавить во включаемый файл "PanelDialog.mqh" методы установки и чтения свойств состояния переключателей с независимой фиксацией "Mail", "Push" и "Alert".
1.3. Изменяем индикатор
Примечание: все вносимые изменения будут отмечаться цветом.
Сначала нужно подключить включаемый файл "PanelDialog.mqh":
#property indicator_chart_window #property indicator_plots 0 #include "PanelDialog.mqh" //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit()
Затем добавим входные параметры:
#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()
Скомпилируем индикатор (F7 в MetaEditor) и проверим отображение входных параметров в терминале:
Рис. 3. Входные параметры индикатора
1.4. Изменяем панель
В панель нужно добавить методы установки и чтения свойств состояния переключателей с независимой фиксацией "Mail", "Push" и "Alert".
Добавим в класс панели новые методы:
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);
Реализация этих методов:
//+------------------------------------------------------------------+
//| 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. Заключительный этап соединения индикатора и панели
В индикаторе "NewBar.mq5", в области объявлений глобальных переменных объявим переменную класса нашей панели:
#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
и добавим в самый конец индикатора функцию OnChartEvent():
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
ExtDialog.ChartEvent(id,lparam,dparam,sparam);
}
В функции OnInit() индикатора "NewBar.mq5", создадим панель и программно нажмем чекбоксы в соответствии с входными параметрами:
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); }
На этом соединение индикатора и панели закончено. В классе панели нами реализованы метод установки состояния чекбокса нажатый/отжатый (SetCheck) и метод получения состояния чекбокса (GetCheck).
2. Соединяем советник и панель
2.1. Советник
За основу взят советник из стандартной поставки ...\MQL5\Experts\Examples\MACD\MACD Sample.mq5.
2.2. Панель
Вид панели "PanelDialog2.mqh" после внесения окончательных изменений:
Рис. 4. Панель номер два
Что мы получим после объединения советника "MACD Sample.mq5" и панели "PanelDialog2.mqh"? На текущем таймфрейме, на котором будет работать советник, можно будет оперативно менять параметры советника ("Lots", "Trailing Stop Level (in pips)" и другие), а также параметры оповещения о торговых событиях советника ("Mail", "Push", "Alert").
Измененные параметры советника ("Lots", "Trailing Stop Level (in pips)" и другие) применяются после щелчка на кнопке "Применить изменения". Изменение параметров оповещения о торговых событиях советника ("Mail", "Push", "Alert") применяются автоматически — щелкать на кнопке "Применить изменения" не нужно.
2.3. Советнику и панели нужно общаться
Рис. 5. Общение советника и панели
При запуске советник должен передавать свои параметры в панель. Панель, после щелчка по кнопке "Применить изменения" и если данные были изменены, тоже должна возвращать измененные параметры в советник для его инициализации с новыми параметрами.
2.4. Шаг первый. Внесение изменений в советник
План действий: возьмите советник из стандартной поставки ...\MQL5\Experts\Examples\MACD\MACD Sample.mq5 и скопируйте его в свою папку. Например, можно создать папку "Notification" и в нее скопировать советник:
Рис. 6. Создание новой папки
В области глобальных переменных советника (не путайте их с глобальными переменными терминала) объявим новые переменные, которые будут отвечать за способ отправки уведомления о торговых действиях советника. Обратите внимание, что эти переменные, как и другие внешние переменные, имеют приставку "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)
Чуть ниже добавим дубликаты всех внешних переменных советника. Дубликаты будут иметь приставку "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 | //+------------------------------------------------------------------+
В OnInit() пропишем копирование значений из внешних переменных советника в значения переменных-дубликатов:
//| 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())
На данном этапе в функциях советника CSampleExpert::InitIndicators, CSampleExpert::InitCheckParameters и CSampleExpert::Init используются внешние переменные советника с приставкой "Inp". Нам нужно в этих функциях заменить внешние переменные на их дубликаты (дубликаты у нас с приставкой "Ext"). Я предлагаю сделать это довольно оригинальным методом:
После замены нужно обязательно проверить правильность действий — выполнить компиляцию файла. Ошибок быть не должно.
2.5. Шаг второй. Внесение изменений в панель
Панель, изображенная на рис. 4, — это заготовка. В ней еще нет ни функций для "общения" с советником, ни функций обработки вводимых данных. Файл заготовки панели "PanelDialog2Original.mqh" тоже скопируйте в папку "Notification".
Добавим в класс панели внутренние переменные, в которых потом будем хранить состояние всех введенных данных. Обратите внимание на переменную "mModification" — о ней будет сказано в пункте 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 |
Сразу за этим в конструкторе класса панели инициализируем внутренние переменные:
//+------------------------------------------------------------------+ //| 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 |
В функцию CControlsDialog::Create добавим установку группы элементов переключателей в соответствии со внутренними переменными:
if(!CreateButtonOK()) return(false); //--- SetCheck(0,mMail); SetCheck(1,mPush); SetCheck(2,mAlert_); //--- succeed return(true); }
2.6. Шаг третий. Внесение изменений в советник
До сих пор советник и панель были двумя отдельными, независимыми друг от друга файлами. Свяжем их и объявим переменную класса нашей панели "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
Чтобы панель заработала и стала видна, ее нужно создать и запустить на выполнение. Также необходимо добавить функцию OnChartEvent(), которая является обработчиком события ChartEvent, и функцию OnDeinit(). OnInit() в советнике приобретет такой вид:
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); }
В OnDeinit() уничтожаем нашу панель, а саму функцию OnDeinit() пропишем сразу после 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)
Функцию OnChartEvent() добавим в самый конец советника (после функции 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); } //+------------------------------------------------------------------+
Теперь советник можно cкомпилировать и проверить на графике. Советник будет запускаться вместе с панелью:
Рис. 7. Советник вместе с панелью
2.7. Шаг четвертый. Внесение изменений в панель. Большая интеграция
Сначала запускается советник, потом пользователь устанавливает его входные параметры, и только после этого запускается панель. Именно по этой причине в панели нужно предусмотреть функции для обмена данными между нею и советником.
Добавим метод Initialization() — этот метод принимает параметры и инициализирует этими параметрами внутренние переменные панели. Объявление:
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);
Само тело метода (вставим его перед 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)
Так как внутренние переменные панели проинициализированы данными, то теперь нужно правильно заполнить элементы управления панели — поля ввода. Поскольку полей ввода у нас шесть, то я приведу пример на базе m_edit1. Строка, в которой присваивался текст, была такая:
... if(!m_edit1.Text("Edit1")) ...
а стала такая:
... if(!m_edit1.Text(DoubleToString(mLots,2))) ...
Таким образом, каждому полю ввода соответствует своя внутренняя переменная.
Следующий метод — GetValues() — отвечает за возврат значений внутренних переменных:
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);
Его тело вставим после 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)
Так как именно панель отвечает за отправку сообщений в ответ на любые торговые действия советника, значит, в ней нужен специальный метод, отвечающий за это. Объявим его:
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);
Его тело вставим после 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)
Чтобы запомнить, производились ли изменения параметров в панели, введена внутренняя переменная — флаг "mModification" (о ней упоминалось ранее, в пункте 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);
Контроль за изменениями будем проводить в "CControlsDialog::OnClickButtonOK" — обработчике нажатия на кнопку "Применить изменения":
//+------------------------------------------------------------------+ //| 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; } }
Также панель проверяет введенные данные в обработчиках:
void OnChangeCheckGroup(void); void OnChangeEdit1(void); void OnChangeEdit2(void); void OnChangeEdit3(void); void OnChangeEdit4(void); void OnChangeEdit5(void); void OnChangeEdit6(void); void OnClickButtonOK(void);
Их описание я пропущу.
2.8. Шаг пятый. Внесение изменений в советник. Последнее редактирование
На данный момент панель не работает в тестере стратегий, поэтому нужно защититься — ввести внутреннюю переменную, а именно — флаг "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
Вносим изменения в OnInit() — защищаемся от запуска в тестере стратегий, а также перед визуализацией панели инициализируем ее параметры:
//--- 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); }
В OnChartEvent() проверяем, были ли изменены параметры в панели. Если они изменены, значит, необходимо запустить инициализацию советника с новыми параметрами:
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { ExtDialog.ChartEvent(id,lparam,dparam,sparam); // Дальше опрашиваем переменную bool в панели: изменялись ли параметры // Если параметры изменились - значит, опрашиваем параметры панели и вызываем // CSampleExpert::Init(void) if(ExtDialog.Modification()) { ExtDialog.GetValues(ExtMail,ExtPush,ExtAlert, ExtLots,ExtTakeProfit,ExtTrailingStop, ExtMACDOpenLevel,ExtMACDCloseLevel,ExtMATrendPeriod); if(ExtExpert.Init()) { ExtDialog.Modification(false); Print("Параметры изменены, ",ExtLots,", ",ExtTakeProfit,", ",ExtTrailingStop,", ", ExtMACDOpenLevel,", ",ExtMACDCloseLevel,", ",ExtMATrendPeriod); } else { ExtDialog.Modification(false); Print("Ошибка изменения параметров"); } } } //+------------------------------------------------------------------+
Заключение
Соединить панель и индикатор оказалось несложно. Для этого нужно в классе панели реализовать весь функционал (размеры и размещение элементов управления, реакцию на события), а в индикаторе объявить переменную класса нашей панели и добавить функцию OnChartEvent().
Соединить советник с более сложной панелью оказалось более серьезной задачей, главным образом из-за необходимости "общения" советника и панели. Трудоемкость задачи в большой степени зависит от того, насколько сама панель готова к соединению. Иными словами, чем больше в панели изначально реализовано функций и возможностей по интеграции с другими программами, тем легче будет соединить ее с другой программой (индикатором или советником).
К статье приложены файлы:
- NewBarOriginal.mq5 — изначальный файл индикатора.
- PanelDialogOriginal.mqh — изначальный файл панели.
- NewBar.mq5 — измененный файл индикатора.
- PanelDialog.mqh — измененный файл панели.
- PanelDialog2Original.mqh — изначальный файл второй панели.
- PanelDialog2.mqh — измененный файл второй панели.
- MACD Sample.mq5 — измененный файл советника.





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Запретить никак, но можно прописать в функции обработки клика проверку флага - если флаг true - значит обрабатываем нажатия, а если флаг false - значит выходим из функции.
это уже сделал... не все ровно не то... например когда у меня закрывается много ордеров по нажатию кнопки на панели хочу что-бы панелька полностью не отвечала, а то если кликнуть (в это время) на кнопку бай, то ивент все-ровно будет, и будет вход в обработчик ивента после того когда отработает закрытие
Сделайте правильно - обработку запретного флага сразу по входу в функцию.
Хотя... Минутку...
Добавлено:
Нет. Нужно просто обрабатывать нажатие в зависимости от запрещающего флага.
Сделайте правильно - обработку запретного флага сразу по входу в функцию.
Хотя... Минутку...
Добавлено:
Нет. Нужно просто обрабатывать нажатие в зависимости от запрещающего флага.
#include <Controls\Panel.mqh>
#include <Controls\Edit.mqh>
// #include <Controls\Defines.mqh>
#include <Controls\Button.mqh>
Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий
Почему съезжает панель при обновлении настроек эксперта?
Andrey Khatimlianskii, 2016.03.10 13:17
Примерно так: