English
preview
MQL5 Bootstrap (I): Набор переиспользуемых функций для работы с позициями и ордерами

MQL5 Bootstrap (I): Набор переиспользуемых функций для работы с позициями и ордерами

MetaTrader 5Трейдинг |
91 1
Omega J Msigwa
Omega J Msigwa

Содержание


Введение

Случалось ли вам во время разработки снова и снова переписывать одни и те же функции? Если вы создали больше нескольких советников, скриптов или индикаторов, скорее всего, такое уже бывало.

В разных торговых системах одни и те же шаблонные задачи повторяются постоянно. Например, почти каждому советнику нужны мониторинг и управление торговыми операциями, трейлинг-стопы, перевод в безубыток, частичное закрытие, подсчёт позиций, проверка наличия ордеров, анализ истории сделок и надёжные методы закрытия позиций или удаления ордеров. Это не особенности конкретной стратегии, а структурная необходимость.


Хотя у каждого разработчика свой подход к написанию кода, повторное создание таких шаблонных функций в каждой новой программе неэффективно и контрпродуктивно по нескольким причинам:

  1. Повышенный риск ошибокКаждый раз, когда вы переписываете одну и ту же логику, появляется риск внести новые несоответствия или трудноуловимые ошибки.


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

  2. Несогласованное поведение в разных проектах.

    Без общей базовой библиотеки каждый советник может немного по-своему обрабатывать проверку наличия позиций, отмену ордеров, расчёт рисков, логику закрытия позиций и другие операции. Со временем такая неоднородность усложняет отладку и делает сопровождение кода ещё более хаотичным.

  3. Потеря времени на разработку«Время, потраченное на переписывание шаблонного кода, — это время, не потраченное на улучшение логики стратегии, оптимизацию производительности или поиск новых решений».

    Как разработчики, мы должны концентрироваться на проектировании стратегии и надёжности системы, а не на многократной реализации фильтров позиций или базовых торговых утилит.

  4. Более сложное сопровождениеЕсли вы обнаружили ошибку в логике закрытия позиций и ранее скопировали этот код в 10 разных советников, теперь придётся исправлять 10 отдельных файлов.

    Централизованный переиспользуемый модуль позволяет исправить проблему один раз и сразу применить это исправление везде.

  5. Плохая масштабируемостьПо мере роста и усложнения проектов дублированный код становится всё сложнее поддерживать. 

  6. Снижение читаемости кода. Логика стратегии может оказаться скрытой под повторяющимся служебным кодом. Возможно, вы уже видели советник, настолько компактный и перегруженный, что в нём трудно найти саму торговую логику!

    Когда базовые утилиты вынесены в переиспользуемые библиотеки и модули в папке include, наши MQL5-программы, например советники, становятся чище, понятнее и сосредоточены именно на торговой логике.

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

Идея вдохновлена Bootstrap (ранее Twitter Bootstrap). Это фронтенд-фреймворк для веб-разработчиков. Он получил огромную популярность благодаря идее предоставить набор готовых CSS- и JavaScript-компонентов, избавляющих от необходимости проектировать типовые элементы интерфейса с нуля.

В первой статье этой серии мы напишем и соберём переиспользуемые функции и код для работы с позициями и ордерами.


Проверка наличия позиций

В стратегиях, основанных на одном сигнале или на торговле только в одном направлении за раз, такие функции полезны: они помогают проверить наличие позиции перед открытием новой или предотвратить открытие дополнительных позиций.

//+------------------------------------------------------------------+
//|  Returns true if a position exists filtered by                   |
//|  symbol, magic, type, or ticket                                  |
//+------------------------------------------------------------------+
bool PositionExists(const string symbol="",
                    const long magic=LONG_MAX,
                    const int type=-1,
                    const long ticket=-1)
  {
   bool use_symbol = (symbol != "");
   bool use_magic  = (magic  != LONG_MAX);
   bool use_type   = (type   != -1);
   bool use_ticket = (ticket != -1);
   
   CPositionInfo pos;
   
   for(int i = PositionsTotal()-1; i >= 0; --i)
     {
      if(!pos.SelectByIndex(i))
         continue;

      if(use_symbol && pos.Symbol() != symbol)
         continue;
      if(use_magic  && pos.Magic()  != magic)
         continue;
      if(use_type   && (int)pos.PositionType() != type)
         continue;
      if(use_ticket && pos.Ticket() != ticket)
         continue;

      //--- passed all active filters
      return true;
     }
   return false;
  }

Чтобы эту функцию можно было использовать в разных ситуациях, мы задаём четыре необязательных параметра: символ, magic-номер, тип позиции и тикет. Это позволяет фильтровать и проверять позиции по конкретным параметрам.

Все аргументы имеют значения по умолчанию, поэтому они являются необязательными. 

Если все активные фильтры выполняются, функция возвращает true; в противном случае она возвращает false.

Чтобы упростить работу, создадим функции с той же задачей, но с более конкретными именами.

A: Проверка наличия позиции по magic-номеру

Magic-номера помогают различать торговые объекты (ордера и позиции), открытые другими советниками или стратегиями. Очень часто нужно проверить, существуют ли позиции с определённым magic-номером.

//+------------------------------------------------------------------+
//|  Returns true if a position exists with a specified ticket number|
//+------------------------------------------------------------------+
bool PositionExistsByMagic(int magic)
  {
   return PositionExists("", magic);
  }

B: Проверка наличия позиции по конкретному инструменту

Для операций, привязанных к символу, удобна следующая функция. Она проверяет, существует ли позиция по заданному символу (инструменту).

//+------------------------------------------------------------------+
//|  Returns true if a position exists with a specified symbol       |
//+------------------------------------------------------------------+
bool PositionExistsBySymbol(string symbol)
  {
   return PositionExists(symbol);
  }

C: Проверка наличия позиции заданного типа

//+------------------------------------------------------------------+
//|  Returns true if a position exists with a specified type         |
//+------------------------------------------------------------------+
bool PositionExistsByType(ENUM_POSITION_TYPE type)
  {
   return PositionExists("", LONG_MAX, type);
  }

Эта функция принимает аргумент типа позиции и использует его, чтобы проверить, существует ли позиция заданного типа (true) или нет (false).

D: Проверка наличия позиции с заданным тикетом

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

//+------------------------------------------------------------------+
//|  Returns true if a position exists with a specified ticket number|
//+------------------------------------------------------------------+
bool PositionExistsByTicket(long ticket)
  {
   return PositionExists("", LONG_MAX, -1, ticket);
  }


Счётчики позиций

Счётчики позиций полезны почти в каждом советнике: они не только показывают количество активных позиций на рынке, но и могут использоваться для определения наличия позиции, как и рассмотренные выше методы.

//+------------------------------------------------------------------+
//|  Counts positions filtered by symbol, magic, type, or ticket     |
//+------------------------------------------------------------------+
int PositionCount(const string symbol="",
                  const long magic=LONG_MAX,
                  const int type=-1,
                  const long ticket=-1)
  {
   bool use_symbol = (symbol != "");
   bool use_magic  = (magic  != LONG_MAX);
   bool use_type   = (type   != -1);
   bool use_ticket = (ticket != -1);
   
   CPositionInfo pos;
   
   int count = 0;
   for(int i = PositionsTotal()-1; i >= 0; --i)
     {
      if(!pos.SelectByIndex(i))
         continue;

      if(use_symbol && pos.Symbol() != symbol)
         continue;
      if(use_magic  && pos.Magic()  != magic)
         continue;
      if(use_type   && (int)pos.PositionType() != type)
         continue;
      if(use_ticket && pos.Ticket() != ticket)
         continue;

      //--- passed all active filters
      count++;
     }

   return count;
  }

В целом логика похожа. Однако вместо возврата true при обнаружении позиции функция подсчитывает позиции, увеличивая переменную count, а в конце возвращает итоговое значение.

Как и в предыдущих методах, введём несколько вариантов подсчёта позиций по конкретным атрибутам.

A: Подсчёт позиций с определённым magic-номером

//+------------------------------------------------------------------+
//|         Counts positions with a specified magic number           |
//+------------------------------------------------------------------+
int PositionCountByMagic(int magic)
  {
   return PositionCount("", magic);
  }

B: Подсчёт позиций по конкретному инструменту

//+------------------------------------------------------------------+
//|         Counts positions with a specified symbol                 |
//+------------------------------------------------------------------+
int PositionCountBySymbol(string symbol)
  {
   return PositionCount(symbol);
  }

C: Подсчёт позиций конкретного типа

//+------------------------------------------------------------------+
//|         Counts positions with a specified type                   |
//+------------------------------------------------------------------+
int PositionCountByType(int type)
  {
   return PositionCount("", LONG_MAX, type);
  }


Закрытие позиций

Функция для немедленного закрытия позиций полезна, но не все позиции закрываются только по Stop Loss и Take Profit. Иногда по той или иной причине нужно закрыть одну или несколько позиций и выйти из рынка.

Примеры ситуаций, когда необходимо закрывать позиции:

  • Перед новостями на валютных рынках, чтобы избежать высокой волатильности, возникающей во время выхода новости.
  • При достижении лимитов риска, то есть когда значения баланса счёта, маржи или средств (equity) опускаются ниже определённого порога или допустимых значений.

//+------------------------------------------------------------------+
//|  Closes positions filtered by symbol, magic, type, or ticket     |
//+------------------------------------------------------------------+
void PositionClose(const long deviation_points=LONG_MAX,
                    const string symbol="",
                    const long magic=LONG_MAX,
                    const int type=-1,
                    const long ticket=-1)
  {
   bool use_symbol = (symbol != "");
   bool use_magic  = (magic  != LONG_MAX);
   bool use_type   = (type   != -1);
   bool use_ticket = (ticket != -1);
   
   CPositionInfo pos;
   CTrade trade;
   
   for(int i = PositionsTotal()-1; i >= 0; --i)
     {
      if(!pos.SelectByIndex(i))
         continue;

      if(use_symbol && pos.Symbol() != symbol)
         continue;
      if(use_magic  && pos.Magic()  != magic)
         continue;
      if(use_type   && (int)pos.PositionType() != type)
         continue;
      if(use_ticket && pos.Ticket() != (ulong)ticket)
         continue;

      const string sym = pos.Symbol();
      
      //---
      
      trade.SetExpertMagicNumber(magic);
      trade.SetTypeFillingBySymbol(pos.Symbol());
      
      if (!trade.PositionClose(pos.Ticket(), deviation_points))
          printf("Failed to close position #%I64u", pos.Ticket());
     }
  }

Все аргументы необязательны: если фильтры не переданы, функция закрывает все позиции, доступные в терминале MetaTrader 5.

Чтобы упростить работу, нам нужны специализированные варианты этой функции.

Функция Описание
void ClosePositionsBySymbol(string symbol, long deviation_points=LONG_MAX)
Закрывает позицию, используя символ в качестве фильтра.
void ClosePositionsByMagic(int magic, long deviation_points=LONG_MAX)
Закрывает позицию с указанным magic-номером.
void ClosePositionsByType(ENUM_POSITION_TYPE type, long deviation_points=LONG_MAX)
Закрывает позицию заданного типа.

Дополнительно можно добавить функции для закрытия прибыльных и убыточных сделок соответственно.

Функция Описание
void CloseProfitablePositions(const long deviation_points=LONG_MAX,
                              const string symbol="",
                              const long magic=LONG_MAX,
                              const int type=-1,
                              const long ticket=-1)
Закрывает все прибыльные позиции с заданными свойствами.
void CloseLosingPositions(const long deviation_points=LONG_MAX,
                          const string symbol="",
                          const long magic=LONG_MAX,
                          const int type=-1,
                          const long ticket=-1)
Закрывает все убыточные позиции с заданными свойствами.


Получение информации о последней позиции

Информация о последней позиции полезна для торговых систем и стратегий, которые опираются на данные о самой свежей сделке, чтобы определить, где и как следует разместить новую позицию или ордер.

Функции для таких задач применимы в следующих случаях:

Ниже приведена функция для получения информации о самой последней позиции.

//+------------------------------------------------------------------+
//|  Returns the most recently opened position filtered by           |
//|  symbol, magic, or type                                          |
//+------------------------------------------------------------------+
bool GetRecentPosition(CPositionInfo &pos_info,
                       const string symbol="",
                       const long magic=LONG_MAX,
                       const int type=-1)
  {
   bool use_symbol = (symbol != "");
   bool use_magic  = (magic  != LONG_MAX);
   bool use_type   = (type   != -1);
   
   ulong recent_ticket = 0;
   ulong last_time_msc = 0;
   
   CPositionInfo pos;
   
   for(int i = PositionsTotal()-1; i >= 0; --i)
     {
      if(!pos.SelectByIndex(i))
         continue;

      if(use_symbol && pos.Symbol() != symbol)
         continue;
      if(use_magic  && pos.Magic()  != magic)
         continue;
      if(use_type   && (int)pos.PositionType() != type)
         continue;

      //--- passed all active filters
      
      ulong t = pos.TimeMsc();
      if (t > last_time_msc)
         {
            last_time_msc = t;
            recent_ticket = pos.Ticket();
         }
     }
   
   if(recent_ticket == 0)
      return false;
      
   return pos_info.SelectByTicket(recent_ticket); //--- select the latest position using it's ticket
  }

В отличие от предыдущих функций, эта принимает объект CPositionInfo по ссылке. Этот объект заполняется данными о самой последней позиции.

Функция находит позицию с самой поздней временной меткой и сохраняет её тикет для использования после цикла. Используя тикет самой последней позиции, метод SelectByTicket заполняет переменную info.

Ниже приведена таблица с вариантами этой функции.

Функция Описание
GetRecentPositionBySymbol Получает наиболее недавно открытую позицию, используя только её символ в качестве фильтра.
GetRecentPositionByMagic Получает наиболее недавно открытую позицию, используя в качестве фильтра только её magic-номер.
GetRecentPositionByType Получает наиболее недавно открытую позицию из терминала, используя только её тип в качестве фильтра.


Получение информации о самой старой позиции

Хотя, на мой взгляд, это менее практично, чем получение самой последней позиции, всё же полезно иметь функцию, выполняющую обратную задачу.

Логика противоположна предыдущему варианту:

//+-------------------------------------------------------------------+
//|Returns the oldest open position filtered by symbol, magic, or type|
//+-------------------------------------------------------------------+
bool GetOldestPosition(CPositionInfo &pos_info,
                       const string symbol="",
                       const long magic=LONG_MAX,
                       const int type=-1)
  {
   bool use_symbol = (symbol != "");
   bool use_magic  = (magic  != LONG_MAX);
   bool use_type   = (type   != -1);
   
   ulong oldest_ticket = 0;
   ulong oldest_time_msc = ULONG_MAX;
   
   CPositionInfo pos;
   
   for(int i = PositionsTotal()-1; i >= 0; --i)
     {
      if(!pos.SelectByIndex(i))
         continue;

      if(use_symbol && pos.Symbol() != symbol)
         continue;
      if(use_magic  && pos.Magic()  != magic)
         continue;
      if(use_type   && (int)pos.PositionType() != type)
         continue;

      //--- passed all active filters
      
      ulong t = pos.TimeMsc();
      if (t < oldest_time_msc)
         {
            oldest_time_msc = t;
            oldest_ticket = pos.Ticket();
         }
     }
   
   if(oldest_ticket == 0)
      return false;
      
   return pos_info.SelectByTicket(oldest_ticket); //--- select the oldest position using it's ticket
  }

Функция находит позицию с самым ранним временем открытия в миллисекундах. После нахождения она выбирается и присваивается переменной info, переданной по ссылке.

Ниже приведена таблица с вариантами этой функции.

Функция Описание
GetOldestPositionBySymbol Получает самую старую позицию, используя только её символ в качестве фильтра.
GetOldestPositionByMagic Получает самую старую позицию, используя в качестве фильтра только её magic-номер.
GetOldestPositionByType Получает самую старую позицию из терминала, используя только её тип в качестве фильтра.

Создадим простой советник и проверим на практике некоторые из рассмотренных функций.


Пример разворотной стратегии на пересечении SMA

В основе примера — две простые скользящие средние (SMA) с периодами 10 и 20. Когда происходит бычье пересечение, то есть SMA(10) пересекает SMA(20) снизу вверх, это бычий сигнал: следует открыть позицию на покупку, а позицию в противоположном направлении (позицию на продажу) закрыть.

И наоборот, когда SMA(10) пересекает SMA(20) сверху вниз, должна выполняться обратная операция.

Открывается позиция на продажу и закрывается противоположная позиция. 

SMA crossover EA.mq5

#include <Bootstrap\positions.mqh>
#include <Bootstrap\orders.mqh>

#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>

CTrade m_trade;
CSymbolInfo m_symbol;

//+------------------------------------------------------------------+

input int magic_number = 14022026;
input uint slippage = 100;

input uint short_sma_period = 10;
input uint long_sma_period = 20;

//+------------------------------------------------------------------+

int short_handle, long_handle;
double long_sma_buff[], short_sma_buff[];
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   if(!m_symbol.Name(Symbol()))
     {
      printf("Failed to get symbolinfo, error = %d", GetLastError());
      return INIT_FAILED;
     }
//---
   m_trade.SetExpertMagicNumber(magic_number);
   m_trade.SetDeviationInPoints(slippage);
   m_trade.SetTypeFillingBySymbol(Symbol());
//---
   short_handle = iMA(Symbol(), Period(), short_sma_period, 0, MODE_SMA, PRICE_CLOSE);
   long_handle = iMA(Symbol(), Period(), long_sma_period, 0, MODE_SMA, PRICE_CLOSE);
   TesterHideIndicators(false);
//ChartIndicatorAdd(0, 0, short_handle);
//ChartIndicatorAdd(0, 0, long_handle);
//---
   ArraySetAsSeries(long_sma_buff, true);
   ArraySetAsSeries(short_sma_buff, true);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(!m_symbol.RefreshRates())
     {
      printf("Failed to fetch latest tick information, error = %d", GetLastError());
      return;
     }
   if(CopyBuffer(long_handle, 0, 0, 2, long_sma_buff) < 0)
      return;
   if(CopyBuffer(short_handle, 0, 0, 2, short_sma_buff) < 0)
      return;
   double curr_long_sma = long_sma_buff[0], prev_long_sma = long_sma_buff[1],
          curr_short_sma = short_sma_buff[0], prev_short_sma = short_sma_buff[1];
//--- long signal
   if(curr_short_sma > curr_long_sma && prev_short_sma < prev_long_sma)
      if(!PositionExists(Symbol(), magic_number, POSITION_TYPE_BUY))
        {
         ClosePositions(slippage, Symbol(), magic_number, POSITION_TYPE_SELL); //close an opposite trade
         m_trade.Buy(m_symbol.LotsMin(), Symbol(), m_symbol.Ask());
        }
//--- short signal
   if(curr_short_sma < curr_long_sma && prev_short_sma > prev_long_sma)
      if(!PositionExists(Symbol(), magic_number, POSITION_TYPE_SELL))
        {
         ClosePositions(slippage, Symbol(), magic_number, POSITION_TYPE_BUY); //close an opposite trade
         m_trade.Sell(m_symbol.LotsMin(), Symbol(), m_symbol.Bid());
        }
  }

Результаты.


Заключение

Функции, показанные в этой статье, взяты из Bootstrap\positions.mqh(ссылка приведена во вложениях). Они покрывают самые распространённые задачи для открытых позиций: проверку наличия, счётчики, вспомогательные функции для группового закрытия позиций и утилиты для получения наиболее недавно открытой или самой старой позиции на основе времени открытия.

Поскольку названия и назначение этих помощников достаточно очевидны, я не стал расширять статью эквивалентами для отложенных ордеров . Тем не менее в проект входит отдельный модуль — Bootstrap\orders.mqh— который предоставляет переиспользуемые обёртки в том же стиле для работы с ордерами (отложенными ордерами), включая:

  • CancelOrders, CancelOrdersByMagic, CancelOrdersBySymbol, CancelOrdersByTicket, CancelOrdersByType
  • OldestOrder, RecentOrder
  • OrderCount, OrderCountByMagic, OrderCountBySymbol, OrderCountByTicket, OrderCountByType
  • OrderExists, OrderExistsByMagic, OrderExistsBySymbol, OrderExistsByTicket, OrderExistsByType

На практике разделение помощников на positions.mqh и orders.mqh соответствует тому, как MetaTrader 5 разделяет торговые объекты:

  • Позиции представляют текущую чистую экспозицию по символу по символу (а на неттинговых счетах обычно одну позицию на символ).
  • Ордера представляют инструкции (особенно отложенные ордера), которые могут исполниться и превратиться в сделки/позиции, а могут и не исполниться.
  • Поэтому закрытие и отмена — разные операции: позиции закрываются торговыми запросами, а отложенные ордера удаляются/отменяются.

Эта библиотека намеренно не является заменой стандартной библиотеке MQL5. Многие задачи уже реализованы во встроенных торговых классах MetaQuotes (например, CTrade, CPositionInfo, COrderInfo, помощниках CHistory* и т. д.), и их дублирование увеличило бы затраты на сопровождение без дополнительной пользы. Вместо этого цель — предоставить тонкий переиспользуемый слой, который стандартизирует повторяющиеся части, которые приходится постоянно переписывать: фильтрацию по символу, magic-номеру, типу позиции, тикету, а также единообразное выполнение массовых операций.

Главная мысль в том, что эти помощники — это архитектурный шаблон и базовый фундамент — слой bootstrap. Его стоит расширять утилитами, специфичными для конкретного проекта, такими как:

  • лимиты риска и экспозиции (максимум позиций на символ/magic-номер, максимальный объём, правила максимальной просадки),
  • процедуры частичного закрытия,
  • механизмы безубытка и трейлинг-стопа,
  • фильтры времени нахождения в сделке,
  • защита по спреду/проскальзыванию и обработка режима исполнения,
  • аналитика истории/сделок для более взвешенных решений по исполнению.

Включённый файл "Experts\SMA crossover EA.mq5" — это минимальный тестовый советник, который демонстрирует, как помощники позволяют коду стратегии сосредоточиться на сигналах (пересечении SMA), а библиотека берёт на себя повторяющуюся повторяющуюся торговую обвязку: проверку, подсчёт, закрытие и выбор позиций.

С наилучшими пожеланиями.


Таблица вложений

Название файла Описание и использование
Bootstrap\orders.mqh Содержит служебные функции для работы с отложенными ордерами.
Bootstrap\positions.mqh Содержит служебные функции для работы с позициями.
Experts\SMA crossover EA.mq5 Советник для тестирования функций, рассмотренных в статье, на простой торговой стратегии пересечения скользящих средних. 

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/21398

Прикрепленные файлы |
Attachments.zip (4.81 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Chacha Ian Maroa
Chacha Ian Maroa | 2 июн. 2026 в 17:26
Да, это отличный проект и идея. В MQL5 должны быть стандартизированные библиотеки для различных типичных шаблонов и задач.
Алгоритм бактериальной эволюции — Bacterial Evolutionary Algorithm (BEA) Алгоритм бактериальной эволюции — Bacterial Evolutionary Algorithm (BEA)
Статья посвящена бактериальному эволюционному алгоритму Нава — Фурухаси (BEA) и его двум ключевым операторам: посегментной бактериальной мутации для локальной оптимизации и горизонтальному переносу генов для обмена удачными фрагментами решений. Мы реализуем BEA в каркасе C_AO как конечный автомат под фиксированный бюджет оценок, тестируем на Hilly, Forest и Megacity и поясняем терминологическую развилку с моделью Нумаоки.
Создание пользовательских индикаторов в MQL5 (Часть 5): Доработка WaveTrend Crossover с помощью Canvas — градиентный туман, сигнальные пузырьки и управление рисками Создание пользовательских индикаторов в MQL5 (Часть 5): Доработка WaveTrend Crossover с помощью Canvas — градиентный туман, сигнальные пузырьки и управление рисками
В этой статье мы улучшим индикатор Smart WaveTrend Crossover в MQL5, добавив отрисовку с использованием Canvas для градиентных туманных наложений, сигнальные боксы для обнаружения пробоев, а также метки сигналов в виде пузырьков с надписями BUY/SELL или простых треугольников. Мы также реализуем функции управления рисками с динамическими уровнями Take Profit и Stop Loss, рассчитываемыми через множители свечей или проценты и отображаемыми с помощью линий и таблицы, а также добавим параметры фильтрации по тренду и расширения сигнальных боксов.
Неопределённость как модель (Часть 7): Стохастические регрессоры Неопределённость как модель (Часть 7): Стохастические регрессоры
Материал — практическое руководство по множественной регрессии со стохастическими факторами в алготрейдинге с акцентом на эндогенность. Показаны EDA/CDA-приёмы, тест Дарбина—Ву—Хаусмана и двухэтапный МНК для получения состоятельных коэффициентов. Все расчёты реализованы в MQL5 и проверены на внутридневной волатильности EURUSD. Читатель получит рабочий конвейер для выявления эндогенности и корректной спецификации моделей.
Моделирование рынка: Position View (VIII) Моделирование рынка: Position View (VIII)
В предыдущей статье мы рассмотрели, как можно реализовать индикатор позиции для закрытия открытой позиции непосредственно с графика, взаимодействуя с доступным на нем объектом. После завершения и проверки работы первого механизма мы приступили к внесению изменений, чтобы обеспечить возможность удаления линий тейк-профита и стоп-лосса для открытой позиции. Однако, поскольку необходимые изменения требовали подробных объяснений, в той же статье я показал только те изменения, которые необходимо было внести в советник, но всё ещё требовалось показать изменения, которые необходимо было внести в индикатор позиции.