К содержанию
CyberSoftLabRoman Leonov

Разработка

C++ и MQL5

Быстрые советники MetaTrader 5 и свои подключения к бирже. Здесь ошибка в размере лота стоит денег сразу, а не на тесте.

Когда MQL5

Терминал уже стоит у клиента, нужен советник на FX / MOEX / CFD. Событийная модель терминала — ваш рантайм. Не тащите сюда веб-скрейпинг и тяжёлый ML.

Когда чистый C++

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

События советника

OnInitПроверка символа, таймера, файлов состояния. Если нельзя торговать — return INIT_FAILED.
OnTickГорячий путь. Никаких HTTP и тяжёлых циклов. Только котировка → решение.
OnTimerWatchdog, сверка позиций, защита капитала, «биржа молчит».
OnTradeTransactionИстина по сделкам. Не верьте локальному флагу «ордер отправлен».
OnDeinitСнять таймер, сбросить графические объекты, сохранить state.

Как устроен бот

Схема по слоям

В советнике MT5 данные и ордера живут в OnTick, контроль — в OnTimer, журнал пишется в файл. Стратегия и риск — отдельные include, не один OnTick на тысячу строк.

  1. 01

    Данные

    Котировки с биржи или из терминала. Свечи и тики, проверка что в истории нет дыр.

  2. 02

    Стратегия

    Правила входа и выхода. Только «купить / продать / ждать» — без ключей и без размера лота.

  3. 03

    Риск

    Сколько лотов, где стоп, лимит на день. Если риск говорит нет — ордера нет.

  4. 04

    Исполнение

    Отправка ордера, повтор если сеть моргнула, сверка с реальной позицией.

  5. 05

    Журнал

    Что решили, почему, какой номер ордера, какой результат с комиссией.

  6. 06

    Контроль

    Пульс бота, сообщение в Telegram, кнопка «всё закрыть и остановиться».

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

Библиотека

Полезные функции

Вставляйте в include. Имена оставьте своими, смысл не размывайте: нормализация, спред, риск, heartbeat, magic.

Нормализация лота под шаг брокера

MQL5 молча отвергнет объём, если он не кратен lot step. Нормализуйте до OrderSend, не после ошибки.

mql5 / c++
double NormalizeLot(const double lot)
{
   const double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   const double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   const double step   = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   if(step <= 0) return minLot;
   double n = MathFloor(lot / step + 1e-8) * step;
   n = MathMax(minLot, MathMin(maxLot, n));
   int digits = 0;
   double s = step;
   while(s < 1.0 && digits < 8) { s *= 10.0; digits++; }
   return NormalizeDouble(n, digits);
}
Фильтр спреда перед входом

Скальпер без фильтра спреда торгует комиссию брокера. Порог — в пунктах символа, не «на глаз».

mql5 / c++
bool SpreadOk(const int maxPoints)
{
   long spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
   return spread > 0 && spread <= maxPoints;
}

bool StopsLevelOk(const double entry, const double sl)
{
   int stops = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   return MathAbs(entry - sl) >= stops * point;
}
Сайзинг от риска в MQL5

Тик-стоимость разная у FX, фьючерса и CFD. Считайте через SYMBOL_TRADE_TICK_VALUE, не через «лот = 0.1».

mql5 / c++
double LotFromRisk(const double riskMoney, const double entry, const double sl)
{
   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double dist = MathAbs(entry - sl);
   if(tickSize <= 0 || tickValue <= 0 || dist <= 0) return 0;
   double ticks = dist / tickSize;
   double lot = riskMoney / (ticks * tickValue);
   return NormalizeLot(lot);
}
Watchdog на OnTimer

OnTick молчит на тонком рынке. Таймер проверяет связь, просадку и «зависший» ордер.

mql5 / c++
datetime g_lastBeat = 0;

void OnInitWatchdog(const int seconds)
{
   EventSetTimer(seconds);
   g_lastBeat = TimeCurrent();
}

void OnTimer()
{
   if(TimeCurrent() - g_lastBeat > 30)
      Print("heartbeat lost — flatten and halt");
   // EquityProtect();
   // ReconcilePending();
}

void MarkBeat()
{
   g_lastBeat = TimeCurrent();
}
Изоляция по magic number

Два советника на одном счёте без magic — это взаимное закрытие позиций. Смотрите только свои ордера.

mql5 / c++
bool IsOurs(const ulong ticket, const long magic)
{
   if(!PositionSelectByTicket(ticket)) return false;
   return PositionGetInteger(POSITION_MAGIC) == magic
       && PositionGetString(POSITION_SYMBOL) == _Symbol;
}

int CountOurs(const long magic)
{
   int n = 0;
   for(int i = PositionsTotal() - 1; i >= 0; --i)
   {
      ulong ticket = PositionGetTicket(i);
      if(IsOurs(ticket, magic)) n++;
   }
   return n;
}

Цена и пункты

Digits, tick size и point — три разных сущности. Стоп «в пунктах» без tick size на фьючерсе даёт ложный лот.

Ошибки торговли

После CTrade проверяйте retcode. Requote, no money, off quotes — разные ветки, не один Print().

Графика

Объекты на графике — для человека. Логика не должна зависеть от того, виден ли трендлайн.

Подарок

Индикатор TradingView

Sigma Bands + RSI Filter MTF. Бесплатно: зоны, где цена слишком далеко ушла от средней. Ставите в Pine Editor и торгуете лимитками от полос, а не от каждого зигзага.

Средняя и сигмы

Индикатор считает среднюю цену и насколько рынок ушёл от неё. Это и есть сигма: обычное отклонение, а не «магический уровень».

Полосы покупки и продажи

Нижние полосы (−2σ и −3σ) — зона лимитной покупки. Верхние (+2σ и +3σ) — зона продажи. Это карта, не приказ купить прямо сейчас.

Фильтр RSI

Чтобы не ловить каждый шум у полосы, смотрит RSI: внизу рынок должен быть перепродан, вверху — перекуплен.

Несколько таймфреймов

Сверяет M15, H1, H4, день и неделю. Сильный сигнал — когда несколько графиков смотрят в одну сторону.

Связь

Нужен советник или ядро на C++

Разберём идею, соберём бота, проверим риск. Пишите в Telegram — отвечаю лично.