English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Рецепты MQL5 - Озвучиваем торговые события в MetaTrader 5

Рецепты MQL5 - Озвучиваем торговые события в MetaTrader 5

MetaTrader 5Примеры | 4 октября 2013, 12:45
5 894 2
Anatoli Kazharski
Anatoli Kazharski

Введение

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

 

Процесс разработки

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

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

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

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

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

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

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

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

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

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

//--- Звуковые файлы
#resource "\\Files\\Sounds\\alert.wav"
#resource "\\Files\\Sounds\\AHOOGA.wav"
#resource "\\Files\\Sounds\\APPLAUSE.wav"
#resource "\\Files\\Sounds\\BONK.wav"
#resource "\\Files\\Sounds\\CARBRAKE.wav"
#resource "\\Files\\Sounds\\CASHREG.wav"
#resource "\\Files\\Sounds\\CLAP.wav"
#resource "\\Files\\Sounds\\CORKPOP.wav"
#resource "\\Files\\Sounds\\DOG.wav"
#resource "\\Files\\Sounds\\DRIVEBY.wav"
#resource "\\Files\\Sounds\\DRUMROLL.wav"
#resource "\\Files\\Sounds\\EXPLODE.wav"
#resource "\\Files\\Sounds\\FINALBEL.wav"
#resource "\\Files\\Sounds\\FROG.wav"
#resource "\\Files\\Sounds\\GLASS.wav"
#resource "\\Files\\Sounds\\GUNSHOT.wav"
#resource "\\Files\\Sounds\\LASER.wav"
#resource "\\Files\\Sounds\\LATNWHIS.wav"
#resource "\\Files\\Sounds\\PIG.wav"
#resource "\\Files\\Sounds\\RICOCHET.wav"
#resource "\\Files\\Sounds\\RINGIN.wav"
#resource "\\Files\\Sounds\\SIREN.wav"
#resource "\\Files\\Sounds\\TRAIN.wav"
#resource "\\Files\\Sounds\\UH_OH.wav"
#resource "\\Files\\Sounds\\VERYGOOD.wav"
#resource "\\Files\\Sounds\\WHOOSH.wav"

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

//--- Путь к звуковым файлам
string sound_paths[ARRAY_SIZE]=
  {
   "::Files\\Sounds\\alert.wav",
   "::Files\\Sounds\\AHOOGA.wav",
   "::Files\\Sounds\\APPLAUSE.wav",
   "::Files\\Sounds\\BONK.wav",
   "::Files\\Sounds\\CARBRAKE.wav",
   "::Files\\Sounds\\CASHREG.wav",
   "::Files\\Sounds\\CLAP.wav",
   "::Files\\Sounds\\CORKPOP.wav",
   "::Files\\Sounds\\DOG.wav",
   "::Files\\Sounds\\DRIVEBY.wav",
   "::Files\\Sounds\\DRUMROLL.wav",
   "::Files\\Sounds\\EXPLODE.wav",
   "::Files\\Sounds\\FINALBEL.wav",
   "::Files\\Sounds\\FROG.wav",
   "::Files\\Sounds\\GLASS.wav",
   "::Files\\Sounds\\GUNSHOT.wav",
   "::Files\\Sounds\\LASER.wav",
   "::Files\\Sounds\\LATNWHIS.wav",
   "::Files\\Sounds\\PIG.wav",
   "::Files\\Sounds\\RICOCHET.wav",
   "::Files\\Sounds\\RINGIN.wav",
   "::Files\\Sounds\\SIREN.wav",
   "::Files\\Sounds\\TRAIN.wav",
   "::Files\\Sounds\\UH_OH.wav",
   "::Files\\Sounds\\VERYGOOD.wav",
   "::Files\\Sounds\\WHOOSH.wav"
  };
//--- Имена графических объектов
string sound_names[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 sound_texts[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(MathRand()%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   column_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++)
     {
      //--- Увеличим счетчик столбцов
      column_count++;
      //--- Получим цвет для кнопки
      button_color=GetRandomColor();
      //--- Нарисуем кнопку
      CreateButton(0,0,sound_names[i],sound_texts[i],
                   ANCHOR_LEFT_UPPER,CORNER_LEFT_UPPER,"Arial",8,
                   clrWhite,button_color,button_color,x_size,y_size,x_dist,y_dist,1);
      //--- Если уже установлено две кнопки в этом ряду
      if(column_count==2)
        {
         x_dist=10;        // Координату X вернем в исходное положение
         y_dist+=20;       // Координату Y установим для следующего ряда
         column_count=0;   // Обнулим счетчик
        }
      else
      //--- Установим координату X для следующей кнопки 
         x_dist+=x_size;
     }
//--- Обновим график
   ChartRedraw(0);
  }

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

//+------------------------------------------------------------------+
//| Удаляет информационную панель                                    |
//+------------------------------------------------------------------+
void DeleteSoundPanel()
  {
//--- Удалить свойства позиции и их значения
   for(int i=0; i<ARRAY_SIZE; i++)
      DeleteObjectByName(name_sound_object[i]);
//--- Перерисовать график
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Удаляет объекты по имени                                         |
//+------------------------------------------------------------------+
void DeleteObjectByName(string name)
  {
//--- Если объект найден
   if(ObjectFind(ChartID(),name)>=0)
     {
      //--- Если была ошибка при удалении, сообщим об этом
      if(!ObjectDelete(ChartID(),name))
         Print("Ошибка ("+IntegerToString(GetLastError())+") при удалении объекта!");
     }
  }

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

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {
//--- Установим звуковую панель
   SetSoundPanel();
  }
//+------------------------------------------------------------------+
//| Deinitialization function of the expert                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Удалим звуковую панель
   DeleteSoundPanel();
  }

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

//+------------------------------------------------------------------+
//| Изменяет цвета на звуковой панели                                |
//+------------------------------------------------------------------+
void ChangeColorsOnSoundPanel()
  {
   color clr=clrNONE; // Цвет кнопки
//--- В цикле пройдем по всем кнопкам и изменим их цвет
   for(int i=0; i<ARRAY_SIZE; i++)
     {
      //--- Получим новый цвет
      clr=GetRandomColor();
      //--- Установим цвет рамки
      ObjectSetInteger(0,sound_names[i],OBJPROP_BGCOLOR,clr);
      //--- Установим цвет фона
      ObjectSetInteger(0,sound_names[i],OBJPROP_BORDER_COLOR,clr);
      //--- Отжатое состояние кнопки
      ObjectSetInteger(0,sound_names[i],OBJPROP_STATE,false);
      //--- Обновим график
      ChartRedraw(0);
      //--- Подождем (задержка) 20 миллисекунд
      Sleep(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)
        {
         //--- Воспроизведем звук по имени объекта
         //    5019 - ERR_FILE_NOT_EXIST - Файл не существует
         if(!PlaySound(GetSoundPath(sparam)))
            Print("Error: ",GetLastError());
         //--- Изменим цвета всех кнопок
         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("");
  }

Теперь все готово. После набрасывания эксперта на график будет установлена звуковая панель (программу можно скачать в приложении к статье):

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

Рис. 2 - Звуковая панель на графике

Итак, принцип работы со звуковыми файлами теперь понятен. Теперь вернемся к нашему советнику из предыдущей статьи Рецепты MQL5 - Сохраняем результаты оптимизации торгового эксперта по указанным критериям и определимся со звуками, которые хотелось бы слышать в торговом эксперте. Создадим файл Resources.mqh и подключим его к основному файлу эксперта.

//--- Подключаем свои библиотеки
#include "Include/Errors.mqh"
#include "Include/Enums.mqh"
#include "Include/Resources.mqh"
#include "Include/TradeSignals.mqh"
#include "Include/TradeFunctions.mqh"
#include "Include/ToString.mqh"
#include "Include/Auxiliary.mqh"

Выберем файлы для основных торговых событий.

//--- Звуковые файлы
#resource "\\Files\\Sounds\\AHOOGA.WAV"   // Ошибка
#resource "\\Files\\Sounds\\CASHREG.WAV"  // Открытие позиции/увеличение объема позиции/срабатывание отложенного ордера
#resource "\\Files\\Sounds\\WHOOSH.WAV"   // Установка/модификация отложенного ордера/Stop Loss/Take Profit
#resource "\\Files\\Sounds\\VERYGOOD.WAV" // Закрытие позиции с прибылью
#resource "\\Files\\Sounds\\DRIVEBY.WAV"  // Закрытие позиции с убытком
//--- Путь к звуковым файлам
string SoundError          = "::Files\\Sounds\\AHOOGA.WAV";
string SoundOpenPosition   = "::Files\\Sounds\\CASHREG.WAV";
string SoundAdjustOrder    = "::Files\\Sounds\\WHOOSH.WAV";
string SoundCloseWithProfit= "::Files\\Sounds\\VERYGOOD.WAV";
string SoundCloseWithLoss  = "::Files\\Sounds\\DRIVEBY.WAV";

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

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

//--- Внешние параметры эксперта
input  int        NumberOfBars =2;    // Количество направленных баров
sinput double     Lot          =0.1;  // Лот
input  double     TakeProfit   =100;  // Тейк Профит
input  double     StopLoss     =50;   // Стоп Лосс
input  double     TrailingStop =10;   // Трейлинг Стоп
input  bool       Reverse      =true; // Разворот позиции
sinput bool       UseSound     =true; // Звуковые оповещения

В файле Include\Enums.mqh создадим перечисление ENUM_SOUNDS для звуков.

//--- Звуки
enum ENUM_SOUNDS
  {
   SOUND_ERROR             =0,   // Ошибка
   SOUND_OPEN_POSITION     = 1,  // Открытие позиции/увеличение объема позиции/срабатывание отложенного ордера
   SOUND_ADJUST_ORDER      = 2,  // Установка защитного стопа/уровня фиксации прибыли/отложенного ордера
   SOUND_CLOSE_WITH_PROFIT = 3,  // Закрытие позиции с прибылью
   SOUND_CLOSE_WITH_LOSS   = 4   // Закрытие позиции с убытком
  };

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

//+------------------------------------------------------------------+
//| Воспроизведение звука                                            |
//+------------------------------------------------------------------+
void PlaySoundByID(ENUM_SOUNDS id)
  {
//--- Если это реал-тайм и звуки включены
   if(IsRealtime() && UseSound)
     {
      //--- Воспроизведем звук по переданному идентификатору
      switch(id)
        {
         case SOUND_ERROR              : PlaySound(SoundError);            break;
         case SOUND_OPEN_POSITION      : PlaySound(SoundOpenPosition);     break;
         case SOUND_ADJUST_ORDER       : PlaySound(SoundAdjustOrder);      break;
         case SOUND_CLOSE_WITH_PROFIT  : PlaySound(SoundCloseWithProfit);  break;
         case SOUND_CLOSE_WITH_LOSS    : PlaySound(SoundCloseWithLoss);    break;
        }
     }
  }

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

//+------------------------------------------------------------------+
//| Открывает позицию                                                |
//+------------------------------------------------------------------+
void OpenPosition(double lot,
                  ENUM_ORDER_TYPE order_type,
                  double price,
                  double sl,
                  double tp,
                  string comment)
  {
//--- Установим номер мэджика в торговую структуру
   trade.SetExpertMagicNumber(0);
//--- Установим размер проскальзывания в пунктах
   trade.SetDeviationInPoints(CorrectValueBySymbolDigits(10));
//--- Режим Instant Execution и Market Execution
//    *** Начиная с 803 билда, уровни Stop Loss и Take Profit                             ***
//    *** можно устанавливать при открытии позиции в режиме SYMBOL_TRADE_EXECUTION_MARKET ***
   if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT ||
      symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET)
     {
      //--- Если позиция не открылась
      if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment))
        {
         //--- Воспроизвести звук ошибки и вывести сообщение об этом
         PlaySoundByID(SOUND_ERROR);
         Print("Ошибка при открытии позиции: ",GetLastError()," - ",ErrorDescription(GetLastError()));
        }
      //--- Иначе воспроизвести звук открытия позиции
      else
         PlaySoundByID(SOUND_OPEN_POSITION);
     }
  }

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

//+------------------------------------------------------------------+
//| Звуковое оповещение                                              |
//+------------------------------------------------------------------+
void SoundNotification()
  {
//--- Если реал-тайм и включен звук
   if(IsRealtime() && 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,D_SYMBOL);
            //--- Если символ сделки и текущий символ равны
            if(deal.symbol==_Symbol)
              {
               //--- получим направление сделки
               GetHistoryDealProperties(ticket,D_ENTRY);
               //--- Если это закрытие позиции, сокращение объема или разворот
               if(deal.entry==DEAL_ENTRY_OUT || deal.entry==DEAL_ENTRY_INOUT)
                 {
                  //--- Если тикет текущей сделки из списка (последняя сделка символа) равен предыдущему
                  //    или это инициализация тикета последней сделки
                  if(ticket==last_ticket || last_ticket==0)
                    {
                     //--- Запомним тикет и выйдем
                     last_ticket=ticket;
                     return;
                    }
                  //--- Получим результат сделки
                  GetHistoryDealProperties(ticket,D_PROFIT);
                  //--- Если прибыль
                  if(deal.profit>=0)
                    {
                     //--- Звук прибыли
                     PlaySoundByID(SOUND_CLOSE_WITH_PROFIT);
                     //--- Сохраним номер тикета
                     last_ticket=ticket;
                     return;
                    }
                  //--- Если убыток
                  if(deal.profit<0)
                    {
                     //--- Звук убытка
                     PlaySoundByID(SOUND_CLOSE_WITH_LOSS);
                     //--- Сохраним номер тикета
                     last_ticket=ticket;
                     return;
                    }
                 }
              }
           }
        }
     }
  }

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

//+------------------------------------------------------------------+
//| Инициализация                                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Инициализируем новый бар
   CheckNewBar();
//--- Инициализация тикетов последних сделок символа
   SoundNotification();
//--- Инициализация прошла успешно
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Отслеживание торговых событий                                    |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- Звуковое оповещение
   SoundNotification();
  }

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

//+------------------------------------------------------------------+
//| Изменяет уровень Trailing Stop                                   |
//+------------------------------------------------------------------+
void ModifyTrailingStop()
  {
//--- Если включен трейлинг и StopLoss
   if(TrailingStop>0 && StopLoss>0)
     {
      double new_sl    =0.0;    // Для расчета нового уровня Stop loss
      bool   condition =false;  // Для проверки условия на модификацию
      //--- Получим флаг наличия/отсутствия позиции
      pos.exists=PositionSelect(_Symbol);
      //--- Если есть позиция
      if(pos.exists)
        {
         //--- Получим свойства символа
         GetSymbolProperties(S_ALL);
         //--- Получим свойства позиции
         GetPositionProperties(P_ALL);
         //--- Получим уровень для Stop Loss
         new_sl=CalculateTrailingStop(pos.type);
         //--- В зависимости от типа позиции проверим соответствующее условие на модификацию Trailing Stop
         switch(pos.type)
           {
            case POSITION_TYPE_BUY  :
               //--- Если новое значение для Stop Loss выше,
               //    чем текущее значение плюс установленный шаг
               condition=new_sl>pos.sl+CorrectValueBySymbolDigits(TrailingStop*symb.point);
               break;
            case POSITION_TYPE_SELL :
               //--- Если новое значение для Stop Loss ниже,
               //    чем текущее значение минус установленный шаг
               condition=new_sl<pos.sl-CorrectValueBySymbolDigits(TrailingStop*symb.point);
               break;
           }
         //--- Если Stop Loss есть, то сравним значения перед модификацией
         if(pos.sl>0)
           {
            //--- Если выполняется условие на модификацию ордера, т.е. новое значение ниже/выше, 
            //    чем текущее, модифицируем защитный уровень позиции
            if(condition)
              {
               if(!trade.PositionModify(_Symbol,new_sl,pos.tp))
                  Print("Ошибка при модификации позиции: ",GetLastError()," - ",ErrorDescription(GetLastError()));
              }
           }
         //--- Если Stop Loss нет, то просто установим его
         if(pos.sl==0)
           {
            if(!trade.PositionModify(_Symbol,new_sl,pos.tp))
               Print("Ошибка при модификации позиции: ",GetLastError()," - ",ErrorDescription(GetLastError()));
           }
        }
     }
     //--- Звук модификации ордера
     PlaySoundByID(SOUND_ADJUST_ORDER);
  }

 

Заключение

На этом все. Все файлы для тестов можно скачать в приложении к статье. По теме звуков в терминале хочется также отметить одно интересное решение в Code Base CMIDI (автор Integer): с его помощью в MetaTrader 5 можно воспроизводить MIDI-файлы. Успехов!

Прикрепленные файлы |
sounds.zip (321.8 KB)
soundpanel.mq5 (14.1 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (2)
Ivan Negreshniy
Ivan Negreshniy | 4 окт. 2013 в 20:14

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

Однако современные средства мультимедиа уже далеко продвинулись после простой оцифровки звуков WAV, возьмите хоть Microsoft Speech,  давно доступно в Windows и его эффективно можно применять.
Например, предлагаемые вами звуки торговых событий, логично было бы снабдить конкретными голосовыми комментами со значениями прибылей убитков и.т.д., с рекомендациями и советами.
Должны же советники, когда нибудь начать советовать нам, нормальным, человеческим языком...)

Главное, что для этого не требуется писать сложный программный код.
Чтоб не быть голословным, привожу пример бейсик скрипта для речевого синтеза тестовой строки "HELLO WORLD!" по технологии MS Agent, выполняемой через Hlaiman движок.    

Set HLAIM = CreateObject("HLAIM.SHELLMDI")
HLAIM.InitProgram.Agent.Action = "SPEAK: HELLO WORLD!"
WScript.Sleep(3000)
Кто читал мою статью и установил Hlaiman EA Generator, может проверить работу этого кода, скопировав и запустив его в файле, например -  hello.vbs.
Аналогичные файлы с различными текстовыми сообщениями можно создавать и использовать наряду с *.wav файлами для настройки событий МТ терминала, кроме того этот вызов, не сложно интегрировать непосредственно в MQL код.
Anatoli Kazharski
Anatoli Kazharski | 4 окт. 2013 в 20:32
hlaiman:

...
Например, предлагаемые вами звуки торговых событий, логично было бы снабдить конкретными голосовыми комментами со значениями прибылей убитков и.т.д., с рекомендациями и советами.
Должны же советники, когда нибудь начать советовать нам, нормальным, человеческим языком...)

...

Статья, как всегда, является простым примером. Мне например нравится программа FL Studio 11. Можно синтезировать любой звук. Или сначала записать (в том числе и голос), а потом качественно обработать. 

 

Технические индикаторы как цифровые фильтры Технические индикаторы как цифровые фильтры
В данной статье технические индикаторы рассматриваются как цифровые фильтры. Объясняется принцип работы и основные характеристики цифровых фильтров. Рассматриваются практические способы получения ядра фильтра в терминале MetaTrader 5 и интеграция с готовым анализатором спектра, предложенным в статье "Строим анализатор спектра". В качестве примеров приведены импульсные и спектральные характеристики типичных цифровых фильтров.
Итоги MetaTrader AppStore за 3 квартал 2013 года Итоги MetaTrader AppStore за 3 квартал 2013 года
Подошел к концу очередной квартал этого года, и мы решили подвести его итоги для MetaTrader AppStore - магазина торговых роботов и технических индикаторов для платформ MetaTrader. Всего к концу отчетного квартала более 500 разработчиков разместили в Маркете свыше 1 200 продуктов для MetaTrader 4 и MetaTrader 5.
Рецепты MQL5 - Наблюдение за несколькими таймфреймами в одном окне Рецепты MQL5 - Наблюдение за несколькими таймфреймами в одном окне
MetaTrader 5 предлагает на выбор 21 таймфрейм для анализа. На график можно также поместить специальный объект-график и уже в нем задать символ, таймфрейм и еще некоторые свойства. В этой статье рассмотрим такие графические объекты более подробно: создадим индикатор с элементами управления (кнопками), с помощью которых можно будет устанавливать в подокно сразу несколько объектов-графиков. При этом объекты-графики будут точно вписываться и автоматически подстраиваться под размер подокна при изменении размеров главного окна графика или терминала.
Рецепты MQL5 - Сохраняем результаты оптимизации торгового эксперта по указанным критериям Рецепты MQL5 - Сохраняем результаты оптимизации торгового эксперта по указанным критериям
Продолжим серию статей по программированию на MQL5. На этот раз рассмотрим, как можно получать результаты по каждому проходу оптимизации непосредственно во время оптимизации параметров эксперта. При этом сделаем так, что если условия, которые будут настраиваться во внешних параметрах, исполняются, то показатели этого прохода будут записываться в файл. Кроме показателей тестов будем сохранять еще параметры, по которым был получен этот результат.