Многие трейдеры на Московской бирже хотели бы автоматизировать свои торговые алгоритмы, но не знают с чего начать. А ведь давно есть проработанные решения, которые максимально облегчают первые шаги в алготрейдинге.
Язык MQL5 изначально поддерживает все торговые возможности платформы MetaTrader 5 — в нем множество торговых функций для работы с ордерами, позициями и торговыми запросами. При этом не имеет значения, на каком рынке вы торгуете - фьючерсы, акции, опционы и т.д.
Средствами MQL5 вы можете создать торговый запрос и отослать его на сервер с помощью функций OrderSend() или OrderSendAsync(), получить результат его выполнения, просмотреть торговую историю, узнать спецификацию контракта для инструмента, обработать торговое событие и получить еще множество другой необходимой информации.
Существует несколько основных типов торговых операций, которые вам могут понадобиться в торговом роботе:
Все эти операции реализуются с помощью функции OrderSend(), существует также и асинхронный вариант этой функции — OrderSendAsync(). Всё многообразие торговых операций описывается структурой MqlTradeRequest, содержащей описание торгового запроса. Поэтому единственные трудности с торговыми операциями могут заключаться только в правильном заполнении структуры MqlTradeRequest и обработке результата выполнения запроса.
В соответствии с правилами вашей торговой системы вы можете совершить покупку или продажу по цене рынка (BUY или SELL), а можете поместить отложенный ордер на совершение покупки/продажи на некотором расстоянии от текущей цены рынка:
Типы этих стандартных ордеров соответствуют перечислению ENUM_ORDER_TYPE.
Кроме того, вам может понадобиться модифицировать или вовсе удалить отложенный ордер, это также делается с помощью функций OrderSend()/OrderSendAsync(). Изменение открытой позиций тоже не представляет сложности, так как происходит в результате совершения всё тех же торговых операций.
В этой статье мы покажем не только, как легко и просто программировать покупки и продажи в MQL5, но также подскажем, как работать с торговым счетом и свойствами символов. В этом нам помогут торговые классы Стандартной библиотеки.
Первым делом при запуске торгового робота в дело необходимо получить информацию о торговом счете, на котором он будет торговать.
Для работы со счетом есть класс CAccountInfo, который как раз и разрабатывался для этих целей. Добавим в наш код подключение файла AccountInfo.mqh и объявим переменную этого класса account:
#include <Trade\AccountInfo.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- объект для работы со счетом CAccountInfo account; //--- получим номер счета, на котором запущен советник long login=account.Login(); Print("Login=",login); //--- выведем валюту счета Print("Валюта счета: ",account.Currency()); //--- выведем баланс и текущую прибыль на счете Print("Balance=",account.Balance()," Profit=",account.Profit()," Equity=",account.Equity()); //--- выведем тип счета Print("Тип счета: ",account.TradeModeDescription()); //--- выясним, можно ли вообще торговать на данном счете if(account.TradeAllowed()) Print("Торговля на данном счете разрешена"); else Print("Торговля на счете запрещена: возможно, вход был совершен по инвест-паролю"); //--- режим вычисления маржи Print("Режим вычисления маржи: ",account.MarginModeDescription()); //--- выясним, разрешено ли торговать на счете с помощью эксперта if(account.TradeExpert()) Print("Автоматическая торговля на счете разрешена"); else Print("Запрещена автоматическая торговля с помощью экспертов и скриптов"); //--- допустимое количество ордеров задано или нет int orders_limit=account.LimitOrders(); if(orders_limit!=0)Print("Максимально допустимое количество действующих отложенных ордеров: ",orders_limit); //--- выведем имя компании и сервера Print(account.Company(),": server ",account.Server()); Print(__FUNCTION__," completed"); //--- }
Как видно из приведенного кода, с помощью переменной account в функции OnInit() можно получить много полезной информации. Вы можете добавить этот код в своего эксперта и вам будет гораздо проще разбирать логи при анализе его работы.
Результат запуска скрипта показан на картинке.
Информацию о счете мы получили, но для совершения торговых операций нужно знать еще свойства актива, по которому мы собираемся торговать. Для этого предназначен еще один удобный класс CSymbolInfo с большим количеством методов. Мы приведем в примере только небольшую их часть.
#include<Trade\SymbolInfo.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- объект для получения свойств символа CSymbolInfo symbol_info; //--- зададим имя символа, для которого будем получать информацию symbol_info.Name(_Symbol); //--- получим текущие котировки и выведем symbol_info.RefreshRates(); Print(symbol_info.Name()," (",symbol_info.Description(),")", " Bid=",symbol_info.Bid()," Ask=",symbol_info.Ask()); //--- получим количество знаков после запятой и размер пункта Print("Digits=",symbol_info.Digits(), ", Point=",DoubleToString(symbol_info.Point(),symbol_info.Digits())); //--- запросим тип исполнения ордеров, нет ли ограничений Print("Ограничения на торговые операции: ",EnumToString(symbol_info.TradeMode()), " (",symbol_info.TradeModeDescription(),")"); //--- выясним режим заключения сделок Print("Режим исполнения сделок: ",EnumToString(symbol_info.TradeExecution()), " (",symbol_info.TradeExecutionDescription(),")"); //--- выясним способ вычисления стоимости контрактов Print("Вычисление стоимости контракта: ",EnumToString(symbol_info.TradeCalcMode()), " (",symbol_info.TradeCalcModeDescription(),")"); //--- размер контракта Print("Размер стандартного контракта: ",symbol_info.ContractSize()); //--- размер начальной маржи для 1 контракта Print("Начальная маржа для стандартного контракта: ",symbol_info.MarginInitial()," ",symbol_info.CurrencyBase()); //--- минимальный, максимальный размеры объема в торговых операциях Print("Volume info: LotsMin=",symbol_info.LotsMin()," LotsMax=",symbol_info.LotsMax(), " LotsStep=",symbol_info.LotsStep()); //--- Print(__FUNCTION__," completed"); }
И на рисунке показаны свойства символа Si-6.16 из секции срочного рынка Московской биржи (FORTS). Теперь вы готовы перейти непосредственно к торговле.
Для отправки торговых приказов в языке MQL5 существует две функции — OrderSend() и OrderSendAsync(). На самом деле это две реализации одной функции. Если OrderSend() отправляет торговый запрос и ждет результата его выполнения, то асинхронная OrderSendAsync() просто выстреливает запрос и позволяет работать программе дальше, не дожидаясь ответа торгового сервера. Таким образом, торговать в MQL5 действительно просто, достаточно использовать только одну функцию для всех торговых операций
Обе функции получают в качестве первого параметра структуру MqlTradeRequest, которая содержит более десятка полей. Состав требуемых полей зависит от типа торговой операции, поэтому не все поля требуется заполнять. В случае неправильного значения или отсутствия обязательного поля запрос не пройдет проверку в самом терминале и просто не будет отправлен на сервер. При этом 5 из этих полей требуют указания корректного значения из предопределенных перечислений.
Столь большое количество полей торгового запроса вызвано необходимостью описать множество свойств ордера, которые могут меняться в зависимости от политики исполнения, времени истечения и некоторых других параметров. Вам не понадобится заучивать все эти тонкости, просто используйте готовый класс CTrade. Вот так примерно может выглядеть использование этого класса в вашем торговом роботе:
#include<Trade\Trade.mqh> //--- объект для проведения торговых операций CTrade trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- зададим MagicNumber для идентификации своих ордеров int MagicNumber=123456; trade.SetExpertMagicNumber(MagicNumber); //--- установим допустимое проскальзывание в пунктах при совершении покупки/продажи int deviation=10; trade.SetDeviationInPoints(deviation); //--- режим заполнения ордера, нужно использовать тот режим, который разрешается сервером trade.SetTypeFilling(ORDER_FILLING_RETURN); //--- режим логирования: лучше не вызывать этот метод вообще, класс сам выставит оптимальный режим trade.LogLevel(1); //--- какую функцию использовать для торговли: true - OrderSendAsync(), false - OrderSend() trade.SetAsyncMode(true); //--- return(0); }
Для торговли на бирже как правило используется режим исполнения ORDER_FILLING_RETURN. Справка гласит:
Данный режим используется только в режимах «Исполнение по рынку» и «Биржевое исполнение»: для рыночных (ORDER_TYPE_BUY и ORDER_TYPE_SELL), лимитных и стоп-лимитных ордеров (ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT, ORDER_TYPE_BUY_STOP_LIMIT и ORDER_TYPE_SELL_STOP_LIMIT). В случае частичного исполнения рыночный или лимитный ордер с остаточным объемом не снимается, а продолжает действовать.
Для ордеров ORDER_TYPE_BUY_STOP_LIMIT и ORDER_TYPE_SELL_STOP_LIMIT при активации будет создан соответствующий лимитный ордер ORDER_TYPE_BUY_LIMIT/ORDER_TYPE_SELL_LIMIT с типом исполнения ORDER_FILLING_RETURN.
Ну а теперь пришло время посмотреть, как CTrade помогает в торговых операциях.
Покупка/продажа по текущей цене
Часто в торговых стратегиях необходимо совершить покупку или продажу по текущей цене прямо сейчас. CTrade знаком с такой ситуацией и просит лишь необходимый объем торговой операции. Все остальные параметры — цену открытия и название символа, уровни Stop Loss и Take Profit, комментарий к ордеру — можно не указывать.
//--- 1. пример покупки по текущему символу if(!trade.Buy(1)) { //--- сообщим о неудаче Print("Метод Buy() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод Buy() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
По умолчанию если имя инструмента не указано, CTrade будет использовать имя символа, на графике которого он запущен. Это очень удобно для простых стратегий. Для робота, который торгует сразу на нескольких инструментах, вам необходимо каждый раз явно указывать символ, по которому будет проводиться торговая операция.
//--- 2. пример покупки по указанному символу if(!trade.Buy(1,"Si-6.16")) { //--- сообщим о неудаче Print("Метод Buy() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод Buy() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Можно указать все параметры ордера: уровни Stop Loss/Take Profit, цена открытия и комментарий.
//--- 3. пример покупки по указанному символу символу с заданными SL и TP double volume=1; // укажем объем торговой операции string symbol="Si-6.16"; // укажем символ, на котором проводится операция int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // пункт double bid=SymbolInfoDouble(symbol,SYMBOL_BID); // текущая цена для закрытия LONG double SL=bid-100*point; // ненормализованное значение SL SL=NormalizeDouble(SL,digits); // нормализуем Stop Loss double TP=bid+100*point; // ненормализованное значение TP TP=NormalizeDouble(TP,digits); // нормализуем Take Profit //--- получим текущую цену открытия для LONG позиций double open_price=SymbolInfoDouble(symbol,SYMBOL_ASK); string comment=StringFormat("Buy %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(open_price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); if(!trade.Buy(volume,symbol,open_price,SL,TP,comment)) { //--- сообщим о неудаче Print("Метод Buy() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод Buy() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Напомним, MagicNumber и допустимое проскальзывание мы задали при инициализации экземпляра CTrade, поэтому они не требуются. Хотя их тоже можно задавать непосредственно перед каждой торговой операцией, если это необходимо.
Выставление лимитного ордера
Для отправки лимитного ордера используется соответствующий метод класса BuyLimit() или SellLimit(). Для большинства случаев может подойти укороченный вариант, когда указываются только цена открытия и объем. Цена открытия для BuyLimit должна быть ниже текущей цены, а для SellLimit должна быть выше. То есть эти ордера используются для входа в рынок по лучшей цене, например, в стратегиях с расчетом на отскок от уровня поддержки. При этом используется тот символ, на котором запущен эксперт:
//--- 1. пример установки отложенного ордера BuyLimit string symbol="Si-6.16"; // укажем символ, на котором выставляется ордер int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // пункт double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // текущая цена покупки double price=ask-100*point; // ненормализованное цена открытия price=NormalizeDouble(price,digits); // нормализуем цену открытия //--- все готово, отправляем на сервер отложенный ордер Buy Limit if(!trade.BuyLimit(1,price)) { //--- сообщим о неудаче Print("Метод BuyLimit() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод BuyLimit() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Можно использовать и более подробный вариант с указанием всех параметров: уровни SL/TP, время истечения, название инструмента и комментарий к ордеру.
//--- 2. пример установки отложенного ордера BuyLimit со всеми параметрами double volume=1; string symbol="Si-6.16"; // укажем символ, на котором выставляется ордер int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // пункт double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // текущая цена покупки double price=ask-100*point; // ненормализованное цена открытия price=NormalizeDouble(price,digits); // нормализуем цену открытия int SL_pips=100; // Stop Loss в пунктах int TP_pips=100; // Take Profit в пунктах double SL=price-SL_pips*point; // ненормализованное значение SL SL=NormalizeDouble(SL,digits); // нормализуем Stop Loss double TP=price+TP_pips*point; // ненормализованное значение TP TP=NormalizeDouble(TP,digits); // нормализуем Take Profit datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1); string comment=StringFormat("Buy Limit %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); //--- все готово, отправляем на сервер отложенный ордер Buy Limit if(!trade.BuyLimit(volume,price,symbol,SL,TP,ORDER_TIME_DAY,expiration,comment)) { //--- сообщим о неудаче Print("Метод BuyLimit() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод BuyLimit() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Во втором варианте необходимо правильно указать уровни SL и TP. Не забывайте, что для покупок уровень Take Profit должен быть выше цены открытия, а уровень Stop Loss — ниже цены открытия. Для ордеров SellLimit всё наоборот. Вы легко можете узнать о своей ошибке при тестировании эксперта на исторических данных, класс CTrade автоматически выводит в таких случаях сообщения (если вы сами не вызывали функцию LogLevel).
Выставление стопового ордера
Для отправки стопового ордера используются аналогичные методы BuyStop() и SellStop(). Цена открытия для Buy Stop должна быть выше текущей цены, а для SellStop должна быть ниже. Стоповые ордера используются в стратегиях, которые входят на прорыве некоего уровня сопротивления, а также для ограничения убытков. Простой вариант:
//--- 1. пример установки отложенного ордера Buy Stop string symbol="RTS-6.16"; // укажем символ, на котором выставляется ордер int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // пункт double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // текущая цена покупки double price=ask+100*point; // ненормализованное цена открытия price=NormalizeDouble(price,digits); // нормализуем цену открытия //--- все готово, отправляем на сервер отложенный ордер Buy Stop if(!trade.BuyStop(1,price)) { //--- сообщим о неудаче Print("Метод BuyStop() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод BuyStop() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
И более подробный, когда нужно указать максимум параметров для отложенного ордера BuyStop:
//--- 2. пример установки отложенного ордера Buy Stop со всеми параметрами double volume=1; string symbol="RTS-6.16"; // укажем символ, на котором выставляется ордер int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // пункт double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // текущая цена покупки double price=ask+100*point; // ненормализованное цена открытия price=NormalizeDouble(price,digits); // нормализуем цену открытия int SL_pips=100; // Stop Loss в пунктах int TP_pips=100; // Take Profit в пунктах double SL=price-SL_pips*point; // ненормализованное значение SL SL=NormalizeDouble(SL,digits); // нормализуем Stop Loss double TP=price+TP_pips*point; // ненормализованное значение TP TP=NormalizeDouble(TP,digits); // нормализуем Take Profit datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1); string comment=StringFormat("Buy Stop %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); //--- все готово, отправляем на сервер отложенный ордер Buy Stop if(!trade.BuyStop(volume,price,symbol,SL,TP,ORDER_TIME_DAY,expiration,comment)) { //--- сообщим о неудаче Print("Метод BuyStop() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод BuyStop() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Для отправки ордера SellStop применяется соответствующий метод класса CTrade, главное — правильно указывать цены.
Работа с позицией
Вы можете вместо использования методов Buy() и Sell() пользоваться методами для открытия позиции. Правда, в этом случае придется указать больше деталей:
//--- количество знаков после запятой int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); //--- значение пункта double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); //--- получим цену покупки double price=SymbolInfoDouble(_Symbol,SYMBOL_ASK); //--- вычислим и нормализуем уровни SL и TP double SL=NormalizeDouble(price-100*point,digits); double TP=NormalizeDouble(price+100*point,digits); //--- заполним комментарий string comment="Buy "+_Symbol+" 1 at "+DoubleToString(price,digits); //--- все готово, делаем попытку открыть позицию на покупку if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,1,price,SL,TP,comment)) { //--- сообщим о неудаче Print("Метод PositionOpen() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод PositionOpen() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Для закрытия позиции достаточно указать имя инструмента, остальное класс CTrade сделает сам.
//--- закрываем позицию по текущему символу if(!trade.PositionClose(_Symbol)) { //--- сообщим о неудаче Print("Метод PositionClose() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод PositionClose() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
У открытой позиции можно изменять уровни StopLoss и TakeProfit. Это делается с помощью метода ModifyPosition().
//--- количество знаков после запятой int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); //--- значение пункта double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); //--- получим текущую цену Bid double price=SymbolInfoDouble(_Symbol,SYMBOL_BID); //--- вычислим и нормализуем уровни SL и TP double SL=NormalizeDouble(price-100*point,digits); double TP=NormalizeDouble(price+100*point,digits); //--- все готово, делаем попытку модифицировать позицию на покупку if(!trade.PositionModify(_Symbol,SL,TP)) { //--- сообщим о неудаче Print("Метод PositionModify() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод PositionModify() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Модификация и удаление ордера
Для изменения параметров отложенного ордера в классе CTrade предусмотрен метод OrderModify(), которому необходимо передать все требуемые параметры.
//--- проверим наличие ордера if(!OrderSelect(ticket)) { Print("Ордер #",ticket," не найден"); return; } //--- символ string symbol=OrderGetString(ORDER_SYMBOL); //--- количество знаков после запятой int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); //--- значение пункта double point=SymbolInfoDouble(symbol,SYMBOL_POINT); //--- получим цену открытия double price=OrderGetDouble(ORDER_PRICE_OPEN); //--- вычислим и нормализуем уровни SL и TP double SL=NormalizeDouble(price-200*point,digits); double TP=NormalizeDouble(price+200*point,digits); //--- все готово, делаем попытку модифицировать ордер // if(!trade.OrderModify(ticket,price,SL,TP,(ENUM_ORDER_TYPE_TIME)OrderGetInteger(ORDER_TYPE_TIME),0)) if(!trade.OrderModify(ticket,price,SL,TP,ORDER_TIME_DAY,0)) { //--- сообщим о неудаче Print("Метод OrderModify() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод OrderModify() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Вам необходимо получить тикет ордера, который необходимо изменить, и в зависимости от его типа указать правильные уровни StopLoss и TakeProfit. Кроме того, новая цена открытия должна быть также корректной по отношению к текущей цене.
Для удаления отложенного ордера достаточно знать его тикет:
//--- проверим наличие ордера if(!OrderSelect(ticket)) { Print("Ордер #",ticket," не найден"); return; } //--- все готово, делаем попытку удалить ордер if(!trade.OrderDelete(ticket)) { //--- сообщим о неудаче Print("Метод OrderDelete() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод OrderDelete() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
В классе также есть универсальный метод OrderOpen(), который может выставлять отложенные ордера любого типа. В отличие от специализированных методов BuyLimit, BuyStop, SellLimit и SellStop, он требует указывать больше обязательных параметров. Возможно, кому-то он покажется более удобным.
В этой статье мы показали простые приемы для программирования торговых операций покупки и продажи, а также работу с отложенными ордерами. Но в разделе Торговые классы есть еще несколько удобных помощников для разработчиков роботов на MQL5:
С помощью этих классов вы можете сосредоточиться только на торговой стороне вашей стратегии, сведя все технические вопросы к минимуму. Кроме того, класс CTrade можно использовать для изучения торговых запросов, например, под отладкой. И со временем вы можете создать на его основе собственные классы, в которых реализуете необходимую вам логику по обработке результатов выполнения торгового запроса.
Предложенные в статье способы по разработке торговых роботов на MQL5 предназначены в первую очередь новичкам, хотя многие опытные разработчики также могут найти для себя что-то новое и полезное.
Начните с выполнения простых скриптов (https://c.mql5.com/36/9/mql5-moex-first-steps.zip), и вы поймете, что создать торгового робота гораздо проще, чем вы думали!
Так держать!!!
Если же вопрос о маржа/го/плечо плавает, то это не от нас зависит — некоторые шлюзы так работают.
Главное проблемное место:
OrderSend() на самом деле тоже асинхронная. В том смысле, что после выхода из неё (и не важно что бы она не вернула) мы не знаем толком ничего!
Например:
1) ордер после OrderSend() почти 100% ещё далеко не на бирже, а где-то там у вас в на серверной части ещё только отправляется на биржу и совсем не факт, что он туда попадёт.
2) соответственно и удалять сразу после OrderSend() ордер нельзя (придёт абсолютно невнятная ошибка). Потому что в общем-то и удалять с вашей точки зрения нечего. Хотя! Тикет ваш уже есть! То есть надо бы по уму удалить процесс постановки ордера на биржу. Потому что просто чисто из торговой логике приоритет отмены торгового решения должен быть абсолютный! Понимаете? В любой момент выполнения программы я должен иметь контроль хотя бы над действием «удалить ордер!». Что может быть важнее? А вот нет! Ваша система меня благополучно пошлёт подальше.
3) опять же. Послав запрос «удалить ордер» через ту же OrderSend() и получив якобы положительный ответ я имею обман. На самом деле ордер нифига ещё не удалён с биржи. И опять же нет абсолютной гарантии, что будет удалён. Мало ли у вас там сервак глюкнет. Откуда я знаю? Вот это уже ваще беспредел!
Ну в целом вы наверное уже поняли мои претензии.
Я понимаю, что это сделано для того чтобы совместить в одном терминале кухонный механизм сделок и биржевой, плюс это позволяет ускорить работу роботов, однако на выходе мы имеем совсем не то, что заявлено. Роботов которые будут учитывать сложные ситуации протокола связи в МТ5 написать реально сложно! Приходится писать тысячи строк кода собственного торгового движка и в нём куча всяких исключений и противоречий. Красиво вот как вы обещаете не выйдет!
Или это будет работать «не всегда» =)
Fry (Антон), странно это, проверим.
В документации наглая ложь:
То есть действие якобы завершено. По факту это не так!
Сегодня мы имеем возврат из OrderSend() так быстро, как только можно вернуть тикет. При этом сам процесс установки/удаления ордера в режиме биржевого исполнения с огромной вероятностью не завершён!Fry (Антон), А с каких пор OrderSend() стал возвращать код 10008? Этот код мы можем получить только при асинхронной отправке т.е. OrderSendAsync() , и тогда мы уже проверяем все шаги постановки через события OnTrade() и OnTradeTransaction() которые нам и сообщают где именно наш приказ, а обычный OrderSend() это получение кода возврата 10009.
Единственное что не буду сейчас говорить об этом со 100% точностью, так как с этим вопросом разбирался сам очень давно, и может что то изменилось, но пока склонен к мнению что вы не до конца понимаете механизм отправки торгового приказа.
Просветите меня пожалуйста, буду благодарен.
Про коды возврата — вообще же бред там полный! Коды по сути для биржевого режима не соответствует описанию и названию константы. Там каша с реткодами и ошибками полнейшая сейчас!
Возможно даже вы и правы. Может быть приходит 10009. Сейчас лень проверять. Просто я-то давно сделал вот такую функцию:
И больше не заморачиваюсь. А какой смысл? Всё равно. Всё едино. Сути дела это не меняет. Нам всё равно надо проверять шаги постановки ордера после OrderSend(), через статусы. Потому что ещё раз повторяю — ордер нифига не на бирже и нифига не удалён. Это элементарно подтверждается простейшим кодом. После OrderSend() берём тикет и сразу пытаемся удалить ордер. Результат такого кода будет неопределённый (то работает, то выдаёт ошибку неверного запроса). Вот уже даже это нелогично. Дальше больше.
То что Вы мне пытаетесь сказать я и так понимаю… Ну либо из Ваших объяснений всё равно не пойму.
То что я написал Вы пропустили мимо ушей полностью и зацепились только за важные лично для Вас моменты.
Зацепились за слово «лень» и конкретный реткод.
Да какая разница 8 или 9?
Я же показал, что проверяю через функцию свою и даже код её привёл. Она вернёт true если был один из успешных реткодов. Мне вообще всё равно какой в данном случае!
Вопрос в другом и проблема совершенно в другом месте, а вы цепляетесь опять за одно и тоже.
Проверка показала, что для биржевого исполнения OrderSend на самом деле полусинхронно отрабатывался, возвращая более ранний промежуточный код состояния заявки.
Уже переписали, проверили и теперь OrderSend всегда синхронный. Исправление будет доступно в очередной бете на MetaQuotes-Demo в эту пятницу 3 июня.
Спасибо, что подняли вопрос!
Ура корпоративным блогам! =)
Сегодня как вопрос поставили перед разработчиками, сразу все поняли и тут же исправили.
Прочтите же наконец, чем отличаются типы ордеров.
kbrobot.ru, откройте стакан, пожалуйста, включая все режимы и вот вам все возможности ставить ордеры в любом месте, включая внутри спреда:
На первой же картинке этого поста графически показан где должен ставиться buy stop ордер.
Также я выше поставил скриншот стакана, где стоят десятки кнопочек с детальными тултипами/подсказками и указаниями какие типы ордеров с каждого уровня ставятся.
Лично вам было оказано столько внимания, включая выданные примеры кодов, что странно жаловаться на плохой техсаппорт.
Если да, то почему в тестере выдается Invalid price?
Это не рыночные лимитные ордера, а виртуальные. Они активируются на срабатывании при строго определенных условиях:
— BUY STOP при прорыве цены выше установленного уровня.
Обычно используются как ордеры на прорыв канала вверх. Например, есть ценовой канал 10000 / 11000, сейчас цена 10500/10510 и вы хотите купить на прорыве канала вверх. Для этого ставите BUY STOP 11000 и как только цена касается/пробивает этот уровень, торговый сервер делает BUY MARKET, выкупая что можно с рынка.
— SELL STOP при прорыве цены ниже установленного уровня.
Все аналогично, только при прорыве канала вниз.
В чем прелесть этих BUY STOP / SELL STOP ордеров? Они позволяют избавиться от написания простейших стратегий когда нужно раскидать несколько канальных/прорывных ордеров. Если еще и expiration использовать, то вообще получается удобно — выставил на пару часов вручную стратегию прорывов и никакого программирования.
Так как эти ордеры срабатывают на прорыв, то конечно же, у них есть жесткие условия их установки. Цены должны быть правильные и вариант «дайте мне выставить любую цену и если что, пусть мгновенно исполняется» для этих типов ордеров не подходит.
Как раз пару билдов назад ввели. Их даже вручную можно ставить прямо в скальперском стакане.
Например, чтобы поставить SELL LIMIT ниже рынка, надо зажать Ctrl и кликнуть на красной кнопке продажи любого ценового уровня ниже рынка. В результате выставится SELL LIMIT и тут же отработается.
Аналогично и BUY LIMIT.
Жаль только одного что многие просьбы трейдеров которые занимаются программированием роботов. Вы игнорируете.
Возможно мой опыт кому то пригодится. Не трате время на изучение этой торговой платформы. Я потратил много лет и жалею об этом.
Сергей Привалов, отжигали с претензиями и оскорблениями на ровном месте вы знатно!
Из бана в бан прыгали.
MetaQuotes Software, Никого не оскорблял. Врать не нужно. вам просто не нравилось многое, а когда нервы сдавали. Отправляли в бан. И слава богу, я рад этому, что больше не использую вашу торговую платформу. Очень рад. Я не просто трейдер, я видел вашу изнанку, видел настройки серверов, что и как вы можете делать с котировками… Хотите выложу, со скринами, с комментариями ?
З.Ы. Тут то мне бан вы не выдадите. Рот не заткнете. Это не ваш ресурс.
Потроллить решили в режиме «что я делаю не так». Количество повторений одного вопроса на протяжении недели и полное игнорирование детальных ответов не имеет другого вывода.
сдался — в сторону.
witwayer, с какого перепугу сдался?… хотя если в смысле надоело биться в стену Метаквотовцев. То да надоело ждать у моря погоды. Есть торговые платформы в 10 раз лучше.
Прибыльных роботов я уже 10 лет умею делать.
championship.mql4.com/2006/ru/users/Prival/reports
Искренне когда то считал, что это платформа для трейдеров… ошибался. дураком был. Она не для нас (трейдеров), она для кухонь и дилеров. Они платят, они (кухни) и заказывают музыку...
Я понимаю, что и моя агрессия и других людей возникает не на пустом месте. Просто мы тратили кучу часов своей жизни, чтобы пробиться сквозь «дурдом» той или иной системы. Обидно, когда ты видишь как можно сделать хорошо, а тебе говорят — отойди мальчик.
Лучший вариант потратить время на изучение C#. Еще раз скажу, лучше бы я С# эти годы изучал, чем метаквотовцев. Время самая большая ценность, и мне жаль что я бездарно его потратил.
Роботы это то что на С++ и крутится на серверах в дата центре с картами Solarflare. Все остальное — мясо
Ведь пока вы не встанете прямо в соседний порт со шлюзом/исполнителем/биржей с латенси в 0 микросекунд, вы будете работать с такого же порядка латенси 1-2-5 мс, как и остальные.
Другие ведь тоже легко могут поставить софт в датацентре поближе и сетевые карточки получше (вспоминаем аудиофилов с золотыми коннекторами и улыбаемся). И на таком порядке задержек уже и преимущества С++ перед MQL5 нивелируются.
А чтобы совсем порадовать — любой пользователь MT5 прямо из терминала легко в пару кликов отправляет своего робота в виртуальную среду(это ни в коем случае не виртуалка) в датацентр поближе к брокеру.
В результате получается, что любой трейдер ставит своего робота в 1-2 мс от своего брокера и платит за это всего лишь 10 долларов в месяц. Не надо мучаться с выбором провайдера и не надо платить сумашедшие деньги. Достаточно 1 минуты и все.
Вот видео на русском языке с детальным объяснением: https://www.youtube.com/playlist?list=PLltlMLQ7OLeKDR7ThKohKnhjApfjc9i2A
А вот как выглядит например хостинг для MOEX через Открытие. Латенси 2 мс (считается честное латенси до ядра торгового сервера в сетевом протоколе, а не банальный UDP пинг):
Так как не дошли, то вы находитесь ровно в такой же категории «плаваем в исполнении около 1-5 миллисекунд». А там и сетевые карты и С++ нивелируются на практике.
Мерило именно практика, а не теория.
У нас дай бог 1 мс потерь на шлюзе МТ5 в биржу. В реальности 0-1 мс.
Если на основе форумных домыслов строить рассуждения, то вся картина мира оказывается ошибочной. Реальность такова, что некоторые компании умеют делать свою работу очень хорошо.
Я утверждаю, что серверные потери внутри МТ5 серверов на маршрутизации сделок на MOEX на уровне 0-1 миллисекунды. Там нечему тормозить (и не тормозит), так как шлюз абсолютно прямой без значимой обработки — всем контролем заявок на МОЕХ занимается сама биржа.
Доказательства моих слов с полным приложенным пакетом всех скриптов для публичной проверки будут на следующей неделе опубликованы в отдельном посте тут. Как раз вместе с глубоким разбором скоростных характеристик реального исполнения сделок на MOEX через MT5.
Иначе ведь вышеприведенная фраза заявлена исключительно для доверчивых и непрофессиональных слушателей. Я то знаю, что вы имеете в виду совершенно незначащую часть работы, на что я уже дважды указывал.
Повторю еще раз, что пока вы не имеете латенси на уровне микросекунд (а имеете на уровне миллисекунд, что в тысячи раз больше), все экономии микросекунд на вашей стороне (где вы даже расходы мат логики робота не указываете, да и вариабельна она) является пшиком и самообманом.
Гикпорно HFT трейдеров в виде сетевых карточек или FPGA процессоров полностью аналогично вере аудиофилов в специальные кабеля, позолоту и якобы суперзвук от мегажелезки :)
Тогда вам многое неизвестно. Каждый кулик хвалит свое болото. Проблемы у метаквотовцем и со скоростью и с исполнением. Посмотрел ветку, они очередного трейдера там забанили, он тоже бился с ветряными мельницами, правду рассказывал. Искренне верил что это платформа для трейдера...
www.mql5.com/ru/forum/38456
Все что угодно готовы притянуть за уши, чтобы обвинить и навредить. Указанный автор откровенно переборщил с обвинениями, забыл про метод общения на техническом форуме на уровне предоставления технических обоснований, перешел к прямому подлогу и за это был забанен.
Выскажитесь лучше по статье. Смогли хоть МТ5 в скорости исполнения и функционала обогнать?
Метатрейдер, кстати, отличная программа — именно сам терминал. Его ниша — начинающие трейдеры и алготрейдеры, поглазеть на графики, быстро потестить идею в грубой прикидке. Если бы еще добавили возможность удобно добавить свои котировки — я бы даже, вероятно, платил немного рублей в месяц. Но это точно терминал не для реальной торговли и не для роботов на реальном счете.
Я выше объяснял, что пока вы не подключитесь к движку биржи на уровне задержек в 0-1 микросекунд, а будете как и все ходить в пределах 1-5 миллисекунд (это в 1000-5000 раз медленнее идеала), то никаких потерь на MT5 не получите.
В рамках 1-5 миллисекунд все примерно равны. Кроме того, могу разочаровать — никакие «профессионалы» в виде десятков или сотен пользователей не продвинулись дальше возможностей МТ5.
В реальности там страшненные велосипеды, охраняемые как золотой запас, простейшие стратегии на уровне бури в стакане, провалы как у всех и большое самомнение.
Как раз такие платформы как МТ5 революционизируют масс-маркет и поднимают общий уровень технологичности трейдинга. Именно на масс-маркете и открыто, а не для десятка велосипедистов втихую :)
Вы же не будете спорить, что в настоящее время МТ устойчиво ассоциируется с форекс-кухнями, которые кидают людей, рисуя свои котировки, заваливая реквотами и раздвигая спред.
Хотите, чтобы на МТ обратили внимание трейдеры, которые торгуют на реальных биржевых торгах, а не на квази-форексе и чтобы они начали спрашивать МТ у своих брокеров — идите с другой стороны, сделайте для начала терминал полезным для таких трейдеров. А для этого нужен свой архив котировок по всем инструментам МБ, в том числе до объединения бирж. Нужна возможность в терминале, а не на серверной стороне, создавать свои инструменты и заливать свои котировки. Реал-тайм котировки можно даже впоследствии поставлять за небольшую сумму денег.
Тогда метатрейдер может постепенно начать отбирать долю рынка у аналитических программ (амиброкер, метасток и тд). Это позволит также увеличить количество людей, которые хотят торговать через МТ.
Это очень и очень сложно, прикрутить систему данных (по сути громадное и универсальное рыночное окружение), индикаторы, отладку, тестирование и тд. А потом удостовериться, что все работает правильно.
Большинство собственных велосипедов умирает на этапе рутины, когда оказывается, что нужно сделать гору работы по поддержанию рыночного окружения. Да, именно умирает, а у авторов остается возможность еще несколько лет рассказывать на форумах про свои разработки, забывая уточнять, что они бывшие.
Посмотрите внимательнее на MetaTrader 5, пожалуйста. В нем и неограниченная история, и реальные тики и тестер и тд.
С Амиброкером и Метастоком вы на самом деле опоздали лет на 8. Посмотрите Google Trends — там давно и безраздельно MetaTrader. Давно все у всех отобрано:
Смотрел демо МТ5 лишь мельком, если правильно помню, фьючи видел там склеенные, а хотелось бы реальные без склейки и — свои инструменты без обновления их с сервера МТ.
Посмотрите Метатрейдер 5 внимательнее, пожалуйста. Иначе странно обсуждать и давать советы по системе, которую не знаете.
че нет то?
Превратим смарт лаб в ресурс копипаста!
Отличная идея для следующей статьи: скопировать 1 из разделов учебника!
У Открытия и БКС есть MT5 на срочном рынке. При его подключении брокеры напрочь запрещают работу с опционами на том счёте, даже через квик. Объяснить необходимость этого и когда это исправят, их тех. поддержки не могут. На форуме MQL молчат. Даже непонятно, чьё это требование — брокеров или MQ, непонятно, кому писать. Ведущий блога, может, вы что-то знаете?
Пока в МТ5 опционы не включим, проблема не решится.
Про управление позицией не совсем понятно. Это связано со сложной формулой ГО при связке опционы + фьючерсы? Но ведь текущую маржу для счёта нам транслирует биржа/брокер. А в терминале можно разрешить управление фьючерсной позицией и запретить опционной, как бы их разделить их.
Нигде не попадается информации какие ордера обрабатывает биржа, а какие — сервер MT
Нигде нет информации торговых операций
MetaQuotes Software, как по мне, то от добра добра не ищут. Было время, уходил на другие платформы (QUIK, TSLab), но ностальгия по функционалу и что не менее важно интерфейсу MT5 – просто не оставила мне выбора. Может быть, платформа MT5 не слишком проворна, зато удобна и надежна. Конечно, и здесь каждый может найти огрехи и неудобства, но в целом это рабочая лошадка, и хорошо выполняет свою работу. Но довольно лирики, а теперь непонятки...
Я знал о том, что даже если функция OrderSend() возвращает true, это не свидетельствует об успешном выполнении торговой операции. Но оказалось, что верно и обратное утверждение, если функция OrderSend() возвращает false, то это не свидетельствует также и о неуспешном выполнении торговой операции. Получается, неважно что возвращает функция OrderSend(), все равно надо копать глубже и выяснять уже по вторичным половым признакам — состоялась или нет передача ордера на биржу.
Итак, 2 июня 2016 г., в 13:45:54, робот отправляет на сервер рыночный ордер на продажу 1 лот Si-6.16 с помощью функции OrderSend(). Однако функция OrderSend() возвращает false. Вот небольшой код, который выполняет и контролирует отправку ордера на биржу:
ResetLastError();
if (!OrderSend(sTradeRequest, sTradeResult)) {
err=GetLastError();
Print(«Структура торгового запроса не прошла базовую проверку, err=», err, " ", ErrorToString(err));
return(0);
}
В результате в журнал попадает сообщение:
13:45:54.611 Si-6.16 sell 1 лот. Структура торгового запроса не прошла базовую проверку, err=4756 Не удалось отправить торговый запрос.
Однако, каким-то непонятным способом ордер все-таки пробирается на биржу, что подтверждается приведенными ниже выписками из таблицы ордеров и сделок:
2016.06.02 13:45:59 #36796287 Si-6.16 sell 1.00 / 1.00 market 2016.06.02 13:45:59 filled
2016.06.02 13:45:59 #25905779 sell 1.00 67159 #36796287
Вопрос автору топика, исправление в бете на MetaQuotes-Demo от пятницы 3 июня, о котором говорилось выше, решает эту проблему или она остается?
Проверьте все логи, включая вкладки Experts и Journal, пожалуйста. Есть ли в Journal реконнект к серверу в этот момент? Приведите два более полных лога за указанное время из Experts и Journal, пожалуйста.
Этот код ответа можно получить во время проблем с сетью/реконнектом, когда пакет улетел, а ответ уже не получен из-за сетевого разрыва.