От прогноза цены к узкому торговому уравнению
Введение
Всё началось с довольно красивой идеи. Попробуем описать следующее значение цены EURUSD формулой, в которой есть две предыдущие цены, их квадраты, разность и синус. Формула выглядит достаточно гибкой. Но прежде чем восхищаться её сложностью, зададим самый простой вопрос: она действительно прогнозирует лучше, чем правило "следующая цена примерно равна текущей"?
Проверка быстро охладила энтузиазм. На 165 467 часовых барах с 3 января 2000 года по 31 июля 2026 года ошибка исходного уравнения составила 10.634 pip. У простейшего прогноза Close(t+1)=Close(t) практически те же 10.634 pip. Направление следующего часа формула угадывала в 49.26% случаев. То есть сама сложность ещё ничего не дала.
На этом этапе исследование стало содержательнее. Вместо того чтобы добавлять коэффициенты, мы поменяли сам вопрос. Модель перестала угадывать абсолютную цену и стала оценивать следующее движение. Данные разделили строго по времени. Выходные и другие разрывы исключили из цели. Вместе с направлением начали учитывать величину abs(P(up) - 0.5). Ниже я называю её уверенностью только как ранжирующий показатель модели: она не калибрована и не означает реальную вероятность выигрыша.
Дальше появились версии V3, V4 и V5. Важно сразу развести их статус. V3: исходный исследовательский этап. Час 23:00 был найден после просмотра результатов V3, поэтому V4 уже проверяет постфактум сформированную гипотезу. V5 сохраняет классификатор, но добавляет исторически выбранный параметр Stop Loss. Поэтому это отдельная торговая модификация, а не та же модель. AUDUSD M15, GBPUSD M30 и USDCAD M15 проверялись позднее как тест переносимости. Эти прогоны не являются заранее зарегистрированными независимыми экспериментами.
Эта статья поэтому рассматривается как ретроспективное исследование гипотезы, а не как окончательное подтверждение торговой системы. Мы будем показывать не только удачные участки, но и слабые результаты, а также явно отмечать, где заканчивается проверка прогноза и начинается ещё не проверенное реальное исполнение.
Сразу разведём два вида воспроизводимости, потому что они требуют разного:
| Что воспроизводится | Можно ли | Что для этого нужно |
|---|---|---|
| Метод: признаки, разбиения, правила отбора | Да, на любых котировках | Скрипты из приложения и CSV из истории вашего брокера |
| Опубликованные числа | Только на тех же исходных котировках | CSV, совпадающий с контрольными признаками из раздела о данных |
| Торговая доходность | Не заявляется | Отдельный тест исполнения по Bid/Ask с издержками |
Иногда ниже встречается сокращение OOS (от out-of-sample). Здесь оно означает данные вне обучения: участок истории, который модель не видела при настройке.
С чего всё началось: цена или движение
Исходная формула выглядит так:
X(t+1) = a1*X(t)
+ a2*X(t)^2
+ a3*X(t-1)
+ a4*X(t-1)^2
+ a5*(X(t)-X(t-1))
+ a6*sin(X(t))
+ a7. В ней есть квадраты и синус, поэтому внешне она кажется нелинейной. Но сами коэффициенты входят в формулу линейно. Значит, искать их сложным общим оптимизатором не нужно: достаточно обычного метода наименьших квадратов. Это уже делает проверку проще и честнее.
Но важнее другое. Цена EURUSD сама по себе меняется медленно, а движение за один час обычно мало по сравнению с уровнем цены. Поэтому можно получить красивый R² по абсолютной цене и почти ничего не знать о направлении следующего часа. В V3 исходное уравнение сначала сравнивается с простейшим ориентиром Close(t+1)=Close(t), а затем основная задача переносится на величину следующего движения в пунктах.
| Модель | RMSE, pip | MAE, pip | Точность направления |
|---|---|---|---|
| Close(t+1)=Close(t) | 10.634 | 6.893 | нет |
| Исходное уравнение, метод наименьших квадратов | 10.634 | 6.894 | 49.26% |
| Ridge: прогноз следующего движения | 10.631 | 6.891 | 50.91% |
Итог почти беспощадный: сложная формула практически не выигрывает у простого правила "цена не изменится". Но это полезный отрицательный результат. Он подсказывает, куда идти дальше: не подгонять уровень цены всё точнее, а искать редкие состояния, в которых направление следующего часа становится заметно менее случайным.
Сначала договоримся о данных
Исходный эксперимент EURUSD H1 выполнен на 165 467 барах с 3 января 2000 года по 31 июля 2026 года. Котировки были выгружены из MetaTrader 5 и сохранены в исследовательском архиве RoboForex. Готовый EURUSD_H1.csv для повторения метода не нужен: к исходникам приложен SOLVER_ExportHistory.mq5, который позволяет получить CSV из истории своего брокера. Но это не означает, что другой брокер обязан дать те же числа.
Скрипт получает бары обычной функцией CopyRates() и сохраняет восемь полей, которые ожидает Python-код: Time, Open, High, Low, Close, TickVolume, Spread, RealVolume. Никакой предварительно подготовленной выборки внутри экспортёра нет.
//--- загружаем историю выбранного символа и периода int copied=CopyRates(symbol,timeframe,InpFrom,time_to,rates); //--- сохраняем CSV в MQL5/Files/SOLVER int handle=FileOpen(file_name, FILE_WRITE|FILE_CSV|FILE_ANSI, ','); FileWrite(handle, "Time","Open","High","Low","Close", "TickVolume","Spread","RealVolume");
В универсальном проверочном скрипте величина допустимого шага берётся из периода файла. Поэтому дополнительный тест M15/M30 не притворяется часовым прогнозом: проверяется именно следующий M15- или M30-бар.
В работе используются два разных понятия воспроизводимости. Вычислительная воспроизводимость означает одинаковый код и одинаковые исходные котировки: тогда ожидаются те же результаты. Внешняя проверка означает тот же метод на котировках другого брокера, и здесь значения могут измениться из-за состава баров, серверного времени, DST, спреда и построения истории. Совпадение чисел между брокерами в статье не заявляется.
Чтобы читатель мог проверить, что его CSV совпадает с исходным, ниже даны контрольные признаки файла. Они считаются скриптом dataset_fingerprint.py, который сверяет хэш SHA-256 файла, число строк и границы по времени.
| Признак | Значение |
|---|---|
| Источник | история MetaTrader 5 торгового сервера RoboForex, экспорт скриптом SOLVER_ExportHistory.mq5 |
| Инструмент и период | EURUSD, H1 |
| Число баров | 165 467 |
| Диапазон | с 3 января 2000 года по 31 июля 2026 года (последний бар 23:00) |
| Поля CSV | Time, Open, High, Low, Close, TickVolume, Spread, RealVolume |
| Время | сырое серверное время, без перевода в UTC |
| Название торгового сервера и версия терминала | MetaTrader 5 Client Terminal 5.0.0.6230 (build 6230) |
| Дата экспорта | 2026-10-27 (Asia/Qyzylorda) |
| SHA-256 файла EURUSD_H1.csv | 7e3e2a1ad5c00172461a31e1993a21cd87d946c30e046eb686371ca5ce6852ce |
# dataset_fingerprint.py # Контрольные признаки CSV-файла, выгруженного SOLVER_ExportHistory.mq5. # Запуск: python dataset_fingerprint.py EURUSD_H1.csv import hashlib import sys import pandas as pd path = sys.argv[1] sha256 = hashlib.sha256() with open(path, "rb") as f: for block in iter(lambda: f.read(1 << 20), b""): sha256.update(block) df = pd.read_csv(path, usecols=["Time", "Close"]) print("файл: ", path) print("SHA-256: ", sha256.hexdigest()) print("строк: ", len(df)) print("первый бар:", df["Time"].iloc[0]) print("последний: ", df["Time"].iloc[-1])
Хэш считается по файлу как есть. Если открыть CSV в Excel и пересохранить, перевод строк и форматирование изменятся, и хэш перестанет совпадать, хотя числа в файле останутся теми же. Поэтому сверяйте именно тот файл, который записал экспортёр.
Есть важная деталь, которая существенно влияет на корректность эксперимента. Мы прогнозируем ровно следующий бар выбранного периода. Для H1 это +1 час, для M30 это +30 минут, для M15 это +15 минут. Если между соседними записями находится выходной или другой разрыв, такой переход в цель прогноза не входит.
# период берётся из имени/параметров теста expected_gap_min = timeframe_minutes next_gap_min = (Time(t+1) - Time(t)).total_seconds() / 60.0 eligible = abs(next_gap_min - expected_gap_min) <= tolerance
Сначала фиксируют временные границы обучающей, проверочной и контрольной частей. Затем внутри каждой части исключают переходы, не равные ровно одному бару выбранного периода. Для EURUSD H1 это около +60 минут, для GBPUSD M30 это +30 минут, для M15 это +15 минут. Это не позволяет выходным разрывам случайно стать целью следующего бара.
60% истории -> обучение 20% истории -> выбор порога 20% истории -> контрольная часть после фиксации временных границ: оставляем только переходы ровно на один бар выбранного периода
| Часть | Строк | Последняя граница |
|---|---|---|
| Обучение | 98 270 | 2015-12-07 06:00:00 |
| Выбор порога | 32 760 | 2021-04-07 09:00:00 |
| Контрольные данные | 32 764 | до 2026-07-31 23:00:00 |
Что именно видит модель
V3 использует 80 признаков, и каждый из них рассчитывается только по текущему и прошлым барам. Вместо одной "магической" формулы модель получает несколько простых описаний рынка: что происходило с ценой, насколько она была волатильна, как выглядела последняя свеча, как менялся объём и в какое время суток всё это происходило.
| Группа | Примеры | Назначение |
|---|---|---|
| Лаги доходности | 1, 2, 3, 4, 5, 6, 8, 12, 18, 24, 36, 48, 72 | локальная память движения |
| Импульс и волатильность | окна 2…120 | скорость и масштаб текущего режима |
| Отклонение цены от локальной средней | 6, 12, 24, 48, 72, 120, 240 | положение цены относительно локальной средней |
| Свеча | диапазон, тело, верхняя/нижняя тень, положение закрытия, разрыв | структура последнего бара |
| Объём и спред | тиковый объём, отклонение объёма, спред | ликвидность и активность |
| Время | sin/cos часа, вторая гармоника, день недели | внутридневная периодичность |
| Взаимодействия | доходность/волатильность, импульс/волатильность, доходность×тело свечи | нелинейный контекст без будущего |
Если в признаке не хватает значения, пропуск заполняется медианой. Причём медиана рассчитывается только по обучающей части. Контрольные данные не участвуют даже в такой, на первый взгляд, безобидной операции.
Откуда взяты окна и лаги. Они покрывают горизонт от нескольких часов до десяти суток (240 часов), а значения 24, 48 и 72 кратны суткам, что позволяет уловить внутридневную периодичность, которую описывают и признаки времени. Отдельного обоснования каждого значения нет: набор сформировался в ходе разработки на EURUSD. Чувствительность результата к замене окон в статье не показана, и это ограничение. Простая проверка для читателя: оставить более редкий набор (например, лаги 1, 2, 3, 6, 12, 24, 48 и окна 6, 24, 120) и посмотреть, как изменятся AUC и число отобранных сигналов.
V3: разделяем величину движения и направление
Теперь зададим модели два разных вопроса. Первый: насколько большим может быть следующее движение? Для этого используется линейная модель Ridge. Второй: вверх или вниз вероятнее пойдёт следующий час? На этот вопрос отвечает градиентный классификатор HistGradientBoostingClassifier.
Оценка величины движения: признаки -> Ridge -> ожидаемое движение, pip Оценка направления: признаки -> HistGradientBoostingClassifier -> P(up)
Параметры классификатора заранее фиксируются:
learning_rate = 0.05 max_iter = 150 max_leaf_nodes = 31 min_samples_leaf = 100 l2_regularization = 3.0 random_state = 42
Откуда эти значения. Относительно умолчаний scikit-learn ( learning_rate =0.1, max_iter =100, max_leaf_nodes =31, min_samples_leaf =20, l2_regularization =0) это более осторожные настройки: обучение идёт медленнее, в каждом листе требуется больше наблюдений, добавлен L2-штраф. Значение max_leaf_nodes =31 совпадает с умолчанием, а random_state нужен только для повторяемости. Параметры не менялись между V3, V4 и дополнительными рынками. Но в статье нет ни поиска по сетке, ни проверки чувствительности, и это ещё одна степень свободы, с поправкой на которую результаты стоит читать. Если хотите проверить, прогоните небольшую сетку: learning_rate 0.03, 0.05 и 0.10, max_iter 100, 150 и 300, min_samples_leaf 50, 100 и 200. Смотрите на разброс AUC и точности отобранных сигналов, а не на лучшую комбинацию: выбирать её по контрольным данным нельзя.
Если смотреть на все контрольные бары подряд, результат скромный: AUC 0.527, точность направления 51.71%. На этом можно было бы закончить. Но классификатор возвращает не только ответ "вверх/вниз", а ещё и вероятность. Значит, можно проверить другую гипотезу: а что если брать только те редкие моменты, когда модель особенно уверена?
Берём только самые уверенные сигналы
Для ранжирования сигналов используется расстояние оценки классификатора от нейтральной точки 0.5:
confidence = abs(P(up) - 0.5)
На этапе V3 исследовался верхний 1% этого показателя на предыдущей проверочной части: q=0.99. Численный порог вычислялся только по проверочному отрезку, после чего применялся к следующей контрольной части. При этом P(up) не проходил отдельную калибровку, поэтому значение 0.80 нельзя читать как "80% вероятности роста". Здесь это только способ упорядочить сигналы по степени удалённости от 0.5.
После такого отбора осталось 543 сигнала, примерно 1.66% всех допустимых баров. Направление оказалось верным в 70.53% случаев. 95-процентный доверительный интервал Уилсона составляет от 66.57% до 74.21%. Средний результат до торговых издержек составил 1.131 pip (пипс) на сигнал.
Но праздновать рано. Во-первых, преимущество чувствительно к издержкам. Во-вторых, q=0.99 это один из исследованных вариантов правила отбора, а не заранее зарегистрированная константа всей работы. Позднее в V4 рассматривались другие q. Поэтому результаты разных этапов ниже не объединяются в одну подтверждающую гипотезу.
Какие гипотезы менялись по ходу работы
Чтобы не создавать впечатление, будто V3, V4 и V5 были заранее одним неизменным экспериментом, зафиксируем исследовательскую последовательность явно.
| Этап | Правило | Статус |
|---|---|---|
| V3 | q=0.99, все часы EURUSD H1 | исследовательский этап; по нему был замечен час 23:00 |
| V4, первое разбиение | специализация 23:00, на проверочной части лучшим оказался q=0.95 | новая постфактум сформированная гипотеза |
| V4, июнь–июль 2026 | на предыдущей проверочной части выбран q=0.90 | короткий отложенный участок, всего 4 сигнала |
| Последовательная проверка 100 окон | уровень квантиля q=0.90 фиксирован для этого эксперимента; численный порог θ рассчитывается заново только по прошлой проверочной части каждого окна | ретроспективная последовательная проверка; это не 100 независимых экспериментов |
| V5 | те же сигналы последовательной проверки + исторически выбранный Stop Loss 4.5 pip | отдельная исследовательская модификация исполнения |
Следовательно, переход q=0.99 → q=0.95 → q=0.90 это не плавающий параметр одной заранее заданной стратегии, а след разных исследовательских этапов. Именно поэтому итоговые цифры следует читать как развитие и проверку гипотезы, а не как результат единственного заранее зарегистрированного теста.
Проверяем идею на других символах и периодах
После первоначального исследования EURUSD H1, уже в ходе редакционной проверки, тот же V3 был дополнительно запущен на трёх наборах из того же архива RoboForex: AUDUSD M15, GBPUSD M30 и USDCAD M15. Это полезная проверка переносимости, но она не является независимой внешней валидацией: поставщик котировок остаётся тем же, а выбор самих дополнительных рынков не был заранее зарегистрирован.
В этих дополнительных прогонах правила V3 не подстраивались под конкретный рынок: использовались те же 80 признаков, разделение 60/20/20, тот же HistGradientBoostingClassifier, те же гиперпараметры и q=0.99, причём численный порог уверенности рассчитывался только по собственной проверочной части каждого набора. Менялись лишь символ, период, длительность бара и pip.
| Символ / период | Баров | RMSE простого прогноза, pip | RMSE исходного уравнения, pip | AUC | Отобрано | Точность | 95% интервал | Средний результат, pip | Сценарий: результат до издержек − 1.0 pip* |
|---|---|---|---|---|---|---|---|---|---|
| EURUSD H1 | 165 467 | 10.634 | 10.634 | 0.527 | 543 | 70.53% | 66.57…74.21% | +1.131 | +0.131 |
| AUDUSD M15 | 622 719 | 4.609 | 4.611 | 0.517 | 987 | 52.89% | 49.77…55.98% | +0.334 | −0.666 |
| GBPUSD M30 | 330 738 | 9.739 | 9.756 | 0.522 | 933 | 58.84% | 55.65…61.96% | +0.925 | −0.075 |
| USDCAD M15 | 660 198 | 5.575 | 5.577 | 0.519 | 2 473 | 53.46% | 51.49…55.42% | +0.392 | −0.608 |
Таблица показывает прежде всего качество прогноза и чувствительность к условной надбавке. Последний столбец со знаком "*" это не оценка реальной чистой доходности и не основание ранжировать рынки: одинаковые 1.0 pip не отражают разные реальные спреды, комиссии, волатильность и качество исполнения AUDUSD, GBPUSD и USDCAD. Для торгового сравнения каждому рынку нужен собственный Bid/Ask-тест.
По направлению EURUSD H1 в этой выборке выглядит сильнее дополнительных наборов, однако это нельзя трактовать как доказанную универсальную специализацию. На GBPUSD M30 наблюдаемая точность выше 50%, на AUDUSD M15 интервал включает 50%, на USDCAD M15 отклонение невелико. Эти результаты показывают, что эффект не переносится одинаково, но не дают независимого подтверждения EURUSD: все четыре набора происходят из одного источника котировок и были исследованы в рамках одной работы.
Почему внимание привлёк час 23:00
Час 23:00 выделился при просмотре результатов V3 на EURUSD H1: среди высокоуверенных сигналов этого часа было 328 наблюдений, 81.10% верных направлений и средний результат 1.431 pip в принятой метрике от Close к следующему Close. Это находка, сделанная постфактум: час не был задан до анализа суточного распределения.
Поэтому V4 не превращает 23:00 в доказанную закономерность. Последующие разбиения и последовательная проверка показывают, как эта уже найденная гипотеза ведёт себя дальше на той же длинной исторической базе, но не устраняют смещение отбора. Независимое подтверждение потребует данных, которые появились после фиксации всей логики, либо отдельного внешнего источника с заранее определённой нормализацией времени.
бар-кандидат: Time.hour == 23 следующий бар: ровно Time + 1 час прогноз: знак разности Close(00:00) - Close(23:00)
V4: проверяем переход 23:00 → 00:00 отдельно
V4 использует те же 80 признаков и тот же классификатор, но обучается только на барах 23:00, за которыми действительно идёт бар 00:00 ровно через час. В первом опыте получилось 3 782 наблюдения для обучения, 1 100 для выбора порога и 1 100 для отдельной проверки.
В первом специализированном разбиении на предыдущей проверочной части сравнивались несколько q. Лучшим по среднему результату до издержек на этой проверочной части оказался q=0.95. После этого соответствующий численный порог был заморожен и применён к следующей контрольной части, где получилось 121 сигнал. Это отдельный исследовательский вариант V4; он не является тем же самым правилом, что последующий q=0.90 в 100 окнах.
| Метрика | V4: контрольная выборка |
|---|---|
| Все бары 23:00: точность направления | 65.09% |
| AUC | 0.644 |
| Отобранных сигналов | 121 |
| Точность отобранных сигналов | 81.82% |
| 95%-й интервал Уилсона | 74.00% … 87.68% |
| Средний результат до издержек | 1.574 pip |
| Суммарный результат до издержек | 190.4 pip |
Результат этого разбиения выглядит сильным, но его нельзя считать независимым подтверждением часа 23:00: сам час уже был выбран на предыдущем анализе EURUSD H1, а q=0.95 выбран внутри этого этапа. Поэтому дальше мы используем эти числа как диагностический результат, а не как окончательную оценку системы.
Июнь–июль 2026: короткий отложенный участок
Для этого опыта вся история до 1 июня 2026 года использовалась как доступная разработчику история: 80% для обучения, 20% для выбора правила отбора. Затем модель и численный порог фиксировались, а июнь–июль не участвовали в расчёте порога. Это временной отложенный участок относительно данного запуска, но не полностью независимый от всей предшествующей исследовательской работы.
На предыдущей проверочной части в этом конкретном запуске среди рассматривавшихся q по среднему результату до издержек был выбран q=0.90: 119 сигналов на проверочной части, точность 77.31% и средний результат 1.839 pip. Этот q не следует смешивать с q=0.95 первого V4-разбиения: это другой эксперимент и другая временная граница.
А вот два новых месяца оказались почти пустыми: всего 36 подходящих баров 23:00 и лишь 4 сигнала после отбора. Три направления из четырёх были угаданы верно, но общий результат составил -1.5 pip. Одна более крупная ошибка перекрыла три маленьких правильных движения.
| Метрика | Июнь–июль 2026 |
|---|---|
| Подходящих баров 23:00 | 36 |
| Отобранных сигналов | 4 |
| Точность направления | 75.00% |
| Средний результат до издержек | -0.375 pip |
| Суммарный результат до издержек | -1.5 pip |
Это отличный пример того, почему доля верных направлений и деньги не одно и то же. И ещё один урок: четыре сделки слишком мало, чтобы делать вывод о стабильности системы.
Сто последовательных OOS-отрезков: но не сто независимых экспериментов
Далее гипотеза 23:00 проверяется на 100 последовательных двухмесячных OOS-отрезках. Первая тестовая область начинается 1 декабря 2009 года, последняя начинается 1 июня 2026 года и заканчивается 1 августа 2026 года. Слово "100" здесь означает 100 последовательных временных участков одного исторического исследования, а не 100 независимых подтверждений.
Для каждого окна берётся только история, расположенная строго раньше его двухмесячного OOS-отрезка. Эта прошлая история хронологически делится 80/20: модель обучается на первых 80%, а на последних 20% рассчитывается порог уверенности. После этого и модель, и численный порог применяются к следующим двум месяцам без использования будущего OOS для настройки.
вся история строго до OOS-окна
|
+--> 80% обучение модели
|
+--> 20% проверочная часть
|
+--> θ_i = квантиль 0.90 от уверенности на проверочной части
следующие 2 месяца:
уверенность >= θ_i -> сигнал
OOS-данные в θ_i не участвуют Считаются два варианта отбора сигналов:
- подбираемый q: исследовательский вариант, где на каждой прошлой проверочной части выбирается один из q=0.70, 0.80, 0.90, 0.95, 0.98 или 0.99 по результату на проверочной части;
- фиксированный уровень q=0.90: сам квантиль не меняется между окнами, но численный порог θ_i каждый раз вычисляется по предыдущей проверочной части и затем переносится в следующий OOS-отрезок.
Ключевое уточнение: "фиксированный q=0.90" означает фиксированный уровень квантиля, но не фиксированный численный порог. Порог не рассчитывается по будущему OOS. В 100 окнах значения θ_i, полученные только из прошлого, лежали примерно от 0.236869 до 0.338515, медиана 0.308505. Например, для первого окна θ=0.320684, а для последнего окна, начинающегося 1 июня 2026 года, θ=0.310343; оба значения были известны до начала соответствующего OOS-периода.
Что показали 100 последовательных OOS-отрезков
| Метрика | Порог подбирается | q=0.90; θ_i из прошлой проверочной части |
|---|---|---|
| Окон | 100 | 100 |
| Окон с сигналами | 93 | 96 |
| OOS-сигналов | 430 | 415 |
| Общая точность направления | 65.81% | 68.67% |
| Средний результат до издержек | 2.321 pip | 1.608 pip |
| Суммарный результат до издержек | 998.1 pip | 667.3 pip |
| Доля прибыльных окон | 72.04% | 72.92% |
| Медианная точность по окнам | 66.67% | 66.67% |
В варианте с фиксированным уровнем q=0.90 получилось 415 OOS-сигналов: 285 верных и 130 ошибочных, наблюдаемая точность 68.67%. Обычный интервал Уилсона равен 64.06…72.95%, но он предполагает независимость наблюдений и поэтому здесь приведён только как ориентир. Блочная повторная выборка целых двухмесячных окон с 20 000 повторов дала для точности 95%-й интервал 63.74…73.39%, а для среднего результата до издержек 0.826…2.428 pip на сигнал.
Средний записанный в исходной истории спред равен 0.638 pip. Простое вычитание этого числа или условных 1.0 pip из каждого сигнала это лишь анализ чувствительности, а не Bid/Ask-моделирование сделки. При блоковой повторной выборке двухмесячных окон 95%-й интервал среднего результата после условного вычета 1.0 pip составляет −0.174…1.428 pip и уже включает ноль. Поэтому этот расчёт нельзя использовать как доказательство положительного реального математического ожидания после издержек.

Рисунок 1. Наблюдаемая точность направления на 100 последовательных OOS-отрезках при q=0.90. Это временные сегменты одного ретроспективного исследования, а не независимые повторения эксперимента.
Что происходило в последние годы
Посмотрим на последние 20 окон, то есть примерно с апреля 2023 года до конца июля 2026 года. Здесь было 103 сигнала. Направление оказалось верным в 78.64% случаев, средний результат до издержек составил 1.241 pip, суммарный 127.8 pip.
Это выше среднего по всей истории, но я бы не называл это "усилением преимущества". Эти 20 окон всё равно принадлежат той же исторической последовательности. Для нас важнее другое: положительный результат не остался только в далёких 2010-х годах.
Разброс между окнами большой. Худшее окно началось в июне 2016 года: 5 сигналов, 20% верных направлений и -51.0 pip. Лучшее началось в декабре 2010 года: 4 сигнала, 100% верных направлений и +80.0 pip. Поэтому отдельный двухмесячный участок почти всегда рассказывает слишком громкую историю о слишком маленькой выборке.
V5: классификатор тот же, но торговое правило уже новое
После последовательной проверки был добавлен ещё один исследовательский слой: ограничение неблагоприятного движения внутри следующего часа. Для каждого из 415 сигналов по High/Low следующего H1-бара оценивалось, насколько далеко цена уходила против направления прогноза.
Классификатор действительно не переобучается, однако V5 не является неизменной V4: добавление Stop Loss создаёт новый параметр торгового правила. Поэтому V5 следует рассматривать как отдельную исторически настроенную модификацию, а не просто как прежнюю модель с нейтральной защитой риска.
Инструмент: EURUSD H1 Переход: 23:00 -> 00:00, строго +1 час Модель: HistGradientBoostingClassifier Отбор: фиксированный q=0.90 Направление: BUY при P(up) >= 0.5, иначе SELL Stop Loss: 4.5 pip Take Profit: нет Трейлинг: нет Усреднение: нет Мартингейл: нет Если Stop Loss не сработал: выход на следующем H1 Close
Кроме того, эта конструкция пока остаётся ретроспективной: вход в расчёте привязан к Close бара 23:00, тогда как признаки этого бара полностью известны только после его закрытия. Ниже отдельно разберём, почему это важно для реального исполнения.
Почему в V5 рассматривается Stop Loss 4.5 pip
На первых 70 двухмесячных окнах были просмотрены Stop Loss от 0.5 до 30.0 pip. Последние 30 окон не использовались при этом просмотре и оставались для последующей проверки выбранного значения.
Здесь важно исправить прежнюю формулировку: 4.5 pip не является математически оптимальным уровнем. Заранее заданной функции качества вида "максимизировать X при ограничении Y" до просмотра кривой не было. Уровень 4.5 pip был выбран дискреционно как небольшой лимит единичного убытка при высокой средней величине на первых 70 окнах. Таблица показывает, что, например, 16.5 pip давал немного больший средний результат на этих же 70 окнах. Следовательно, выбор 4.5 pip содержит ещё одну исследовательскую степень свободы и должен считаться исследовательским.
| Stop Loss, pip | Первые 70: среднее | Последние 30: среднее | Все окна: среднее | Макс. убыток |
|---|---|---|---|---|
| 3.5 | 1.561 | 1.405 | 1.505 | -3.5 |
| 4.0 | 1.415 | 1.411 | 1.413 | -4.0 |
| 4.5 | 1.798 | 1.410 | 1.658 | -4.5 |
| 5.0 | 1.732 | 1.419 | 1.619 | -5.0 |
| 5.5 | 1.785 | 1.399 | 1.645 | -5.5 |
| 10.0 | 1.567 | 1.445 | 1.523 | -10.0 |
| 16.5 | 1.822 | 1.442 | 1.685 | -16.5 |

Рисунок 2. Историческая чувствительность результата к Stop Loss. Первые 70 окон использовались для просмотра кривой; последние 30 для проверки уже выбранного 4.5 pip. Поскольку критерий выбора 4.5 pip не был формализован заранее, этот отложенный участок не превращает параметр в подтверждённый оптимум.
V5 на тех же 415 сигналах: диагностический, а не исполняемый P/L
В текущем расчёте предполагается условный вход по Close бара 23:00. Если High/Low следующего H1-бара показывает движение против позиции не менее 4.5 pip, результат записывается как −4.5 pip; иначе используется следующий H1 Close. Это позволяет изучить геометрию сигнала, но не является полноценным торговым тестом на истории.
| Метрика | V5: историческая модель Close/High/Low |
|---|---|
| Сделок | 415 |
| Точность направления до SL | 68.67% |
| Средний результат до издержек | 1.658 pip на сделку |
| Суммарный результат до издержек | 688.0 pip |
| Доля прибыльных сделок | 63.13% |
| Прибыль / убыток (Profit Factor), диагностическая историческая величина | 2.47 |
| Максимальный убыток одной сделки | −4.5 pip до проскальзывания и других издержек |
| Максимальная просадка до издержек, диагностическая историческая величина | -30.9 pip |
| Среднее после простого вычитания записанного спреда* | 1.020 pip на сделку |
На последних 30 окнах средний результат этой исторической конструкции составил 1.410 pip на сигнал. Блочная повторная выборка по целым двухмесячным окнам (20 000 повторов) по всем 415 сигналам даёт ориентировочный 95%-й интервал для среднего V5-результата 1.074…2.309 pip и для Profit Factor 1.86…3.28. Такой подход лучше сохраняет возможную зависимость сигналов внутри одного рыночного периода, чем простая модель независимых сделок. Для максимальной просадки отдельный доверительный интервал не приводится: это показатель, зависящий от порядка сделок, поэтому здесь он остаётся только описательной исторической величиной.
Значение 1.020 pip получено простым вычитанием записанного спреда. В расчёте не моделируются Ask/Bid, комиссия, проскальзывание, сторона срабатывания Stop Loss и ночное расширение спреда. Поэтому Profit Factor 2.47, просадка и "результат после спреда" это диагностические исторические величины. Торговыми показателями они не являются и как показатели реально исполняемой стратегии интерпретироваться не должны. Торговые метрики можно будет публиковать только после отдельного теста исполнения по Bid/Ask.

Рисунок 3. Накопленный результат тех же 415 сигналов без фиксированного стопа и после добавления Stop Loss 4.5 pip.
Прогноз от Close к следующему Close ещё не является торговой сделкой
Это принципиальное ограничение текущего исследования. Цель V4/V5 определена как Close(00:00) − Close(23:00). Но признаки бара 23:00, включая его Close, становятся окончательно известны только в момент закрытия этого бара. Значит, реальная программа не может сначала увидеть финальный Close 23:00, вычислить сигнал и одновременно гарантированно войти по той же цене.
исследовательская метрика: признаки бара 23:00 -> сигнал результат = сигнал * (Close_00 - Close_23) исполняемый тест: после закрытия 23:00 -> сигнал BUY -> вход по следующему Ask, выход/SL по Bid SELL -> вход по следующему Bid, выход/SL по Ask + комиссия + проскальзывание + возможный перенос позиции
Следовательно, все результаты от Close к следующему Close, Stop Loss по High/Low и Profit Factor выше это диагностика прогностической гипотезы, а не доказательство торговой доходности. В исполняемом тесте BUY должен входить по Ask и закрываться/срабатывать по Bid, а SELL входить по Bid и закрываться/срабатывать по Ask. Нужно также учитывать комиссию, проскальзывание и возможный перенос позиции. Для перехода около серверной полуночи это особенно важно, потому что реальный спред может заметно отличаться от среднего исторического значения. Только такой тест сможет ответить, сохраняется ли преимущество после исполнения.
Верное направление ещё не означает прибыль
Июнь–июль 2026 года дали наглядный пример: три направления из четырёх угаданы верно, а денежный результат всё равно отрицательный. Причина проста. Для показателя точности движения +0.5 pip и +20 pip одинаково считаются "правильными". Для счёта, конечно, нет.
Поэтому систему нужно оценивать сразу с нескольких сторон:
- точность направления и 95-процентный доверительный интервал;
- средний и суммарный результат до издержек;
- чувствительность результата к торговым издержкам;
- доля прибыльных двухмесячных окон;
- максимальная просадка последовательности сделок;
- поведение системы в последних временных отрезках;
- отдельная проверка параметров риска на более поздней части истории.
Ни один показатель сам по себе не обещает будущую прибыль. Но вместе они помогают не принять один удачный кусок истории за закономерность.
Как поставить следующий исполняемый тест в MQL5
Ниже код следует читать не как доказательство результатов таблицы V5, а как заготовку для следующего теста исполнения. Вероятность prob_up должна быть рассчитана после закрытия сигнального бара; затем порог θ_i, полученный только из прошлой проверочной части, проверяется до отправки приказа. Вход должен фиксироваться по фактической стороне рынка после появления сигнала, а не по уже известному Close сигнального бара.
//+------------------------------------------------------------------+ //| Возвращает направление сделки | //+------------------------------------------------------------------+ int V5Direction(const double prob_up) { //--- вероятность 0.5 и выше соответствует покупке if(prob_up>=0.5) return(1); //--- иначе работаем в сторону продажи return(-1); } //+------------------------------------------------------------------+ //| Проверяет порог уверенности, рассчитанный по q=0.90 | //+------------------------------------------------------------------+ bool V5ConfidencePassed(const double prob_up, const double confidence_threshold) { //--- чем дальше вероятность от 0.5, тем увереннее классификатор double confidence=MathAbs(prob_up-0.5); return(confidence>=confidence_threshold); } //+------------------------------------------------------------------+ //| Рассчитывает цену фиксированного Stop Loss | //+------------------------------------------------------------------+ double V5StopPrice(const int direction, const double entry_price, const double pip_size) { //--- в V5 расстояние до Stop Loss зафиксировано на уровне 4.5 pip const double stop_distance=4.5*pip_size; if(direction>0) return(entry_price-stop_distance); return(entry_price+stop_distance); }
Функция V5StopPrice ожидает уже рассчитанное значение pip_size. Для EURUSD на пятизнаковом счёте один pip равен 10 значениям Point, но в рабочем советнике число вручную записывать не нужно: размер pip вычисляется через SYMBOL_DIGITS и SYMBOL_POINT.
//+------------------------------------------------------------------+ //| Возвращает размер одного pip для валютной пары | //+------------------------------------------------------------------+ double PipSize(const string symbol) { const int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); const double point =SymbolInfoDouble(symbol,SYMBOL_POINT); //--- 3 и 5 знаков после запятой: один pip равен десяти пунктам if(digits==3 || digits==5) return(10.0*point); //--- 2 и 4 знака: pip совпадает с пунктом return(point); } //--- пример использования для текущего символа const double pip=PipSize(_Symbol); const double sl =NormalizeDouble(V5StopPrice(direction,entry_price,pip), (int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS));
Функция рассчитана на валютные пары: для металлов, индексов и криптовалют правило другое. Перед отправкой приказа цену стопа нужно также сверить с минимальной дистанцией SYMBOL_TRADE_STOPS_LEVEL.
Что нельзя менять перед тестом в MetaTrader 5
Час 23:00 в исходном исследовании это сырой час сервера RoboForex. Нормализация к UTC и отдельная обработка переходов DST не выполнялись. Поэтому запуск в 23:00 у другого брокера может соответствовать другой рыночной сессии. Для внешней проверки необходимо заранее определить правило преобразования времени и не подбирать час заново после просмотра результата.
| Параметр | Замороженное значение V5 |
|---|---|
| Инструмент | EURUSD |
| Период | H1 |
| Час решения | 23:00 время сервера брокера |
| Что прогнозируем | исследовательская цель: следующий H1 Close относительно Close 23:00 |
| Вход для будущего исполняемого теста | первая доступная Bid/Ask-цена после закрытия сигнального бара; в текущих исторических цифрах не моделировалась |
| Правило отбора | q=0.90; численный θ_i только из предшествующей проверочной части |
| Граница направления | P(up)=0.5 |
| Фиксированный SL | 4.5 pip |
| Take Profit | нет |
| Выход | в исторической метрике: следующий H1 Close; реальное исполнение ещё не проверено |
| Усреднение / мартингейл | нет |
Границы доказательств
Это единственное место, где собраны все ограничения работы. В других разделах повторяются только те предупреждения, без которых нельзя правильно прочитать конкретный результат.
Данные. Основные и дополнительные котировки происходят из одного архива RoboForex, независимого поставщика нет. Время сырое серверное, без перевода в UTC.
Отбор гипотезы. Час 23:00, набор из 80 признаков, варианты q, Stop Loss 4.5 pip и (в меньшей степени) гиперпараметры сформировались в ходе исследования и не были зарегистрированы заранее. Проверялось много вариантов: абсолютная цена и движение, часы суток, несколько q, дополнительные рынки и периоды, затем сетка Stop Loss. Формальной коррекции на множественные сравнения не применялось. Поэтому интервалы и блочная повторная выборка описывают разброс условно выбранной гипотезы, а не скорректированный подтверждающий тест. Повторные проверки на той же истории смещение отбора не устраняют.
Выборка. 415 сигналов за 16.7 года, то есть около 25 в год. Сигналы могут зависеть от общих рыночных режимов. Интервал Уилсона предполагает независимость, поэтому дополнительно использована повторная выборка целых двухмесячных окон: она снижает чувствительность к кластеризации, но независимость режимов не доказывает. Сто окон не равны ста независимым экспериментам.
Уверенность. P(up) не калибровался методами Платта или изотонической регрессии. Значение abs(P(up)-0.5) используется только для ранжирования.
Исполнение. V5 считается по Close/High/Low. Profit Factor, просадка и "результат после спреда" это диагностические исторические величины, а не показатели исполняемой стратегии. Одинаковый вычет 1 pip на разных рынках лишь сценарий чувствительности.
Чего нет. Проверки на данных после 31 июля 2026 года, независимого поставщика котировок и теста исполнения по Bid/Ask.
План подтверждающего теста
Чтобы подтверждение не повторило смещение отбора, перед стартом нужно зафиксировать всё, что здесь выбиралось по ходу работы. Минимальный состав записи:
| Что фиксируется | Значение |
|---|---|
| Данные | источник, версия архива и SHA-256; правило перевода времени в UTC с учётом DST |
| Инструмент и час | EURUSD H1, переход 23:00 → 00:00 в нормализованном времени |
| Признаки и модель | те же 80 признаков, те же гиперпараметры, версия скрипта и её хэш |
| Отбор и риск | q=0.90, θ_i из предшествующей проверочной части, Stop Loss 4.5 pip |
| Исполнение | сигнал после закрытия бара, вход и стоп по Bid/Ask, комиссия, проскальзывание |
| Критерий успеха | записывается до старта вместе с минимальным числом сигналов, например: нижняя граница блочного 95%-го интервала среднего результата после издержек выше нуля |
Нужно понимать темп накопления. При 25 сигналах в год на новых данных сотня сигналов наберётся примерно за четыре года. Быстрее проверить гипотезу на истории независимого брокера, но эта история перекрывается с той, на которой гипотеза была найдена, поэтому такая проверка слабее настоящей проверки на будущем.
Как повторить эксперимент
- Открыть нужный символ и период в MetaTrader 5 и запустить SOLVER_ExportHistory.mq5. Скрипт создаст CSV в MQL5/Files/SOLVER. На другом брокере это повторяет метод, но не обязано воспроизводить исходные числа.
- Запустить dataset_fingerprint.py и сравнить хэш, число строк и границы с контрольными признаками из раздела о данных.
- Для V3 запустить solver_multimarket_validation.py. Строго отделять обучение, проверочную часть и OOS по времени; q=0.99 не пересчитывать по OOS.
- В последовательной проверке 100 окон EURUSD H1 фиксировать именно уровень квантиля q=0.90. В каждом окне численный θ_i получать только из предшествующей проверочной части, затем без изменений применять к следующим двум месяцам.
- Для исторического V5 можно воспроизвести опубликованную диагностику по Close/High/Low с 4.5 pip.
- Для торговой проверки создать отдельный исполняемый тест: сигнал после закрытия 23:00, вход по следующей доступной Ask/Bid, Stop Loss по соответствующей стороне котировки, комиссия и проскальзывание.
- Любое изменение источника данных, признаков, рынка, времени, q, модели или риска считать новой гипотезой и показывать отдельно.
Заключение
Мы начали с красивого нелинейного уравнения цены и довольно быстро выяснили, что оно почти не лучше простого прогноза Close(t+1)=Close(t). Это оказался не провал, а полезная развилка: стало понятно, что нужно менять не коэффициенты, а постановку задачи.
В V3 модель стала прогнозировать следующее движение и отбирать редкие сигналы по ранжирующему показателю уверенности. Переход 23:00 → 00:00 остался постфактум найденной гипотезой EURUSD H1: на 100 последовательных участках без утечки порога из OOS она показала точность 68.67% на 415 сигналах, но это одна историческая база. Дополнительные рынки показали, что сила EURUSD H1 не переносится автоматически.
Опубликованные 1.658 pip и Profit Factor 2.47 для V5 относятся к ретроспективной модели по Close/High/Low и являются диагностическими величинами. Что именно ещё не доказано и как это проверить, собрано в разделах "Границы доказательств" и "План подтверждающего теста". До такой проверки результаты следует считать исследовательскими, а главный итог работы видеть не в готовой прибыльной стратегии, а в более узкой гипотезе, которую теперь можно проверять без скрытых допущений.
Приложенные файлы
| Файл | Тип | Назначение |
|---|---|---|
| SOLVER_ExportHistory.mq5 | MQL5 | Экспортирует историю выбранного символа и периода из MetaTrader 5 в CSV нужного формата. |
| dataset_fingerprint.py | Python | Считает контрольные признаки CSV: хэш SHA-256, число строк, первый и последний бар. Нужен для сверки с исходным файлом. |
| solver_multimarket_validation.py | Python | Универсальная проверка V3 на одном или нескольких символах/периодах: 80 признаков, разделение 60/20/20, строгий следующий бар и q=0.99. |
| price_equation_eurusd_h1_v4_rolling_100.py | Python | Специализированная EURUSD H1 проверка: 100 последовательных двухмесячных OOS-отрезков; q=0.90 фиксирован как уровень квантиля, а численный θ_i рассчитывается только по предыдущей проверочной части. |
Готовые файлы котировок к статье не прикладываются: вместо них даны контрольные признаки данных. Скрипты позволяют повторить метод на собственных данных, а опубликованные числа воспроизводятся только на файле, совпадающем с контрольными признаками. Экспорт с другого брокера является внешней проверкой и может дать другие результаты.
Литература
- Friedman J. H. Greedy Function Approximation: A Gradient Boosting Machine. The Annals of Statistics, 2001.
- Bergmeir C., Benítez J. M. On the Use of Cross-Validation for Time Series Predictor Evaluation. Information Sciences, 2012.
- Wilson E. B. Probable Inference, the Law of Succession, and Statistical Inference. Journal of the American Statistical Association, 1927.
- scikit-learn. HistGradientBoostingClassifier, официальная документация.
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.