Обсуждение статьи "Передовые алгоритмы исполнения ордеров на MQL5: TWAP, VWAP и ордера Iceberg"

 

Опубликована статья Передовые алгоритмы исполнения ордеров на MQL5: TWAP, VWAP и ордера Iceberg:

Фреймворк MQL5, предоставляющий розничным трейдерам алгоритмы исполнения институционального уровня (TWAP, VWAP, Iceberg) с помощью унифицированного менеджера исполнения и анализатора эффективности для более плавного и точного разделения ордеров и аналитики.

«Конечно», — можете вы пожать плечами, — «но я не перемещаю институциональные суммы». Вот в чем загвоздка: вам это делать не обязательно. Независимо от того, используете ли вы половину лота или несколько мини-лотов, волатильность все равно может исказить ваше исполнение. Эти инструменты помогут вам:

  • Укрощение проскальзывания: Даже скромные ордера могут колебаться на нестабильных рынках.
  • Отточите своё преимущество: Многоуровневые исполнения часто дают вам более выгодную среднюю цену, чем одноразовая игра.
  • Оставайтесь в дзен-состоянии: Автоматизированные рабочие процессы исключают соблазн панически покупать или продавать.
  • Масштабируйте плавно: По мере роста вашего счета исполнение остается четким, независимо от того, насколько объемными становятся ваши ордера.
  • Оставайтесь незамеченными: В частности, ордера Iceberg скрывают истинный размер вашего ордера, заставляя любопытные алгоритмы гадать.

Сегодняшняя демократизированная среда означает, что те же технологии исполнения, которые когда-то требовали многомиллионных бюджетов, теперь могут работать на вашей персональной торговой станции. Добавив в свою платформу отточенный код на MQL5 для стратегий TWAP, VWAP и Iceberg, вы вооружитесь институциональной огневой мощью, не покидая при этом сферу розничной торговли.


Автор: N Soumik

 

Отличная статья!

Какие пары вы рекомендуете для этого Algo?

Какие таймфреймы? М5, М30 и т.д.

Какая сессия?

Спасибо и наилучшие пожелания

 
Отличная статья
Тестировал ваш алгоритм.
В файле ExecutionAlgorithm.mqh добавил эту строку request.type_filling = ORDER_FILLING_IOC; при выставлении ордера, чтобы исправить проблему с выставлением ордера.
Протестировал на M5, открыта только 1 сделка за 2 месяца, частичных ордеров не открывалось.
Протестировал на H1, не применил ни SL, ни TP, все сделки закрылись в убыток.

также при компиляции выдает предупреждения
возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 271 41
возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 272 22
возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 449 17
возможная потеря данных из-за преобразования типа из 'long' в 'double' PerformanceAnalyzer.mqh 222 17
возможная потеря данных из-за преобразования типа из 'long' в 'double' ExecutionManager.mqh 418 17


подскажите, как протестировать algo,
временные рамки. и любые другие рекомендации.
 
исправить предупреждения
также при компиляции выдает предупреждения
возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP .mqh 271 41
Возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP .mqh 272 22
возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP .mqh 449 17
возможная потеря данных из-за преобразования типа из 'long' в 'double' PerformanceAnalyzer .mqh 222 17
возможна потеря данных из-за преобразования типа из 'long' в 'double' ExecutionManager .mqh 418 17


я изменил строку кода
m_volumeProfile[intervalIndex] += rates[i].tick_volu

на
m_volumeProfile[intervalIndex] += (double)rates[i].tick_volume;

Это устранило предупреждения
Теперь нужна ваша помощь в отношении других моих запросов, как
Таймфрейм
А также
почему все сделки во время бэктеста приводят к убыткам
как проверить эту большую работу от вас.
 
i_vergo возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 271 41
возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 272 22
возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 449 17
возможная потеря данных из-за преобразования типа из 'long' в 'double' PerformanceAnalyzer.mqh 222 17
возможная потеря данных из-за преобразования типа из 'long' в 'double' ExecutionManager.mqh 418 17


подскажите, как протестировать algo,
временные рамки. и любые другие рекомендации.

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

 

Я согласен с Домиником, поскольку предупреждения - это всего лишь предупреждения. Результаты I_Virgo, вероятно, связаны с тем, что он использовал неправильный таймфрейм и валютную пару. Судя по отчету Back Test из почти 2000 баров, это должен был быть либо M1, либо M5 в качестве таймфрейма с неизвестной парой.

Было бы неплохо, если бы MQ добавил в отчет Back Test таймфрейм и валютную пару или пары, а также более подробно разделил результаты по парам, чтобы мы могли более точно воспроизвести результаты обратного тестирования автора, а также определить его применимость к парам Форекс. Также было бы очень полезно, если бы советник мог выводить текст на график во время работы.


Я также считаю, что это отличная статья, и планирую тщательно изучить ее в ожидании адаптации его методов к другим советникам


CapeCoddah

 
//+------------------------------------------------------------------+
//| Базовый класс для всех алгоритмов выполнения|
//+------------------------------------------------------------------+
class CExecutionAlgorithm
{
protected:
   string            m_symbol;           // Торговый символ
   double            m_totalVolume;      // Общий объем для выполнения
   double            m_executedVolume;   // Объем уже выполнен
   double            m_remainingVolume;  // Оставшийся объем для выполнения
   datetime          m_startTime;        // Время начала выполнения
   datetime          m_endTime;          // Время окончания выполнения
   int               m_slippage;         // Допущенное отставание в очках
   bool              m_isActive;         // Активен ли алгоритм в данный момент
   
   // Статистика
   double            m_avgExecutionPrice; // Средняя цена исполнения
   int               m_totalOrders;       // Общее количество размещенных заказов
   int               m_filledOrders;      // Количество выполненных заказов
   
public:
   // Конструктор
   CExecutionAlgorithm(string symbol, double volume, datetime startTime, datetime endTime, int slippage);
   
   // Деструктор
   virtual ~CExecutionAlgorithm();
   
   // Виртуальные методы, которые должны быть реализованы производными классами
   virtual bool      Initialize();
   virtual bool      Execute() = 0;
   virtual bool      Update() = 0;
   virtual bool      Terminate() = 0;
   
   // Общие методы
   bool              IsActive() { return m_isActive; }
   double            GetExecutedVolume() { return m_executedVolume; }
   double            GetRemainingVolume() { return m_remainingVolume; }
   double            GetAverageExecutionPrice() { return m_avgExecutionPrice; }
   
   // Вспомогательные методы
   bool              PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0);
   bool              ModifyOrder(ulong ticket, double price, double sl, double tp);
   bool              CancelOrder(ulong ticket);
   void              UpdateAverageExecutionPrice(double price, double volume);
   
   // Метод-помощник для получения соответствующего режима заполнения
   ENUM_ORDER_TYPE_FILLING GetFillingMode();
};

//+------------------------------------------------------------------+
//| Конструктор|
//+------------------------------------------------------------------+
CExecutionAlgorithm::CExecutionAlgorithm(string symbol, double volume, 
                                       datetime startTime, datetime endTime, 
                                       int slippage)
{
   m_symbol = symbol;
   m_totalVolume = volume;
   m_executedVolume = 0.0;
   m_remainingVolume = volume;
   m_startTime = startTime;
   m_endTime = endTime;
   m_slippage = slippage;
   m_isActive = false;
   
   m_avgExecutionPrice = 0.0;
   m_totalOrders = 0;
   m_filledOrders = 0;
}

//+------------------------------------------------------------------+
//| Деструктор|
//+------------------------------------------------------------------+
CExecutionAlgorithm::~CExecutionAlgorithm()
{
   // Очистите ресурсы, если это необходимо
}

//+------------------------------------------------------------------+
//| Инициализация алгоритма|
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::Initialize()
{
   // Проверка вводимых данных
   if(m_symbol == "" || m_totalVolume <= 0.0)
   {
      Print("Invalid inputs for execution algorithm");
      return false;
   }
   
   // Проверьте, существует ли символ
   if(!SymbolSelect(m_symbol, true))
   {
      Print("Symbol not found: ", m_symbol);
      return false;
   }
   
   // Сброс статистики
   m_executedVolume = 0.0;
   m_remainingVolume = m_totalVolume;
   m_avgExecutionPrice = 0.0;
   m_totalOrders = 0;
   m_filledOrders = 0;
   
   return true;
}

//+------------------------------------------------------------------+
//| Получите соответствующий режим заполнения для символа |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE_FILLING CExecutionAlgorithm::GetFillingMode()
{
   // Получение режимов заполнения символов
   int filling_modes = (int)SymbolInfoInteger(m_symbol, SYMBOL_FILLING_MODE);
   
   // Проверьте доступные режимы заполнения в порядке предпочтения
   if((filling_modes & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK)
      return ORDER_FILLING_FOK;
   else if((filling_modes & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC)
      return ORDER_FILLING_IOC;
   else
      return ORDER_FILLING_RETURN;
}

//+------------------------------------------------------------------+
//| Разместить заказ|
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0)
{
   // Проверка вводимых данных
   if(volume <= 0.0)
   {
      Print("Invalid order volume");
      return false;
   }
   
   // Подготовьте запрос
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   
   request.symbol = m_symbol;
   request.volume = volume;
   request.type = orderType;
   request.deviation = m_slippage;
   request.magic = 123456; // Магическое число для идентификации
   
   // Установите соответствующее действие и цену в зависимости от типа ордера
   if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
   {
      // Рыночный ордер
      request.action = TRADE_ACTION_DEAL;
      request.type_filling = GetFillingMode();
      
      if(orderType == ORDER_TYPE_BUY)
         request.price = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
      else
         request.price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
   }
   else
   {
      // Отложенный ордер
      request.action = TRADE_ACTION_PENDING;
      if(price <= 0.0)
      {
         Print("Price must be specified for pending orders");
         return false;
      }
      request.price = price;
   }
   
   // Отправить заказ
   if(!OrderSend(request, result))
   {
      Print("OrderSend error: ", GetLastError());
      return false;
   }
   
   // Проверьте результат
   if(result.retcode != TRADE_RETCODE_DONE)
   {
      Print("OrderSend failed with code: ", result.retcode, " - ", result.comment);
      return false;
   }
   
   // Обновление статистики
   m_totalOrders++;
   
   // Для рыночных ордеров обновлять статистику выполнения немедленно
   if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
   {
      m_filledOrders++;
      UpdateAverageExecutionPrice(request.price, volume);
      m_executedVolume += volume;
      m_remainingVolume -= volume;
   }
   
   Print("Order placed successfully. Ticket: ", result.order, " Volume: ", volume, " Price: ", request.price);
   
   return true;
}

//+------------------------------------------------------------------+
//| Изменить существующий заказ|
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::ModifyOrder(ulong ticket, double price, double sl, double tp)
{
   // Подготовьте запрос
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   
   request.action = TRADE_ACTION_MODIFY;
   request.order = ticket;
   request.price = price;
   request.sl = sl;
   request.tp = tp;
   
   // Отправьте запрос на модификацию
   if(!OrderSend(request, result))
   {
      Print("OrderModify error: ", GetLastError());
      return false;
   }
   
   // Проверьте результат
   if(result.retcode != TRADE_RETCODE_DONE)
   {
      Print("OrderModify failed with code: ", result.retcode, " - ", result.comment);
      return false;
   }
   
   Print("Order modified successfully. Ticket: ", ticket);
   
   return true;
}

//+------------------------------------------------------------------+
//| Отменить существующий заказ|
//+------------------------------------------------------------------+
bool CExecutionAlgorithm::CancelOrder(ulong ticket)
{
   // Подготовьте запрос
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   
   request.action = TRADE_ACTION_REMOVE;
   request.order = ticket;
   
   // Отправьте запрос на отмену
   if(!OrderSend(request, result))
   {
      Print("OrderCancel error: ", GetLastError());
      return false;
   }
   
   // Проверьте результат
   if(result.retcode != TRADE_RETCODE_DONE)
   {
      Print("OrderCancel failed with code: ", result.retcode, " - ", result.comment);
      return false;
   }
   
   Print("Order cancelled successfully. Ticket: ", ticket);
   
   return true;
}

//+------------------------------------------------------------------+
//| Обновление средней цены выполнения|
//+------------------------------------------------------------------+
void CExecutionAlgorithm::UpdateAverageExecutionPrice(double price, double volume)
{
   // Рассчитываем новую среднюю цену исполнения
   if(m_executedVolume > 0.0)
   {
      // Средневзвешенное значение старой и новой цен
      m_avgExecutionPrice = (m_avgExecutionPrice * m_executedVolume + price * volume) / 
                           (m_executedVolume + volume);
   }
   else
   {
      // Первое выполнение
      m_avgExecutionPrice = price;
   }
}
//+------------------------------------------------------------------+