Советник на MQL5 с нуля. Часть 1: скелет робота и риск без мартингейла

Разбираем устройство советника на MQL5 на примере нашего собственного робота: три обязательные функции, расчёт объёма от расстояния до стопа и почему высокий процент побед обычно означает спрятанный убыток.

Admin
Обложка статьи: Советник на MQL5 с нуля. Часть 1: скелет робота и риск без мартингейла

Советник на MQL5 с нуля. Часть 1: скелет робота и риск без мартингейла

Мы держим на живых демо-счетах полтора десятка бесплатных советников из каталога и публикуем их результаты. Когда набралось 449 сделок, выяснилась неприятная закономерность: роботы, которые сливали счёт, почти все имели высокий процент прибыльных сделок. Один закрыл в плюс 68% сделок и обнулил депозит.

Поэтому мы написали свой — не чтобы обогнать рынок, а чтобы показать на коде, чем устроен робот, который не может уничтожить счёт за один день. Он лежит в каталоге вместе с исходником и торгует на нашем демо-счёте. В этой части разберём скелет и работу с риском, во второй — сборку и прогон в тестере из командной строки.

Из чего состоит советник

Любой советник на MQL5 — это три функции, которые вызывает терминал.

int OnInit()                      // один раз при запуске на графике
void OnTick()                     // на каждом изменении цены
void OnDeinit(const int reason)   // при снятии с графика

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

#include <Trade\Trade.mqh>

CTrade trade;

int OnInit()
{
   trade.SetExpertMagicNumber(MagicNumber);
   trade.SetTypeFillingBySymbol(_Symbol);
   trade.SetDeviationInPoints(20);
   return(INIT_SUCCEEDED);
}

Класс CTrade из стандартной библиотеки берёт на себя формирование торговых запросов — писать структуру MqlTradeRequest вручную в 2026 году незачем.

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

static datetime lastBar = 0;
datetime barTime = iTime(_Symbol, PERIOD_M15, 0);
if(barTime == lastBar) return;   // эта свеча уже обработана
lastBar = barTime;

Параметры: почему не в пунктах

Первая версия нашего советника не совершила ни одной сделки за четыре года истории. Причина оказалась в фильтре, записанном в пунктах: у золота разрядность котировок у разных брокеров отличается в десять раз, и «диапазон не шире 3000 пунктов» означало у одного брокера три доллара, а у другого тридцать. Фильтр отсекал вообще всё.

Поэтому пороги задаются в процентах от цены — такая настройка переносится между брокерами и инструментами без правок:

input double MinRangePct = 0.15;  // тихий день, ловить нечего
input double MaxRangePct = 2.0;   // новостной разрыв, пробой ложный

double rangePct = (rangeHigh - rangeLow) / price * 100.0;
if(rangePct < MinRangePct || rangePct > MaxRangePct) return;

То же касается спреда. Абсолютный лимит бесполезен: на золоте спред бывает 90–190 пунктов, и порог в 60 отсекает всё подряд. Значение имеет не сам спред, а какую долю хода он съедает:

double spreadShare = spreadPrice / (rangeHigh - rangeLow) * 100.0;
if(spreadShare > MaxSpreadRangePct) return;   // издержки съедят движение

Главное: объём считается от стопа, а не наоборот

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

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

double RiskLots(double stopDistance)
{
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   if(tickValue <= 0 || tickSize <= 0) return(0);

   // Сколько денег готовы потерять на этой сделке
   double moneyRisk  = AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100.0;
   // Во сколько обойдётся стоп при объёме в один лот
   double lossPerLot = (stopDistance / tickSize) * tickValue;

   double lots = moneyRisk / lossPerLot;

   double minLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   lots = MathFloor(lots / lotStep) * lotStep;

   // Депозита не хватает на такой стоп — сделку пропускаем,
   // а не рискуем больше заявленного
   if(lots < minLot) return(0);
   return(lots);
}

Обратите внимание на последнюю строку. Соблазн велик: округлить до минимального лота и всё-таки войти. Именно так робот незаметно начинает рисковать втрое больше, чем написано в настройках. Пропустить сделку дешевле.

Важная деталь: SYMBOL_TRADE_TICK_VALUE и SYMBOL_TRADE_TICK_SIZE нужно спрашивать у терминала, а не зашивать в код. Для золота, валютной пары и индекса эти числа разные, и робот, написанный «под EURUSD», на золоте посчитает объём с ошибкой в сто раз.

Безубыток: страховка, а не панацея

Когда сделка ушла в плюс на заранее заданное число «рисков», стоп переносится в точку входа. Дальше сделка либо заработает, либо закроется в ноль, но убыточной уже не станет.

double risk   = MathAbs(entry - sl);
double profit = (type == POSITION_TYPE_BUY) ? (price - entry) : (entry - price);

if(BreakevenAtR > 0 && profit >= risk * BreakevenAtR)
   trade.PositionModify(ticket, entry, tp);   // стоп на цену входа

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

Что получилось

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

Параметры подбирались на 2022–2024 годах и проверялись на 2025–2026, которых подбор не видел:

Выборка Результат Сделок Прибыльных Профит-фактор Просадка
Обучение 2022–2024 +33,0% 327 43,7% 1,35 7,5%
Проверка 2025–2026 +11,6% 145 46,2% 1,33 3,9%
Другой брокер, 2022–2025 +41,5% 335 30,4% 1,41 9,2%

Третья строка — тот же советник и те же настройки, но на котировках другого брокера. Разница в результате берётся не из стратегии, а из спреда и разрядности котировок: у одного брокера золото называется GOLD#, у другого XAUUSD. Если стратегия рассыпается при смене брокера — она держалась не на рынке, а на конкретном прайс-фиде.

Прибыльных сделок меньше половины — и это нормально при цели втрое больше риска. Робот из нашего теста с 68% побед выглядел кратно лучше по этой колонке и при этом уничтожил депозит. Процент побед не говорит о советнике почти ничего; смотреть надо на просадку и профит-фактор.

Честно и про ограничения: на EURUSD с теми же настройками профит-фактор около единицы, то есть преимущество именно на золоте. И backtest остаётся backtest — робот уже стоит на нашем демо-счёте, результат будет виден в мониторинге без возможности что-либо подкрутить.

Дальше

Во второй части — то, чего почти нет в русскоязычных руководствах: как компилировать советник из командной строки через metaeditor64.exe и гонять его в тестере стратегий без единого клика в интерфейсе, через ini-конфиг. Это превращает проверку гипотезы из десятиминутного ручного ритуала в десятисекундную команду, и только благодаря этому мы смогли перебрать сотню конфигураций за вечер.

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

Ссылка на брокера в тексте партнёрская: по ней мы получаем комиссию, а вы — те же условия, что и по прямой. Мы упоминаем только то, чем пользуемся сами.

Поделиться статьёй:

Комментарии (0)

Чтобы оставить комментарий, необходимо войти в систему

Пока нет комментариев

Будьте первым, кто оставит комментарий!

Хотите больше полезных статей?

Подпишитесь на наш блог и получайте новые статьи прямо на почту

Читать все статьи