Почти все трейдеры приходят на рынок для того, чтобы заработать денег, хотя есть и доля тех, кому важен не сам торговый результат, а участие в процессе, драйв.
Впрочем, получить удовольствие от процесса можно не только торгуя вручную, но и занимаясь разработкой автоматических торговых систем. Ведь создание торгового робота может быть таким же интересным занятием, как и чтение хорошего детектива.
В процессе разработки торгового алгоритма приходится решать множество технических вопросов, но среди них есть три самых важных, ключевых вопроса:
Ответ на первый вопрос необходим для выбора подходящего инструмента, он зависит от многих факторов, в том числе и от принципиальной возможности автоматизации торговой системы для данного рынка.
Второй вопрос подразумевает формализацию торговых правил, которые четко указывают моменты и направления для входа в рынок или для закрытия позиций.
А третий вопрос на этом фоне совсем простой — как совершать операции покупки и продажи на данном языке программирования?
Сейчас мы рассмотрим каким образом можно реализовать торговые операции в алгоритмической торговле на языке MQL5.
MQL5 является языком программирования торговых стратегий и имеет множество торговых функций для работы с ордерами, позициями и торговыми запросами. Таким образом, создание торговых роботов на MQL5 для алготрейдинга является наименее трудозатратным с точки зрения разработчика.
Средствами MQL5 вы можете создать торговый запрос и отослать его на сервер с помощью функций OrderSend() или OrderSendAsync(), получить результат его выполнения, просмотреть торговую историю, узнать спецификацию контракта для инструмента, обработать торговое событие и запросить еще множество другой необходимой информации.
Кроме того, на языке MQL5 можно писать собственные технические индикаторы и использовать уже встроенные, рисовать на графике любые разметки и построения, создавать собственный пользовательский интерфейс и многое другое.
Но это уже другая тема и вы можете посмотреть примеры реализации в многочисленных статьях.
Существует несколько основных типов торговых операций, которые вам могут понадобиться в торговом роботе:
Все эти операции реализуются с помощью функции OrderSend(), существует также и асинхронный вариант этой функции — OrderSendAsync(). Всё многообразие торговых операций описывается структурой MqlTradeRequest, содержащей описание торгового запроса.
Поэтому единственные трудности с торговыми операциями могут заключаться только в правильном заполнении структуры MqlTradeRequest и обработке результата выполнения запроса.
В соответствии с правилами вашей торговой системы вы можете совершить покупку или продажу по цене рынка (BUY или SELL), а можете поместить отложенный ордер на совершение покупки/продажи на некотором расстоянии от текущей цены рынка:
Типы этих стандартных ордеров соответствуют перечислению ENUM_ORDER_TYPE.
Кроме того, вам может понадобиться модифицировать или вовсе удалить отложенный ордер, это также делается с помощью функций OrderSend()/OrderSendAsync(). Изменение открытой позиций тоже не представляет сложности, так как происходит в результате совершения всё тех же торговых операций.
Если вы до этого представляли себе торговые операции как нечто сложное и запутанное, то теперь самое время изменить своё мнение. Мы покажем не только, как легко и просто программировать покупки и продажи в MQL5, но также подскажем, как работать с торговым счетом и свойствами символов. Торговые классы помогут нам.
Первым делом при запуске торгового робота в дело необходимо получить информацию о торговом счете, на котором он будет торговать. Так как мы пишем учебный код, то сделаем проверку на тот случай, если вдруг эксперт оказался запущен на реальном счете.
Для работы со счетом есть класс CAccountInfo, который как раз и разрабатывался для этих целей. Добавим в наш код подключение файла AccountInfo.mqh и объявим переменную этого класса account:
#include <Trade\AccountInfo.mqh> //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- объект для работы со счетом CAccountInfo account; //--- получим номер счета, на котором запущен советник long login=account.Login(); Print("Login=",login); //--- выясним тип счета ENUM_ACCOUNT_TRADE_MODE account_type=account.TradeMode(); //--- если счет оказался реальным, прекращаем работу эксперта немедленно! if(account_type==ACCOUNT_TRADE_MODE_REAL) { MessageBox("Работа на реальном счете запрещена, выходим","Эксперт запущен на реальном счете!"); return(-1); } //--- выведем тип счета Print("Тип счета: ",EnumToString(account_type)); //--- выясним, можно ли вообще торговать на данном счете if(account.TradeAllowed()) Print("Торговля на данном счете разрешена"); else Print("Торговля на счете запрещена: возможно, вход был совершен по инвест-паролю"); //--- выясним, разрешено ли торговать на счете с помощью эксперта 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("Balance=",account.Balance()," Profit=",account.Profit()," Equity=",account.Equity()); Print(__FUNCTION__," completed"); //--- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- }
Как видно из приведенного кода, с помощью переменной account в функции OnInit() можно получить много полезной информации. Вы можете добавить этот код в своего эксперта и вам будет гораздо проще разбирать логи при анализе его работы.
Результат запуска советника на конкурсном счете Automated Trading Championship 2012 показан на картинке.
Информацию о счете мы получили, но для совершения торговых операций нужно знать еще свойства символа, по которому мы собираемся торговать. Для этого предназначен еще один удобный класс CSymbolInfo с большим количеством методов.
Мы приведем в примере только небольшую их часть.
#include<Trade\SymbolInfo.mqh> //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- объект для получения свойств символа 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("StopsLevel=",symbol_info.StopsLevel()," pips, FreezeLevel=", symbol_info.FreezeLevel()," pips"); //--- получим количество знаков после запятой и размер пункта Print("Digits=",symbol_info.Digits(), ", Point=",DoubleToString(symbol_info.Point(),symbol_info.Digits())); //--- информация о спреде Print("SpreadFloat=",symbol_info.SpreadFloat(),", Spread(текущий)=", symbol_info.Spread()," pips"); //--- запросим тип исполнения ордеров, нет ли ограничений 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(), " (",symbol_info.CurrencyBase(),")"); //--- минимальный, максимальный размеры объема в торговых операциях Print("Volume info: LotsMin=",symbol_info.LotsMin()," LotsMax=",symbol_info.LotsMax(), " LotsStep=",symbol_info.LotsStep()); //--- Print(__FUNCTION__," completed"); //--- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- }
И на рисунке показаны свойства символа EURUSD на Чемпионате Automated Trading Championship. Таким образом, вы готовы перейти непосредственно к торговле.
Торговля в 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); }
Ну а теперь пришло время посмотреть, как CTrade помогает в торговых операциях.
Покупка/продажа по текущей цене
Часто в торговых стратегиях необходимо совершить покупку или продажу по текущей цене прямо сейчас. CTrade знаком с такой ситуацией и просит лишь необходимый объем торговой операции.
Все остальные параметры — цену открытия и название символа, уровни Stop Loss и Take Profit, комментарий к ордеру — можно не указывать.
//--- 1. пример покупки по текущему символу if(!trade.Buy(0.1)) { //--- сообщим о неудаче Print("Метод Buy() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод Buy() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
По умолчанию если имя инструмента не указано, CTrade будет использовать имя символа, на графике которого он запущен. Это очень удобно для простых стратегий.
Для мультивалютных стратегий вам необходимо каждый раз явно указывать символ, по которому будет проводиться торговая операция.
//--- 2. пример покупки по указанному символу if(!trade.Buy(0.1,"GBPUSD")) { //--- сообщим о неудаче Print("Метод Buy() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод Buy() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Можно указать все параметры ордера: уровни Stop Loss/Take Profit, цена открытия и комментарий.
//--- 3. пример покупки по указанному символу символу с заданными SL и TP double volume=0.1; // укажем объем торговой операции string symbol="GBPUSD"; // укажем символ, на котором проводится операция int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // пункт double bid=SymbolInfoDouble(symbol,SYMBOL_BID); // текущая цена для закрытия LONG double SL=bid-1000*point; // ненормализованное значение SL SL=NormalizeDouble(SL,digits); // нормализуем Stop Loss double TP=bid+1000*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(),")"); }
Напомним, Magic Number и допустимое проскальзывание мы задали при инициализации экземпляра CTrade, поэтому они не требуются. Хотя их тоже можно задавать непосредственно перед каждой торговой операцией, если это необходимо.
Выставление лимитного ордера
Для отправки лимитного ордера используется соответствующий метод класса BuyLimit() или SellLimit(). Для большинства случаев может подойти укороченный вариант, когда указываются только цена открытия и объем. Цена открытия для Buy Limit должна быть ниже текущей цены, а для Sell Limit должна быть выше.
То есть эти ордера используются для входа в рынок по лучшей цене, обычно в стратегиях с расчетом на отскок от уровня поддержки. При этом используется тот символ, на котором запущен эксперт:
//--- 1. пример установки отложенного ордера Buy LImit string symbol="GBPUSD"; // укажем символ, на котором выставляется ордер int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // пункт double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // текущая цена покупки double price=1000*point; // ненормализованное цена открытия price=NormalizeDouble(price,digits); // нормализуем цену открытия //--- все готово, отправляем на сервер отложенный ордер Buy Limit if(!trade.BuyLimit(0.1,price)) { //--- сообщим о неудаче Print("Метод BuyLimit() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод BuyLimit() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Можно использовать и более подробный вариант с указанием всех параметров: уровни SL/TP, время истечения, название инструмента и комментарий к ордеру.
//--- 2. пример установки отложенного ордера BuyLimit со всеми параметрами double volume=0.1; string symbol="GBPUSD"; // укажем символ, на котором выставляется ордер int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // пункт double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // текущая цена покупки double price=1000*point; // ненормализованное цена открытия price=NormalizeDouble(price,digits); // нормализуем цену открытия int SL_pips=300; // Stop Loss в пунктах int TP_pips=500; // 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_GTC,expiration,comment)) { //--- сообщим о неудаче Print("Метод BuyLimit() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод BuyLimit() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Ваша задача во втором варианте — правильно указать уровни SL и TP. Не забывайте, что для покупок уровень Take Profit должен быть выше цены открытия, а уровень Stop Loss — ниже цены открытия. Для ордеров Sell Limit всё наоборот.
Вы легко можете узнать о своей ошибке при тестировании эксперта на исторических данных, класс CTrade автоматически выводит в таких случаях сообщения (если вы сами не вызывали функцию LogLevel).
Выставление стопового ордера
Для отправки стопового ордера используются аналогичные методы BuyStop() и SellStop(). Цена открытия для Buy Stop должна быть выше текущей цены, а для Sell Stop должна быть ниже. Стоповые ордера используются в стратегиях, которые входят на прорыве некоего уровня сопротивления, а также для ограничения убытков.
Простой вариант:
//--- 1. пример установки отложенного ордера Buy Stop string symbol="USDJPY"; // укажем символ, на котором выставляется ордер int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // пункт double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // текущая цена покупки double price=1000*point; // ненормализованное цена открытия price=NormalizeDouble(price,digits); // нормализуем цену открытия //--- все готово, отправляем на сервер отложенный ордер Buy Stop if(!trade.BuyStop(0.1,price)) { //--- сообщим о неудаче Print("Метод BuyStop() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод BuyStop() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
И более подробный, когда нужно указать максимум параметров для отложенного ордера Buy Stop:
//--- 2. пример установки отложенного ордера Buy Stop со всеми параметрами double volume=0.1; string symbol="USDJPY"; // укажем символ, на котором выставляется ордер int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // пункт double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // текущая цена покупки double price=1000*point; // ненормализованное цена открытия price=NormalizeDouble(price,digits); // нормализуем цену открытия int SL_pips=300; // Stop Loss в пунктах int TP_pips=500; // 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_GTC,expiration,comment)) { //--- сообщим о неудаче Print("Метод BuyStop() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод BuyStop() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Для отправки ордера Sell Stop применяется соответствующий метод класса 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-1000*point,digits); double TP=NormalizeDouble(price+1000*point,digits); //--- заполним комментарий string comment="Buy "+_Symbol+" 0.1 at "+DoubleToString(price,digits); //--- все готово, делаем попытку открыть позицию на покупку if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,0.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(),")"); }
Для модификации у открытой позиции доступны только уровни Stop Loss и Take Profit. Это делается с помощью метода PositionModify().
//--- количество знаков после запятой 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-1000*point,digits); double TP=NormalizeDouble(price+1000*point,digits); //--- все готово, делаем попытку модифицировать позицию на покупку if(!trade.PositionModify(_Symbol,SL,TP)) { //--- сообщим о неудаче Print("Метод PositionModify() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод PositionModify() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Модификация и удаление ордера
Для изменения параметров отложенного ордера в классе CTrade предусмотрен метод OrderModify(), которому необходимо передать все требуемые параметры.
//--- тикет ордера указан только для примера, его нужно получить ulong ticket=1234556; //--- символ также указан для примера, его нужно получить string symbol="EURUSD"; //--- количество знаков после запятой 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-1000*point,digits); double TP=NormalizeDouble(price+1000*point,digits); //--- зададим срок действия одни сутки datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1); //--- все готово, делаем попытку модифицировать ордер if(!trade.OrderModify(ticket,price,SL,TP,ORDER_TIME_GTC,expiration)) { //--- сообщим о неудаче Print("Метод OrderModify() потерпел неудачу. Код возврата=",trade.ResultRetcode(), ". Описание кода: ",trade.ResultRetcodeDescription()); } else { Print("Метод OrderModify() выполнен успешно. Код возврата=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Вам необходимо получить тикет ордера, который необходимо изменить, и в зависимости от его типа указать правильные уровни Stop Loss и Take Profit. Кроме того, новая цена открытия должна быть также корректной по отношению к текущей цене.
Для удаления отложенного ордера достаточно знать его тикет:
//--- тикет ордера указан только для примера, его нужно получить ulong ticket=1234556; //--- все готово, делаем попытку модифицировать позицию на покупку 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. Большинство новичков в алготрейдинге начинают с изучения простых классических торговых систем, например, сигналов на пересечении скользящих средних.
Для этого вам потребуется научиться работать с техническими индикаторами — как их создавать и как их использовать в своем торговом роботе.
Рекомендуем прочитать статьи из разделов Индикаторы и Примеры->Индикаторы, начинайте читать с наиболее ранних по дате публикации, это позволит вам продвигаться от простого к сложному.
Чтобы сразу получить представление о том, как работать с индикаторами и начать их использовать, смотрите статью MQL5 для «чайников»: Получение значений технических индикаторов в своих экспертах.
В любом новом деле первые трудности по прошествии небольшого отрезка времени оказываются самыми простыми вопросами, которые вам приходилось и приходится решать.
Предложенные в статье способы по разработке торговых роботов на MQL5 предназначены в первую очередь новичкам, хотя многие опытные разработчики также могут найти для себя что-то новое и полезное.
Язык MQL5 предоставляет для алготрейдинга не только неограниченные возможности по программированию торговых стратегий, но и позволяет реализовывать их наиболее простым и быстрым способом.
Пользуйтесь торговыми классами из Стандартной библиотеки, а сэкономленное время пусть пригодится вам для более важных вещей, например, для ответа на извечный вопрос трейдера — что такое тренд и как его определить в режиме реального времени.
Испытал маааленький такой диссонанс от названия поста и его содержания, вернее, даже формы — ну там объем, обилие кода...
По тому как альтернативы по сути лишь собственная разработка!
Но не как не известные глючные API с которым лишь потеря времени
robomakerr, WinApi было продумано и ошибок как правило в релизах не было, тут же проблема на проблеме с новыми релизами, на днях пришлось старый код робота модернизировать, так он стал крашить память, в итоге пришлось отказаться от структур в пользу мизерного класса, времени на разбор просто жалко
при чем на сколько я понял проблема с ArrayInitialize и(или) ArrayResize при частом вызове
а еще проблема с чартами и GUI, если объектов данных (не на чарте) слишком много, то система просто не успевает все поудалять и в итоге ппц какой то ))
детских болячек масса
Похожую ошибку мы недавно исправили, в августе будет новый релиз.
забейте. я классом воспользовался и все реализовал через указатели ))
Вот посмотрите распространение по миру: синий в подвале — easylanguage https://trends.google.com/trends/explore?q=easylanguage,mql4,mql5
MQL5 является языком программирования. Их основа слабо меняется за десятки лет.
Я не критикую, не надо от меня отбиваться. Я подсказываю, в какую сторону было бы правильнее развиваться.
Странно думать, что мы что-то не знаем в EasyLanguage. Мы его оставили далеко позади больше 10 лет назад.
Интересно, процедурным программированием можно написать торгового бота?
а лучше связка Python <---> С++, сейчас сам этим занимаюсь ))
А MQL5 — это прикладной язык, специально предназначенный для нативной работы с рыночным окружением и торговыми заявками.
и вообще не пойму, зачем нужно было создавать копию С++, только с проблемами? из-за мнимой безопасности в окружении терминала? но ведь это не решает проблему, реверс через ассемблер ни кто не отменял ))
ps. если чем то оскорбил, приношу извинения заранее, но просто уже надоело подпрыгивать из-за проблем с каждым новым обновлением МТ5, стабильности нет, стандарты вы не хотите писать, вот поэтому у вас пополняется (временно) армия начинающих программистов, а потом они начинают понимать через несколько лет, что потратили время зря на МТ5 и в итоге уходят на другие платформы, либо пишут как вы выразились свои костыли, но при этом более функциональные чем МТ5
в МТ5 есть много качественных решений, но они сводятся к нулю проблемами и ограничениями в MQL5
Мы даем мощный инструмент в виде языка. Результаты его использования зависят от рук применяемого.
Так как вы знаете про кодобазу, маркет, фриланс и тд, то легко увидите, что мы как раз прислушиваемся к трейдерам.
Иначе не было бы такой популярности и распространения.
а насчет прислушиваться — введите многопоточность, решите массу проблем сразу с производительностью и не ссылайтесь на прикладной язык, Python тоже прикладной язык, но в нем многопоточность есть хоть и со скрипом GiL
Самый лучший трейдер смартлаба, а вы посмотрите сюда:
— 5 400 MQL программ в полных исходниках
— больше 12 000 MQL программ в магазине приложений
Ну и подробная документация на русском по MQL5 языку на 5626 страниц.
Одни и те же люди. Вот интересно с чем связана такая тенденция, не ужели тупинизм в РФ побеждает созидателей и творцов?
роботы добавляются постоянно ))
Одни и те же люди развиваются. Остальные получается льют и дальше к папке воровать.
как найду свой грааль, так же может не буду тут находиться ))
Немного слукавил, там средний счет маловат. Но среди них есть и те, кто квартиры льет
Вот Стандартная библиотека для MQL5 в исходниках:
— Математика
— OpenCL
— Базовый класс CObject
— Коллекции данных
— Шаблонные коллекции данных
— Файлы
— Строки
— Графические объекты
— Пользовательская графика
— Ценовые графики
— Научные графики
— Индикаторы
— Торговые классы
— Модули стратегий
— Панели и диалоги
Для проведения вычислений в разных областях математики предлагается несколько библиотек:
— Статистика – функции для работы с различными распределениями из теории вероятности
— Нечеткая логика – библиотека нечеткой логики, в которой реализованы системы нечеткого логического вывода Мамдани и Сугено
— ALGLIB – анализ данных (кластеризация, деревья решений, линейная регрессия, нейронные сети), решения дифференциальных уравнений, преобразования Фурье, численное интегрирование, задачи оптимизации, статистический анализа и многое другое.
Еще:
— Визуализируй это! Графическая библиотека в MQL5 как аналог plot из R
— Библиотека матричной алгебры LibMatrix
— Кроссплатформенная библиотека оригинальных математических функций
— Прогнозирование временных рядов в MetaTrader 5 при помощи библиотеки машинного обучения ENCOG
— ALGLIB — библиотека численного анализа
— Fuzzy — библиотека для работы с нечеткой логикой
— Практическая реализация цифровых фильтров на MQL5 для начинающих
— Ядерная оценка неизвестной плотности вероятности
— Взаимодействие MetaTrader 5 и MATLAB
— Simulink: в помощь разработчику эксперта
— Введение в теорию нечеткой логики
— Прогнозирование рыночных движений с помощью байес-классификации и индикаторов на основе сингулярного спектрального анализа
— Статистические распределения в MQL5 — берем лучшее из R и делаем быстрее
— Статистические рецепты для трейдера — Гипотезы
— Третье поколение нейросетей: «Глубокие нейросети»
— Статистические оценки
— Статистические распределения вероятностей в MQL5
— Random Decision Forest в обучении с подкреплением
и еще больше 600 статей с примерами и объяснениями.
а про Python даже писать не стоит, на нем прототипизация делается со скоростью звука ))
У нас версия 3.5, покрытая 55 000 строк юниттестов. Посмотрите в стандартную поставку терминала в /Scripts/UnitTests/Alglib.
Это стандартный пакет юниттестов самого Alglib, который у нас проходит постоянные проверки без ошибок.
Специально сейчас запустил юниттесты алглиба и ни одной ошибки.
разве я не указал проблему?
а в статбиблиотеках уже и не помню что пытался использовать, но в итоге пришлось писать свои реализации, если честно, то лень уже искать т.к. занят другим терминалом ))
ну и про свои юнит тесты — наверное стоит их пересмотреть и все таки увидеть проблему, библиотеки то выдаете в открытое пользование без достаточных тестов среди сообщества программистов
Про публичные юниттесты, включенные в состав терминала я уже написал. На тот же алглиб работает 55000 строк юниттестов. На стандартную математическую библиотеку 6800 строк юниттестов, на библиотеку нечеткой логики — 1300 строк.
Кроме того, у нас более 15000 собственных тестов(тестов, а не строк) проводятся над компилятором и системой исполнения.
я работаю один и если сейчас начну искать то придется бросить свою работу, а она меня кормит, хотите сами перепроверьте и найдете, не хотите, не надо, мне это не важно т.к. известные мне проблемы я обошел сам, но более ни чего стараюсь не писать на MQL5
впечатляет производительность, но видимо не полные тесты раз при выходе новых билдов у народа в кодах появляются проблемы, как пример проблемы с union, с момента их ввода я код нескольких классов переписывал раза три уже
да вы и сами знаете, что ваша справка это не принятый стандарт языка, раз вы постоянно меняете поведение сущностей и синтаксис языка, не публикуя этих изменений, народ только наткнувшись на проблему начинает выяснять и иногда с высокого позволения Рената, ответ получают, но чаще заканчивается все банами на форуме
как думаете, сколько отсеивается после нескольких банов народа?
ps. хотите обижайтесь, хотите нет, но есть принятое сообществом программистов словосочетание — го… код, судя по объему кода, который вы приводите в пример и судя по проблемам, вы либо не дорабатываете либо пишите го… код а не юнит-тесты
не вы лично конечно, а кто то в вашей команде
При выходе новых билдов мы развиваем язык, делая его более эффективным и безопасным. При этом конечно некоторые старые вольности отключаются.
С юнионом совсем все просто было — закрыли небезопасный хак.
Это уж совсем слабые аргументы у вас. Вкупе с какими-то банами выросшые в заведомо ошибочные заявления.
мне достаточно стабильных версий С++ и Python, ошибок там минимум, но для этого есть багрепорт, вольностей ни кто не допускает т.к. четко описаны стандарты и изменения в них, есть много примеров в сети по алгоритмам и коду, что еще нужно для счастья ))
Главное, Alglib зачищен от подозрений.
Для расширения кругозора:
— стабильных C/C++ нет и не было никогда
— переносимости среди C/С++ компиляторов практически не было и нет. или через дикую боль сотен и тысяч #ifdef
— развитие C++ сейчас идет через мучительное выдумывание новых зубодробительных возможностей в погоне заимствований из других языков
— про отсутствие вольностей в Питоне смешно. вы забыли тотальную несовместимость версий 2.x и 3.х в Питоне? Да там почти все переписывать приходится.
Кроме того, тот же Питон фрагментирован десятком подверсий типа CPython, IronPython, Jython и тд с разной степенью ограниченности и специализации.
а про версии Python так вы вообще зря говорите, на то они и имеют порядковые номера и язык хорошо развивается, 2-я ветка языка существует по моему до 19-го года, дальше не будет поддерживаться т.к. 3-я ветка когда была реализована, то это была именно замена 2-й ветке, у вас то тоже mql5, а не mql, понимаете о чем я?
кстати в апреле в Канаде именно Россияне предложили в С++17 идеи из Python т.к. это удобнее и теперь эти идеи закреплены в стандарте, язык то развивается и это хорошо, а не стоит на месте как mql5, который вы передрали с С++ и теперь пытаетесь всем сказать что это чистая ваша разработка не откуда не заимствованная, сами то думаете с чем сравниваете?
вы еще форточников обвините за C# в который был так же заимствован синтаксис Си и С++, и то что там реализовали более удобный интерфейс в .NET Framework, это ведь развитие языка
косяки есть всегда и везде, но у вас развитие буксует из-за того, что начинаете спорить, а не прислушиваетесь, вон сколько уже нафлеймили тут, а воз и ныне там ))
MetaQuotes Language 5 (MQL5) — язык программирования технических индикаторов, торговых роботов и вспомогательных приложений для автоматизации торговли на финансовых рынках. MQL5 является современным языком высокого уровня и разработан MetaQuotes Software Corp. для собственной торгово-информационной платформы. Синтаксис языка максимально близок к С++ и позволяет писать программы в стиле объектно-ориентированного программирования (ООП).
Мы наоборот постоянно говорим, что он близок к С++ по синтаксису и максимально безопасен. Он специализирован для торговой платформы.
Мы потратили 17 лет на эволюцию языков MQL, MQL2, MQL4 и MQL5, чтобы иметь полное право на авторство и обоснованность своих позиций.
Без обид, но мы в разных категориях для технологического спора.
ps. кстати почему не напишите аналог STL?
В частности:
кроме того, у вас нет operator new, а без этого вы не реализуете аллокаторы правильно, из-за чего страдает производительность
прим:operator new разделы Стандарта С++
5.3.4
The new-expression attempts to create an object of the type-id (8.1) or new-type-id to which it is applied. /*дальше нам не интересно*/
18.6.1
void* operator new(std::size_t size) throw(std::bad_alloc);
Effects: The allocation function called by a new-expression (5.3.4) to allocate size bytes of
storage suitably aligned to represent any object of that size /*дальше нам не интересно*/
ps. дальнейший диалог думаю тут не стоит продолжать т.к. я устал что то вам писать, а вы в ответ не принимаете ни чего, приводя свои аргументы, нужно просто признать, что есть проблемы, без них в работе ни куда, но у вас их слишком много и это факт
еще раз скажу — народ от МТ5 уходит не просто так
Константин, не аналог, но дает часть функционала.
Вы почему-то зациклились на требованиях дать как в том или ином языке. Но языки и системы разные со своим прикладным направлением. В этом их суть — оптимизация по прикладной области.
MQL5 оптимизирован в прикладной области торгового языка. На его специализацию и шлифовку потратили уже 17 лет с первой версии MQL в 2001. Эволюция постоянно проверялась на практике.
Оператор new конечно же существует. Странные вещи вы пишите.
Вообще желательно пользоваться поиском по документации. Там не зря 5600 страниц описания.
Для отрезвления, вы читали штатную документацию по какому-либо языку программирования в тысячи страниц? А мы потратили годы на ее проработку и написание. Не забыв профессионально перевести еще на 9 крупнейших языков.
По популярности вы ошибаетесь, так как она наоборот растет. Почитайте хотя бы новости: https://www.metatrader5.com/ru/news в дополнении к статистике MQL5.community.
про популярность МТ5 вы пишите постоянно, но я не знаю нормально торгующих трейдеров с крупными депозитами на этом терминале, с мелкими счетами полно, но это лишь подтверждает то, что я писал выше
Защита памяти — основа защищенного языка. Поэтому переопределять new у нас нельзя.
Не путайте и не требуйте функционала максимально опасного языка С++ от защищенной платформы.
Как раз наличие защищенного языка позволило нам так распространить свои платформы по всему миру. Язык не позволяет написать троянов, программы защищены от взлома и работают в отдельных песочницах внутри терминала с 2001 года.
Когда разработчик пишет робота или индикатор на продажу, то он уверен, что его программу не украдут. Когда покупатель или просто пользователь скачивает эксперта, он уверен, что там нет трояна.
Я не хочу показаться невежливым, но при позиции «лично я не знаю трейдеров, я живу в России, я не знаю что творится в мире» никак нельзя делать такого рода утверждения.
Нельзя так игнорировать действительность, когда в наличии столько доказательств со стороны MetaTrader + MQL.
1.про максимально опасный язык С++
2. про пеереопределение new
3. про полностью управляемый и защищенный язык mql5
по п.1 - у вас в команде наверное мозахисты, если вы взяли за основу именно этот синтаксис ))
по п.2 — вы видимо не понимаете о чем я вам написал про operator new, там не переопределение new или operator, а совершенно другой оператор с другим поведением, если интересно, то прочтите в стандарте про new, operator и operator new, может тогда поймете о чем я говорю, а еще для примера откройте исходник аллокаторов в stl::list
я хотел пару лет назад написать библиотеку контейнеров максимально приближенную по синтаксису к STL, но именно этот момент с operator new в аллокаторах не смог обойти
по п.3 — вы видимо крутитесь в своем мирке и не знаете, что МТ4 и МТ5 уже реверснули и люди пользуют их, ссылки на это давать не буду, но поройтесь в сети TOP, может тогда поймете что изобретаете велосипед, я имею в виду mql5, а не использование того же C# или Python или Java, про С++ не говорю т.к. он для вас очень ужасный и опасный ))
кроме того скомпилированные роботы вскрываются, хотя и дороже это теперь, чем было года 4 назад, говорю же — реверс и ассемблер ни кто не отменял
Поверхностные знания и полное отсутствие прямых доказательств утомляют.