preview
От одиночного CatBoost к Cross-Fitted Stack в MQL5: ансамбль без утечки будущего и отдельный audit-forward 2026

От одиночного CatBoost к Cross-Fitted Stack в MQL5: ансамбль без утечки будущего и отдельный audit-forward 2026

MetaTrader 5Эксперты |
64 0
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Введение

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

В статье описана практическая реализация ансамбля для MetaTrader 5. Базовый уровень составляют модели MIDAS-SINERGY. Поверх них строится отдельный CatBoost stack, оценивающий каждого торгового кандидата по исходному сигналу, треугольному контексту, времени, инструменту и согласованности классификаторов

Основное внимание уделено двум вопросам. Первый — как получить данные для обучения второго уровня так, чтобы будущие результаты не попадали в его признаки даже косвенно. Для этого используется purged expanding walk-forward: каждый очередной год прогнозируется моделями, которые обучались только на более ранней истории, а между train и validation оставляется 72-часовой embargo.

Второй вопрос — как перенести уже обученный ансамбль в MQL5 без внешнего Python-процесса. В итоговом советнике встроена 31 CatBoost-модель: базовые модели, модели треугольного контекста, три участника stack-ансамбля, калибратор вероятности и модель управления размером позиции.. Их деревья выполняются непосредственно средствами MQL5.

Эксперимент проводится на десяти валютных парах с принятием решений по закрытым барам H1. Для оценки качества используется несколько уровней проверки: out-of-fold результаты 2022–2025 годов, отдельный audit 2026 года и последующий тест готового многовалютного советника в Strategy Tester.

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


Практическая проблема: как построить stack без утечки данных

Главная сложность stacking в торговых системах возникает ещё до выбора параметров CatBoost. Необходимо правильно получить прогнозы моделей первого уровня, которые затем станут входными данными для второго уровня.

Рассмотрим распространённую ошибку. Базовая модель обучается на всей доступной выборке. После этого для тех же строк вычисляются её прогнозы, а полученные значения передаются в meta-модель. Формально всё работает: второй уровень получает признаки, CatBoost обучается, метрики растут. Но такой эксперимент уже содержит утечку.

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

Для временных рядов проблема становится ещё серьёзнее. Нельзя просто случайно разбить данные на folds, потому что строки одного периода связаны между собой, а торговые события могут пересекаться по времени. Поэтому в V5 используется последовательный purged expanding walk-forward.

Для каждого validation-года действует правило:

  • train содержит только историю, расположенную раньше начала проверяемого года;
  • между последней обучающей строкой и validation оставляется 72-часовой embargo;
  • прогнозы validation-года сохраняются как out-of-fold и больше не пересчитываются моделью, обученной на этом же периоде.

Таким образом формируется последовательность OOF-прогнозов за 2022, 2023, 2024 и 2025 годы. Именно эти значения используются для построения второго уровня, выбора его policy и обучения калибраторов.

Отдельная проблема появляется уже после завершения ML-части. Даже идеально организованный cross-fitting бесполезен, если итоговый советник считает признаки иначе, чем Python.

Для встроенного CatBoost-инференса должны полностью совпадать:

  • порядок входных признаков;
  • индексы признаков каждого split;
  • пороговые значения border;
  • направление сравнения;
  • индексы листьев;
  • leaf values;
  • scale и bias модели;
  • порядок моделей внутри ансамбля;
  • формула вычисления ensemble mean и disagreement;
  • входы probability calibrator и lot calibrator.

Поэтому перенос в MQL5 рассматривается как часть эксперимента, а не как отдельная задача после обучения. Один и тот же feature vector должен приводить к тому же raw-score как в CatBoost, так и во встроенном evaluator советника.

В работе используется MetaTrader 5 и десять валютных пар: AUDUSD, CADJPY, CHFJPY, EURJPY, EURUSD, GBPCHF, GBPUSD, USDCAD, USDCHF и USDJPY.

Основной временной масштаб — H1. Решение принимается по уже сформированному состоянию рынка, а торговый терминал во время работы не обращается к Python и не загружает модель через ONNX.

Финальные deploy-модели V5 обучаются только на данных, доступных до конца 2025 года. Период 2026 года затем используется отдельно, чтобы проверить поведение уже замороженного ансамбля на новых данных.

В советнике также присутствует экспериментальный Soft Grid. Он является самостоятельным execution-слоем и не используется при расчёте основных OOF-метрик и audit-метрик stack-модели. Его влияние рассматривается отдельно в терминальном тесте, чтобы не смешивать качество ML-фильтра с результатом дополнительного управления позицией.


Архитектура V5 и критерии воспроизводимости

V5 не заменяет предыдущие модели. Immutable V3.10 и V4.00 сохраняются отдельно и используются как baseline. Это позволяет сравнивать новый stack с прежней системой без незаметного переобучения базовой линии.

Рисунок 1. Путь решения в V5: immutable base models и triangle context формируют 39 признаков, три CatBoost members дают raw-score, после чего отдельные модели оценивают вероятность и размер позиции.

Блок Количество
Base models 10
Triangle models 16
V5 stack/calibration models 5
Всего моделей 31
Деревьев 2 360
Split conditions 10 672
Leaf values 60 420

Критерии успешной сборки задаются до тестирования: OOF-прогнозы не должны использовать будущие строки; финальные deploy-members обучаются только до конца 2025 года; 2026 оценивается после заморозки policy; одинаковый feature vector должен приводить к одинаковому raw-score в CatBoost и MQL5.


Откуда берутся candidate rows

Stack не создаёт торговый сигнал из OHLC с нуля. Он получает уже сформированные кандидаты от базовых и triangle-моделей и отвечает на более узкий вопрос: насколько конкретному кандидату можно доверять в текущем контексте.

Источник Строк Роль
V2_DIRECTION 1 182 основное направление базовой модели
V3_STRONG_MOVE 145 кандидаты сильного движения
TRIANGLE_NATIVE 174 нативные сигналы triangle-моделей
Всего 1 501 candidate dataset второго уровня

Candidate dataset начинается 17 января 2020 года и заканчивается 30 июля 2026 года. До построения stack-признаков triangle-выходы агрегируются по времени и символу: учитываются доля согласия с базовым направлением, статистики meta-probability, lot multiplier и сила triangle direction.


Purged expanding walk-forward и embargo

Для каждого календарного года validation используется только история, которая закончилась минимум за 72 часа до начала этого года. Обучающая выборка постепенно расширяется, поэтому процедура называется purged expanding walk-forward. Трёхдневный embargo превышает основной 24-часовой горизонт удержания и снижает риск пересечения зависимых торговых событий на границе train и validation.

train(Y): time < 1 января Y - 72 часа

validation(Y): календарный год Y

OOF год Train Validation Предыдущие OOF для calibration
2022 429 255 0
2023 687 153 253
2024 842 295 408
2025 1 135 197 701
2026 1 329 167 895

Probability calibrator и lot calibrator также обучаются только на OOF-строках предыдущих лет. Поэтому прогноз второго уровня для 2024 года не может использовать ошибки или исходы 2024-2026 годов.


Контракт из 39 stack-признаков

Второй уровень использует 39 полей. Порядок закреплён в feature_contract_stack.json и должен совпадать между Python и MQL5.

Группа Количество Примеры
Исходный кандидат 6 side, score, abs_score, signed_conf, source_native, source_base
Triangle-контекст 14 согласие, probability/lot statistics, direction и confidence
Время 6 hour, day-of-week и month в sin/cos представлении
Symbol one-hot 10 AUDUSD ... USDJPY
Model-family one-hot 3 V2_DIRECTION, V3_STRONG_MOVE, TRIANGLE_NATIVE

Полная функция BuildStackFeatures() ниже показывает тот же порядок, который использует советник. Временные признаки кодируются через sin/cos, а символ и семейство модели передаются one-hot полями.

//+------------------------------------------------------------------+
//| Формирует 39 признаков второго уровня                            |
//| Вход: базовый сигнал, triangle-контекст и время решения          |
//+------------------------------------------------------------------+
void BuildStackFeatures(const int symbol_index,
                        const int signal,
                        const double score,
                        const bool native_signal,
                        const double triangle_count,
                        const double agreement,
                        const double probability_mean,
                        const double probability_max,
                        const double probability_min,
                        const double probability_std,
                        const double lot_mean,
                        const double lot_max,
                        const double lot_min,
                        const double lot_std,
                        const double direction_mean,
                        const double direction_max,
                        const double confidence_mean,
                        const double confidence_max,
                        double &features[])
  {
   ArrayResize(features,STACK_FEATURE_COUNT);
   int index=0;

   //--- признаки исходного кандидата
   features[index++]=signal;
   features[index++]=score;
   features[index++]=MathAbs(score);
   features[index++]=signal*score;
   features[index++]=(native_signal ? 1.0 : 0.0);
   features[index++]=(native_signal ? 0.0 : 1.0);

   //--- агрегированный контекст четырех triangle-моделей
   features[index++]=triangle_count;
   features[index++]=agreement;
   features[index++]=probability_mean;
   features[index++]=probability_max;
   features[index++]=probability_min;
   features[index++]=probability_std;
   features[index++]=lot_mean;
   features[index++]=lot_max;
   features[index++]=lot_min;
   features[index++]=lot_std;
   features[index++]=direction_mean;
   features[index++]=direction_max;
   features[index++]=confidence_mean;
   features[index++]=confidence_max;

   //--- циклическое представление часа, дня недели и месяца
   MqlDateTime time_data;
   TimeToStruct(iTime(g_symbols[symbol_index],PERIOD_H1,0),time_data);

   features[index++]=MathSin(2.0*PI_CONST*time_data.hour/24.0);
   features[index++]=MathCos(2.0*PI_CONST*time_data.hour/24.0);
   features[index++]=MathSin(2.0*PI_CONST*time_data.day_of_week/7.0);
   features[index++]=MathCos(2.0*PI_CONST*time_data.day_of_week/7.0);
   features[index++]=MathSin(2.0*PI_CONST*(time_data.mon-1)/12.0);
   features[index++]=MathCos(2.0*PI_CONST*(time_data.mon-1)/12.0);

   //--- one-hot идентификатор символа
   for(int symbol=0;symbol<SYMBOL_COUNT;symbol++)
      features[index++]=(symbol==symbol_index ? 1.0 : 0.0);

   //--- one-hot семейство модели
   features[index++]=(!native_signal && G_MODEL_FAMILY[symbol_index]==2 ? 1.0 : 0.0);
   features[index++]=(!native_signal && G_MODEL_FAMILY[symbol_index]==1 ? 1.0 : 0.0);
   features[index++]=(native_signal ? 1.0 : 0.0);
  }

Функция не использует будущий результат сделки. Поле net_pips существует только в обучающем наборе как target и для расчёта sample weights.


Три heterogeneous CatBoost members

V5 использует три классификатора с разной глубиной и bootstrap. Цель состоит не в усреднении трёх одинаковых seed, а в получении немного разных границ решений.

Member Depth L2 Bootstrap Subsample RSM
1 4 18 MVS 0.82 0.88
2 5 28 Bernoulli 0.78 0.82
3 6 40 Bayesian - 0.76

Общие параметры OOF-моделей: 220 деревьев, learning_rate 0.025, Logloss, AUC как eval metric, random_strength 0.18 и nan_mode Min. Для Bayesian-member используется bagging_temperature 0.7.

Sample weight состоят из двух частей. Свежесть имеет half-life примерно 2,5 года, а экономически заметные сделки получают дополнительный вес через ограниченную степень абсолютного net_pips. Это сохраняет старую историю, но снижает её влияние относительно более свежих режимов.


Disagreement, probability calibrator и lot calibrator

После трёх raw-score вычисляются среднее и стандартное отклонение. Среднее описывает общее мнение ансамбля, а стандартное отклонение используется как disagreement. Два кандидата с одинаковым mean score могут иметь разную устойчивость, если members сильно расходятся между собой.

Probability calibrator получает только ensemble_raw_mean и ensemble_raw_std. Для него заданы monotone constraints: вероятность не должна падать при росте среднего raw-score и не должна расти при увеличении disagreement. Lot calibrator получает 39 stack-признаков, calibrated probability и disagreement, то есть всего 41 поле.

//+------------------------------------------------------------------+
//| Выполняет инференс V5 Cross-Fitted Stack                         |
//| Выход: вероятность, множитель лота и disagreement                |
//+------------------------------------------------------------------+
bool StackPredict(const double &features[],
                  double &probability,
                  double &lot_multiplier,
                  double &uncertainty)
  {
   double raw[3];

   //--- три heterogeneous CatBoost members
   for(int member=0;member<3;member++)
      raw[member]=Eval1(26+member,features);

   double raw_mean=(raw[0]+raw[1]+raw[2])/3.0;

   //--- disagreement используется как отдельная оценка неопредёленности
   uncertainty=MathSqrt(((raw[0]-raw_mean)*(raw[0]-raw_mean)
                        +(raw[1]-raw_mean)*(raw[1]-raw_mean)
                        +(raw[2]-raw_mean)*(raw[2]-raw_mean))/3.0);

   double raw_probability=Sigmoid(raw_mean);
   double calibrator_features[2];
   calibrator_features[0]=raw_mean;
   calibrator_features[1]=uncertainty;

   double calibrated_probability=Clamp(Eval1(29,calibrator_features),0.01,0.99);
   double blend=ProfileCalibrationBlend();

   //--- Audit Robust частично сохраняет некалиброванную вероятность
   probability=Clamp((1.0-blend)*raw_probability
                     +blend*calibrated_probability,0.01,0.99);

   double lot_features[];
   ArrayResize(lot_features,STACK_LOT_FEATURE_COUNT);

   for(int i=0;i<STACK_FEATURE_COUNT;i++)
      lot_features[i]=features[i];

   lot_features[STACK_FEATURE_COUNT]=calibrated_probability;
   lot_features[STACK_FEATURE_COUNT+1]=uncertainty;

   //--- отдельная regression-модель оценивает множитель риска
   lot_multiplier=Clamp(Eval1(30,lot_features),0.25,1.35);

   return(MathIsValidNumber(probability)
          && MathIsValidNumber(lot_multiplier));
  }

Важная деталь реализации: lot calibrator получает именно calibrated_probability, как в экспортированном советнике. Профиль Audit Robust затем смешивает raw и calibrated probability только для торгового gate.


Перенос деревьев в MQL5

Финальный советник не вызывает Python и не использует ONNX. Все деревья превращены в массивы depths, индексы признаков, borders, leaf values, scale и bias. Ниже показан центральный evaluator, общий для 31 встроенной модели.

//+------------------------------------------------------------------+
//| Выполняет точный инференс встроенной модели CatBoost             |
//| Вход: индекс модели и вектор признаков                           |
//+------------------------------------------------------------------+
bool EvalVector(const int model_index,const double &features[],double &output[])
  {
   if(model_index<0 || model_index>=MODEL_COUNT)
      return(false);

   int output_dim=G_MODEL_OUTPUT_DIM[model_index];
   ArrayResize(output,output_dim);
   ArrayInitialize(output,0.0);

   int tree_first=G_MODEL_TREE_OFFSET[model_index];
   int tree_last =tree_first+G_MODEL_TREE_COUNT[model_index];

   //--- последовательно обходим симметричные деревья модели
   for(int tree=tree_first;tree<tree_last;tree++)
     {
      int leaf_index=0;
      int split_offset=G_TREE_SPLIT_OFFSET[tree];
      int depth=G_TREE_DEPTH[tree];

      //--- строим индекс листа из решений feature > border
      for(int level=0;level<depth;level++)
        {
         int split=split_offset+level;
         double value=features[G_SPLIT_FEATURE[split]];

         if(MathIsValidNumber(value) && value>G_SPLIT_BORDER[split])
            leaf_index|=(1<<level);
        }

      int leaf_offset=G_TREE_LEAF_OFFSET[tree]+leaf_index*output_dim;

      for(int j=0;j<output_dim;j++)
         output[j]+=G_LEAF_VALUE[leaf_offset+j];
     }

   int bias_offset=G_MODEL_BIAS_OFFSET[model_index];

   //--- применяем scale и bias, экспортированные из CatBoost
   for(int j=0;j<output_dim;j++)
      output[j]=output[j]*G_MODEL_SCALE[model_index]+G_MODEL_BIAS[bias_offset+j];

   return(true);
  }

Статическая проверка пакета насчитала 31 модель, 2 360 деревьев, 10 672 split conditions и 60 420 leaf values. Unsupported split types не обнаружены, а максимальные индексы признаков находятся внутри соответствующих feature contracts.


Профили V5 и policy freeze

После получения probability система применяет один из четырёх V5-профилей. Порог и доля calibration задаются отдельными функциями.

//+------------------------------------------------------------------+
//| Возвращает минимальную вероятность выбранного V5-профиля         |
//+------------------------------------------------------------------+
double ProfileThreshold()
  {
   if(InpProfile==V5_AUDIT_ROBUST)
      return(0.5400);

   if(InpProfile==V5_BALANCED)
      return(0.5375);

   if(InpProfile==V5_SELECTIVE)
      return(0.6000);

   if(InpProfile==V5_ULTRA_SELECTIVE)
      return(0.6200);

   return(0.0);
  }

//+------------------------------------------------------------------+
//| Возвращает долю калиброванной вероятности                        |
//+------------------------------------------------------------------+
double ProfileCalibrationBlend()
  {
   if(InpProfile==V5_AUDIT_ROBUST)
      return(0.25);

   return(1.0);
  }
Профиль Threshold Calibration blend Назначение
V5 Audit Robust 0.5400 0.25 policy выбран по OOF 2022-2025
V5 Balanced 0.5375 1.00 больше кандидатов
V5 Selective 0.6000 1.00 сильная фильтрация
V5 Ultra Selective 0.6200 1.00 максимальная селективность

Для Audit Robust threshold 0.54 и calibration blend 0.25 выбирались только по OOF 2022-2025. После этого 2026 использовался как отдельный audit без повторной настройки policy.


Интеграция stack gate в торговый цикл

После появления базового или native triangle-кандидата советник собирает triangle statistics, строит 39 полей и пропускает сигнал через StackPredict(). Если вероятность ниже профильного threshold или uncertainty превышает разрешённый предел, сигнал обнуляется до открытия позиции.

//--- фрагмент ProcessSymbol(): V5 gate после формирования кандидата
if(signal!=0 && IsV5Profile())
  {
   double triangle_count;
   double agreement;
   double probability_mean;
   double probability_max;
   double probability_min;
   double probability_std;
   double lot_mean;
   double lot_max;
   double lot_min;
   double lot_std;
   double direction_mean;
   double direction_max;
   double confidence_mean;
   double confidence_max;

   //--- для native-кандидата triangle-контекст уже известен
   if(native_signal)
     {
      triangle_count=1.0;
      agreement=1.0;
      probability_mean=triangle_probability;
      probability_max=triangle_probability;
      probability_min=triangle_probability;
      probability_std=0.0;
      lot_mean=triangle_lot;
      lot_max=triangle_lot;
      lot_min=triangle_lot;
      lot_std=0.0;
     }
   else
     {
      TriangleAggregate(symbol_index,signal,
                        triangle_count,agreement,
                        probability_mean,probability_max,
                        probability_min,probability_std,
                        lot_mean,lot_max,lot_min,lot_std,
                        direction_mean,direction_max,
                        confidence_mean,confidence_max);
     }

   double stack_features[];
   BuildStackFeatures(symbol_index,signal,score,native_signal,
                      triangle_count,agreement,
                      probability_mean,probability_max,
                      probability_min,probability_std,
                      lot_mean,lot_max,lot_min,lot_std,
                      direction_mean,direction_max,
                      confidence_mean,confidence_max,
                      stack_features);

   if(!StackPredict(stack_features,model_probability,
                    model_lot,uncertainty)
      || model_probability<ProfileThreshold()
      || uncertainty>InpMaximumStackUncertainty)
      signal=0;
  }

Это именно фрагмент ProcessSymbol(), а не полный обработчик. После gate код отдельно проверяет число уже открытых корзин, ATR, spread, длительность существующей позиции и размер риска.


Сравнение V4 и V5 на 2020-2026

Общий исторический результат не показывает безусловного превосходства V5. Immutable V4 остаётся лучшим по accumulated net pips, тогда как Selective-профили V5 уменьшают просадку и повышают Profit Factor ценой меньшего числа сделок.

Профиль Сделки Net, pips PF Max DD, pips Win rate
V4 Immutable 1 005 14 267.7 1.654 -1 800.3 56.1%
V5 Audit Robust 989 12 352.8 1.585 -1 573.1 54.9%
V5 Balanced 1 069 13 675.2 1.605 -1 083.1 55.3%
V5 Selective 406 6 948.1 1.877 -562.0 55.7%
V5 Ultra Selective 388 6 876.5 1.943 -588.3 56.2%

Рисунок 2. Immutable V4 сохраняет максимальный accumulated Net. Более строгие V5-профили уменьшают число сделок.

Рисунок 3. Selective и Ultra Selective повышают исторический Profit Factor, но это преимущество необходимо проверять отдельно на новом режиме.


Отдельный deploy audit 2026

Финальные три stack-members обучены только на данных до 31 декабря 2025 года. Период с 1 января по 31 июля 2026 года содержит 167 candidate rows и используется как отдельный deploy audit.

Профиль Сделки Net, pips PF Max DD, pips
V4 Immutable 124 -260.6 0.910 -917.3
V5 Audit Robust 129 +221.4 1.091 -524.4
V5 Balanced 164 -475.1 0.870 -858.9
V5 Selective 0 0.0 0.000 0.0
V5 Ultra Selective 0 0.0 0.000 0.0

Рисунок 4. На замороженном audit-отрезке положительный Net сохранил только профиль V5 Audit Robust. Balanced и immutable V4 оказались отрицательными, а два наиболее строгих профиля не открыли сделок.

PF 1.091 у Audit Robust нельзя интерпретировать как доказанный стабильный edge. Это слабый положительный audit, который прежде всего показывает различие между выбором policy на OOF и поведением после смены режима.


Почему калибровка ухудшилась

На 2026 audit calibrated probability оказалась хуже raw ensemble сразу по трём прогнозным метрикам.

Метрика Raw ensemble Calibrated
AUC 0.51005 0.47552
Brier 0.25514 0.25638
LogLoss 0.70359 0.70603

Это полезный отрицательный результат. Даже OOF-calibrator способен устареть при смене режима. Именно поэтому Audit Robust использует 75% raw probability и только 25% calibrated probability вместо полного доверия к калибратору.


Риск, удержание и Soft Grid

По умолчанию советник использует риск 0.20% equity на начальную позицию, ATR period 24, virtual risk distance 3 ATR, максимум пять одновременно активных символов и hold limit 24 H1-бара. Для live-режима предусмотрены ATR-protected SL/TP.

Soft Grid является отдельным экспериментальным слоем и по умолчанию выключен: InpUseSoftGrid=false. Ни одна таблица performance выше не включает grid. В коде ограничены максимальное число уровней, шаг добавления в ATR, максимальный basket risk и отключение новых добавлений при equity drawdown.

Такое разделение важно для воспроизводимости: результат stack нельзя приписывать martingale-логике, которая не участвовала в расчёте опубликованных метрик.


Tree parity и статическая проверка

Все пять новых V5-моделей проверены flat evaluator против CatBoost. Максимальная ошибка raw prediction равна 0.0 для stack_member_1, stack_member_2, stack_member_3, probability calibrator и lot calibrator.

Дополнительная static validation подтвердила баланс фигурных, круглых и квадратных скобок, отсутствие duplicate function names, отсутствие ONNX references, корректные индексы признаков и выключенный grid по умолчанию.

При этом компиляция в MetaEditor не выполнялась. Поэтому статическую проверку нельзя выдавать за «0 errors, 0 warnings». Перед публикацией исходный .mq5 необходимо открыть в MetaEditor, применить MetaQuotes Style через Ctrl+<, скомпилировать и заменить листинги в статье из окончательно отформатированного исходника.


Форвардный бэктест итогового советника в Strategy Tester

До этого момента результаты V5 относились преимущественно к исследовательскому candidate/trade simulation. После экспорта деревьев появился следующий уровень проверки: запуск уже готового многовалютного советника MIDAS_SINERGY_V5_CROSSFIT_STACK_SAFE_GRID непосредственно в Тестере стратегий.

Здесь важно разделить два интервала. Отчёт тестера охватывает 01.01.2025–09.08.2026, но финальные deploy-members V5 обучены только на данных до конца 2025 года. Поэтому весь интервал нельзя называть независимым форвардом. Участок 2025 года используется как проверка интеграции и исполнения, а строгим post-training forward в этом отчёте является только 2026 год.

Параметры терминального теста

Параметр Значение
Сервер RoboForex-ECN, Build 6090
Период отчёта 01.01.2025 - 09.08.2026
Рабочий таймфрейм H1
Профиль V5_AUDIT_ROBUST
Торгуемых символов 9 символов
Риск начальной позиции 0.20% equity
Выход MIDAS_LIVE_ATR_PROTECTED
Максимальное удержание 24 H1-бара
Calibrated lots включены, диапазон 0.35-1.25
Soft Grid включён
Максимум grid-уровней 3 уровня
Шаг grid 0.90 ATR
Максимальный basket risk 0.45% equity
Минимальная probability для grid 0.58 probability
Начальный депозит 100 000 USD
Кредитное плечо 1:500

Хотя тестер был запущен с EURUSD как основным символом, торговля непосредственно по EURUSD была отключена параметром InpTradeEURUSD=false. Советник работал как многовалютный: в отчёте зафиксировано девять реально использованных символов.

Ещё одна особенность этого запуска — InpMaximumStackUncertainty=99.0. Такое значение практически отключает фильтрацию по disagreement. Поэтому данный тест проверяет прежде всего probability gate, calibrated position sizing, ATR-защиту и Soft Grid, но не является строгой проверкой ограничительного uncertainty gate.

Результат полного терминального теста 2025-2026

Метрика Значение
History Quality 100%
Bars 9 918
Ticks 2 296 804
Total Trades 1 090
Total Net Profit 20 205.42 USD
Gross Profit 58 616.06 USD
Gross Loss -38 410.64 USD
Profit Factor 1.53
Expected Payoff 18.54 USD
Sharpe Ratio 3.20
Recovery Factor 6.49
Balance Drawdown Maximal 2 196.23 USD (1.87%)
Equity Drawdown Maximal 3 113.83 USD (2.65%)
Profit Trades 668 (61.28%)
Loss Trades 422 (38.72%)

Баланс вырос со 100 000 до 120 205.42 USD, то есть примерно на 20.21%. При этом максимальная equity-просадка составила 2.65%. На всём тестовом интервале отношение итоговой прибыли к максимальной equity-просадке соответствует Recovery Factor 6.49.

Структура сделок заметно асимметрична. Советник выполнил 942 long-сделки и 148 short-сделок. Доля прибыльных long составила 59,98%, short 69,59%. Среднее время удержания позиции равно 22:52:01, минимальное - один час, максимальное - 61:11:00.

Последняя величина особенно важна. Реальный терминальный execution contract уже не идентичен упрощённому исследовательскому правилу «ровно 24 H1-бара». В тесте используются ATR-protected exits и Soft Grid, поэтому фактическая жизнь корзины может отличаться от первоначального фиксированного горизонта.

Строгий forward после заморозки deploy-моделей

Для оценки участка, который финальные модели уже не могли видеть во время обучения, таблица Deals была отдельно пересчитана начиная с 1 января 2026 года. Последний баланс 2025 года составлял 113 078.06 USD. К завершению теста он вырос до 120 205.42 USD.

Метрика 2026 forward Значение
Начальный баланс 113 078.06 USD
Конечный баланс 120 205.42 USD
Net +7 127.36 USD
Доходность относительно баланса на 01.01.2026 +6.30%
Закрытых сделок 424
Gross Profit 22 052.65 USD
Gross Loss с учётом комиссий входа -14 925.29 USD
Profit Factor 1.478
Max Balance Drawdown 2 196.23 USD (1.87%)

Таким образом, после границы финального обучения система не просто сохранила положительный баланс, но и осталась прибыльной при достаточно большом числе сделок. Profit Factor уменьшился с 1.53 на полном тесте до примерно 1.48 на строгом forward, однако остался выше единицы.

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

Месяц 2026 Сделки Net, USD PF
Январь 56 +93.09 1.035
Февраль 52 +2 509.44 2.535
Март 54 +763.89 1.336
Апрель 56 +801.72 1.436
Май 69 -19.57 0.993
Июнь 78 +1 447.13 1.778
Июль 45 +1 214.85 1.745
Август до окончания теста 14 +316.81 1.886

Картина по месяцам важнее самого итогового Net. Семь из восьми календарных отрезков завершились положительно. Единственным отрицательным месяцем оказался май, причём результат -19.57 USD практически нейтрален относительно размера счёта. Самый сильный вклад дал февраль: +2 509.44 USD при PF 2.535.

При этом результат нельзя напрямую сравнивать с таблицей deploy audit из предыдущего раздела. Там использовалась исследовательская симуляция с другим execution contract и без Soft Grid. Здесь тестируется готовый MQL5-советник с ATR-protected exits, динамическим lot multiplier и включённым Soft Grid. Следовательно, разница между +221.4 pips исследовательского audit и +7 127.36 USD терминального forward не является улучшением одной и той же метрики - это два разных эксперимента.

Особенно важно, что в этом запуске Soft Grid действительно активен. В исходной части статьи он был отключён и не участвовал в опубликованных исторических результатах. Поэтому новый тест впервые проверяет совместную систему Cross-Fitted Stack + calibrated risk + ATR protection + ограниченный Soft Grid.

Но из этого следует и ограничение интерпретации. По одному отчёту нельзя определить, какую долю результата создал сам stack, а какую - ATR-выходы или grid additions. Для строгой абляции необходим второй тест на том же периоде и тех же котировках с InpUseSoftGrid=false. Только разница этих двух запусков позволит отдельно оценить добавочную стоимость grid-слоя.

Также отчёт содержит History Quality 100% и 2 296 804 обработанных ticks, но сам HTML-файл не указывает название режима моделирования. Поэтому здесь корректно говорить о подтверждённом тесте Strategy Tester с 100%-ным качеством доступной истории, но не следует автоматически подписывать его как "Every tick based on real ticks", пока это отдельно не подтверждено настройками тестера или скриншотом.

Итог терминальной проверки. После заморозки V5 deploy-моделей советник сохранил положительный результат на 2026 году: +7 127.36 USD, около +6.30% к начальному балансу forward-периода, 424 закрытые сделки, PF около 1.48 и максимальная balance-просадка 1.87%. Это уже значительно более сильная проверка, чем offline candidate simulation, поскольку здесь проходят полный MQL5 inference, portfolio logic, расчёт объёма, комиссии, swap, ATR-exit и многовалютное исполнение. Однако это всё ещё исторический тест, а не live-forward. Включённый Soft Grid требует отдельной no-grid абляции.


Как воспроизвести результат

  1. Распаковать MQL5.zip в каталог данных MetaTrader 5.
  2. Открыть MQL5/Experts/MIDAS_V5/MIDAS_SINERGY_V5_CROSSFIT_STACK_SAFE_GRID.mq5 в MetaEditor.
  3. Применить MetaQuotes Style и скомпилировать исходник.
  4. Добавить требуемые валютные пары в Market Watch или указать брокерские prefix/suffix.
  5. Для воспроизведения исследовательской логики оставить InpUseSoftGrid=false и InpExitMode=MIDAS_AUDIT_HOLD24.
  6. Выбрать профиль. Production default исходного пакета - V4_MAX_NET_PF_IMMUTABLE; V5_AUDIT_ROBUST используется для отдельной проверки stack.
  7. Сопоставить журнал и сделки терминала с OOF/audit CSV, не изменяя thresholds после просмотра 2026.
//+------------------------------------------------------------------+
//| Инициализирует символы, состояние риска и секундный таймер       |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(InpTimerSeconds<1)
      return(INIT_PARAMETERS_INCORRECT);

   g_equity_peak=AccountInfoDouble(ACCOUNT_EQUITY);

   //--- разрешаем брокерские символы и восстанавливаем grid-состояние
   for(int i=0;i<SYMBOL_COUNT;i++)
     {
      g_symbols[i]=InpSymbolPrefix+G_BASE_SYMBOLS[i]+InpSymbolSuffix;
      g_enabled[i]=EnabledInput(i);
      g_last_bar[i]=0;
      LoadGridState(i);

      if(g_enabled[i] && !SymbolSelect(g_symbols[i],true))
         g_enabled[i]=false;
     }

   EventSetTimer(InpTimerSeconds);
   Print("MIDAS-SINERGY V5 Cross-Fitted Stack + Safe Grid initialized");

   return(INIT_SUCCEEDED);
  }

Строка журнала при успешной инициализации остаётся на английском, как требует единый программный интерфейс: MIDAS-SINERGY V5 Cross-Fitted Stack + Safe Grid initialized.


Ограничения

  • Метрики разделов 11-13 относятся к historical candidate/trade simulation; отдельный подтверждённый Strategy Tester report рассмотрен в разделе 16.
  • Отдельный журнал компиляции MetaEditor и явное подтверждение режима "Every tick based on real ticks" в приложенном Strategy Tester report отсутствуют.
  • 2026 audit содержит только семь месяцев.
  • OOF уменьшает leakage, но не устраняет model-selection risk полностью.
  • Пороговые значения остаются результатом исторического выбора.
  • Полная probability calibration ухудшила AUC, Brier и LogLoss в 2026.
  • Selective и Ultra Selective не открыли сделок в 2026 audit.
  • Soft Grid участвовал в терминальном тесте раздела 16, но для оценки его отдельного вклада требуется повторный no-grid тест на тех же данных.
  • Exact tree parity доказывает совпадение инференса только при одинаковом входном векторе; она не моделирует spread, slippage и broker execution.


Заключение

После реализации разработчик получает воспроизводимый второй уровень торговой ML-системы: 39-полевой stack contract, три heterogeneous CatBoost members, disagreement, отдельную probability calibration, отдельную ML-регрессию размера позиции и embedded evaluator для 31 модели в MQL5. OOF-прогнозы формируются через purged expanding walk-forward с 72-часовым embargo, а deploy-members замораживаются до начала audit 2026.

Эксперимент не показывает, что более сложная версия автоматически лучше. Immutable V4 остаётся сильнее по общей прибыли на 2020-2026, а некоторые V5-профили улучшают PF и просадку ценой меньшей активности. На отдельном 2026 audit только Audit Robust сохранил положительный Net, тогда как полная калибровка вероятности ухудшила прогнозные метрики.

Практический результат статьи состоит не в обещании прибыли, а в разделении трёх проверяемых задач: отсутствие leakage при stacking, frozen policy перед audit и точный перенос деревьев в терминал. Следующий обязательный шаг перед реальным использованием - локальная компиляция MetaEditor, тест "Every tick based on real ticks" и новый forward после 31 июля 2026 года без изменения уже замороженных моделей и thresholds.


Приложенные файлы

Публикационный архив собран в минимальном воспроизводимом виде. Для работы готового советника требуется только основной файл .mq5: все 31 модели уже встроены в него. Готовые CSV с прогнозами и сделками в архив не включаются. При необходимости читатель может экспортировать собственную историю из MetaTrader 5 и заново получить промежуточные наборы данных, OOF-прогнозы, отчёты и экспортированный советник с помощью приложенного Python-pipeline.

Имя файла Тип файла Описание
MQL5/Experts/MIDAS_V5/MIDAS_SINERGY_V5_CROSSFIT_STACK_SAFE_GRID.mq5 Исходный код советника Основной торговый модуль с 31 встроенной CatBoost-моделью. Для работы советника внешние CSV, JSON или Python-процесс не требуются.
MQL5/Scripts/MIDAS_V5/MIDAS_EXPORT_HISTORY.mq5 MQL5 Script Экспортирует историю используемых валютных пар из MetaTrader 5 для воспроизведения эксперимента на данных собственного брокера.
MQL5/Files/MIDAS_V5/reproduce/reproduce_v5.py Python Главная точка запуска воспроизводимого конвейера. Проверяет входные данные и последовательно запускает стадии V2, V3, V4 и V5.
MQL5/Files/MIDAS_V5/reproduce/pipeline/*.py Python Стадии подготовки кандидатов, walk-forward обучения, triangle-моделей, cross-fitted stack, калибровки, audit и экспорта деревьев в MQL5.
MQL5/Files/MIDAS_V5/reproduce/requirements.txt TXT Зависимости Python, необходимые для повторного построения исследовательского pipeline.
MQL5/Files/MIDAS_V5/feature_contract_stack.json JSON Фиксированный порядок 39 stack-признаков и контракт входов второго уровня.
MQL5/Profiles/Tester/MIDAS_V5/*.set SET Готовые профили Strategy Tester для V4 Immutable и основных режимов V5, включая отдельный экспериментальный профиль Soft Grid.
MQL5/Files/MIDAS_V5/reproduce/README_REPRODUCE_RU.txt TXT Пошаговая инструкция по экспорту собственной истории и повторному запуску полного V2→V5 pipeline.
MQL5.zip Архив проекта Минимальный публикационный пакет. После распаковки в каталог данных терминала сохраняет стандартную структуру MQL5.


Литература

  1. Wolpert D. H. Stacked Generalization. Neural Networks, 1992.
  2. Huber P. J. Robust Estimation of a Location Parameter. Annals of Mathematical Statistics, 1964.
  3. Niculescu-Mizil A., Caruana R. Predicting Good Probabilities with Supervised Learning. ICML, 2005.
  4. Prokhorenkova L. et al. CatBoost: Unbiased Boosting with Categorical Features. NeurIPS, 2018.
  5. Lopez de Prado M. Advances in Financial Machine Learning. Wiley, 2018.
  6. Bergmeir C., Benitez J. M. On the Use of Cross-Validation for Time Series Predictor Evaluation. Information Sciences, 2012.

Прикрепленные файлы |
MQL5.zip (3150.43 KB)
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Машинное обучение и Data Science (Часть 48): Насколько важны трансформеры для трейдинга? Машинное обучение и Data Science (Часть 48): Насколько важны трансформеры для трейдинга?
От ChatGPT до Gemini и множества других инструментов на базе искусственного интеллекта для генерации текста, изображений и видео – трансформеры произвели фурор в мире искусственного интеллекта. Но применимы ли они в финансовой сфере, в частности в трейдинге? Постараемся разобраться.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
База данных — это просто (Часть 1): Легковесный ORM-фреймворк для MQL5 на базе SQLite База данных — это просто (Часть 1): Легковесный ORM-фреймворк для MQL5 на базе SQLite
В данной статье представлен структурированный подход к управлению данными SQLite в MQL5 с помощью ORM-слоя для MetaTrader 5. В нем представлены базовые классы для моделирования сущностей и доступа к базе данных, fluent API для CRUD, хуки рефлексии для OnGet/OnSet, а также макросы для быстрого определения моделей. В практических примерах кода показано создание таблиц, привязка полей, вставка, обновление, выборка и удаление записей. Разработчики получают готовые к повторному использованию типобезопасные компоненты, которые сводят к минимуму однотипный SQL-код.