Торговая стратегия Heads or Tails (Орёл или Решка) разбор кода торгового робота.

Торговая стратегия Heads or Tails (Орёл или Решка) разбор кода торгового робота.

16 января 2026, 11:31
Vladimir Pastushak
0
14

Торговая стратегия «Орёл или решка» относится к категории высокорисковых краткосрочных торговых подходов, используемых преимущественно на фондовом рынке и рынке Forex. Её название обусловлено случайностью принятия решений, подобно подбрасыванию монеты («орел» — покупать актив, «решка» — продавать). Эта стратегия основана исключительно на интуитивных решениях или случайных сигналах и игнорирует фундаментальные факторы анализа рынка.


В базу кодов добавлен исходный код торговой стратегии:

MetaTrader 5: https://www.mql5.com/ru/code/11637


#property copyright "Copyright 2025, Trading-Go." // Setting copyright property
#property link      "https://www.mql5.com/en/channels/tradingo-go-en"  // Setting developer resource link
#property version   "26.010" // Program version

Приведенный код является директивами компилятора для программы MetaTrader Expert Advisor (EA) или индикатора, написанного на языке MQL4/MQL5.

Рассмотрим каждую строку отдельно:

1. #property copyright "Copyright 2025, Trading-Go."

Эта строка устанавливает юридическое право собственности на исходный код EA или индикатор. Она определяет владельца авторских прав и помогает обозначить принадлежность продукта определённой организации или лицу ("Trading-Go"). Эта информация отображается в окне свойств EA/индикатора в клиентском терминале MetaTrader.

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

3. #property version "26.010"

Здесь задаётся версия программного продукта. Обычно разработчики указывают версию в формате «XX.XX», где первая цифра представляет собой номер основной версии, вторая — минорной версии, третья — патча. Версия помогает пользователям легко отслеживать обновления и поддерживать совместимость инструментов.

#include <Trade\Trade.mqh> // Including Trade.mqh library
CTrade        trade; // Object of class CTrade to manage trades

#include <Trade\PositionInfo.mqh> // Including PositionInfo.mqh library
CPositionInfo posit; // Object of class CPositionInfo to process positions

Этот блок кода включает библиотеки и создаёт объекты классов для управления торговыми операциями и позициями в торговой платформе MetaTrader.

Давайте разберём каждую строчку детально:

1. #include <Trade\Trade.mqh>

Это директива включения заголовочного файла (Trade.mqh), содержащего класс CTrade. Данный файл находится в папке /Trade, что означает, что библиотека относится к стандартной библиотеке торговых операций, предоставляемой платформой MetaTrader. Класс CTrade используется для удобного взаимодействия с торговлей, включая открытие позиций, закрытие сделок, модификацию ордеров и получение информации о состоянии торговли.

2. CTrade trade;

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

3. #include <Trade\PositionInfo.mqh>

Директива подключения второго заголовочного файла (PositionInfo.mqh) из той же директории /Trade. Файл содержит определение класса CPositionInfo, который предназначен для обработки информации о текущих открытых позициях на счёте. Через этот класс можно извлекать детальные сведения о каждой открытой сделке, такие как объём сделки, цена открытия, прибыль/убыток и многое другое.

4. CPositionInfo posit;

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

Общее назначение блока:

Данный блок обеспечивает основу для автоматизации процесса торговли путём предоставления высокоуровневого API (интерфейса прикладного программирования). С помощью созданных объектов trade и posit можно реализовать автоматическое принятие торговых решений, мониторинг позиций и автоматизацию рутинных операций.

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

input double  iLots         = 0.10; // Input parameter for lot size (trade volume)
input int     iTakeProfit   = 450;  // Input parameter for profit fixing level (Take Profit)
input int     iStopLoss     = 390;  // Input parameter for loss limitation level (Stop Loss)
input int     iMagicNumber  = 227; // Input parameter for unique deal number (Magic Number)
input int     iSlippage     = 30; // Input parameter for maximum price slippage

string sy = ""; // Variable to store instrument symbol
double pt = 0; // Variable for point calculation step
int    dt = 0; // Variable for decimal places count

Рассмотрим каждый элемент приведённого блока кода по отдельности, объясняя его предназначение и роль в процессе настройки автоматического эксперта (Expert Advisor) в торговой платформе MetaTrader.


Входные параметры (Input Parameters):

1. iLots = 0.10;

Это входной параметр типа double, определяющий размер лота (объем сделки). Значением по умолчанию установлено значение 0.10. Размер лота регулирует количество активов, покупаемых или продаваемых в одной сделке. Например, если инструмент EURUSD, то лот размером 0.1 соответствует 10 тысячам единиц базовой валюты (например, евро).

2. iTakeProfit = 450;

Параметр типа int, задающий уровень фиксации прибыли (Take Profit). Значение по умолчанию равно 450. Take Profit автоматически закрывает позицию, если рыночная цена достигает указанного уровня прибыли относительно цены входа в сделку. Уровень указывается в пунктах (pips).

3. iStopLoss = 390;

Параметр типа int, устанавливающий уровень ограничения убытков (Stop Loss). Значение по умолчанию составляет 390. Stop Loss автоматически закрывает позицию, если рынок движется против вашей позиции и убыток достигает заданного уровня. Убыток фиксируется в пунктах.

4. iMagicNumber = 227;

Параметр типа int, служащий уникальным идентификационным номером (Magic Number) для транзакций. Каждое событие (открытие позиции, закрытие и др.) получает уникальный Magic Number, позволяющий фильтровать сделки по этому номеру. Значение по умолчанию — 227.

5. iSlippage = 30;

Параметр типа int, ограничивающий максимальное отклонение цены исполнения ордера от указанной цены. Установленное значение — 30 пунктов. Если фактическая цена отличается больше, чем на указанное число пунктов, сделка не исполняется. Используется для защиты от чрезмерного проскальзывания цен.

Локальные переменные (Local Variables):

1. sy = "";

Переменная типа string, хранит символ финансового инструмента (например, "EURUSD"), используемого в торговле. Изначально пустая строка ("").

2. pt = 0;

Переменная типа double, рассчитанная для хранения размера пункта конкретного инструмента. Используется для вычисления уровней Take Profit и Stop Loss исходя из количества пунктов. По умолчанию равна нулю (0).

3. dt = 0;

Переменная типа int, предназначенная для подсчета числа десятичных знаков после запятой у котировок выбранного инструмента. Количество десятичных знаков важно для правильного расчета профита и стоп-лосса, а также других метрик. Начальное значение — ноль (0).

Этот блок кода предназначен для задания начальных настроек и подготовки среды для автоматического эксперта в MetaTrader. Блок вводных параметров позволяет гибко настраивать поведение EA прямо перед началом торговли.

   sy = _Symbol; // Getting current trading instrument
   pt = _Point; // Getting minimum unit change size
   dt = _Digits; // Getting number of decimal digits in price

   trade.SetExpertMagicNumber(iMagicNumber); // Setting unique deal number for trade operations

   trade.SetDeviationInPoints(iSlippage); // Setting maximum price deviation points

   trade.SetTypeFillingBySymbol(sy); // Setting order execution type according to instrument settings

   trade.SetMarginMode(); // Setting margin mode


Этот блок кода демонстрирует инициализацию основных параметров и конфигурацию торговых операций в автоматическом эксперте (Expert Advisor) в MetaTrader. Каждый оператор играет важную роль в подготовке условий для успешной работы EA.

Рассмотрим подробней каждое выражение.

Переменные окружения:

1. sy = _Symbol;

Присваивает глобальной переменной sy название текущего торгового инструмента, с которым работает EA. _Symbol — встроенная константа, возвращающая наименование актива, выбранного в терминале. Таким образом, мы получаем доступ к имени инструмента, необходимого для дальнейших расчётов и работы с ним.

2. pt = _Point;

Задает переменную pt, которая принимает минимальное изменение стоимости инструмента в пунктах (_Point). Это базовая единица измерения изменения цены, необходимая для правильной интерпретации сигналов системы и корректировки приказов.

3. dt = _Digits;

Определяет переменную dt, представляющую количество десятичных знаков в цене текущего инструмента. Поскольку разные инструменты имеют разное количество знаков после запятой (например, USDJPY имеет два знака, EURUSD четыре), эта переменная важна для корректного округления чисел и вычислений.


Настройка торгового объекта:

4. trade.SetExpertMagicNumber(iMagicNumber);

Устанавливает уникальный магический номер (Magic Number) для всех сделок, инициированных данным EA. Магический номер используется для идентификации сделок, выполненных конкретным EA, и упрощает фильтрацию позиций по этому критерию. Уникальность номера гарантирует отсутствие путаницы среди различных роботизированных стратегий.

5. trade.SetDeviationInPoints(iSlippage);

Задавая максимальный допуск отклонения цены исполнения ордера от ожидаемой цены (slippage), обеспечивается защита от больших колебаний рынка. Установка значения в пункте предотвращает исполнение сделок, если цена изменилась слишком сильно. Чем меньше показатель, тем точнее будут исполняться ваши запросы.

6. trade.SetTypeFillingBySymbol(sy);

Настраивает режим заполнения ордеров в зависимости от особенностей самого инструмента (_Symbol). Некоторые инструменты требуют мгновенного исполнения («Market Execution»), тогда как другие могут допускать отложенное исполнение («Instant Execution»). Данная команда автоматически выбирает правильный режим согласно спецификациям символа.

7. trade.SetMarginMode();

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

   double stepvol = SymbolInfoDouble(sy, SYMBOL_VOLUME_STEP); // Get the lot size change step for the selected symbol
   if(stepvol > 0.0) // If the lot size step is positive, apply calculation of adjusted lot size
      lt = stepvol * (MathFloor(iLots / stepvol) - 1); // Round down the lot size to the nearest step value and decrease by one step
//---
   double minvol = SymbolInfoDouble(sy, SYMBOL_VOLUME_MIN); // Get minimum allowed lot size for the specified symbol
   if(lt < minvol) // If the adjusted lot size is less than the minimum possible value, reset it to zero
      lt = 0.0;

   ::MathSrand(GetTickCount()); // Generating initial number for random generator

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

Давайте рассмотрим каждый шаг подробнее:

1. Определение шага изменения объёма:

double stepvol = SymbolInfoDouble(sy, SYMBOL_VOLUME_STEP);

Команда SymbolInfoDouble() получает шаг изменения объема (лот) для выбранного символа (SYMBOL_VOLUME_STEP). Шаг определяет минимальный интервал, на который можно изменять размер лота. Например, для некоторых финансовых инструментов шаг равен 0.01, значит, торговля возможна только целыми сотнями долей лота.

2. Проверка положительности шага:

if(stepvol > 0.0)

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

3. Округление и уменьшение лота на одну ступень:

lt = stepvol * (MathFloor(iLots / stepvol) - 1);

Алгоритм уменьшает указанный трейдером объем сделки (iLots) на одну единицу минимального шага.

Вот пошагово, что происходит внутри выражения:

iLots / stepvol: рассчитывается целое количество шагов, укладывающихся в введённый лот.

MathFloor(): округляет дробное значение вниз до ближайшего целого числа.

Вычитаем единицу: уменьшается количество полных шагов на один.

Умножаем обратно на шаг: получаем новый скорректированный объем лота.

4. Получение минимально возможного лота:

double minvol = SymbolInfoDouble(sy, SYMBOL_VOLUME_MIN);

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

5. Проверка минимальной границы:

if(lt < minvol)

   lt = 0.0;

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

6. Генерация начального значения генератора случайных чисел:

::MathSrand(GetTickCount());

Генерируется стартовое значение для последующего использования функций случайных чисел. Команда берёт текущее значение счётчика тиктов (GetTickCount()) и использует его как зерно для последующих псевдослучайных последовательностей. Это делается для увеличения непредсказуемости результатов случайных процессов.

   int total = ::PositionsTotal(), b = 0, s = 0; // Total open positions and purchase/sale counters

   double Bid = ::SymbolInfoDouble(sy, SYMBOL_BID); // Current bid price (selling price)
   double Ask = ::SymbolInfoDouble(sy, SYMBOL_ASK); // Current ask price (buying price)

   double new_sl = 0, new_tp = 0, old_sl = 0, old_tp = 0; // New and old stop loss and take profit levels

   if(Bid <= 0 || Ask <= 0) // If prices are incorrect
      return; // Exit function

Представленный блок кода выполняет ряд важных подготовительных операций перед выполнением дальнейшей логики торговли в автоматическом эксперте (Expert Advisor) в торговой платформе MetaTrader. Давайте рассмотрим каждый этап подробно.

Инициализация переменных:

1. Получение общего количества открытых позиций:

int total = ::PositionsTotal();

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

2. Инициализация счётчиков покупок и продаж:

b = 0, s = 0;

Переменные b и s используются для учёта количества открытых длинных (buy) и коротких (sell) позиций соответственно. Они изначально сбрасываются в ноль и позже увеличиваются при анализе позиций.


Извлечение текущих рыночных цен:

3. Запрашиваем цену BID (цена продажи):

Bid = ::SymbolInfoDouble(sy, SYMBOL_BID);

Функция SymbolInfoDouble() возвращает последнюю известную цену покупки (BID) для текущего инструмента (sy). Цена BID отражает лучшую рыночную цену, по которой трейдер может продать инструмент немедленно.

4. Запрашиваем цену ASK (цена покупки):

Ask = ::SymbolInfoDouble(sy, SYMBOL_ASK);

Аналогично предыдущему пункту, запрашивается последняя известная цена продажи (ASK) для инструмента. Цена ASK показывает наилучшую цену, по которой возможно купить инструмент мгновенно.

Подготовка уровней стопа и профита:

5. Объявляем новые и старые уровни Stop-Loss и Take-Profit:

new_sl = 0, new_tp = 0, old_sl = 0, old_tp = 0;

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

Безопасность и проверка корректности цен:

6. Проверяем корректность полученных цен:

if(Bid <= 0 || Ask <= 0)

   return;

Осуществляется простая проверка на наличие некорректных цен. Если цена BID или ASK равна нулю или отрицательна, программа выходит из текущей процедуры (return), чтобы избежать ошибочных операций. Такое условие защищает систему от нежелательных последствий из-за неверных данных.

 for(int i = 0; i < total; i++) // Loop through open positions
      if(posit.SelectByIndex(i)) // Select position by index
         if(posit.Symbol() == sy) // Check instrument of position
            if(posit.Magic() == iMagicNumber) // Check unique number of position
              {

               old_sl = ::NormalizeDouble(posit.StopLoss(),   dt); // Converting old stop loss to required decimal places
               old_tp = ::NormalizeDouble(posit.TakeProfit(), dt); // Converting old take profit to required decimal places

               if(posit.PositionType() == POSITION_TYPE_BUY) // If position is BUY (purchase)
                 {
                  new_sl = ::NormalizeDouble(Ask - iStopLoss   * pt, dt); // New stop loss below current ask price
                  new_tp = ::NormalizeDouble(Ask + iTakeProfit * pt, dt); // New take profit above current ask price
                  b++;                                                 // Increment purchases counter
                 }

               if(posit.PositionType() == POSITION_TYPE_SELL) // If position is SELL (sale)
                 {
                  new_sl = ::NormalizeDouble(Bid + iStopLoss   * pt, dt); // New stop loss above current bid price
                  new_tp = ::NormalizeDouble(Bid - iTakeProfit * pt, dt); // New take profit below current bid price
                  s++; // Increment sales counter
                 }

               if(old_sl == 0 || old_tp == 0) // If new levels differ from old ones
                  trade.PositionModify(posit.Ticket(), new_sl, new_tp);// Modify position with new SL and TP levels
              }

Рассматриваемый блок кода реализует цикл перебора открытых позиций на счёте и обновление уровней стоп-лосса (SL) и тэйк-профита (TP) для соответствующих позиций. Давайте рассмотрим каждую операцию последовательно:


Перебор открытых позиций:

for(int i = 0; i < total; i++)

Здесь осуществляется итерация по списку открытых позиций. Переменная total была предварительно установлена количеством открытых позиций на счете (PositionsTotal()). Каждая открытая позиция обрабатывается индивидуально.


Выбор позиции по индексу:

if(posit.SelectByIndex(i))

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


Проверка соответствия символа и магического номера:

if(posit.Symbol() == sy && posit.Magic() == iMagicNumber)

Сначала проверяется, совпадает ли торговый инструмент выбранной позиции с символом, указанным в конфигурации (sy). Затем сравнивается уникальный номер позиции (Magic Number) с установленным в параметрах эксперта (iMagicNumber). Оба условия необходимы для точной выборки нужной позиции.


Сохранение старых уровней SL и TP:

old_sl = NormalizeDouble(posit.StopLoss(), dt);

old_tp = NormalizeDouble(posit.TakeProfit(), dt);

Для выбранной позиции сохраняются существующие уровни стоп-лосса и тэйк-профита. Важно нормализовать полученные значения, учитывая количество десятичных знаков инструмента (dt), чтобы обеспечить правильную обработку плавающих значений.


Обработка длинной позиции (BUY):

if(posit.PositionType() == POSITION_TYPE_BUY)

Если выбранная позиция открыта на покупку (POSITION_TYPE_BUY), выполняется следующий блок:

Расчёт нового уровня Stop-Loss: новая линия стоп-лосса располагается ниже текущей рыночной цены (Ask), уменьшенной на заранее установленный уровень стоп-лосса (iStopLoss), выраженный в пунктах (pt).

Расчёт нового уровня Take-Profit: новый уровень фиксации прибыли размещён выше текущей рыночной цены (Ask), увеличенной на заданный уровень профита (iTakeProfit), выраженный в пунктах.

Учёт статистики: увеличивается счётчик открытых покупок (b++).


Обработка короткой позиции (SELL):

if(posit.PositionType() == POSITION_TYPE_SELL)

Если позиция короткая (POSITION_TYPE_SELL), выполняются аналогичные действия:

Новый Stop-Loss: выше текущей цены (Bid), увеличенной на размер стоп-лосса.

Новый Take-Profit: ниже текущей цены (Bid), уменьшенной на заданный уровень профита.

Статистика: увеличивается счётчик продаж (s++).


Изменение уровней SL и TP:

if(old_sl == 0 || old_tp == 0)

   trade.PositionModify(posit.Ticket(), new_sl, new_tp);

Если старое значение SL или TP отсутствовало (равно нулю), позиция модифицируется новыми уровнями. Метод PositionModify() применяется для внесения изменений в уровни защиты и фиксацию прибыли.

   if((b + s) == 0) // If there are no active positions
      if(::MathRand() % 2 == 0) // Randomly choosing direction of opening position
        {
         if(trade.CheckVolume(sy, lt, Ask, ORDER_TYPE_BUY)) // Verification of the sufficiency of funds to perform the required trading operation
            if(trade.Buy(lt)) // Opening long position (BUY)
               return; // End function execution
        }
      else
         if(trade.CheckVolume(sy, lt, Bid, ORDER_TYPE_SELL)) // Verification of the sufficiency of funds to perform the required trading operation
            if(trade.Sell(lt)) // Opening short position (SELL)
               return; // End function execution


Данный блок кода описывает алгоритм случайного открытия либо длинной (BUY), либо короткой (SELL) позиции, если на данный момент на счёте отсутствуют активные позиции. Рассмотрим процесс подробно:


Анализ наличия открытых позиций:

if((b + s) == 0)

Проверяется сумма двух счётчиков — количества открытых позиций на покупку (b) и продажу (s). Если сумма равна нулю, это означает, что на счёте нет активных позиций. Только в таком случае запускается процедура открытия новой позиции.


Случайный выбор направления позиции:

if(MathRand() % 2 == 0)

Выбирается направление будущей позиции случайно. Для этого используется генератор случайных чисел (MathRand()). Оператор % 2 возвращает остаток от деления результата случайного числа на 2, давая вероятность 50% на получение нуля (случайное число чётное) и 50% на ненулевое значение (нечётное число).


Если остаток равен нулю, выбирается покупка (ORDER_TYPE_BUY).

Иначе открывается короткая позиция (ORDER_TYPE_SELL).

Открытие длинной позиции (BUY):

if(trade.CheckVolume(sy, lt, Ask, ORDER_TYPE_BUY))

Перед покупкой проверяется достаточность средств для совершения требуемой сделки методом CheckVolume(). Аргументы:

  • Инструмент (sy),
  • Объем лота (lt),
  • Текущая цена покупки (Ask),
  • Тип заказа (ORDER_TYPE_BUY).

Метод проверяет баланс и доступность средств для открытия позиции нужного объема.

if(trade.Buy(lt))

Если средства достаточно, открываем длинную позицию командой Buy(), передавая ей объем лота (lt). Если операция успешна, работа блока прекращается (return).


Открытие короткой позиции (SELL):

else

   if(trade.CheckVolume(sy, lt, Bid, ORDER_TYPE_SELL))

При условии, что предыдущий случай не сработал (выбор упал на продажу), аналогично проверяются средства для короткой позиции:

  • Инструмент (sy),
  • Объем лота (lt),
  • Текущая цена продажи (Bid),
  • Тип заказа (ORDER_TYPE_SELL).

if(trade.Sell(lt))

Если всё успешно, создаётся короткая позиция командой Sell(), и выполнение останавливается (return).


void OnDeinit(const int reason) // Deinitialization function
  {

  }

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

Например, если ваш советник работал с графиками, создавал окна вывода или выполнял сетевые запросы, логичным действием было бы закрыть окно графика, освободить память или завершить соединение.


Итог:

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