Система голосовых уведомлений торговых событий и сигналов
Содержание
- Введение
- Разработка системы голосовых уведомлений
- Практическое применение в индикаторах
- Практическое применение в торговом эксперте
- Практическое применение в инструментарии быстрой торговли
- Заключение
Введение
В торговом терминале MetaTrader 5 присутствует система настройки звуковых уведомлений, которая включает в себя 11 событий, на которые можно поставить тот или иной звуковой сигнал. Однако при использовании терминала существует намного больше ситуаций при которых было бы полезно получать звуковую информацию — будь то появление сигнала торговой системы или же информация о том, что какая-либо выставленная позиция была открыта, закрыта или изменена активным торговым экспертом. В настоящее время голосовые помощники уже давно заняли заметную роль в жизни человека, будь то навигатор, голосовой поисковик или же переводчик. Поэтому данную идею вполне можно было использовать при торговле в терминале MetaTrader 5. В данной статье я постараюсь разработать простую и понятную систему голосовых уведомлений для различных торговых событиях, состояниях рынка или же сигналов торговых систем.
Разработка системы голосовых уведомлений
Прежде чем приступить к созданию самой системы, хотелось бы обратить внимание на то, что выбранные мною события для реализации голосовых уведомлений, это лишь стандартный набор, выбранный мною для демонстрации самой системы. Поэтому для тех, кому его будет недостаточно, система будет позволять добавить собственные события и их голосовое уведомление. Расширение системы под собственные нужны будет достаточно простым для понимания даже для людей со скромными знаниями в MQL5.
Система реализована в виде класса CSoundsLib во включаемом файле. Поэтому создадим в папке MQL5/Include папку SoundsLib и в ней файл SoundsLib.mqh. Перед созданием класса введем два перечисления, которые нужны в дальнейшем при работе с голосовыми уведомлениями. Первое их них LANGUAGE — для выбора языка уведомлений. В системе они будут на двух языках — на русском и английском.
//+------------------------------------------------------------------+ //| Перечисление для переключения языка голосовых уведомлений | //+------------------------------------------------------------------+ enum LANGUAGE { RUSSIAN, // Russian ENGLISH // English };
Второе перечисление содержит в себе тот набор событий, которые я выбрал в качестве демонстрации, и далее в статье будет показано, как их встраивать в уже готовые системы разных видов — от индикаторов и экспертов, до приложения, такого как инструментарий быстрой торговли. Итак, перечисление будет называться MESSAGE:
//+------------------------------------------------------------------+ //| Список голосовых уведомлений | //+------------------------------------------------------------------+ enum MESSAGE { STATUS_ON, // Статус включенных голосовых уведомлений SIGNAL_BUY, // Сигнал на покупку SIGNAL_SELL, // Сигнал на продажу BUY_ORDER_SET, // Ордер на покупку установлен SELL_ORDER_SET, // Ордер на продажу установлен BUYLIMIT_ORDER_SET, // Лимитный ордер на покупку установлен BUYSTOP_ORDER_SET, // Стоп ордер на покупку установлен SELLLIMIT_ORDER_SET, // Лимитный ордер на продажу установлен SELLSTOP_ORDER_SET, // Стоп ордер на продажу установлен BUYLIMIT_ORDER_DELETE, // Лимитный ордер на покупку удален BUYSTOP_ORDER_DELETE, // Стоп ордер на покупку удален SELLLIMIT_ORDER_DELETE, // Лимитный ордер на продажу удален SELLSTOP_ORDER_DELETE, // Стоп ордер на продажу удален BUY_ORDER_CLOSE_PROFIT, // Ордер на покупку закрыт с прибылью BUY_ORDER_CLOSE_LOSS, // Ордер на покупку закрыт с убытком SELL_ORDER_CLOSE_PROFIT, // Ордер на продажу закрыт с прибылью SELL_ORDER_CLOSE_LOSS, // Ордер на продажу закрыт с убытком BUY_ORDER_CLOSE_TP, // Ордер на покупку закрыт по тейк-профиту BUY_ORDER_CLOSE_SL, // Ордер на покупку закрыт по стоп-лосс SELL_ORDER_CLOSE_TP, // Ордер на продажу закрыт по тейк-профиту SELL_ORDER_CLOSE_SL, // Ордер на продажу закрыт по стоп-лосс MARKET_CLOSE, // Рынок закрыт AUTO_TRADING_ON, // Автоматическая торговля разрешена AUTO_TRADING_OFF, // Автоматическая торговля запрещена };
В базовом наборе содержится 24 уведомления. Большая часть из них связана с работой и состоянием открытых позиций или отложенных ордеров. А также присутствуют те, что будут озвучивать торговое окружение. И последние три — это общие. Уведомление о статусе включенной системы голосовых уведомлений, а также сообщения о наличие сигнала на покупку или продажу — это полезно при работе с ручными или полуавтоматическими системами торговых экспертов или же с различными сигналами от индикаторов, как простыми, так и в составе какой либо торговой стратегии.
Теперь создадим класс CSoundsLib, пропишем в нем методы, необходимые для работы.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CSoundsLib { private: LANGUAGE m_language; bool m_activity_status; public: CSoundsLib(void); ~CSoundsLib(void); //--- Установка языка уведомлений void Language(LANGUAGE lang); //--- Установка/получения статуса активности голосовых уведомлений void IsActive(bool flag); bool IsActive(void); //--- Проигрывание заданного уведомления bool Message(MESSAGE msg); };
В приватной секции две переменные m_language и m_activity_status, которые нужны для созданных ниже методов Language() и IsActive(). То есть для установки языка голосовых уведомлений и получения/установки активности самой системы. Реализация их представлена в листинге ниже:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSoundsLib::Language(LANGUAGE lang) { m_language=lang; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CSoundsLib::IsActive(void) { return(m_activity_status); }
Еще один метод — это Message(), который проигрывает само уведомление, установленное из перечисления MESSAGE. Реализация этого метода также проста для понимания:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CSoundsLib::Message(MESSAGE msg) { if(!m_activity_status) return(false); string name=(m_language==RUSSIAN ? EnumToString(msg)+"_RU" : EnumToString(msg)+"_EN"); if(PlaySound("\\Files\\SoundsLib\\"+name+".wav")) return(true); else { if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian") Print("Файл не найден"); else Print("File not found"); return(false); } }
Хотелось бы обратить внимание на несколько важных моментов, потому как понимание их будет напрямую влиять на правильность расширения данной системы собственными голосовыми уведомлениями. Первый момент, это верное место установки звуковых файлов, а именно — по умолчанию они находятся в папке MQL5/Files/SoundsLib. При этом папку SoundsLib необходимо создать. Второй момент, это названия и формат звуковых файлов, которые будут помещаться в созданную папку. Обратим внимание на эти строки кода — в них к перечислению типа MESSAGE в конце добавляется суффикс _RU или _EN. Поэтому имя файла, соответствующее, например, голосовому уведомлению о сигнале на покупку SIGNAL_BUY, будет иметь связь с двумя звуковыми файлами: SIGNAL_BUY _RU и SIGNAL_BUY_EN для русской и английской озвучки. Помимо этого следует помнить, что системная функция PlaySound() может проигрывать файл только в формате *.WAV, и поэтому полные имена файлов в нашем примере с расширениями в папке SoundsLib будут выглядеть так:
Рис.1 Полное имя и расширение звукового файла.
Поэтому установленным в перечислении MESSAGE 24 событиям будут соответствовать 48 звуковых файлов. По 2 на каждое событие на двух языках. Далее я покажу собственный способ создания голосовых уведомлений, но каждый волен использовать тот, который ему больше понравится. Для статьи я использовал бесплатный сервис перевода текстовых сообщений в звуковые файлы.
Рис.2 Сервис перевода текста в звуковой файл.
В данном инструменте предостаточно функционала для реализации требуемой задачи. Можно выбрать как язык голосового движка, так и типы с необходимым нам форматом. Однако, для английского голосового движка нет формата WAV, поэтому либо через любой онлайн конвертер, либо через любой другой софт для работы со звуковыми файлами можно легко перевести формат mp3 в wav. С помощью данного сервиса получим все необходимые файлы для нашей системы и положим их в папку по адресу MQL5\Files\SoundsLib с правильным форматом и названиями согласно перечислению MESSAGE и суффиксами языка. Вот какой список у меня получился:
Рис.3 Полный список звуковых файлов для голосовых уведомлений.
Для максимально ясного понимания ниже я приведу пошаговую инструкцию по созданию собственных голосовых уведомлений.
Шаг 1. Добавление в систему вашего голосового события.
Для этого перейдем в файл SoundsLib.mqh и найдем перечисление MESSAGE. В него следует добавить ваше событие с понятным названием. Пример названий приведены на рис.3 и выше в коде.
Шаг 2. Создание звукового файла будущего голосового уведомления.
Переходим в сервис(или же любой какой вам нравится) и там под себя настраиваем любые параметры (указаны на рис.2) и сохраняем файл в формате WAV в папку MQL5\Files\SoundsLib с названием "Имя вашего события в перечислении MESSAGE"+_RU(_EN) в зависимости от принадлежности к русскому или английскому языку. Если вы всё сделали верно, то новый звуковой файл будет связан с новым событием, которое вы добавили на шаге 1 и он готов к использованию.
Практическое применение в индикаторах
Теперь посмотрим как это работает на различных примерах. Создадим составной индикатор на основе двух сигналов индикаторов, описанных в таблице ниже:
Параметр | Описание |
---|---|
Используемый индикатор | ADXCloud |
Используемый индикатор | ColorZerolagRVI |
Таймфрейм | Любой |
Условия на покупку | Облако ADXCloud зеленого цвета, облако ColorZerolagRVI переходит из красной в зеленую зону. |
Условия на продажу | Облако ADXCloud красного цвета, облако ColorZerolagRVI переходит из зеленой в красную зону. |
Примеры входа по двум сигналам данных индикаторов представлены на рис.4, они достаточно просты, однако мы сделаем из них составной сигнальный индикатор, который на графике будет стрелочками показывать точки входа в рынок.
Рис.4 Условия входа по сигналам данных индикаторов.
//+------------------------------------------------------------------+ //| Example.mq5 | //| Alex2356 | //| https://www.mql5.com/ru/users/alex2356 | //+------------------------------------------------------------------+ #property copyright "Alex2356" #property link "https://www.mql5.com/ru/users/alex2356" #property version "1.00" #property indicator_chart_window //--- для расчета и отрисовки индикатора использовано два буфера #property indicator_buffers 2 //--- использовано графических построения #property indicator_plots 2 #property indicator_label1 "Buy Signal" #property indicator_type1 DRAW_ARROW //--- #property indicator_label2 "Sell Signal" #property indicator_type2 DRAW_ARROW //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ input group "ADX Cloud Parameters" input int ADXPeriod = 8; input double Alpha1 = 0.25; input double Alpha2 = 0.25; input group "RVI Color Parameters" input uint Smoothing = 15; //---- input double Weight1 = 0.05; input int RVI_period1 = 8; //---- input double Weight2 = 0.10; input int RVI_period2 = 21; //---- input double Weight3 = 0.16; input int RVI_period3 = 34; //---- input double Weight4 = 0.26; input int RVI_period4 = 55; //---- input double Weight5 = 0.43; input int RVI_period5 = 89; //--- double BuySignal[],SellSignal[],ADXCloud[],FastRVI[],SlowRVI[]; int ADX_Handle,RVI_Hadnle,min_rates_total; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- SetIndexBuffer(0,BuySignal,INDICATOR_DATA); SetIndexBuffer(1,SellSignal,INDICATOR_DATA); //--- PlotIndexSetInteger(0,PLOT_ARROW,233); PlotIndexSetInteger(1,PLOT_ARROW,234); //--- PlotIndexSetInteger(0,PLOT_LINE_COLOR,clrDodgerBlue); PlotIndexSetInteger(1,PLOT_LINE_COLOR,clrCrimson); //--- ArraySetAsSeries(SellSignal,true); ArraySetAsSeries(BuySignal,true); PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE); PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,EMPTY_VALUE); PlotIndexSetInteger(0,PLOT_ARROW_SHIFT,20); PlotIndexSetInteger(1,PLOT_ARROW_SHIFT,-20); //--- ADX_Handle=iCustom(Symbol(),PERIOD_CURRENT,"adxcloud",ADXPeriod,Alpha1,Alpha2); if(ADX_Handle==INVALID_HANDLE) { Print(" Не удалось получить хендл индикатора"); return(INIT_FAILED); } //--- RVI_Hadnle=iCustom(Symbol(),PERIOD_CURRENT,"colorzerolagrvi", Smoothing, Weight1,RVI_period1, Weight2,RVI_period2, Weight3,RVI_period3, Weight4,RVI_period4, Weight5,RVI_period5 ); if(RVI_Hadnle==INVALID_HANDLE) { Print(" Не удалось получить хендл индикатора"); return(INIT_FAILED); } //--- 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[]) { //--- c if(BarsCalculated(ADX_Handle)<rates_total || BarsCalculated(RVI_Hadnle)<rates_total || rates_total<min_rates_total) return(0); //--- int limit,to_copy,i; //--- ArraySetAsSeries(ADXCloud,true); ArraySetAsSeries(FastRVI,true); ArraySetAsSeries(SlowRVI,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); //--- if(prev_calculated>rates_total || prev_calculated<=0) limit=rates_total-2; else limit=rates_total-prev_calculated; to_copy=limit+2; //--- if(CopyBuffer(ADX_Handle,0,0,to_copy,ADXCloud)<=0) return(0); //--- if(CopyBuffer(RVI_Hadnle,0,0,to_copy,FastRVI)<=0) return(0); if(CopyBuffer(RVI_Hadnle,1,0,to_copy,SlowRVI)<=0) return(0); //--- for(i=limit-1; i>=0 && !IsStopped(); i--) { if(ADXCloud[i+1]>0 && FastRVI[i+1]>SlowRVI[i+1] && FastRVI[i+2]<SlowRVI[i+2]) { BuySignal[i]=low[i]; SellSignal[i]=EMPTY_VALUE; } else if(ADXCloud[i+1]<0 && FastRVI[i+1]<SlowRVI[i+1] && FastRVI[i+2]>SlowRVI[i+2]) { SellSignal[i]=high[i]; BuySignal[i]=EMPTY_VALUE; } else { BuySignal[i]=EMPTY_VALUE; SellSignal[i]=EMPTY_VALUE; } } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
В результате получится реализация показанная на рис.5 и теперь предстоит внедрить систему голосовых уведомлений.
Рис.5 Стрелочный индикатор на основе двух индикаторов.
Для начала подключим к индикатору файл SoundsLib.mqh:
#include <SoundsLib/SoundsLib.mqh>
Создадим экземпляр класса системы голосовых уведомлений:
CSoundsLib Notify;
В функции инициализации OnInit() установим язык голосовых уведомлений, я поставлю английский. Хотя, если вы используете английский язык уведомлений, то язык можно не устанавливать, так как он задан по умолчанию, но для полного понимая установим.
Notify.Language(ENGLISH);
Так как стрелочный индикатор показывает лишь точки входа в рынок или сигналы на покупку/продажу, то из перечисления MESSAGE мы будет использовать два голосовых уведомления это:
SIGNAL_BUY, // Сигнал на покупку SIGNAL_SELL, // Сигнал на продажу
Однако при внедрении в созданный индикатор голосовые уведомления необходимо, чтобы они приходили не по всем сигналам на истории, а лишь на текущем баре, поэтому цикл поиска сигналов модифицируем следующем образом:
//--- for(i=limit-1; i>=0 && !IsStopped(); i--) { if(ADXCloud[i+1]>0 && FastRVI[i+1]>SlowRVI[i+1] && FastRVI[i+2]<SlowRVI[i+2]) { BuySignal[i]=low[i]; SellSignal[i]=EMPTY_VALUE; if(i==0) Notify.Message(SIGNAL_BUY); } else if(ADXCloud[i+1]<0 && FastRVI[i+1]<SlowRVI[i+1] && FastRVI[i+2]>SlowRVI[i+2]) { SellSignal[i]=high[i]; BuySignal[i]=EMPTY_VALUE; if(i==0) Notify.Message(SIGNAL_SELL); } else { BuySignal[i]=EMPTY_VALUE; SellSignal[i]=EMPTY_VALUE; } }
Здесь мы проверяем, есть ли сигнал на нулевом баре, и при его наличии уведомляем об этом пользователя терминала.
Практическое применение в торговом эксперте
Для индикатора интеграция голосовых уведомлений ограничивается лишь двумя видами, хотя ничего не мешает добавить собственные, например, для осцилляторов уведомление о входе в зону перекупленности, пробой канала для лент Боллинджера и т.д. В эксперте можно использовать из составленного набора намного больше различных уведомлений. Поэтому создадим тестового торгового робота, который будет сообщать не только о том, что он нашел сигнал для входа в рынок, а также будет комментировать дальнейшие свои действия, например каким типом позиции он зашел в рынок. Для начала определимся со стратегией входа будущего эксперта.
Параметр | Описание |
---|---|
Используемый индикатор | ColorStDev |
Используемый индикатор | Три уровня Тироне |
Таймфрейм | Любой |
Условия на покупку | Гистограмма ColorStdDev красного цвета (сильный тренд), при этом текущая цена должна быть выше верхнего уровня Тироне. |
Условия на продажу | Гистограмма ColorStdDev красного цвета (сильный тренд), при этом текущая цена должна быть ниже нижнего уровня Тироне. |
Условия выхода | Тейк-профит/Стоп-лосс |
Визуально точки входа в рынок будут выглядеть как на рис.6 приведенном в качестве примера.
Рис.6 Примеры входа в рынок по данной стратегии.
Теперь реализуем эту стратегию для MetaTrader 5, при этом будем использовать голосовые уведомления при некоторых событиях.
//+------------------------------------------------------------------+ //| VoiceNotify.mq5 | //| Alex2356 | //| https://www.mql5.com/ru/users/alex2356 | //+------------------------------------------------------------------+ #property copyright "Alex2356" #property link "https://www.mql5.com/ru/users/alex2356" #property version "1.00" #include <SoundsLib/SoundsLib.mqh> #include <DoEasy25/Engine.mqh> //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ input uint InpStopLoss = 150; // Stop Loss, in pips input uint InpTakeProfit = 250; // Take Profit, in pips input double InpLot = 0.1; // Take Profit, in pips input ulong InpDeviation = 10; // Deviation input int InpMagic = 2356; // Magic number input LANGUAGE NotifyLanguage = ENGLISH; // Notification Language //--- Параметры индикатора ColorStDev input int StDevPeriod = 12; // Smoothing period StDev input ENUM_MA_METHOD MA_Method = MODE_EMA; // Histogram smoothing method input ENUM_APPLIED_PRICE applied_price = PRICE_CLOSE; // Applied price input int MaxTrendLevel = 90; // Maximum trend level input int MiddLeTrendLevel = 50; // Middle trend level input int FlatLevel = 20; // Flat level //--- Параметры индикатора Tirone Levels input int TironePeriod = 13; // Tirone Period //--- CEngine trade; CSoundsLib notify; int Handle1,Handle2; double stdev[],tirone_b[],tirone_s[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) notify.Message(AUTO_TRADING_OFF); //--- OnInitTrading(); //--- Получение хэндла индикатора ColorStDev Handle1=iCustom(Symbol(),PERIOD_CURRENT,"ArticleVoiceNotify\\colorstddev", StDevPeriod, MA_Method, applied_price, MaxTrendLevel, MiddLeTrendLevel, FlatLevel ); if(Handle1==INVALID_HANDLE) { Print("Failed to get colorstddev handle"); Print("Handle = ",Handle1," error = ",GetLastError()); return(INIT_FAILED); } //--- Получение хэндла индикатора Tirone Levels Handle2=iCustom(Symbol(),PERIOD_CURRENT,"ArticleVoiceNotify\\tirone_levels_x3",TironePeriod,0); if(Handle2==INVALID_HANDLE) { Print("Failed to get Tirone Levels handle"); Print("Handle = ",Handle2," error = ",GetLastError()); return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Если нет рыночных позиций if(ExistPositions(Symbol(),-1,InpMagic)<1) { //--- Получение данных для расчета if(!GetIndValue()) return; //--- Открытие ордера при начилии сигнала на покупку if(BuySignal()) { notify.Message(SIGNAL_BUY); if(trade.OpenBuy(InpLot,Symbol(),InpMagic,InpStopLoss,InpTakeProfit)) { Sleep(1400); notify.Message(BUY_ORDER_SET); } } //--- Открытие ордера при начилии сигнала на продажу if(SellSignal()) { notify.Message(SIGNAL_SELL); if(trade.OpenSell(InpLot,Symbol(),InpMagic,InpStopLoss,InpTakeProfit)) { Sleep(1400); notify.Message(SELL_ORDER_SET); } } } } //+------------------------------------------------------------------+ //| Условия на покупку | //+------------------------------------------------------------------+ bool BuySignal() { return(tirone_b[1]>iClose(Symbol(),PERIOD_CURRENT,1) && stdev[0]>FlatLevel)?true:false; } //+------------------------------------------------------------------+ //| Условия на продажу | //+------------------------------------------------------------------+ bool SellSignal() { return(tirone_b[1]<iClose(Symbol(),PERIOD_CURRENT,1) && stdev[0]>FlatLevel)?true:false; } //+------------------------------------------------------------------+ //| Получение текущих значений индикаторов | //+------------------------------------------------------------------+ bool GetIndValue() { return(CopyBuffer(Handle1,0,0,2,stdev)<=0 || CopyBuffer(Handle2,0,0,2,tirone_b)<=0 || CopyBuffer(Handle2,2,0,2,tirone_s)<=0 )?false:true; } //+----------------------------------------------------------------------------+ //| Возвращает количество открытых ордеров | //+----------------------------------------------------------------------------+ //| Параметры: | //| op - операция (-1 - любая позиция) | //| mn - MagicNumber (-1 - любой магик) | //+----------------------------------------------------------------------------+ int ExistPositions(string sy,int op=-1,int mn=-1) { int pos=0; uint total=PositionsTotal(); //--- for(uint i=0; i<total; i++) { if(SelectByIndex(i)) if(PositionGetString(POSITION_SYMBOL)==sy) if(op<0 || PositionGetInteger(POSITION_TYPE)==op) if(mn<0 || PositionGetInteger(POSITION_MAGIC)==mn) pos++; } return(pos); } //+------------------------------------------------------------------+ //| Select a position on the index | //+------------------------------------------------------------------+ bool SelectByIndex(const int index) { ENUM_ACCOUNT_MARGIN_MODE margin_mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE); //--- if(margin_mode==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) { ulong ticket=PositionGetTicket(index); if(ticket==0) return(false); } else { string name=PositionGetSymbol(index); if(name=="") return(false); } //--- return(true); } //+------------------------------------------------------------------+ //| Инициализация торгового окружения | //+------------------------------------------------------------------+ void OnInitTrading() { string array_used_symbols[]; //--- Заполнение массива используемых символов CreateUsedSymbolsArray(SYMBOLS_MODE_CURRENT,"",array_used_symbols); //--- Установка типа используемого списка символов в коллекции символов и заполнение списка таймсерий символов trade.SetUsedSymbols(array_used_symbols); //--- Передача в торговый класс всех имеющихся коллекций trade.TradingOnInit(); trade.TradingSetMagic(InpMagic); trade.TradingSetLogLevel(LOG_LEVEL_ERROR_MSG); //--- Установка синхронной передачи приказов для всех используемых символов trade.TradingSetAsyncMode(false); //--- Установка корректных типов истечения и заливки ордера всем торговым объектам trade.TradingSetCorrectTypeExpiration(); trade.TradingSetCorrectTypeFilling(); } //+------------------------------------------------------------------+
Рассмотрим данный листинг более подробно с точки зрения использования уведомлений. В инициализации торгового робота была использована проверка на разрешенность торговли автоматическим системам в терминале. Если же такая опция отключена, то соответствующим голосовым уведомлением об этом оповещается пользователь. Далее в функции OnTick() при нахождении заданного торгового сигнала, эксперт сообщает, что был найден сигнал на покупку или продажу соответственно ему. Происходит попытка установки позиции согласно сигналу и если всё проходит успешно, озвучивается еще одно голосовое уведомление о том, что позиция на рынке установлена.
Такого рода уведомления намного эффективнее в плане донесения информации до пользователя терминала, потому как не всегда можно своевременно увидеть нужное текстовое уведомление от эксперта во вкладке Эксперты или же на другом текстовом уведомлении. Что же касательно алертов, то тут из стандартной поставки информация доносится лишь разного рода звуками, значения которых в разных индикаторах и экспертах может быть различно. С голосовыми уведомлениями всё четко и понятно, потому как необходимое событие доносится речью.
Практическое применение в инструментарии быстрой торговли
В двух своих предыдущих статьях я разрабатывал инструмент для трейдеров, которые занимаются торговлей руками, то есть сами ищут сигналы на вход в рынок, сами устанавливают ордера, их сопровождают и закрывают. В качестве примера, в рамках данной статьи, хотелось бы дополнить то приложение системой голосовых уведомлений. Тем самым показать, что в такой инструмент добавить это не составит большого труда. За основу я возьму проект, который прикреплен в конце этой статьи. Для начала определимся со списком действий и событий, в которые будут внедрятся голосовые уведомления.
- Успешное открытие позиции на покупку или продажу.
- Успешная установка отложенных ордеров и их удаление.
- Проверка на возможность установки ордеров с помощью советника.
Перед тем как приступить к интеграции голосовых уведомлений, подключим библиотеку к этому проекту. Для этого откроем файл Program.mqh и там, в самом начале, сделаем это.
//+------------------------------------------------------------------+ //| Program.mqh | //| Alex2356 | //| https://www.mql5.com/ru/users/alex2356/ | //+------------------------------------------------------------------+ #include <EasyAndFastGUI\WndEvents.mqh> #include <DoEasy25\Engine.mqh> #include "Defines.mqh" #include <SoundsLib/SoundsLib.mqh>
Теперь перейдем в приватную секцию класса CFastTrading и создадим переменную-экземпляр класс CSoundsLib.
//---
CSoundsLib m_notify;
Необходимо также задать два новых параметра в самом инструментарии для возможности включать или отключать голосовые уведомления и выбирать их язык. Заходим в SimpleTrading.mq5 и в разделе Входные параметры эксперта пропишем новые:
//+------------------------------------------------------------------+ //| Входные параметры эксперта | //+------------------------------------------------------------------+ input int Inp_BaseFont = 10; // Base FontSize input color Caption = C'0,130,225'; // Caption Color input color Background = clrWhite; // Back color input LANG Language = ENGLISH; // Interface language input ulong MagicNumber = 1111; // Magic Number //--- input bool UseVoiceNotify = true; // Use Voice Notify input LANGUAGE NotifyLanguage = ENGLISH; // Notification Language
И чтобы их передать в экземпляр класса CSoundsLib m_notify в публичной секции базового класса CFastTrading, создадим два метода и реализуем их:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::SetNotifyLanguage(LANGUAGE lang) { m_notify.Language(lang); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::UseVoiceNotify(bool state) { m_notify.IsActive(state); } //+------------------------------------------------------------------+
Теперь применим их в функции OnInit() файла SimpleTrading.mq5 и передадим входные параметры в только что созданные методы.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- tick_counter=GetTickCount(); //--- Инициализация переменных класса program.FontName("Trebuchet MS"); program.FontSize(Inp_BaseFont); program.BackgroundColor(Background); program.CaptionColor(Caption); program.SetLanguage(Language); program.SetMagicNumber(MagicNumber); program.UseVoiceNotify(UseVoiceNotify); program.SetNotifyLanguage(NotifyLanguage); //--- Установим торговую панель if(!program.CreateGUI()) { Print(__FUNCTION__," > Не удалось создать графический интерфейс!"); return(INIT_FAILED); } program.OnInitEvent(); //--- return(INIT_SUCCEEDED); }
Тем самым мы настроили основные входные параметры системы голосовых уведомлений. Теперь найдем методы установки рыночных позиций на покупку и продажу. В базовом классе CFastTrading это методы SetBuyOrder() и SetSellOrder(). Зайдем в тело метода выставления позиции на покупку и в местах, где происходит проверка на успешное открытие позиции пропишем соответствующее голосовое уведомление BUY_ORDER_SET:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetBuyOrder(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buy_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_B)) { //--- double lot; if(m_switch_button[0].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[0].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[0].GetValue())); if(m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[0].GetValue()); double sl=double(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) { m_notify.Message(BUY_ORDER_SET); return(true); } } else if(!m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[0].GetValue()); int sl=int(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) { m_notify.Message(BUY_ORDER_SET); return(true); } } else if(m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[0].GetValue()); int sl=int(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) { m_notify.Message(BUY_ORDER_SET); return(true); } } else if(!m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[0].GetValue()); double sl=double(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) { m_notify.Message(BUY_ORDER_SET); return(true); } } } return(false); }
Аналогичное изменение проделаем и с методом открытия позиций на продажу, но только с вызовом уведомления голосом SELL_ORDER_SET:
bool CFastTrading::SetSellOrder(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_sell_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_S)) { //--- double lot; if(m_switch_button[3].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[1].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[1].GetValue())); //--- if(m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[1].GetValue()); double sl=double(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) { m_notify.Message(SELL_ORDER_SET); return(true); } } else if(!m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[1].GetValue()); int sl=int(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) { m_notify.Message(SELL_ORDER_SET); return(true); } } else if(!m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[1].GetValue()); double sl=double(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) { m_notify.Message(SELL_ORDER_SET); return(true); } } else if(m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[1].GetValue()); int sl=int(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) { m_notify.Message(SELL_ORDER_SET); return(true); } } } return(false); }
Теперь перейдем к отложенным ордерам. В инструментарии их четыре вида, и для установки каждого существует свой метод:
bool SetBuyStopOrder(int id,long lparam); bool SetSellStopOrder(int id,long lparam); bool SetBuyLimitOrder(int id,long lparam); bool SetSellLimitOrder(int id,long lparam);
Для каждого из них необходимо установить соответствующее голосовое уведомление. Сделаем это на примере BuyStop-ордера, потому как с остальными необходимо проделать похожие действия. Как видно из листинга ниже, используется уведомление BUYSTOP_ORDER_SET.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetBuyStopOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buystop_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_1)) { //--- double lot; if(m_p_switch_button[0].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[2].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[2].GetValue())); //--- double pr=double(m_pr_edit[0].GetValue()); //--- if(m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[2].GetValue()); double sl=double(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) { m_notify.Message(BUYSTOP_ORDER_SET); return(true); } } else if(!m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[2].GetValue()); int sl=int(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) { m_notify.Message(BUYSTOP_ORDER_SET); return(true); } } else if(m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[2].GetValue()); int sl=int(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) { m_notify.Message(BUYSTOP_ORDER_SET); return(true); } } else if(!m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[2].GetValue()); double sl=double(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) { m_notify.Message(BUYSTOP_ORDER_SET); return(true); } } } return(false); }
Если с установкой уведомлений голосом для выставления отложенных ордеров мы закончили, то теперь осталось внедрить уведомления для удаления ранее выставленных. Для этого найдем метод RemoveOrder() и в нем происходит определение какой из отложенных ордеров в таблице выбран и далее происходит возможность работы с ним — редактирование или удаление. Но в данном методе происходит удаление отложенного ордера по нажатию на кнопку удалить.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::RemoveOrder(long lparam) { //--- Проверка идентификатора элемента if(lparam==m_small_button[3].Id()) { //--- Получим индекс и символ if(m_table_orders.SelectedItem()==WRONG_VALUE) return(false); int row=m_table_orders.SelectedItem(); ulong ticket=(ulong)m_table_orders.GetValue(0,row); //--- if(OrderSelect(ticket)) { string position_symbol=OrderGetString(ORDER_SYMBOL); // символ ulong magic=OrderGetInteger(ORDER_MAGIC); // MagicNumber позиции ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); // тип позиции if(type==ORDER_TYPE_BUY_STOP) m_notify.Message(BUYSTOP_ORDER_DELETE); else if(type==ORDER_TYPE_SELL_STOP) m_notify.Message(SELLSTOP_ORDER_DELETE); else if(type==ORDER_TYPE_BUY_LIMIT) m_notify.Message(BUYLIMIT_ORDER_DELETE); else if(type==ORDER_TYPE_SELL_LIMIT) m_notify.Message(SELLLIMIT_ORDER_DELETE); //--- объявление запроса и результата MqlTradeRequest request; MqlTradeResult result; //--- обнуление значений запроса и результата ZeroMemory(request); ZeroMemory(result); //--- установка параметров операции request.action=TRADE_ACTION_REMOVE; // тип торговой операции request.order = ticket; // тикет ордера //--- отправка запроса bool res=true; for(int j=0; j<5; j++) { res=OrderSend(request,result); if(res && result.retcode==TRADE_RETCODE_DONE) return(true); else PrintFormat("OrderSend error %d",GetLastError()); // если отправить запрос не удалось, вывести код ошибки } } } //--- return(false); }
Рассмотрим изменение тела метода более подробно. После определения тикета выбранного ордера мы получаем необходимые данные для того, чтобы установить запрос на его удаление посредством заполнения структуры MqlTradeRequest и вызовом метода OrderSend(). Для того чтобы понимать какой тип у отложенного ордера, который был выбран в таблице, мы используем значение переменной type. Исходя из ее значения мы назначаем методом Message() соответствующий тип голосового уведомления.
И последнее, что было поставлено задачей добавить в инструментарий, это сообщать голосом, если в терминале MetaTrader 5 запрещен автоматический режим торговли. Потому как инструментарий, по сути, является торговым экспертом, и хоть ордера выставляются им только действиями пользователя, то есть вручную, но терминал и брокер распознает их как автоторговлю. Для добавления проверки на разрешение нужно в базовом классе приложения перейти в обработчик событий OnEvent() в секцию Завершение создания интерфейса ON_END_CREATE_GUI и внизу прописать проверку с назначением голосового уведомления:
//--- Завершение создания интерфейса if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI) { //--- SetButtonParam(m_switch_button[0],LOT); SetButtonParam(m_switch_button[1],POINTS); SetButtonParam(m_switch_button[2],POINTS); SetButtonParam(m_switch_button[3],LOT); SetButtonParam(m_switch_button[4],POINTS); SetButtonParam(m_switch_button[5],POINTS); //--- SetButtonParam(m_p_switch_button[0],LOT); SetButtonParam(m_p_switch_button[1],POINTS); SetButtonParam(m_p_switch_button[2],POINTS); SetButtonParam(m_p_switch_button[3],LOT); SetButtonParam(m_p_switch_button[4],POINTS); SetButtonParam(m_p_switch_button[5],POINTS); SetButtonParam(m_p_switch_button[6],LOT); SetButtonParam(m_p_switch_button[7],POINTS); SetButtonParam(m_p_switch_button[8],POINTS); SetButtonParam(m_p_switch_button[9],LOT); SetButtonParam(m_p_switch_button[10],POINTS); SetButtonParam(m_p_switch_button[11],POINTS); //--- if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) m_notify.Message(AUTO_TRADING_OFF); }
В заключении, на примере инструментария быстрой торговли, в записанном ниже видеоролике продемонстрирована работа голосовых уведомлений при работе с рыночными позициями и отложенными ордерами.
Заключение
В конце статьи приложен архив со всеми перечисленными файлами, отсортированными по папкам. Поэтому для корректной работы достаточно положить папку MQL5 в корень терминала. Для того чтобы найти корень терминала, в котором находится папка MQL5, нужно в MetaTrader 5 нажать комбинацию клавиш Ctrl+Shift+D или воспользоваться контекстным меню, как показано на рис.7 ниже.
Рис.7 Поиск папки MQL5 в корне терминала MetaTrader 5
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования