Методы дистанционного управления работой советников

Dmitriy Gizlyk | 17 сентября, 2018

Содержание

Введение

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

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

1. Постановка задачи

На первый взгляд, задача кажется понятной: нужно создать программу, которая при поступлении внешних команд будет отдавать приказы экспертам. Но рассмотрев вопрос глубже мы понимаем, что в MetaTrader 5 отсутствует возможность прямого программного воздействие на работу стороннего советника. Каждый советник работает в отдельном потоке и нет возможности определить наличие запущенного советника как каком-либо из открытых графиков. Решение этого вопроса было предложено пользователем fxsaber в библиотеке "Expert - библиотека для MetaTrader 5".

В данном коде автором было предложено воспользоваться возможностью сохранения шаблонов. На первый взгляд сохранение шаблонов никак не влияет на работу программ, запущенных на графике. Это конечно так. Но при сохранении шаблона графика в файл записываются все существующие на графике графические объекты, а также все запущенные на графике программы с указанием их параметров. Последующее применение сохраненного шаблона к графику позволяет восстановить на графике все графические объекты и программы с сохраненными в файле параметрами.

Второй стороной такого воздействия является полное удаление всех объектов и программ с графика, существующих до загрузки шаблона. Иными словами, если на графике запущен советник, а в загружаемом шаблоне советника нет, то советник будет удален с графика, и наоборот. В результате предложенного такого обходного варианта работа по загрузке и удалению советника с графика сводится к редактированию файла шаблона.

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

Редактирование шаблонов также имеет свои нюансы. По умолчанию шаблоны сохраняются в специальную директорию "каталог_данных\Profiles\Templates\". Но MQL5 позволяет работать только с файлами из "песочницы". Решение данного вопроса также было найдено fxsaber, он предложил при указании имени файла шаблона добавить путь в песочницу. В результате такого действия удалось получить доступ к файлу шаблона без применения сторонних библиотек.

Поблагодарим пользователя fxsaber за его острый ум и неординарность мышления.

Определившись с методами управления советниками, давайте подумаем о модели обмена сообщениями программы с пользователем. В наше время человек вынужден быть мобильным и часто бывает вдали от своего компьютера, но практически всегда с ним его смартфон. И для iPhone, и для Android существуют приложения терминала MetaTrader 5. Подключив такой терминал к своему счету пользователь может проводить анализ ценовых движений на графике и вести ручную торговлю. Но, к сожалению, на сегодняшний день мобильные терминалы не позволяют использовать электронные советники и сторонние индикаторы. Поэтому для работы электронных советников по-прежнему используются десктопные версии терминала, запущенные на компьютере пользователя или, что встречается все чаще, на виртуальном хостинге.

Подключенный к счету мобильный терминал позволяет нам увидеть картину по счету, но между мобильным и десктопным терминалом отсутствуют каналы прямой связи. Единственное, на что мы можем влиять — это выставление и удаление ордеров. Выставляемые ордера сразу отображаются на счете и могут быть отслежены советником, запущенном в десктопном терминале. Таким образом, вместе с выставлением ордеров мы можем передавать управляющие команды в наш мастер-советник. Остается только определить список команд и каким кодом их передать. На решении этих вопросов я более подробно остановлюсь в следующих главах.

2. Разбираем файл шаблона

Приступая к работе, предлагаю разобрать структуру файла шаблона. Ниже представлен пример шаблона графика EURUSD M30 с установленными советником ExpertMACD и индикатором MACD.

<chart>
id=130874249669663027
symbol=EURUSD
period_type=0
period_size=30
digits=5
.....
.....
windows_total=2

<expert>
name=ExpertMACD
path=Experts\Advisors\ExpertMACD.ex5
expertmode=0
<inputs>
Inp_Expert_Title=ExpertMACD
Inp_Signal_MACD_PeriodFast=12
Inp_Signal_MACD_PeriodSlow=24
Inp_Signal_MACD_PeriodSignal=9
Inp_Signal_MACD_TakeProfit=50
Inp_Signal_MACD_StopLoss=20
</inputs>
</expert>

<window>
height=162.766545
objects=103

<indicator>
name=Main
........
</indicator>
<object>
.......
</object>

<object>
........
</object>
........
........
<object>
........
</object>

</window>

<window>
height=50.000000
objects=0

<indicator>
name=MACD
path=
apply=1
show_data=1
scale_inherit=0
scale_line=0
scale_line_percent=50
scale_line_value=0.000000
scale_fix_min=0
scale_fix_min_val=-0.001895
scale_fix_max=0
scale_fix_max_val=0.001374
expertmode=0
fixed_height=-1

<graph>
name=
draw=2
style=0
width=1
color=12632256
</graph>

<graph>
name=
draw=1
style=2
width=1
color=255
</graph>
fast_ema=12
slow_ema=24
macd_sma=9
</indicator>
</window>
</chart>

Как видно из представленного кода, информация в файле шаблона структурирована и разделена тегами. Начинается файл с тега <chart> с описанием основной информации о графике, где Вы можете найти идентификатор графика, его инструмент и таймфрейм. Интересующая нас информация об эксперте находится между тегами <expert> и </expert>.

В начале блока представлена информация об эксперте: его краткое имя, отображаемое на графике, и путь к файлу. Затем следует флаг expertmode, его состояние говорит о разрешении советнику на совершение торговых операций. После чего следуют параметры советника, выделенные тегами <inputs> </inputs>. Далее следует информация о под окнах графика. Каждое подокно выделено тегами <window> и </window>, между которыми описаны запущенные индикаторы (выделены тегами <indicator> ... </indicator>) и нанесенные графические объекты (выделены тегами <object> ... </object>).

Следует также обратить внимание, что если запущенные на графике советник и/или индикатор создают какие-либо графические объекты, то их желательно удалить из шаблона. Т.к. в противном случае при прикреплении шаблона к графику данные объекты будут нанесены на график из шаблона, и при запуске такого советника и/или индикатора, они будут вновь создавать эти же объекты.

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

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

Таким образом, для включения/выключения советника нам достаточно переписать файл шаблона, изменив в нем флаг expertmode на нужное нам значение и попутно удалив скрытые объекты. Применение нового шаблона перезапустит советник с нужным нам свойством.

3. Определяем команды

Определившись с основными принципами работы разрабатываемого советника, пришло время разработать систему обмена сообщениями. Ранее мы уже решили использовать ордера для отправки команд. Но как отправлять команды ордерами, не испортив финансовый результат? Для этих целей мы будем использовать отложенные ордера, которые будут удаляться советником после получения команды.

Выставляя отложенные ордера мы должны быть уверены, что во временном интервале действия отложенного ордера он не сработает. Само решение данного вопроса лежит на поверхности - нужно выставлять ордер на достаточном расстоянии от текущей цены.

К своему удивлению, при работе с мобильным терминалом я обнаружил невозможность оставлять комментарии к открываемым ордерам. В то же время пользователь может видеть комментарии к ордерам, созданным в десктопном терминале. Таким образом, мы можем организовать одностороннюю связь по передаче сообщений от мастер-советника пользователю. Но пользователь не сможет подобным образом отдавать команды.

Итак, мы можем выставлять отложенные ордера, которые будут прочитаны советником, но не можем оставлять в них комментарии. В таком случае, мы можем использовать цену и инструмент ордера для передачи команды. И тут мы должны определить, какую установить цену для кодирования команды, при этом она должна находиться на достаточном расстоянии от текущей цены, чтобы выставленный ордер не сработал ни при каких условиях.

На мой взгляд, такими могут быть цены, близкие к нулю. Теоретически, вероятность приближения цены любого инструмента к нулю ничтожно мала. Таким образом, установив цену ордера в 1-5 тиков мы практически не рискуем получить его срабатывания. Естественно, что в таком случае мы должны будем ограничиться стоп ордерами на продажу, т.к. использование стоп ордеров не влияет на свободную маржу.

Не будем забывать, что в мобильном приложении мы можем читать комментарии ордеров, т.е. наш мастер-советник может нам отправлять информацию таким образом.

Подытожив вышесказанное, предлагается следующая кодировка команд:

Цена Объем Команда
1 1 тик любой Запрос состояния советников.
Мастер-советник выставляет отложенные ордера по инструментам с советниками, в комментарии к ордеру указывается наименование советника и разрешение на торговлю.
2 2 тика любой Остановка торговли советником.
Если комментария к ордеру нет, то останавливаются все советники. Если команда отдана в результате модификации ордера после первой команды, то останавливается конкретный советник на конкретном инструменте.
3 3 тика любой Запуск торговли советником.
Принципы работы аналогичны команде 2.
4 4 тика 1 мин.лот BUY
2 мин. лот SELL
3 мин. лот ВСЕ
Удаляет отложенные ордера.
5 5 тиков 1 мин.лот BUY
2 мин. лот SELL
3 мин. лот ВСЕ
Закрывает позиции.


4. Создаем мастер-эксперт

Теперь, когда у нас есть полное понимание методов работы и канала связи, мы можем приступить к непосредственному написанию нашего советника. В коде нашего советника будут использоваться методы из библиотеки "Expert - библиотека для MetaTrader 5" с незначительным изменением - для удобства нашего использования сделаем все методы библиотеки публичными.

Вначале для удобства использования присвоим мнемонические имена используемым тегам. Часть из них уже объявлена в используемой библиотеке.

#define FILENAME (__FILE__ + ".tpl")
#define PATH "\\Files\\"
#define STRING_END "\r\n"
#define EXPERT_BEGIN ("<expert>" + STRING_END)
#define EXPERT_END ("</expert>" + STRING_END)
#define EXPERT_INPUT_BEGIN ("<inputs>" + STRING_END)
#define EXPERT_INPUT_END ("</inputs>" + STRING_END)
#define EXPERT_CHART_BEGIN ("<chart>" + STRING_END)
#define EXPERT_NAME "name="
#define EXPERT_PATH "path="
#define EXPERT_STOPLEVEL "stops_color="

Дополнительные объявим в коде нашего советника.

#define OBJECT_BEGIN             ("<object>" + STRING_END)
#define OBJECT_END               ("</object>" + STRING_END)
#define OBJECTS_NUMBER           ("objects=")
#define OBJECT_HIDDEN            ("hidden=1")
#define EXPERT_EXPERTMODE        ("expertmode=")

Также следует обратить внимание, что fxsaber позаботился о совместимости своей библиотеки с другим кодом и отменил мнемонические имена в конце библиотеки. Такой подход исключает возникновения ошибок повторного присвоения аналогичного имени другому макросу, но не дает возможность использовать такие объявления за пределами библиотеки. Чтобы повторно не проводить аналогичные объявления макросов в коде советника, закроем комментариями директивы #undef в коде библиотеки.

//#undef EXPERT_STOPLEVEL
//#undef EXPERT_PATH
//#undef EXPERT_NAME
//#undef EXPERT_CHART_BEGIN
//#undef EXPERT_INPUT_END
//#undef EXPERT_INPUT_BEGIN
//#undef EXPERT_END
//#undef EXPERT_BEGIN
//#undef STRING_END
//#undef PATH
//#undef FILENAME

Затем объявим две внешние переменные нашего мастер-советника: время жизни информационных ордеров в минутах и меджик для их идентификации.

sinput int      Expirations =  5;
sinput ulong    Magic       =  88888;

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

#include <fxsaber\Expert.mqh>
#include <Trade\Trade.mqh>

В блоке глобальных переменных объявим экземпляр класса торговых операций и переменные для хранения идентификатора рабочего чарта и тикета последнего командного ордера.

CTrade   *Trade;
ulong     chart;
ulong     last_command;

В функции OnInit проведем инициализацию глобальных переменных.

int OnInit()
  {
//---
   Trade =  new CTrade();
   if(CheckPointer(Trade)==POINTER_INVALID)
      return INIT_FAILED;
   Trade.SetDeviationInPoints(0);
   Trade.SetExpertMagicNumber(Magic);
   Trade.SetMarginMode();
   Trade.SetTypeFillingBySymbol(_Symbol);
//---
   chart=ChartID();
   last_command=0;
//---
   return(INIT_SUCCEEDED);
  }

И не забудем удалить экземпляр класса торговых операций в функции OnDeinit.

void OnDeinit(const int reason)
  {
//---
   if(CheckPointer(Trade)!=POINTER_INVALID)
      delete Trade;
  }


4.1. Диспетчер команд.

Запуск основного функционала мастер-советника будем производить из функции OnTradeTransaction. Следует помнить, что событие вызова данной функции может обрабатываться несколько раз для одного ордера. Поэтому, перед тем как запустить код программы, мы проведем ряд проверок.

Мы проверим наличие тикета ордера для обрабатываемого события. Проверим статус ордера, чтобы не обрабатывать событие удаления ордеров. Проверим меджик ордера, чтобы исключить обработку ордеров советников. Сверим тип ордера, так как наши команды поступают только со стоп-ордерами на продажу. А также проверим тип торговой операции, это должно быть добавление или изменение ордера.

После успешного прохождения всех проверок запускаем диспетчер команд, который организован в функции CheckCommand.

void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest &request,
                        const MqlTradeResult &result)
  {
//---
   if(trans.order>0 && trans.order_state!=ORDER_STATE_REQUEST_CANCEL && request.magic==0 && 
      trans.order_type==ORDER_TYPE_SELL_STOP && 
      (trans.type==TRADE_TRANSACTION_ORDER_ADD || trans.type==TRADE_TRANSACTION_ORDER_UPDATE))
      CheckCommand(trans,request);
  }

При запуске функции CheckCommand, в своих параметрах она получает структуры торговой операции и торгового запроса. Первым делом проверяем — не обрабатывался ли данный запрос ранее. Если команда уже обработана, то выходим из функции.

void CheckCommand(const MqlTradeTransaction &trans,
                  const MqlTradeRequest &request)
  {
   if(last_command==trans.order)
      return;

Если команда еще не обработана, то декодируем цену ордера в команду.

   double tick=SymbolInfoDouble(trans.symbol,SYMBOL_TRADE_TICK_SIZE);
   uint command=(uint)NormalizeDouble(trans.price/tick,0);

Затем с помощью оператора switch вызовем функцию, соответствующую поступившей команде.

   switch(command)
     {
      case 1:
        if(StringLen(request.comment)>0 || trans.type==TRADE_TRANSACTION_ORDER_UPDATE)
           return;
        if(trans.order<=0 || (OrderSelect(trans.order) && StringLen(OrderGetString(ORDER_COMMENT))>0))
           return;
        GetExpertsInfo();
        break;
      case 2:
        ChangeExpertsMode(trans,request,false);
        break;
      case 3:
        ChangeExpertsMode(trans,request,true);
        break;
      case 4:
        DeleteOrders(trans);
        break;
      case 5:
        ClosePosition(trans);
        break;
      default:
        return;
     }

В завершение сохраним тикет последнего командного ордера и удалим ордер со счета.

   last_command=trans.order;
   Trade.OrderDelete(last_command);
  }

С полным кодом всех функций можно ознакомиться во вложении.


4.2. Информация о запущенных советниках.

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

В начале функции удалим ранее выставленные ордера, вызвав функцию DeleteOrdersByMagic.

void GetExpertsInfo(void)
  {
   DeleteOrdersByMagic(Magic);

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

   long i_chart=ChartFirst();
   while(i_chart>=0 && !IsStopped())
     {
      if(i_chart==0 || i_chart==chart)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

На следующем этапе загружаем шаблон графика с предварительной проверкой наличия на графике эксперта. Если эксперта на графике нет, или шаблон не загружен, переходим к следующему графику.

      string temp=EXPERT::TemplateToString(i_chart,true);
      if(temp==NULL)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

Далее находим в шаблоне блок эксперта. Если блок не найден, переходим к следующему графику.

      temp=EXPERT::StringBetween(temp,EXPERT_BEGIN,EXPERT_END);
      if(temp==NULL)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

После чего извлекаем наименование эксперта и его состояние. Из него формируется комментарий будущего информационного ордера. Если советнику разрешена торговля, то перед его наименованием в комментарии поставим букву "T", иначе букву "S".

      string name =  EXPERT::StringBetween(temp,EXPERT_NAME,STRING_END);
      bool state  =  (bool)StringToInteger(EXPERT::StringBetween(temp,EXPERT_EXPERTMODE,STRING_END));
      string comment =  (state ? "T " : "S ")+name;

В заключении цикла определим инструмент графика, срок действия информационного ордера, отправим ордер и перейдем к следующему графику.

      string symbol=ChartSymbol(i_chart);
      ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC;
      datetime expir=0;
      if(Expirations>0)
        {
         expir=TimeCurrent()+Expirations*60;
         type=ORDER_TIME_SPECIFIED;
        }
      Trade.SellStop(SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN),SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE),symbol,0,0,type,expir,comment);
      i_chart=ChartNext(i_chart);
     }
  }

В рассмотренной функции использовались методы из библиотеки fxsaber — получение шаблона графика в строковую переменную и получение подстроки между указанными тегами.

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

  static string TemplateToString( const long Chart_ID = 0, const bool CheckExpert = false )
  {
    short Data[];
    return(((!CheckExpert || EXPERT::Is(Chart_ID)) && ::ChartSaveTemplate((ulong)Chart_ID, PATH + FILENAME) && (::FileLoad(FILENAME, Data) > 0)) ?
           ::ShortArrayToString(Data) : NULL);
  }

Для получения подстроки между указанными тегами сначала определяем позиции начала и конца подстроки.

  static string StringBetween( string &Str, const string StrBegin, const string StrEnd = NULL )
  {
    int PosBegin = ::StringFind(Str, StrBegin);
    PosBegin = (PosBegin >= 0) ? PosBegin + ::StringLen(StrBegin) : 0;

    const int PosEnd = ::StringFind(Str, StrEnd, PosBegin);

Затем вырезаем подстроку для возврата и уменьшаем исходную строку до необработанного остатка.

    const string Res = ::StringSubstr(Str, PosBegin, (PosEnd >= 0) ? PosEnd - PosBegin : -1);
    Str = (PosEnd >= 0) ? ::StringSubstr(Str, PosEnd + ::StringLen(StrEnd)) : NULL;

    if (Str == "")
      Str = NULL;

    return(Res);
  }

С полным кодом всех функций и классов можно ознакомиться во вложении.

4.3. Функция изменения статуса эксперта.

Изменение статуса экспертов осуществляется в функции ChangeExpertsMode. В своих параметрах указанная функция получает структуры торговой операции и торгового запроса, а также новый статус для установки советника.

void ChangeExpertsMode(const MqlTradeTransaction &trans,
                       const MqlTradeRequest &request,
                       bool  ExpertMode)
  {
   string comment=request.comment;
   if(StringLen(comment)<=0 && OrderSelect(trans.order))
      comment=OrderGetString(ORDER_COMMENT);      
   string exp_name=(StringLen(comment)>2 ? StringSubstr(comment,2) : NULL);

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

   long i_chart=ChartFirst();
   while(i_chart>=0 && !IsStopped())
     {
      if(i_chart==0 || i_chart==chart || (StringLen(exp_name)>0 && ChartSymbol()!=trans.symbol))
        {
         i_chart=ChartNext(i_chart);
         continue;
        }
      string temp=EXPERT::TemplateToString(i_chart,true);
      if(temp==NULL)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

На следующем этапе, при необходимости, проверяем наличие нужного эксперта на графике. Если запущенный на графике советник не соответствует запросу, то переходим к следующему графику. Также проверяем текущий статус советника, в случае, если он соответствует указанному для установки, переходим к следующему графику.

      string NewTemplate   =  NULL;
      if(exp_name!=NULL)
        {
         NewTemplate=EXPERT::StringBetween2(temp,NULL,EXPERT_NAME);
         string name=EXPERT::StringBetween(temp,NULL,STRING_END);
         if(name!=exp_name)
           {
            i_chart=ChartNext(i_chart);
            continue;
           }
         NewTemplate+=name+STRING_END;
        }
//---
      NewTemplate+=EXPERT::StringBetween2(temp,NULL,EXPERT_EXPERTMODE);
      bool state  =  (bool)StringToInteger(EXPERT::StringBetween(temp,NULL,STRING_END));
      if(state==ExpertMode)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

После прохождения всех необходимых проверок создаем новый шаблон с заданным статусом эксперта. Удаляем из шаблона скрытые объекты и применяем новый шаблон к графику. После выполнения всех операций переходим к следующему графику.

      NewTemplate+=IntegerToString(ExpertMode)+STRING_END+temp;
      NewTemplate=DeleteHiddenObjects(NewTemplate);
      EXPERT::TemplateApply(i_chart,NewTemplate,true);
//---
      i_chart=ChartNext(i_chart);
     }

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

   GetExpertsInfo();
  }

С полным кодом всех функций можно ознакомиться во вложении.

4.4. Функции удаления ордеров и закрытия позиций

Функции удаления ордеров и закрытия открытых позиций построены по одному алгоритму и отличаются только объектами воздействия. Поэтому в статье предлагаю рассмотреть только одну из них. При вызове функции DeleteOrders в параметрах ей передается структура торговой операции, из которой объем ордера декодируется в направление закрываемых ордеров (согласно таблицы команд из раздела 3).

void DeleteOrders(const MqlTradeTransaction &trans)
  {
   int direct=(int)NormalizeDouble(trans.volume/SymbolInfoDouble(trans.symbol,SYMBOL_VOLUME_MIN),0);

Затем организовывается цикл перебора всех выставленных на счете ордеров. В данном цикле проверяется тип ордера на соответствие поступившей команде. И в случае совпадения отправляется запрос на удаление ордера.

   for(int i=total-1;i>=0;i--)
     {
      ulong ticket=OrderGetTicket((uint)i);
      if(ticket<=0)
         continue;
//---
      switch((ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE))
        {
         case ORDER_TYPE_BUY_LIMIT:
         case ORDER_TYPE_BUY_STOP:
         case ORDER_TYPE_BUY_STOP_LIMIT:
           if(direct==2)
              continue;
           Trade.OrderDelete(ticket);
           break;
         case ORDER_TYPE_SELL_LIMIT:
         case ORDER_TYPE_SELL_STOP:
         case ORDER_TYPE_SELL_STOP_LIMIT:
           if(direct==1)
              continue;
           Trade.OrderDelete(ticket);
           break;
        }
     }
  }

С полным кодом всех функций можно ознакомиться во вложении.


Заключение

В данной статье предложена технология дистанционного управления работой советников в терминале MetaTrader 5. Подобное решение позволяет повысить мобильность трейдеров, использующих в своей работе торговых роботов. Примененный нестандартный подход к использованию стандартных функций позволяет решать более широкие задачи без использования различных dll.

Ссылки

Expert - библиотека для MetaTrader 5

Программы, используемые в статье:

#
Имя
Тип
Описание
1 Expert.mqh Библиотека класса Expert - библиотека для MetaTrader 5
2 Master.mq5 Советник Мастер-советник для управления другими советниками, запущенными в терминале
3 Master.mqproj Файл проекта Проект мастер-советника