English Deutsch 日本語
preview
Изучение стандартной библиотеки MQL5 (часть 1): Знакомство с CTrade, CiMA и CiATR

Изучение стандартной библиотеки MQL5 (часть 1): Знакомство с CTrade, CiMA и CiATR

MetaTrader 5Примеры |
224 0
Clemence Benjamin
Clemence Benjamin

Содержание:

  1. Введение
  2. Реализация
  3. Тестирование и результаты
  4. Заключение
  5. Основные выводы
  6. Прикрепленные файлы



Введение

Многие разработчики, особенно те, кто только начинают работать с MQL5, часто избегают использования классов и библиотек. Такая осторожность вполне объяснима: процедурное программирование кажется более простым и понятным. Однако по мере перехода к созданию более сложных систем приходится менять подход к разработке. Освоение объектно-ориентированного программирования (ООП) — это не просто добавление нового уровня сложности, а путь к более профессиональной, эффективной и, в конечном счете, более простой разработке.

Стандартная библиотека MQL5 идеально воплощает этот принцип. Это не набор запутанных правил, которые необходимо заучить, а коллекция готовых инструментов, созданных для избавления от рутинной работы. Вам не нужно заново изобретать колесо — вы сразу собираете мощный автомобиль из качественно изготовленных компонентов. Библиотека предоставляет готовые элементы, а ваша задача — объединить их в единую систему.

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

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

Более того, потенциал библиотеки до сих пор раскрыт далеко не полностью. Многие ее компоненты остаются незаметными, как скрытые сокровища, которые ожидают, когда их найдут. С каждым обновлением MetaTrader 5 стандартная библиотека становится все более мощной и функциональной. Научившись работать с ней, мы не просто получаем удобный инструмент — мы учимся разрабатывать эффективнее и получать больше возможностей для создания собственных решений.

В этой статье мы и познакомимся с этими скрытыми возможностями. Мы постараемся вместе раскрыть весь потенциал стандартной библиотеки MQL5 и изменить подход к разработке торговых систем. 

В первой части серии мы создадим полноценный советник, демонстрирующий, как можно объединить три популярных класса стандартной библиотеки MQL5 — CTrade, CiMA и CiATR — в практическую торговую систему. Основная задача — представить полностью работоспособное решение, которое читатели смогут сразу скомпилировать, протестировать и адаптировать под свои задачи, не дожидаясь выхода следующих частей.

Предпосылки

Хотя документация MQL5 содержит формальное описание классов, включая CTrade, CiMA и CiATR, их иерархию, методы и краткие пояснения, она практически не показывает, как объединять их в полноценные торговые системы. Для начинающих это может оказаться серьезным препятствием: документация рассказывает, что существует, но не объясняет, как этим эффективно пользоваться. Именно в этом польза подобных статей.

Мы выйдем за рамки простого перечисления API и сосредоточимся на практическом применении. Рассмотрим не только назначение каждого класса, но и обсудим, как интегрировать его в жизненный цикл советника, интерпретировать получаемые данные и настроить взаимодействие с другими классами, чтобы получить полноценную стратегию. Другими словами, эта серия статей должна соединить документацию и реальную практику и помочь пройти путь от базовых определений классов до создания работающего торгового робота.

Почему мы выбрали именно эти классы?

Стандартная библиотека MQL5 входит в пакет платформы MetaTrader 5 по умолчанию, а ее исходные файлы доступны в MetaEditor 5. По умолчанию они расположены в каталоге Include в папке установки MQL5 (например, MQL5\Include\Trade\ or MQL5\Include\Indicators\). Каждый модуль представлен заголовочными файлами (.mqh), которые определяют многократно используемые классы, такие как CTrade, CiMA и CiATR. В MetaEditor эти файлы доступны через панель Навигатора, Include → Standard Library, а также через меню Файл → Открыть. Это означает, что вы можете использовать классы в своих проектах посредством директивы #include, изучать исходный код библиотеки, понимать ее архитектуру, а также расширять и адаптировать ее под собственные задачи.

ShareX_fVhTmSxwMI

Рисунок 1. Доступ к стандартной библиотеке MQL5 в MetaEditor 5

CTrade — механизм исполнения торговых операций

CTrade представляет собой оболочку стандартной библиотеки, которая отвечает за отправку торговых заявок и базовое управление ордерами. Вместо того чтобы каждый раз формировать низкоуровневые структуры MqlTradeRequest, можно использовать простые методы (Buy, Sell, PositionClose и другие). После выполнения операции класс предоставляет стандартизированные результаты, которые можно анализировать. В нашем советнике CTrade будет отвечать за: открытие позиций с рассчитанным объемом, проверку статуса выполнения операций, журналирование причин отказа при отклонении заявок, безопасное повторение запросов и отключение торговли при возникновении системных ошибок. Использование CTrade делает код более компактным, понятным и универсальным при работе с различными брокеров.

#include <Trade\Trade.mqh>

// Global declarations
CTrade trade;

CiATR — волатильность и расчет объема позиции

CiATR (из Indicators\Oscillators.mqh) дает значения индикатора Average True Range (ATR) в ценовых единицах, которые используются для оценки волатильности. На практике мы используем значение ATR, чтобы 1) определять, не слишком ли волатилен или не слишком ли спокоен рынок для торговли; 2) вычислять динамический стоп-лосс (Stop = ATR × коэффициент); 3) преобразовывать расстояние стоп-лосса в денежное выражение на один лот для расчета лотов для сделки. Советник создает экземпляр CiATR один раз, обновляет его на каждом тике, проверяет корректность выходных данных и использует вспомогательную функцию для преобразования ATR → убыток на лот → размер позиции, нормализованный в соответствии с шагом объема брокера.

#include <Indicators\Oscilators.mqh>
CiATR atr;

CiMA — определение тренда

CiMA (из Indicators\Trend.mqh) включает в себя логику скользящих средних и обработку буферов и позволяет легко использовать эти данные в советнике. Мы будем использовать два экземпляра CiMA (быструю и медленную), чтобы создать простой фильтр направления рынка: если быстрая MA находится выше медленной — считается, что рынок находится в восходящем тренде; если ниже — в нисходящем. CiMA также обеспечивает удобный доступ к значениям скользящей на текущем баре и на последнем закрытом баре, что позволяет реализовать стратегию отката цены (например, открывать позицию, когда цена откатывается к быстрой скользящей средней на определенную долю ATR). Как и в случае с другими классами, экземпляр создается один раз, обновляется на каждом тике, с обязательной проверкой на EMPTY_VALUE. Для получения неперерисовывающихся сигналов рекомендуется использовать Main(1), чтобы сигнал генерировался на закрытой свече.

#include <Indicators\Trend.mqh>
CiMA fastMA, slowMA;

Основной жизненный цикл советника (кратко)

  • В OnInit() создаются экземпляры всех классов через Create().
  • OnTick() — на каждом тике вызывается Refresh(), считываются значения через Main() (с проверкой на EMPTY_VALUE), запускается логика принятия решений, рассчитывается размер, а затем выполняются методы CTrade.
  • OnDeinit() — при завершении работы вызывается FullRelease() для индикаторов, удаляются графические объекты.

После того, как мы внедрили эти принципы четкого разделения ответственности между классами, строгого жизненного цикла, правильного определения размера для торговли и проверок исполнения, можно переходить к реализации компактного и понятного советника, который объединяет CiMA, CiATR и CTrade в единую цепочку принятия решений (Инициализация → Обновление → Определение сигнала → Расчет лотов → Исполнение → Управление позицией). Также будут реализованы механизмы безопасности и журналирования, необходимые для надежного тестирования.


Реализация

Теперь перейдем к практической части — как объединить эти знания в работающее торговое решение. Наша задача заключается не в абстрактном описании классов, а в том, чтобы пошагово и структурно объяснить, как каждый класс (включая совершение сделок, анализ индикаторов или мониторинг счета) вписывается в общую торговую логику. В этом разделе мы рассмотрим, как пройти путь от идеи до готового кода. Цель — сделать переход от теории к практике максимально понятным.

Заголовок файла, include-файлы и пользовательские параметры

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

//+------------------------------------------------------------------+
//| CiMA_Trend_Trader.mq5                                            |
//| Compact, working EA that uses CiMA (trend), CiATR (volatility)   |
//| and CTrade (execution). Adjustable inputs.                       |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.00"

#include <Trade\Trade.mqh>
#include <Indicators\Trend.mqh>          // CiMA
#include <Indicators\Oscilators.mqh>     // CiATR

input int    InpFastMAPeriod   = 8;      // Fast MA period
input int    InpSlowMAPeriod   = 34;     // Slow MA period
input int    InpATRPeriod      = 14;     // ATR period
input double InpRiskPercent    = 0.5;    // % of balance risk per trade
input double InpATRstopMult    = 1.5;    // SL = ATR * this
input double InpPullbackFactor = 0.5;    // price within this * ATR of fastMA to consider entry
input double InpTakeProfitRR   = 2.0;    // TP = R * RR
input double InpMaxChopPct     = 0.006;  // skip if ATR/price > this (too choppy)
input double InpMinVolPct      = 0.0005; // skip if ATR/price < this (too quiet)
input double InpMaxDailyLossPct= 2.0;    // disable trading for the day if loss exceeds this %
input int    InpMaxPosPerSym   = 1;      // max open positions per symbol (basic cap)
input int    InpMagicNumber    = 202507; // EA magic number for labeling
input int    InpSlippage       = 5;      // slippage in points (not required for CTrade but useful to log)
input bool   InpShowTradeLabels = true;  // draw arrows/labels on chart

Глобальные объекты и состояние программы

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

// Global objects
CTrade  trade;
CiMA    fastMA, slowMA;
CiATR   atr;

// Internal tracking
datetime g_lastTradeTime = 0;

Инициализация: создание индикаторов и подготовка советника

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

int OnInit()
{
  // Create indicator objects
  if(!fastMA.Create(_Symbol, _Period, InpFastMAPeriod, 0, MODE_EMA, PRICE_CLOSE))
  {
    Print("[OnInit] fastMA.Create failed for ", _Symbol);
    return(INIT_FAILED);
  }
  if(!slowMA.Create(_Symbol, _Period, InpSlowMAPeriod, 0, MODE_EMA, PRICE_CLOSE))
  {
    Print("[OnInit] slowMA.Create failed for ", _Symbol);
    return(INIT_FAILED);
  }
  if(!atr.Create(_Symbol, _Period, InpATRPeriod))
  {
    Print("[OnInit] atr.Create failed for ", _Symbol);
    return(INIT_FAILED);
  }

  EventSetTimer(1); // optional: ensure OnTimer for housekeeping if needed
  Print("CiMA_Trend_Trader initialized: ", _Symbol, " ", EnumToString(_Period));
  return(INIT_SUCCEEDED);
}

Завершение работы и обработчик таймера

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

void OnDeinit(const int reason)
{
  // release indicator resources
  fastMA.FullRelease(true);
  slowMA.FullRelease(true);
  atr.FullRelease(true);
  EventKillTimer();
}

void OnTimer()
{
  // Simple daily reset or housekeeping can be added here if required
}

Подсчет позиций (контроль безопасности)

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

int CountOpenPositionsForSymbol()
{
  int total = PositionsTotal();
  int count = 0;
  for(int i=0;i<total;i++)
  {
    ulong ticket = PositionGetTicket(i);
    if(ticket==0) continue;
    if(!PositionSelectByTicket(ticket)) continue;
    string sym = PositionGetString(POSITION_SYMBOL);
    if(sym == _Symbol) count++;
  }
  return(count);
}

Ограничение дневных убытков (защита капитала)

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

bool IsDailyLossExceeded()
{
  // compute today's closed profit/loss for the account in account currency
  double closedPL = 0.0;
  datetime dayStart = iTime(_Symbol, PERIOD_D1, 0); // start of current day on chart's server timezone
  ulong from = (ulong)dayStart;

  int deals = HistoryDealsTotal();
  for(int i=deals-1;i>=0;i--)
  {
    ulong ticket = HistoryDealGetTicket(i);
    if(ticket==0) continue;
    datetime t = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME);
    if(t < dayStart) continue;
    double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT) + HistoryDealGetDouble(ticket, DEAL_COMMISSION) + HistoryDealGetDouble(ticket, DEAL_SWAP);
    closedPL += profit;
  }

  // If closedPL is negative and its absolute exceeds percent of balance, return true
  double threshold = AccountInfoDouble(ACCOUNT_BALANCE) * (InpMaxDailyLossPct/100.0);
  if(closedPL < -threshold) return(true);
  return(false);
}

Нормализация объема (учет требований брокера)

Разные брокеры устанавливают собственные ограничения на максимальный и минимальный объем, а также шаг изменения объема. Эта вспомогательная функция приводит исходный рассчитанный размер позиции к допустимому значению в соответствии с настройками символа. Она возвращает ноль, если объем ниже минимального допустимого значения. Такая нормализация предотвращает отклонение ордеров брокером.

double NormalizeLotToStep(double lots)
{
  double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
  double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
  double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
  if(lotStep <= 0) lotStep = 0.01; // fallback
  if(minLot <= 0) minLot = 0.01;

  // floor to step
  double steps = MathFloor(lots / lotStep);
  double result = steps * lotStep;
  if(result < minLot) result = 0.0;
  if(result > maxLot) result = maxLot;

  // round to reasonable decimals (broker may require 2-3 decimals)
  int digits = 2;
  // attempt to infer decimals from lotStep
  if(lotStep < 0.01) digits = MathMax(0, (int)MathCeil(-MathLog10(lotStep)));
  result = NormalizeDouble(result, digits);
  return(result);
}

Расчет объема позиции на основе ATR

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

double CalculateVolume(double stop_distance_price)
{
  if(stop_distance_price <= 0) return(0.0);

  double risk_amount = AccountInfoDouble(ACCOUNT_BALANCE) * (InpRiskPercent/100.0);

  double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
  double tick_size  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);

  if(tick_value <= 0 || tick_size <= 0)
  {
    // fallback approximate using point and lot value: (this may be inaccurate for some symbols)
    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    double contract_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
    if(point<=0 || contract_size<=0) return(0.0);
    double value_per_price = contract_size; // rough
    double loss_per_lot = stop_distance_price * value_per_price;
    if(loss_per_lot <= 0) return(0.0);
    double lots = risk_amount / loss_per_lot;
    return NormalizeLotToStep(lots);
  }

  double value_per_price = tick_value / tick_size; // money per price unit for 1 lot
  double loss_per_lot = stop_distance_price * value_per_price;
  if(loss_per_lot <= 0) return(0.0);

  double lots = risk_amount / loss_per_lot;
  double normalized = NormalizeLotToStep(lots);
  return(normalized);
}

Визуальное отображение точек входа

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

void DrawEntryLabel(bool isLong, datetime when, double price, string tag)
{
  if(!InpShowTradeLabels) return;
  string objName = tag + "_" + IntegerToString((int)when);
  int arrow = isLong ? 233 : 234; // Wingdings arrows
  // create arrow
  if(!ObjectCreate(0, objName, OBJ_ARROW, 0, when, price))
  {
    // try to remove old and recreate
    ObjectDelete(0, objName);
    ObjectCreate(0, objName, OBJ_ARROW, 0, when, price);
  }
  ObjectSetInteger(0, objName, OBJPROP_COLOR, isLong ? clrBlue : clrRed);
  ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
  ObjectSetInteger(0, objName, OBJPROP_ARROWCODE, arrow);
  ObjectSetString(0, objName, OBJPROP_TEXT, tag);
}

Попытки открытия позиций (длинных и коротких)

Эти процедуры реализуют полный цикл: предварительная проверка → расчет объема → исполнение сделки для каждого направления. Они выполняют защитные проверки, рассчитывают уровни стоп-лосса и тейк-профита, получают нормализованный объем позиции и размещают ордер. При ошибке информация заносится в журнал, а при успешном выполнении фиксируется время сделки и обновляется график.

void TryOpenLong(double atr_val)
{
  // safety checks
  if(CountOpenPositionsForSymbol() >= InpMaxPosPerSym) return;
  if(IsDailyLossExceeded()) return;

  double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
  double stop_distance_price = atr_val * InpATRstopMult;
  if(stop_distance_price<=0) return;

  double sl = price - stop_distance_price;
  double tp = price + stop_distance_price * InpTakeProfitRR;

  double vol = CalculateVolume(stop_distance_price);
  if(vol <= 0) return;

  // attempt to place market buy
  bool ok = trade.Buy(vol, NULL, 0.0, sl, tp, "CiMA_Long_");
  if(!ok)
  {
    PrintFormat("[TryOpenLong] Buy failed. retcode=%d comment=%s", trade.ResultRetcode(), trade.ResultComment());
    return;
  }

  // success
  PrintFormat("[TryOpenLong] Bought %.2f lots at %.5f SL=%.5f TP=%.5f", vol, price, sl, tp);
  g_lastTradeTime = TimeCurrent();
  DrawEntryLabel(true, TimeCurrent(), price, "CiMA_LONG");
}

void TryOpenShort(double atr_val)
{
  // safety checks
  if(CountOpenPositionsForSymbol() >= InpMaxPosPerSym) return;
  if(IsDailyLossExceeded()) return;

  double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
  double stop_distance_price = atr_val * InpATRstopMult;
  if(stop_distance_price<=0) return;

  double sl = price + stop_distance_price;
  double tp = price - stop_distance_price * InpTakeProfitRR;

  double vol = CalculateVolume(stop_distance_price);
  if(vol <= 0) return;

  bool ok = trade.Sell(vol, NULL, 0.0, sl, tp, "CiMA_Short_");
  if(!ok)
  {
    PrintFormat("[TryOpenShort] Sell failed. retcode=%d comment=%s", trade.ResultRetcode(), trade.ResultComment());
    return;
  }

  PrintFormat("[TryOpenShort] Sold %.2f lots at %.5f SL=%.5f TP=%.5f", vol, price, sl, tp);
  g_lastTradeTime = TimeCurrent();
  DrawEntryLabel(false, TimeCurrent(), price, "CiMA_SHORT");
}

Логика принятия решений на каждом тике

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

void OnTick()
{
  // refresh indicators (cheap relative to trading operations)
  // Refresh indicators (the Refresh method returns void in the Standard Library; use flag -1 to update recent data)
  fastMA.Refresh(-1);
  slowMA.Refresh(-1);
  atr.Refresh(-1);
  // Note: Refresh does not return a status — check indicator values for EMPTY_VALUE below and skip until ready
  

  double fast = fastMA.Main(0);
  double slow = slowMA.Main(0);
  double atr_val = atr.Main(0); // price units

  if(fast==EMPTY_VALUE || slow==EMPTY_VALUE || atr_val==EMPTY_VALUE) return;

  double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
  bool uptrend = (fast > slow);
  bool downtrend = (fast < slow);

  // normalized volatility check
  double norm_vol = atr_val / price;
  if(norm_vol > InpMaxChopPct || norm_vol < InpMinVolPct)
  {
    // skip trading in these conditions
    return;
  }

  // Pullback distance between price and fast MA
  double dist = MathAbs(price - fast);

  // Only consider entries on bar close for non-repainting behaviour
  // ensure we use the current completed bar (shift=1) for MA reference if you prefer
  // for simplicity here we use Main(0) but teachers may show Main(1) alternative

  if(uptrend && dist <= InpPullbackFactor * atr_val)
  {
    TryOpenLong(atr_val);
  }
  else if(downtrend && dist <= InpPullbackFactor * atr_val)
  {
    TryOpenShort(atr_val);
  }
}

Небольшая утилита для создания удобных логов

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

string EnumToString(const ENUM_TIMEFRAMES tf)
{
  switch(tf)
  {
    case PERIOD_M1: return("M1");
    case PERIOD_M5: return("M5");
    case PERIOD_M15: return("M15");
    case PERIOD_H1: return("H1");
    case PERIOD_D1: return("D1");
    default: return(IntegerToString((int)tf));
  }
}


Тестирование и результаты

После компиляции советник CiMA-Trend-Trader EA можно протестировать в тестере стратегий. На этом этапе основной целью была не мгновенная прибыльность. Наша цель — убедиться, что советник работает корректно и соответствует основной проектной цели — работает по тренду. В ходе тестирования я заметил, что советник значительно стабильнее работает на старших таймфреймах, начиная с H4, где рыночные тренды более выражены.

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

Первое тестирование на 30-минутном таймфрейме

metatester64_mZwsgVXny3.gif

Рисунок 2. Визуализация тестирования советника CiMA_Trend_Trader на GBPUSD с таймфреймом (M30) за июнь 2024 года

terminal64_co9AY2VC6g.png

Рисунок 3. Постепенное снижение кривой капитала при тестировании на M30

terminal64_lZ68zax1Hi.png

Рисунок 4. Результаты тестирования GBPUSD, M30

Первое тестирование на символе GBPUSD с таймфреймом M30 оказалось неудовлетворительным с точки зрения прибыльности. За год моделирования советник демонстрировал последовательную серию убыточных сделок, а кривая капитала снизилась примерно на 50%. Визуальный анализ в тестере стратегий показал причину: структура рынка GBPUSD на таймфрейме M30 содержит множество краткосрочных ложных движений и периодов консолидации, из-за чего система регулярно входила в "ложные" тренды и получала стоп-лоссы.

Вместо того, чтобы продолжать агрессивно настраивать параметры на основе этих зашумленных сигналов, я принял решение перейти на более высокий таймфрейм H4 и повторно запустить тестирование. Результаты на 4-часовом таймфрейме оказались значительно лучше: тренды стали более выраженными, сигналы — более редкими, а сделки стали лучше соответствовать долгосрочной структуре рынка.

Тестирование на 4-часовом таймфрейме

terminal64_706hvT4Eyn.png

Рисунок5. Небольшой рост капитала при тестировании на таймфрейме H4

terminal64_zrDt2ZmaZM.png

Рисунок 6. Результаты тестирования на GBPUSD, H4

Старшие таймфреймы показали более качественное и устойчивое следование трендам, о чем говорят результаты тестирования выше. Это говорит о том, что наша трендовая система способна давать более надежные результаты с отфильтрованными сигналами на старших периодах. Мое решение перейти на таймфрейм H4 стало дополнительным шагом по улучшению эффективности системы и продемонстрировала ее потенциал. Тем не менее, в алгоритме советника есть еще много параметров, которые можно оптимизировать для дальнейшего улучшения результатов.

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

Ниже на рисунке 7 показан график с таймфреймом H4 с отображением стрелок (история сделок) и торговых сигналов. Тренды хорошо различимы, а скользящие средние, построенные классом CiMA, наглядно демонстрируют направление движения рынка. Это преимущество в значительной степени обусловлено использованием более старшего таймфрейма, который естественным образом фильтрует большую часть рыночного шума с младших таймфреймов.

metatester64_Zz8Xtlaubg.png

Рис. 7. GBPUSD, H4 — визуализация трендов в тестере стратегий


Заключение

Создать полноценного советника с минимальным количеством ошибок вполне реально при использовании стандартной библиотеки MQL5. С помощью классов CTrade, CiMA и CiATR нам удалось реализовать трендовую систему с компактным и понятным кодом. Использование этих классов также уменьшило сложность разработки и сократило количество типичных ошибок, таких как неправильное управление ордерами или создание лишних обработчиков. Использование готовых библиотечных классов позволило сосредоточиться на логике стратегии, а не на низкоуровневых деталях реализации.

В тестере стратегий советник работал стабильно и позволил быстро проводить тестирование на различных таймфреймах. Результаты подтвердили известную закономерность: на старших таймфреймах (например, H4) лучше и точнее определяется тренд, а младшие интервалы (например, M30) сильнее страдают от ложных пробоев и рыночного шума. Это подчёркивает важный принцип проектирования — трендовые системы требуют терпения и долгосрочного подхода, тогда как краткосрочные стратегии нуждаются в дополнительных фильтрах и механизмах защиты.

Пожалуй, самым ценным оказался сам процесс разработки — путь от идеи до кода и полученных результатов. Мы начали с формулировки торговой концепции, реализовали ее с помощью модульных классов стандартной библиотеки и проверили ее работу с помощью визуализации и бэктестирования. Такой системный подход позволил не только создать работоспособного торгового робота, но и закрепить важные принципы профессиональной разработки на MQL5: разделение задач, использование визуальной диагностики и тестирование в различных рыночных режимах. Именно такие методы составляют основу профессиональной MQL5-разработки.

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

Ниже вы найдете таблицу вложений вместе с исходным файлом проекта. Исходный файл проекта CiMA_Trend_Trader.mq5 служит одновременно практической демонстрацией и отправной точкой для дальнейших экспериментов. Вы можете тестировать код, модифицировать его, расширять функциональность, например, интегрировать другие классы стандартной библиотеки, CiCCI, CiMACD или дополнительные фильтры волатильности.

Мы будем рады вашим отзывам, предложениям и идеям по дальнейшему развитию проекта. Следите за продолжением серии публикаций.


Основные выводы

Основной аспект Описание:
Концепция → Код Перед реализацией переводите правила в понятный пошаговый алгоритм и псевдокод — это делает поведение системы воспроизводимым и упрощает тестирование.
ООП-структура Используйте небольшие специализированные классы или модули (индикаторы, управление риском, исполнение сделок), чтобы каждый компонент отвечал только за одну задачу. Такие модули можно повторно использовать и легко заменять.
Управление жизненным циклом Создавайте экземпляры индикаторов один раз, обновляйте их на каждом тике и освобождайте ресурсы при завершении работы.
Преобразование единиц измерения Явно отслеживайте единицы измерения: преобразовывайте ATR в пункты и денежный риск через размер и стоимость тика, чтобы правильно рассчитывать стоп-уровни и размер позиции.
Определение размера позиции Используйте формулу: допустимый риск ÷ убыток на один лот, затем нормализуйте объем согласно требованиям брокера.
Исполнение и обработка ошибок Централизуйте отправку торговых операций, всегда проверяйте коды результатов и реализуйте механизмы безопасного восстановления после ошибок.
Управление состоянием Храните ключевые параметры состояния (количество открытых позиций, время последней сделки, влаги) централизованно, чтобы логика соответствовала требованиям.
Защитное программирование Предусматривайте действия на случай отсутствия данных, нулевой стоимость тика и недостаточной историю цен. Используйте резервные сценарии вместо аварийного завершения работы.
Модульность Разделяйте задачи (сигналы, управление риском, защита, визуализация, исполнение сделок) на отдельные функции или файлы, чтобы упростить тестирование и поддержку.
Журналирование и визуализация Записывайте в журнал решения и используйте графические элементы на графике (скользящие средние, полосы ATR, метки входа) для сопоставления логики работы с визуальным поведением.
Качественное тестирование Проводите бэктестирование с качеством тиковых данных 99% и реалистичными параметрами спреда и свопов. Плохие данные приводят к ошибочным выводам.
Грамотная оптимизация Выполняйте оптимизацию осторожно, используя валидацию вне выборки и анализ устойчивых областей параметров, а не пиковые моменты прибыльности при тестировании.
Контроль производительности Минимизируйте операции на каждом тике, повторно используйте объекты и отслеживайте использование ресурсов.
Защита во время работы Реализуйте ограничения по дневным убыткам, количеству позиций, остановку торговли и проверки состояния для ограничения рисков.
Процесс экспериментов Отслеживайте результаты экспериментов (версии кода, файлы .set, метрики), проводите A/B-сравнения и вносите небольшие, измеримые изменения.


Прикрепленные файлы

Имя файла Description
CiMA_Trend_Trader.mq5 Автономный трендовый советник, демонстрирующий практическое использование трех классов стандартной библиотеки: CiMA (быстрая и медленная скользящие средние), CiATR (ATR для динамических стоп-лоссов и расчета объема позиции) и CTrade (исполнение торговых операций). Поддерживает настраиваемые параметры (периоды MA и ATR, коэффициенты, процент риска, соотношение TP/SL, фильтры волатильности, ограничения по дневным убыткам и количеству позиций), нормализацию объема с учетом требований брокера, защитные проверки, визуальное отображение точек входа и подробное журналирование для проверок в тестере стратегии.

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

Прикрепленные файлы |
Моделирование рынка: Position View (II) Моделирование рынка: Position View (II)
В этой статье я покажу как максимально просто и практично использовать индикатор для отслеживания открытых позиций на торговом сервере. Я делаю это именно так, шаг за шагом, чтобы показать, что вам не обязательно переносить всё это в советник. Многие из вас, вероятно, уже привыкли к этому по той или иной причине. На самом деле это ерунда, так как по мере развития данной реализации станет ясно, что вы сможете создавать или реализовать различные типы индикаторов для этой цели.
Разработка торговой стратегии с использованием подхода Volume Boundary Разработка торговой стратегии с использованием подхода Volume Boundary
В мире технического анализа цена часто оказывается в центре внимания. Трейдеры тщательно размечают поддержку, сопротивление и паттерны, но нередко игнорируют ключевую силу, которая движет этими ценовыми движениями: объем. В этой статье рассматривается новый подход к анализу объема — индикатор Volume Boundary. Такое преобразование с использованием сложных сглаживающих функций, таких как кривая «бабочка» и тройная синусоида, облегчает интерпретацию данных и позволяет разрабатывать системные торговые стратегии.
Алгоритм оптимизации койотов — Coyote Optimization Algorithm (COA) Алгоритм оптимизации койотов — Coyote Optimization Algorithm (COA)
Представляем MQL5-реализацию Coyote Optimization Algorithm: стаи с локальными альфами, медианная тенденция и встроенный кроссовер обеспечивают параллельное исследование областей пространства и контроль преждевременной сходимости. Алгоритм встроен в C_AO и проверен на стандартном стенде и композитном античит-тесте. В статье — код, псевдокод и разбор операторов, позволяющие применить COA для оптимизации параметров торговой системы.
Создание  профессиональной торговой системы на основе Heikin Ashi (Часть 1): Разработка пользовательского индикатора Создание профессиональной торговой системы на основе Heikin Ashi (Часть 1): Разработка пользовательского индикатора
Эта статья — первая часть серии из двух материалов, предназначенной для освоения практических навыков и лучших практик написания пользовательских индикаторов на MQL5. На практическом примере Heikin Ashi в статье рассматривается теория графиков Heikin Ashi, объясняется, как рассчитываются свечи Heikin Ashi, и показывается их применение в техническом анализе. Центральная часть материала — пошаговое руководство по разработке полнофункционального индикатора Heikin Ashi с нуля, с понятными пояснениями, которые помогают читателям разобраться, что именно писать в коде и почему. Эти базовые знания подготовят почву для второй части, где мы создадим советник, торгующий на основе логики Heikin Ashi.