Озвучиваем торговые события в MetaTrader 5

Озвучиваем торговые события в MetaTrader 5
В этой статье рассмотрим такие вопросы, как включение в файл эксперта звуковых файлов и соответственно озвучивание торговых событий. Включение в файл означает, что звуковые файлы будут внутри эксперта, и если передать скомпилированную версию эксперта (*.ex5) другому пользователю, то не нужно будет передавать ему папку со звуковыми файлами и объяснять при этом, в какую директорию их разместить.

Для теста возьмём эксперта из предыдущей статьи Сохраняем результаты оптимизации торгового эксперта по указанным критериям. Я убрал из него всё, что не относится к текущей теме, чтобы было проще.

Чтобы озвучить торговое событие средствами MQL5 можно воспользоваться функциями Alert() и PlaySound().

Если использовать функцию Alert(), то будет воспроизводиться всегда один и тот же звук и при этом будет открываться окно с сообщением. Как это выглядит можно посмотреть в статье Введение в MQL5. Вывод информации в печать в разных режимах.


Звук для алерта можно установить в настройках терминала: Главное меню/Сервис/Настройки или одновременное нажатие клавиш Ctrl+O. Далее в разделе События нужно установить флажок Разрешить звуковые оповещения о событиях и в выпадающем списке алертов выбрать подходящий звук (см. рисунок ниже).


Вкладка События в настройках терминала

Но есть возможность для каждого события, которое воспроизводит пользовательская программа, установить уникальный звук и для этого понадобится функция PlaySound(). Прежде, чем встраивать звуковые оповещения в торгового эксперта, сделаем эксперта для тестов. Сделаем так, чтобы при загрузке эксперта на график открывалась звуковая панель. Звуковую панель соберём из графических объектов типа кнопка (OBJ_BUTTON). Каждой кнопке будет присвоен уникальный звук. Нажимая на ту или иную кнопку будет воспроизводиться звук этой кнопки.

Я скачал в интернете 25 разных звуковых файлов в формате *.wav (скачать их можно в конце статьи). Их нужно расположить в директории Metatrader 5\MQL5\Sounds. Теперь с помощью Мастера MQL5 создадим нового эксперта. В самом начале обозначим размер массива по количеству кнопок на звуковой панели (всего их будет 26).

//---
#define ARRAY_SIZE 26 // Размер массива
//---

Далее нужно указать директории и имена файлов, которые должны стать ресурсами эксперта. Это можно сделать с помощью директивы #resource. После директивы в двойных кавычках нужно указывать путь к файлу. Ниже показано, как нужно указать пути в нашем случае:

//---
// ЗВУКОВЫЕ ФАЙЛЫ
#resource "\\Sounds\\alert.wav"
#resource "\\Sounds\\AHOOGA.wav"
#resource "\\Sounds\\APPLAUSE.wav"
#resource "\\Sounds\\BONK.wav"
#resource "\\Sounds\\CARBRAKE.wav"
#resource "\\Sounds\\CASHREG.wav"
#resource "\\Sounds\\CLAP.wav"
#resource "\\Sounds\\CORKPOP.wav"
#resource "\\Sounds\\DOG.wav"
#resource "\\Sounds\\DRIVEBY.wav"
#resource "\\Sounds\\DRUMROLL.wav"
#resource "\\Sounds\\EXPLODE.wav"
#resource "\\Sounds\\FINALBEL.wav"
#resource "\\Sounds\\FROG.wav"
#resource "\\Sounds\\GLASS.wav"
#resource "\\Sounds\\GUNSHOT.wav"
#resource "\\Sounds\\LASER.wav"
#resource "\\Sounds\\LATNWHIS.wav"
#resource "\\Sounds\\PIG.wav"
#resource "\\Sounds\\RICOCHET.wav"
#resource "\\Sounds\\RINGIN.wav"
#resource "\\Sounds\\SIREN.wav"
#resource "\\Sounds\\TRAIN.wav"
#resource "\\Sounds\\UH_OH.wav"
#resource "\\Sounds\\VERYGOOD.wav"
#resource "\\Sounds\\WHOOSH.wav"
//---

Теперь нужно создать три строковых массива, в которых будут содержать путь к файлам ресурсов, имена графических объектов и отображаемый текст на графических объектах. Обратите внимание на два двоеточия в пути к файлам. Ознакомьтесь с дополнительной информацией о ресурсах для программ на MQL5 в Справочном руководстве.

//---
// Путь к звуковым файлам
string path_sound_object[ARRAY_SIZE]=
  {
   "::Sounds\\alert.wav",
   "::Sounds\\AHOOGA.wav",
   "::Sounds\\APPLAUSE.wav",
   "::Sounds\\BONK.wav",
   "::Sounds\\CARBRAKE.wav",
   "::Sounds\\CASHREG.wav",
   "::Sounds\\CLAP.wav",
   "::Sounds\\CORKPOP.wav",
   "::Sounds\\DOG.wav",
   "::Sounds\\DRIVEBY.wav",
   "::Sounds\\DRUMROLL.wav",
   "::Sounds\\EXPLODE.wav",
   "::Sounds\\FINALBEL.wav",
   "::Sounds\\FROG.wav",
   "::Sounds\\GLASS.wav",
   "::Sounds\\GUNSHOT.wav",
   "::Sounds\\LASER.wav",
   "::Sounds\\LATNWHIS.wav",
   "::Sounds\\PIG.wav",
   "::Sounds\\RICOCHET.wav",
   "::Sounds\\RINGIN.wav",
   "::Sounds\\SIREN.wav",
   "::Sounds\\TRAIN.wav",
   "::Sounds\\UH_OH.wav",
   "::Sounds\\VERYGOOD.wav",
   "::Sounds\\WHOOSH.wav"
  };
//---
// Имена графических объектов
string name_sound_object[ARRAY_SIZE]=
  {
   "sound_button01","sound_button02",
   "sound_button03","sound_button04",
   "sound_button05","sound_button06",
   "sound_button07","sound_button08",
   "sound_button09","sound_button10",
   "sound_button11","sound_button12",
   "sound_button13","sound_button14",
   "sound_button15","sound_button16",
   "sound_button17","sound_button18",
   "sound_button19","sound_button20",
   "sound_button21","sound_button22",
   "sound_button23","sound_button24",
   "sound_button25","sound_button26"
  };
//---
// Отображаемый текст на графических объектах
string text_sound_object[ARRAY_SIZE]=
  {
   "ALERT","AHOOGA","APPLAUSE","BONK","CARBRAKE","CASHREG",
   "CLAP","CORKPOP","DOG","DRIVEBY","DRUMROLL","EXPLODE","FINALBEL","FROG",
   "GLASS","GUNSHOT","LASER","LATNWHIS","PIG","RICOCHET","RINGIN",
   "SIREN","TRAIN","UH_OH","VERYGOOD","WHOOSH"
  };
//---

Создадим функцию CreateButton(), которая будет создавать графический объект "Кнопка" на графике с указанными свойствами:

//+------------------------------------------------------------------+
//| СОЗДАНИЕ ОБЪЕКТА BUTTON                                          |
//+------------------------------------------------------------------+
void CreateButton(long              chart_id,         // id графика
                  int               sub_window,       // номер окна
                  string            name,             // имя объекта
                  string            text,             // отображаемое имя
                  ENUM_ANCHOR_POINT anchor,           // точка привязки
                  ENUM_BASE_CORNER  corner,           // угол графика
                  string            font_name,        // шрифт
                  int               font_size,        // размер шрифта
                  color             font_color,       // цвет шрифта
                  color             background_color, // цвет фона
                  color             border_color,     // цвет рамки
                  int               x_size,           // ширина
                  int               y_size,           // высота
                  int               x_distance,       // координата по шкале X
                  int               y_distance,       // координата по шкале Y
                  long              z_order)          // приоритет
  {
   if(ObjectCreate(chart_id,name,OBJ_BUTTON,sub_window,0,0)) // создание объекта
     {
      ObjectSetString(chart_id,name,OBJPROP_TEXT,text);                  // установка имени
      ObjectSetString(chart_id,name,OBJPROP_FONT,font_name);             // установка шрифта
      ObjectSetInteger(chart_id,name,OBJPROP_COLOR,font_color);          // установка цвета шрифта
      ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,background_color);  // установка цвета фона
      ObjectSetInteger(chart_id,name,OBJPROP_BORDER_COLOR,border_color); // установка цвета фона
      ObjectSetInteger(chart_id,name,OBJPROP_ANCHOR,anchor);             // установка точки привязки
      ObjectSetInteger(chart_id,name,OBJPROP_CORNER,corner);             // установка угла привязки
      ObjectSetInteger(chart_id,name,OBJPROP_FONTSIZE,font_size);        // установка размера шрифта
      ObjectSetInteger(chart_id,name,OBJPROP_XSIZE,x_size);              // установка ширины X
      ObjectSetInteger(chart_id,name,OBJPROP_YSIZE,y_size);              // установка высоты Y
      ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x_distance);      // установка координаты X
      ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y_distance);      // установка координаты Y
      ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false);          // нельзя выделить объект, если FALSE
      ObjectSetInteger(chart_id,name,OBJPROP_STATE,false);               // состояние кнопки (нажата/отжата)
      ObjectSetInteger(chart_id,name,OBJPROP_ZORDER,z_order);            // Приоритет выше/ниже
      ObjectSetString(chart_id,name,OBJPROP_TOOLTIP,"\n");               // нет всплывающей подсказки, если "\n"
     }
  }
//---

Чтобы было повеселей, цвет для каждой кнопки будет выбираться случайным образом из указанного списка. Для этого напишем функцию GetRandomColor(). Ниже можно ознакомиться с кодом этой функции:

//+------------------------------------------------------------------+
//| ВОЗВРАЩАЕТ СЛУЧАЙНЫЙ ЦВЕТ                                        |
//+------------------------------------------------------------------+
color GetRandomColor()
  {
// Выберем случайный цвет от 0 до 25
   switch(rand()%26)
     {
      case 0  : return(clrOrange);           break;
      case 1  : return(clrGold);             break;
      case 2  : return(clrChocolate);        break;
      case 3  : return(clrChartreuse);       break;
      case 4  : return(clrLime);             break;
      case 5  : return(clrSpringGreen);      break;
      case 6  : return(clrMediumBlue);       break;
      case 7  : return(clrDeepSkyBlue);      break;
      case 8  : return(clrBlue);             break;
      case 9  : return(clrSeaGreen);         break;
      case 10 : return(clrRed);              break;
      case 11 : return(clrSlateGray);        break;
      case 12 : return(clrPeru);             break;
      case 13 : return(clrBlueViolet);       break;
      case 14 : return(clrIndianRed);        break;
      case 15 : return(clrMediumOrchid);     break;
      case 16 : return(clrCrimson);          break;
      case 17 : return(clrMediumAquamarine); break;
      case 18 : return(clrDarkGray);         break;
      case 19 : return(clrSandyBrown);       break;
      case 20 : return(clrMediumSlateBlue);  break;
      case 21 : return(clrTan);              break;
      case 22 : return(clrDarkSalmon);       break;
      case 23 : return(clrBurlyWood);        break;
      case 24 : return(clrHotPink);          break;
      case 25 : return(clrLightSteelBlue);   break;
      //---
      default : return(clrGold);
     }
//---
   return(clrGold);
  }
//---

Теперь, напишем функцию SetSoundPanel(), которая будет устанавливать звуковую панель на график:

//+------------------------------------------------------------------+
//| УСТАНОВИТЬ ЗВУКОВУЮ ПАНЕЛЬ                                       |
//+------------------------------------------------------------------+
void SetSoundPanel()
  {
   int count=0; // Счётчик столбцов
   int x_dist=10; // Отступ от левого края графика
   int y_dist=15; // Отступ от верхней части графика
   int x_size=100; // Ширина кнопок
   int y_size=20; // Высота кнопок
   color button_color=clrNONE; // Цвет кнопок
//---
// Установим объекты
   for(int i=0; i<ARRAY_SIZE; i++)
     {
      count++; // Увеличим счётчик столбцов
      //---
      button_color=GetRandomColor(); // Получим цвет для кнопки
      //---
      // Установим кнопку
      CreateButton(0,0,name_sound_object[i],text_sound_object[i],
                   ANCHOR_LEFT_UPPER,CORNER_LEFT_UPPER,"Arial",8,
                   clrWhite,button_color,button_color,x_size,y_size,x_dist,y_dist,1);
      //---
      // Если уже установлено две кнопки в этом ряду
      if(count==2)
        {
         x_dist=10; // Координату X вернём в исходное положение
         y_dist+=20; // Координату Y установим для следующего ряда
         count=0; // Обнулим счётчик
        }
      else
        {
         x_dist+=x_size; // Установим координату X для следующей кнопки 
        }
     }
//---
   ChartRedraw(0); // Обновим график
  }
//---

Для удаления панели с графика будем использовать функции представленные ниже:

//+------------------------------------------------------------------+
//| УДАЛЯЕТ ИНФОРМАЦИОННУЮ ПАНЕЛЬ                                    |
//+------------------------------------------------------------------+
void DeleteSoundPanel()
  {
//--- Удалить свойства позиции и их значения
   for(int i=0; i<ARRAY_SIZE; i++)
      DeleteObjectByName(name_sound_object[i]); // Удалить свойство
//---
   ChartRedraw(); // Перерисовать график
  }
//+------------------------------------------------------------------+
//| УДАЛЯЕТ ОБЪЕКТЫ ПО ИМЕНИ                                         |
//+------------------------------------------------------------------+
void DeleteObjectByName(string Name)
  {
   int nm_obj=0;   // Возвращаемый номер подокна, в котором находится объект
   bool res=false; // Результат после попытки удалить объект
//---
// Найдём объект по имени
   nm_obj=ObjectFind(ChartID(),Name);
//---
   if(nm_obj>=0) // Если найден,..
     {
      res=ObjectDelete(ChartID(),Name); // ...удалим его
      //---
      // Если была ошибка при удалении,..
      if(!res) // ...сообщим об этом
        {
         Print("Ошибка ("+IntegerToString(GetLastError())+") при удалении объекта!");
        }
     }
  }
//---

Итак, при загрузке эксперта в функции OnInit() панель будет установлена на график, а при удалении эксперта в функции OnDeinit() панель будет удалена (см. код ниже).

//+------------------------------------------------------------------+
//| ИНИЦИАЛИЗАЦИЯ                                                    |
//+------------------------------------------------------------------+
void OnInit()
  {
   SetSoundPanel(); // Установим звуковую панель
  }
//+------------------------------------------------------------------+
//| ДЕИНИЦИАЛИЗАЦИЯ                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   DeleteSoundPanel(); // Удалим звуковую панель
  }
//---

Осталось настроить взаимодействие с панелью, чтобы нажимая на кнопки воспроизводились соответствующие им звуки. Чтобы не умереть со скуки сделаем ещё так, чтобы при нажатии на одну из кнопок звуковой панели, цвета кнопок изменялись. Для этого нам понадобится функция ChangeColorsOnSoundPanel(), код которой представлен ниже:

//+------------------------------------------------------------------+
//| ИЗМЕНИТЬ ЦВЕТА НА ЗВУКОВОЙ ПАНЕЛИ                                |
//+------------------------------------------------------------------+
void ChangeColorsOnSoundPanel()
  {
   color clr=clrNONE; // Цвет кнопки
//---
// В цикле пройдём по всем кнопкам и изменим их цвет
   for(int i=0; i<ARRAY_SIZE; i++)
     {
      clr=GetRandomColor(); // Получим новый цвет
      //---
      ObjectSetInteger(0,name_sound_object[i],OBJPROP_BGCOLOR,clr); // Установим цвет рамки
      ObjectSetInteger(0,name_sound_object[i],OBJPROP_BORDER_COLOR,clr); // Установим цвет фона
      ObjectSetInteger(0,name_sound_object[i],OBJPROP_STATE,false); // Отжатое состояние кнопки
      ChartRedraw(0); // Обновим график
      Sleep(20); // Подождём (задержка) 20 миллисекунд
     }
  }
//---

И наконец в функции OnChartEvent() нужно разместить вот такой код:

//+------------------------------------------------------------------+
//| СОБЫТИЯ                                                          |
//+------------------------------------------------------------------+
void OnChartEvent(const int     id,     // идентификатор события  
                  const long&   lparam, // параметр события типа long
                  const double& dparam, // параметр события типа double
                  const string& sparam) // параметр события типа string
  {
// Если было событие "Нажатие левой кнопкой мыши на объекте"
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      // Если в имени объекта содержится "sound_button"
      if(StringFind(sparam,"sound_button",0)>=0)
        {
         // Воспроизведём звук по имени объекта
         if(!PlaySound(GetSoundPath(sparam)))
            Print("Error: ",GetLastError()); // 5019 - ERR_FILE_NOT_EXIST - Файл не существует
         //---
         // Изменим цвета всех кнопок
         ChangeColorsOnSoundPanel();
        }
     }
  }
//---

В коде выше в выделенной строке видно, что в функцию PlaySound() с помощью пользовательской функции GetSoundPath() передаётся путь, откуда будет взят звуковой файл. Ниже можно ознакомиться с кодом функции GetSoundPath():

//+------------------------------------------------------------------+
//| ВОЗВРАЩАЕТ ПУТЬ К ЗВУКОВОМУ ФАЙЛУ ПО ИМЕНИ ОБЪЕКТА               |
//+------------------------------------------------------------------+
string GetSoundPath(string object_name)
  {
// Пройдёмся в цикле по всем объектам звуковой панели
   for(int i=0; i<ARRAY_SIZE; i++)
     {
      // Если имя объекта, на который нажали на графике
      // совпадает с одним из тех, который есть в панели
      if(object_name==name_sound_object[i])
         return(path_sound_object[i]); // Вернём путь к файлу
     }
//---
   return("");
  }
//---

Теперь всё готово. Установив эксперта на график будет установлена звуковая панель, как показано на рисунке ниже:

Звуковая панель на графике

Эту программу можно скачать в конце статьи.

Определимся теперь со звуками, которые хотелось бы слышать в торговом эксперте. Создадим файл RESOURCES.mqh и подключим его к основному файлу (выделенная строка в коде ниже).

//+------------------------------------------------------------------+
//| БИБЛИОТЕКИ С ФУНКЦИЯМИ                                           |
//+------------------------------------------------------------------+
#include "Include/ENUMS.mqh"
#include "Include/RESOURCES.mqh"
#include "Include/ARRAYS.mqh"
#include "Include/CHECKS.mqh"
#include "Include/ERRORS.mqh"
#include "Include/TRADE_SIGNALS.mqh"
#include "Include/TRADE_FUNCTIONS.mqh"
#include "Include/GET_STRING.mqh"
#include "Include/ADD_FUNCTIONS.mqh"
//---

Ниже показано содержимое файла RESOURCES.mqh для торгового эксперта. По комментариям можно определить, какие звуки и для чего предназначены.

//+------------------------------------------------------------------+
//|                                                    RESOURCES.mqh |
//+------------------------------------------------------------------+
// ЗВУКОВЫЕ ФАЙЛЫ
#resource "\\Sounds\\AHOOGA.WAV"   // Ошибка
#resource "\\Sounds\\CASHREG.WAV"  // Открытие позиции/увеличение объёма позиции/срабатывание отложенного ордера
#resource "\\Sounds\\WHOOSH.WAV"   // Установка/модификация отложенного ордера/Stop Loss/Take Profit
#resource "\\Sounds\\VERYGOOD.WAV" // Закрытие позиции с прибылью
#resource "\\Sounds\\DRIVEBY.WAV"  // Закрытие позиции с убытком
//---
// ПУТЬ К ЗВУКОВЫМ ФАЙЛАМ
string SoundError       = "::Sounds\\AHOOGA.WAV";
string SoundOpenPos     = "::Sounds\\CASHREG.WAV";
string SoundSetModOrder = "::Sounds\\WHOOSH.WAV";
string SoundCloseProfit = "::Sounds\\VERYGOOD.WAV";
string SoundCloseLoss   = "::Sounds\\DRIVEBY.WAV";
//---

Хочу ещё написать, что кроме звуковых файлов в качестве ресурсов можно хранить в эксперте картинки в формате *.bmp, которые можно использовать для создания интерфейса, текстовые файлы и даже индикаторы. Эксперты для MetaTrader 5 теперь вполне могут быть полноценными приложениями. Это очень удобно, когда можно передать всего лишь один файл вместо нескольких.

Двигаемся дальше. Во внешних параметрах нужно добавить параметр UseSound, чтобы была возможность отключить воспроизведение звуков:

//+------------------------------------------------------------------+
//| ВНЕШНИЕ ПАРАМЕТРЫ ЭКСПЕРТА                                       |
//+------------------------------------------------------------------+
input int     AmountBars      = 2;    // Amount Bear/Bull Bars For Buy/Sell
sinput double Lot             = 0.1;  // Lot
input double  TakeProfit      = 100;  // Take Profit (p)
input double  StopLoss        = 50;   // Stop Loss (p)
input double  TrailingSL      = 10;   // Trailing Stop (p)
input bool    ReversePosition = true; // On/Off Reverse
//---
sinput string dlm=""; // * * * * * * * * * * * * * * * * * * * * * * * * * *
//---
sinput bool   UseSound        = true; // On/Off Sound
//---

Создадим перечисление ENUM_SOUNDS для звуков. Эти идентификаторы понадобятся для пользовательской функции PlaySoundByID().

Код перечисления ENUM_SOUNDS:

//+------------------------------------------------------------------+
//| ПЕРЕЧИСЛЕНИЕ ЗВУКОВ                                              |
//+------------------------------------------------------------------+
enum ENUM_SOUNDS
  {
   SOUND_ERROR        = 0, // Ошибка
   SOUND_OPEN_POS     = 1, // Открытие позиции/увеличение объёма позиции/срабатывание отложенного ордера
   SOUND_SETMOD_ORDER = 2, // Установка защитного стопа/уровня фиксации прибыли/отложенного ордера
   SOUND_CLOSE_PROFIT = 3, // Закрытие позиции с прибылью
   SOUND_CLOSE_LOSS   = 4  // Закрытие позиции с убытком
  };
//---

Код функции PlaySoundByID():

//+------------------------------------------------------------------+
//| ВОСПРОИЗВЕДЕНИЕ ЗВУКА                                            |
//+------------------------------------------------------------------+
void PlaySoundByID(ENUM_SOUNDS id)
  {
// Если это реал-тайм и звуки включены
   if(NotTest() && UseSound)
     {
      // Воспроизведём звук по переданному идентификатору
      switch(id)
        {
         case SOUND_ERROR        : PlaySound(SoundError);       break;
         case SOUND_OPEN_POS     : PlaySound(SoundOpenPos);     break;
         case SOUND_SETMOD_ORDER : PlaySound(SoundSetModOrder); break;
         case SOUND_CLOSE_PROFIT : PlaySound(SoundCloseProfit); break;
         case SOUND_CLOSE_LOSS   : PlaySound(SoundCloseLoss);   break;
        }
     }
  }
//---

Во время торговых операций, которые совершает эксперт, звуковые эффекты можно воспроизводить внутри функций. Например, посмотрите в коде ниже, как это реализовано в функции OpenPosition(). Я выделил строки, в которых используется функция для воспроизведения звука по его идентификатору.

//+------------------------------------------------------------------+
//| ОТКРЫТИЕ ПОЗИЦИИ                                                 |
//+------------------------------------------------------------------+
void OpenPosition(double lot,
                  ENUM_ORDER_TYPE type_ord,
                  double oprice,
                  double sl,
                  double tp,
                  string comment)
  {
   trd.SetExpertMagicNumber(0); // Установим номер мэджика в торговую структуру
   trd.SetDeviationInPoints(DgtMlt(100)); // Установим размер проскальзывания в пунктах
//---
// В режиме Instant Execution позицию можно открыть
// сразу с установленными уровнями Stop Loss и Take Profit
   if(smb.trade_exec==SYMBOL_TRADE_EXECUTION_INSTANT)
     {
      // Если позиция не открылась, вывести сообщение об этом
      if(!trd.PositionOpen(_Symbol,type_ord,lot,oprice,sl,tp,comment))
        {
         PlaySoundByID(SOUND_ERROR);
         Print("Ошибка при открытии позиции: ",GetLastError()," - ",ErrorDesc(GetLastError()));
        }
      else { PlaySoundByID(SOUND_OPEN_POS); }
     }
//---
// В режиме Market Execution сначала нужно открыть позицию и
// только после этого можно установить уровни Stop Loss и Take Profit
   if(smb.trade_exec==SYMBOL_TRADE_EXECUTION_MARKET)
     {
      // Если позиции нет, то сначала откроем позицию
      // а затем установим Stop Loss и Take Profit
      if(!pos.exist)
        {
         // Если позиция не открылась, вывести сообщение об этом
         if(!trd.PositionOpen(_Symbol,type_ord,lot,oprice,0,0,comment))
           {
            PlaySoundByID(SOUND_ERROR);
            Print("Ошибка при открытии позиции: ",GetLastError()," - ",ErrorDesc(GetLastError()));
           }
         else // Если позиция открылась, то сначала выберем её и...
           {
            if((pos.exist=PositionSelect(_Symbol))) // ...если позиция есть, то...
              {
               // ...установим Stop Loss и Take Profit
               if(!trd.PositionModify(_Symbol,sl,tp))
                 {
                  PlaySoundByID(SOUND_ERROR);
                  Print("Ошибка при модификации позиции: ",GetLastError()," - ",ErrorDesc(GetLastError()));
                 }
               else { PlaySoundByID(SOUND_SETMOD_ORDER); }
              }
           }
        }
      else
        {// Если позиция есть, то увеличим её объём и 
         // оставим Stop Loss и Take Profit на их прежнем уровне
         // Если позиция не открылась, вывести сообщение об этом
         if(!trd.PositionOpen(_Symbol,type_ord,lot,oprice,sl,tp,comment))
           {
            PlaySoundByID(SOUND_ERROR);
            Print("Ошибка при открытии позиции: ",GetLastError()," - ",ErrorDesc(GetLastError()));
           }
         else { PlaySoundByID(SOUND_OPEN_POS); }
        }
     }
  }
//---

В случае же, когда позиция закрывается по Stop Loss, Take Profit, вручную или любым другим способом, то это событие будет отслеживаться в функции OnTrade(), в которой будет размещена пользовательская функция SoundNotification(). Именно в этой пользовательской функции и будут производиться проверки. Проверки заключаются в том, что, если по текущему символу в истории сделок появилась новая сделка с идентификатором DEAL_ENTRY_OUT или DEAL_ENTRY_INOUT, которые могут быть в случае закрытия позиции, сокращения её объёма (частичное закрытие) или переворот позиции, то далее программа проверит была ли закрыта эта сделка с прибылью или убытком и воспроизведёт соответствующий звук.

Код функции SoundNotification():

//+------------------------------------------------------------------+
//| ЗВУКОВОЕ ОПОВЕЩЕНИЕ                                              |
//+------------------------------------------------------------------+
void SoundNotification()
  {
// Если реал-тайм и включен звук
   if(NotTest() && UseSound)
     {
      ulong ticket=0; // Тикет сделки
      int total=0; // Общее кол-во сделок
      static ulong last_ticket=0; // Последний тикет до этой проверки
      //---
      // Получим всю историю. Если история не получена, выйдем
      if(!HistorySelect(0,TimeCurrent()+1000)) { return; }
      //---
      // Получим количество сделок в полученном списке
      total=HistoryDealsTotal();
      //---
      // Пройдем в цикле по всем сделкам в полученном списке
      // от последней сделки в списке к первой
      for(int i=total-1; i>=0; i--)
        {
         // Если тикет сделки по её позиции в списке получен, то...
         if((ticket=HistoryDealGetTicket(i))>0)
           {
            GetHistoryDealProperties(ticket,HD_SYMBOL); // получим символ сделки
            //---
            // Если символ сделки и текущий символ равны
            if(hdl.symbol==_Symbol)
              {
               GetHistoryDealProperties(ticket,HD_ENTRY); // получим направление сделки
               //---
               // Если это закрытие позиции, сокращение объёма или переворот
               if(hdl.entry==DEAL_ENTRY_OUT || hdl.entry==DEAL_ENTRY_INOUT)
                 {
                  // Если тикет текущей сделки из списка (последняя сделка символа) равен предыдущему или...
                  // это инициализация тикета последней сделки
                  if(ticket==last_ticket || last_ticket==0)
                    { last_ticket=ticket; return; } // ...выйдем
                  //---
                  GetHistoryDealProperties(ticket,HD_PROFIT); // получим результат сделки
                  //---
                  if(hdl.profit>0) // Если прибыль
                    {
                     PlaySoundByID(SOUND_CLOSE_PROFIT); // Звук прибыли
                     //---
                     last_ticket=ticket; // Сохраним номер тикета
                     return;
                    }
                  //---
                  if(hdl.profit<0) // Если убыток
                    {
                     PlaySoundByID(SOUND_CLOSE_LOSS); // Звук убытка
                     //---
                     last_ticket=ticket; // Сохраним номер тикета
                     return;
                    }
                 }
              }
           }
        }
     }
  }
//---

Функцию SoundNotification() нужно разместить в функциях OnInit() и OnTrade():

//+------------------------------------------------------------------+
//| ИНИЦИАЛИЗАЦИЯ                                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
   CheckNewBar(); // Инициализируем новый бар
   SoundNotification(); // Инициализация тикетов последних сделок символа
//---
// Всё ок
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| ТОРГОВЫЕ СОБЫТИЯ                                                 |
//+------------------------------------------------------------------+
void OnTrade()
  {
   SoundNotification(); // Звуковое оповещение
  }
//---

Также воспроизведение звука было добавлено в функцию TrailingStopLoss() для модификации защитного уровня.

На этом всё. Все файлы для тестов можно скачать ниже. Посмотрите также одно интересное решение в базе кода на сайте mql5.com (автор Integer): CMIDI. С помощью этого класса можно использовать MIDI.

MIDI (англ. Musical Instrument Digital Interface — цифровой интерфейс музыкальных инструментов) — стандарт цифровой звукозаписи на формат обмена данными между электронными музыкальными инструментами.

Успехов!




Скачать звуковые файлы Sounds.zip
Скачать эксперт для тестирования всех звуковых файлов !expTEST.mq5
Скачать эксперт SoundedTradeEvents.zip


Комментариев нет :

Отправить комментарий