Типичные ошибки и способы их устранения при работе с торговым окружением

 
В данной теме будем обсуждать часто встречающиеся ошибки при работе с торговым окружением терминала в тех или иных алгоритмах, методы их устранения и избегания в дальнейшем.
 
Ок.
Для затравки:
Советник анализирует направление (или профит, ... ) последнего закрывшегося ордера. Последний закрылся 2 суток назад.
А трейдер выставил глубину истории 1 день.
OrdersHistoryTotal() не видит уже тот ордер.
Решение?
 
Andrei Fandeev:
Ок.
Для затравки:
Советник анализирует направление (или профит, ... ) последнего закрывшегося ордера. Последний закрылся 2 суток назад.
А трейдер выставил глубину истории 1 день.
OrdersHistoryTotal() не видит уже тот ордер.
Решение?

Для МТ4 - по-моему, однозначно, при запуске советника выдавать пользователю предупреждение о необходимости использования требуемой глубины истории.

МТ5 позволяет загрузить историю на нужную глубину.

 
Artyom Trishkin:

Для МТ4 - по-моему, однозначно, при запуске советника выдавать пользователю предупреждение о необходимости использования требуемой глубины истории.

Полагаться на пользователя - плохая затея. Нужна проверка.
Артём, в МТ4 не нашёл Get для получения значения установленной глубины истории.
Её действительно невозможно получить программно?

 
Andrei Fandeev:

Полагаться на пользователя - плохая затея. Нужна проверка.
Артём, в МТ4 не нашёл Get для получения значения установленной глубины истории.
Её действительно невозможно получить программно?

Действительно.

 

Есть две парадигмы работы с торговым окружением, написания советников.

  1. Событийные входы (OnTick, OnTimer и т.д.) зависят друг от друга. Есть информация, которую ОБЯЗАТЕЛЬНО (не для ускорения, вроде кеша, а для работоспособности) нужно иметь между событиями. Например, нужно сохранить результат OrderSendAsync и использовать его в OnTradeTransaction. Кеши - это НЕ обязательная информация и используются только для ускорения. Поэтому их не учитываем сразу.
  2. Событийные входы (OnTick, OnTimer и т.д.) НЕ зависят друг от друга. Каждый вход с чистого листа. Примерно, как Скрипт, который Вы запускаете самостоятельно на каждое событие.
В MT4, наверное, все можно было решить через второй вариант. MT5 - не так однозначно.


Преимущества второго варианта перед первым

  • Можно в любом месте (штатно и нештатно) завершить работу алгоритма.
  • В любом месте запустить/продолжить.
  • Высокая надежность.


Недостатки

  • В Тестере будет уступать по производительности первому варианту.
  • Логика "с чистого листа" заставляет писать несколько "нелогичный" код, что требует привычки на первом этапе.

 

Как сравнить API торговых окружений? Представим большое количество разнообразных ТС. И представим свой идеальный виртуальный API, который бы позволял минимальными усилиями воплощать ТС в надежном коде.

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


MT4 и MT5 по этому критерию являются отличными API. Сложны только исходные API, но они позволяют (нет архитектурных/технических ограничений) написать великолепную обертку, а значит хороши.

Поэтому, когда говорят, что MT5 сложнее MT4 - имеют в виду, что не встретили еще MT5-обертки (возможно, она еще не написана), которая настолько же удобна, как используемая ими обертка в MT4.


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

 

У MT4 исходный API довольно высокоуровневый, поэтому мало кто пытается написать универсальную более удобную обертку. Но с MT5 дела обстоят иначе - исходный низкоуровневый API просто ТРЕБУЕТ написания хоть какой-то обертки. Поэтому в данной теме не много смысла обсуждать особенности каждой из оберток. Скорее, важно показать, что обертка все же потребуется. Какие особенности низкоуровневого API все же нужно учитывать при написании высокоуровневого API.

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

Исходя из этого утверждения нужно научиться писать MT5-код, ничем не уступающий по удобству MT4-коду. API обертки могут быть разными (как правило, это синтаксис), но архитектурно MT5 не должен уступать MT4, иначе теряется смысл выделенного утверждения. Поэтому более громоздкий MT5-код следует воспринимать, не как повод для критики, а как помощь для зашивания его в не менее удобную в работе обертку, чем MT4.

 
Возьмем простой MT4-шаблон ТС

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Организация цикла перебора ордеров

fxsaber, 2018.02.15 23:19

// Шаблон большинства ТС

#property strict // обязательно

// Сигнал на покупку
bool BuySignal( const string Symb ) { return(true); }

// Сигнал на продажу
bool SellSignal( const string Symb ) { return(false); }

// Находит ордер соответствующего типа
bool OrdersScan( const string Symb, const int Type )
{
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS) && (OrderType() == Type) && (OrderSymbol() == Symb))
      return(true);    
    
  return(false);  
}

// Торговое действие на сигнал
bool Action( const string Symb, const int Type, const double Lots = 1 )
{
  bool Res = true;    
  
  // Закрыли противоположные сигналу позиции
  while ((OrdersScan(Symb, 1 - Type)) && (Res = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 100)));

  // Открыли позицию по сигналу
  return(Res && !OrdersScan(Symb, Type) && OrderSend(Symb, Type, Lots, SymbolInfoDouble(Symb, Type ? SYMBOL_BID : SYMBOL_ASK), 100, 0, 0));
}

// Шаблон торговой стратегии
void Strategy( const string Symb )
{
  if (BuySignal(Symb))
    Action(Symb, OP_BUY);
  else if (SellSignal(Symb))
    Action(Symb, OP_SELL);
}

void OnTick()
{
  Strategy(_Symbol);
}

Он ни капли не показывает удобство MT4, а только служит исходной точкой сравнения. Эта та нижняя планка удобства, которую должна уметь держать MT5-обертка. Шаблон написан на основе второй парадигмы.


Казалось бы, пишем то же самое на MT5

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Организация цикла перебора ордеров

fxsaber, 2018.02.15 22:30

// Шаблон большинства ТС

#include <Trade/Trade.mqh>

// Сигнал на покупку
bool BuySignal( const string Symb ) { return(true); }

// Сигнал на продажу
bool SellSignal( const string Symb ) { return(false); }

// Находит позицию соответствующего типа
bool PositionsScan( const string Symb, const ENUM_POSITION_TYPE Type )
{
  for (int i = PositionsTotal() - 1; i >= 0; i--)
    if ((PositionGetSymbol(i) == Symb) && (PositionGetInteger(POSITION_TYPE) == Type))
      return(true);    
    
  return(false);  
}

// Торговое действие на сигнал
bool Action( const string Symb, const ENUM_POSITION_TYPE Type, const double Lots = 1 )
{
  static CTrade Trade;    
  bool Res = true;    
  
  // Закрыли противоположные сигналу позиции
  while ((PositionsScan(Symb, (ENUM_POSITION_TYPE)(1 - Type))) && (Res = Trade.PositionClose(PositionGetInteger(POSITION_TICKET))));

  // Открыли позицию по сигналу
  return(Res && !PositionsScan(Symb, Type) && (Type ? Trade.Sell(Lots, Symb) : Trade.Buy(Lots, Symb)));
}

// Шаблон торговой стратегии
void Strategy( const string Symb )
{
  if (BuySignal(Symb))
    Action(Symb, POSITION_TYPE_BUY);
  else if (SellSignal(Symb))
    Action(Symb, POSITION_TYPE_SELL);
}

void OnTick()
{
  Strategy(_Symbol);
}

По какой-то причине некоторые пишут больше кода для таких же ТС. Но на самом деле этот код справляется не хуже. Большинство ТС требуют написания только BuySignal и SellSignal. Больше ничего не нужно.

Пример шаблона специально написан с помощью СБ. Поэтому вопрос к спецам по MT5, код правильный?


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

 
fxsaber:

Нужно уметь правильно считывать торговое окружение.

Правильный вариант на простом примере

// Возвращает количество позиций по символу
int GetAmountPositions( const string Symb )
{
  int Res = 0;
  
  for (int i = PositionsTotal() - 1; i >= 0; i--)
    if (PositionGetSymbol(i) == Symb)
      Res++;
      
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderGetTicket(i) && (OrderGetInteger(ORDER_TYPE) <= ORDER_TYPE_SELL) &&
        !OrderGetInteger(ORDER_POSITION_ID) && (OrderGetString(ORDER_SYMBOL) == Symb))
      Res++;  

/*
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS) && (OrderType() <= OP_SELL) && (OrderSymbol() == Symb))
      Res++;
*/      
  return(Res);
}

Смысл вкратце таков: если стоит маркет-ордер, считай, что и это "позиция". В кавычках, потому что это оберточная позиция. Выделенный код, как правило, нигде не фигурирует. Но он позволяет избегать повторных открытий позиций. Самое интересное здесь - выделенное красным. Необходимость этой фишки не сразу осознать.

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


ЗЫ Вставьте этот код сюда и проверьте результат на демо-сервере.

 
fxsaber:

Правильный вариант на простом примере

Смысл вкратце таков: если стоит маркет-ордер, считай, что и это "позиция". В кавычках, потому что это оберточная позиция. Выделенный код, как правило, нигде не фигурирует. Но он позволяет избегать повторных открытий позиций. Самое интересное здесь - выделенное красным. Необходимость этой фишки не сразу осознать.

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


ЗЫ Вставьте этот код сюда и проверьте результат на демо-сервере.

Вопрос: что будет, если после отсылки торгового приказа до следующего тика маркет-ордер не будет выставлен сервером?

Причина обращения: