Создание помехоустойчивых торговых роботов для взаимодействия с брокерскими платформами MT4 и МТ5

Торговые роботы – специальный тип программного обеспечения (ПО), предназначенный для выполнения операций открытия, сопровождения и закрытия ордеров в соответствии с алгоритмом, реализующим используемую для получения прибыли торговую систему. В настоящее время для торговли на многие трейдеры пользуются терминалами популярных торговых платформ MetaTrader 4 (информационно-торговая платформа для организации брокерского обслуживания на рынках Forex, CFD и Futures) и MetaTrader 5 (позволяет совершать торговые операции на финансовых рынках: Forex, фондовые рынки (биржи), Фьючерсы и Контракты на разницу). Для работы трейдерами может использоваться ПО, разработанное для стационарных (MetaTrader 4(5) Client Terminal) и мобильных устройств (MetaTrader 4 Mobile Terminals, терминалы MetaTrader 5 для Android и для iOS). Торговые роботы, как правило, используют достаточно сложные алгоритмы обрабатывающие большие объемы данных [1,2], по результатам обработки принимаются управляющие решения. По причине сложности реализации инструментарий для создания роботов, предоставляемый разработчиками торговых платформ, для терминалов мобильных устройств в настоящее время отсутствует. Однако трейдеры могут выполнять операции с одним торговым счетом, обращаясь с нескольких устройств, в том числе и с мобильных терминалов. Большинство используемых торговых систем крайне чувствительно к скорости исполнения ордеров.
Отказы в работе торговых роботов (и вызванные этим финансовые потери) связанные с некорректной работой внешних систем перечислены в статьях по разработке отказоустойчивых роботов [3,4]. Основные причины: обрывы соединения клиента торгового терминала с сервером, вызванные проблемами доступности подключения к сети Интернет; низкая скорость передачи данных от терминала к серверу (падение скорости передачи больше, чем минимально необходимая скорость передачи для надежной и устойчивой работы торговой программы); снижение скорости обработки данных торговым терминалом и роботом, выполняющимся под управлением терминала. Следует отметить и такие возможные причины, как зависание операционной системы или неработоспособность компьютера по причине отключения электропитания. Все названные проблемы могут оперативно купироваться трейдером, например, путем использования резервных каналов связи, дублирования физического оборудования, обеспечение отказоустойчивый решений путем организации кластера или размещение торгового ПО на виртуальных машинах, размещаемых в облаке. Следует заметить, что для многих типов счетов могут возникать ситуации, когда команды торгового терминала (открытие, закрытие и модификация ордеров) не обрабатываются или обрабатываются неприемлемо долго по причине перегруженности сервера платформы (например, данная ситуация возникает достаточно часто во время выхода важных новостей).
Ситуаций, могущих приводить к утрате управления торговыми роботами, множество [2,3], но, после их анализа, можно предложить универсальное решение, препятствующие возникновению катастрофических убытков, если трейдер теряет доступ к счету на некоторое время, причем время это — может измеряться не секундами или минутами, а днями. Достоинство решения —  стабилизация ситуации с незафиксированными убытками по открытым ордерам, окончательное решение по работе с залокированными ордерами принимается   трейдером. Техническое решение (для роботов платформы МТ4): торговый робот, работающий по алгоритму, разработанному в соответствии с торговой системой, не использует отложенные торговые ордера (выставляются ордера только типа OP_BUY или OP_SELL), в исполняемом коде данного робота присутствует функция, выставляющая отложенные ордера (OP_SELLSTOP или OP_BUYSTOP)  на заданном расстоянии (в пунктах) от размещенного рыночного ордера на покупку или продажу, таким объемом, чтобы при срабатывании отложенного ордера получался замок – например, был открыт ордер на продажу объемом 0.2 лота, отложенный ордер на покупку выставляется объемом 0.2 лота. Срабатывание отложенного ордера стабилизирует ситуацию, так как  растущий убыток по первому ордеру компенсируется прибылью по второму.
Отметим, что если ситуация не будет разрешена в течение суток, то незафиксированный убыток хотя и медленно, но будет расти за счет того, что взымается комиссия за перенос открытых позиций  на следующие сутки или комиссия (для безсвоповых типов счетов). Функция, выставляющая отложенные страхующе-локирующие ордера, может быть реализована в виде отдельной программы. Возможен вариант, когда программа работы с локирущими ордерами, отслеживающая работу со счетом, запускается на нескольких компьютерах  одновременно. Если фиксируется открытие нового ордера, то программа определяет его тип, размер лота и выставляет отложенный локирующий ордер равным лотом. В случае закрытия страхуемого ордера, программа удаляет соответствующий данному ордеру отложенный ордер. Так как все отложенные ордера выставляются только как ордера подстраховки (локирующие  при срабатывании), то это позволяет решать проблемы связанные с невозможностью выполнения операций оперативного управления. После восстановления контроля над счетом, трейдер, отключает программы для автоматизированной торговли на счете и в режиме ручного управления выполняет операции закрытия или модификации залокированных ордеров.  Для предотвращения выставления большого числа залокированных ордеров, в основной части алгоритма торгового робота целесообразно предусмотреть запрет на открытие новых ордеров, если по торгуемому финансовому инструменту зафиксировано наличие пары открытых залокированых ордеров (отложенный локирующий ордер сработал).
На странице портала http://www.mctrewards.ru/proekty/locker (эта страница – см. ниже) размещена дополнительная информация по рассматриваемой теме, представлены исходные кода ПО.
Список использованной литературы:
1. Особенности эволюционной оптимизации для хаотических процессов. Мусаев А.А., Ананченко И.В. В сборнике: Актуальные вопросы экономики и современного менеджмента. Сборник научных трудов по итогам международной научно-практической конференции. Инновационный центр развития образования и науки. Самара, 2015. с. 102-105.
2. Торговые роботы и управление в хаотических средах: обзор и критический анализ. Ананченко И.В., Мусаев А.А. Труды СПИИРАН. 2014. № 3 (34). c. 178-203.
3. Взаимодействие торгового робота с брокерской платформой: проблема помехоустойчивого управления. Мусаев А.А. Труды СПИИРАН. 2009. № 10. с. 176-183.
4. Вариант построения помехоустойчивого торгового робота. Мусаев А.А. Труды СПИИРАН. 2010. № 1 (12). С. 215-225.

Программа, реализующая алгоритм работы с одним ордером

Выставляется ордер (например, buy) с заданным тейкпрофитом (например, 20п). В этот же момент советник выставляет отложенный ордер (sell) на расстоянии ххх пунктов от выставленного ордера. Лот sell=buy.Если ордер закрывается по ТП, то советник удаляет отложенный ордер (sell). Если же срабатывает отложник (sell), то с  выставленного ордера (buy) убирается ТП. Получается замок.

lot = 0.01 Лот

tip = 0 0 – sell, 1- buy

profit = 100 Прибыль в пунктах

dist = 300 Дистанция на которую отступаем от выставленного ордера и ставим отложенный

magic = 12345 – магик, отмечает этим номером пару выставленных ордеров (открытый и отложенный).

//+——————————————————————+
//| locker.mq4
//| Copyright 2015, Igor Anantchenko
//| https://mstrewards.ru
//+——————————————————————+
#property copyright «Copyright 2015, Igor Anantchenko»
#property link «https://mstrewards.ru»
#property version «1.00″
#property strict
extern double lot = 0.01;
extern int tip = 0; // 0 – sell, 1- buy
int flag=0;
extern int profit = 100 ;
extern int dist = 300 ;
bool finih;
extern int magic = 12345;
double minstoplevel;
double takeprofit;
int ticket = -1;
int ticket1 = -1;
double price;
int order_type;
//+——————————————————————+
//| Expert initialization function |
//+——————————————————————+
int OnInit()
{
//—

if (tip==1)
{
//— получим минимальное значение Stop level
price=Ask;
takeprofit=NormalizeDouble(Bid+profit*Point,Digits);
//— размещаем рыночный ордер на покупку
ticket=OrderSend(Symbol(),OP_BUY,lot,price,3,0,takeprofit,» Locker «,magic,0,clrGreen);
if(ticket<0)
{
Print(«OrderSend завершилась с ошибкой #»,GetLastError());
}
else
{Print(«Функция OrderSend успешно выполнена»); flag=1;}
//—
}

if (tip==0)
{
//— получим минимальное значение Stop level
price=Bid;
takeprofit=NormalizeDouble(Ask-profit*Point,Digits);
//— размещаем рыночный ордер на продажу
ticket=OrderSend(Symbol(),OP_SELL,lot,price,3,0,takeprofit,» Locker «,magic,0,clrGreen);
if(ticket<0)
{
Print(«OrderSend завершилась с ошибкой #»,GetLastError());
}
else
{Print(«Функция OrderSend успешно выполнена»); flag=1;}
//—
}

if (flag==1 && tip == 0)
{
// Выставляем отложенный ордер на покупку

//— получим минимальное значение Stop level
price=Ask;
//— размещаем рыночный ордер на покупку 1 лота
ticket1=OrderSend(Symbol(),OP_BUYSTOP,lot,NormalizeDouble(Ask+dist*Point,Digits),3,0,0,» Locker «,magic,0,clrGreen);
if(ticket1<0)
{
Print(«OrderSend завершилась с ошибкой #»,GetLastError());
}
else
{Print(«Функция OrderSend успешно выполнена»); flag=2;}
//—

}

if (flag==1 && tip == 1)
{
// Выставляем отложенный ордер на продажу
//— получим минимальное значение Stop level
price=Bid;
//— размещаем рыночный ордер на продажу
ticket1=OrderSend(Symbol(),OP_SELLSTOP,lot,NormalizeDouble(Bid-dist*Point,Digits),3,0,0,» Locker «,magic,0,clrGreen);
if(ticket1<0) { Print(«OrderSend завершилась с ошибкой #»,GetLastError()); } else {Print(«Функция OrderSend успешно выполнена»); flag=2;}

//— }

//— return(INIT_SUCCEEDED); }

//+——————————————————————+

//| Expert deinitialization function |

//+——————————————————————+

//void OnDeinit(const int reason) {

//— }

//+——————————————————————+

//| Expert tick function |

//+——————————————————————+

void OnTick() { Comment(» Ордер 1 номер = «,ticket,» Ордер 2 номер = «,ticket1);

//—

if (flag==4) return;

if(OrderSelect(ticket, SELECT_BY_TICKET)==true)

{ if(OrderCloseTime()==0)

// Проверить не открыт ли второй ордер

//———————————————–

if(OrderSelect(ticket1, SELECT_BY_TICKET)==true)

{ order_type=OrderType(); if (order_type>1) return; // Первый ордер открыт, второй не открыт – ждать

// Второй ордер открыт – надо убрать стоп по профиту у первого ордера и при успехе сделать flag=4

bool res=OrderModify(ticket,OrderOpenPrice(),0,0,0,Blue);
if(!res)
Print(«Ошибка модификации ордера. Код ошибки=»,GetLastError());
else
flag=4;

}

//———————————————–
if(OrderCloseTime()!=0)
{
// Удалить второй ордер
finih=OrderDelete(ticket1,clrRed);
if(finih==true) flag=4;
}
}
else
Print(«OrderSelect() вернул ошибку – «,GetLastError());

}
//+——————————————————————+