Возможности Мастера MQL5, которые вам нужно знать (Часть 93): Использование суффиксного автомата и автокодировщика в пользовательском классе управления капиталом
Введение
Проблема традиционного управления капиталом не только в том, что оно учитывает лоты, но и в том, что реагирует слишком поздно, опираясь на уже понесенные убытки. Для трейдеров, работающих с геометрическими паттернами, такой запаздывающий подход особенно опасен. В этой статье создается CMoneySuffixAE, пользовательская модель риска, принципиально отличающаяся от стандартных встроенных классов. Вместо анализа истории закрытых убыточных сделок она проактивно рассчитывает размер позиций на основе текущей рыночной структуры и геометрической целостности в реальном времени.
Мы продолжаем серию о Мастере MQL5, где в прошлой статье рассматривалась реализация пользовательского сигнала входа на основе дерева Фенвика и CNN. По сути, Мастер MQL5 поддерживает три режима кастомизации: трейлинг-стоп, сигнал входа и управление капиталом. Эта серия показывает, как создавать пользовательские реализации всех трех режимов для различных стилей торговли.
Предыдущая статья – управление капиталом на основе объемаВ этой статье снова рассматривается пользовательская реализация управления капиталом. В прошлой реализации такого пользовательского класса использовалось дерево Фенвика (двоичное индексированное дерево) для мониторинга объема, измеряемого индикатором On-Balance-Volume. Этот алгоритм сочетался с одномерной сверточной нейронной сетью (CNN) для сглаживания признаков. Стратегии расчета размера позиции не универсальны: они различаются в зависимости от торгового подхода и рыночного режима. В предыдущей статье о пользовательском управлении капиталом рассматривалась реализация, специально созданная для трейдеров, работающих с моментумом на рынках с ярко выраженным трендом. Это объясняется проактивным расчетом размера сделок на основе непрерывного накопления объема. Однако что, если стратегии или рыночный взгляд некоторых трейдеров не учитывают объем? Для многих трейдеров на рынке Форекс это не является аномалией.
Геометрическая модель рискаНекоторые трейдеры сосредоточены на алгоритмических паттернах или на работе в строго структурированных геометрических рынках. Что такое геометрические рынки? Под ними можно понимать рынки, характеризующиеся фрактальными точками или хорошо распознаваемыми и исторически повторяющимися паттернами. К ним часто относятся: гармонические паттерны, например Гартли, летучая мышь и бабочка; последовательности волн Эллиотта; типичные графические паттерны, например голова с плечами, треугольники и клинья. Это рынки, в которых хорошо читаются последовательности паттернов и которые могут быть как диапазонными, так и трендовыми.
Поэтому для геометрического трейдера торговый объем вторичен по сравнению с исторической динамикой цены. Как правило, такие трейдеры проактивно уменьшают размер позиции, если недавние ценовые паттерны начинают повторяться. Одновременно с этим капитал защищается от внезапных беспрецедентных структурных аномалий. Чисто реактивные модели риска, основанные на результатах торговли, зачастую плохо подготовлены к работе с такими фрактальными ситуациями.
Наша проблема – структурные сломыЧтобы создать надежного советника, необходимо подобрать математические инструменты, соответствующие конкретной рыночной среде. Трейдер, работающий с моментумом, опирается на накопление объема, как было показано в предыдущей реализации пользовательского класса управления капиталом с деревом Фенвика. Однако для геометрического трейдера, ищущего точные паттерны Price Action или фракталы, объем для него отходит на второй план. Основой их стратегии является историческое повторение.
Тем не менее, у геометрической торговли есть особая критическая уязвимость – структурные сломы. Это часто происходит при макроэкономическом шоке, резко разрывающем ожидаемый геометрический ритм рынка. В результате ранее надежные паттерны могут резко утратить работоспособность. Если бы такие трейдеры использовали встроенные классы, например CMoneySizeOptimized, они были бы не готовы к работе с этими аномалиями.
При изучении кода этого встроенного класса видно, что он опирается на запаздывающий счетчик убытков. Его логика состоит в уменьшении размера позиции после серии сделок, закрытых с убытком. Если трейдер полагается на геометрические паттерны, то в таких пилообразных рыночных ситуациях на фоне важных новостей его стратегия будет лишь реагировать на счетчики убытков. Если рынок нарушает структуру, ожидание трех убыточных сделок перед снижением риска может серьезно повредить портфелю.
Идея модели – два механизма вместо одного счетчика убытковПоэтому было решено создать специализированный проактивный механизм. Для этого нужны два компонента: алгоритм последовательностей, способный в реальном времени определять точные ценовые паттерны и уверенно наращивать размер позиции после подтверждения сетапов; а также детектор аномалий, резко снижающий риск, как только геометрия рынка становится нечитаемой. Это позволяет сохранить капитал задолго до первой убыточной сделки.
Говоря кратко, суффиксный автомат оценивает знакомство с паттерном, а автокодировщик проверяет целостность структуры.
Описание модели
Чтобы прояснить потенциал этой модели, основанной на геометрии, отойдем от синтаксиса MQL5 и сосредоточимся на базовых алгоритмических концепциях и принципах работы сети. Данное сравнение показывает, почему эти два решения подходят для разных торговых условий. Замена дерева Фенвика суффиксным автоматом подобна замене бухгалтера генетиком. Замена одномерной CNN автокодировщиком подобна замене фильтра камеры мастером компрессии.
Дерево Фенвика против суффиксного автомата
В предыдущей статье о пользовательском управлении капиталом дерево Фенвика выступало в роли скрупулезного бухгалтера. Бухгалтер отслеживает накопительные итоги, которыми в данном случае служит непрерывная сумма объема, отражающая давление покупателей или продавцов на рынке. Он хорошо отвечает на вопрос: "Какое суммарное давление вызывает это движение?" Поэтому это был подходящий инструмент для трейдеров, работающих с сильными трендами. Однако "бухгалтер" не учитывает конкретную форму рынка.
Трейдеры, работающие с геометрическими паттернами, в отличие от последователей моментума, сосредоточены не на общем объеме, а на точном историческом повторении. Таким образом, если вместо "бухгалтера" взять "генетика", он проигнорирует объем и преобразует хаотичные необработанные цены графика в четкий дискретный алфавит "ДНК цены". Для простоты: более высокая цена закрытия обозначается как Up (U), более низкая – как Down (D), а отсутствие изменения – как Flat (F).
Когда советник предлагает сделку, он передает "генетику" недавнюю 16-баровую последовательность "ДНК цены". Поскольку суффиксный автомат уже преобразовал всю историю рынка в оптимизированный индекс, "генетику" не нужно перелистывать историю рынка страницу за страницей. Результат получается мгновенно: "Эта точная геометрическая конфигурация совпадает с историческими данными на 90%". Этот подход позволяет трейдеру, работающему с паттернами, увеличивать или уменьшать размер лота на основе точного знакомства с последовательностью, а не суммарного моментума.
Одномерная CNN против автокодировщика
Кроме того, в предыдущей статье с пользовательским классом управления капиталом нейронная сеть работала как специализированный фильтр камеры. Эту роль выполняла одномерная CNN. Математическое окно перемещалось по непрерывным данным, сглаживая хаотичный шум и выделяя реальную направленность тренда. Как инструмент обучения с учителем она позволяла уточнить направление тренда. Это работало в предыдущей реализации. Однако для трейдеров, ищущих геометрические паттерны, подтверждение тренда не слишком полезно, если базовая структура рынка активно разрушается. В геометрических рынках это, как отмечено выше, происходит из-за макроэкономических шоков. Поэтому здесь уместнее структурный защитный механизм, а фильтр камеры заменяется автокодировщиком.
Чтобы понять принцип работы, представьте мастера компрессии – художника, который рассматривает недавний геометрический график, сжимает его в компактный формат с ограниченной памятью, а затем пытается восстановить исходное изображение. Если фрактальные точки текущего рынка следуют заданному ритму, "художник" легко выделит этот паттерн на основе принципа исторического повторения. Однако, если этот ритм нарушается и становится хаотичным или нечитаемым, попытка "художника" сжать и восстановить возникший хаос, скорее всего, приведет к нераспознаваемому результату. В этой статье эта "неупорядоченность", то есть математическая ошибка восстановления, используется как окончательный фильтр. Она будет служить детектором аномалий, проактивно снижающим риск при нарушении структуры геометрических рынков. Цель состоит в том, чтобы сделать это до возникновения необходимости использовать стоп-лосс или альтернативные заранее заданные механизмы выхода.
Математическое определение
Теперь формализуются две проверки, позволяющие реализовать изложенные выше идеи в работоспособном пользовательском классе MQL5. Для трейдера, работающего с геометрическими паттернами, математическая задача имеет два аспекта. Во-первых, необходимо точно выделять и индексировать дискретные последовательности в реальном времени, а во-вторых, измерять непрерывную геометрическую целостность рынка. Этот подход явно отличается от реактивного подхода, основанного на подсчете убытков и используемого во встроенном классе.
Суффиксный автомат
В теории вычислительных графов суффиксный автомат (SA) формально определяется как наименьший конечный детерминированный автомат, распознающий все возможные суффиксы заданной строки. Если обозначить S как историческую "ДНК цены", то есть строку длины n, составленную из категориального алфавита σ = {U, D, F} или (Вверх, Вниз, Равно), то ее автомат представляет собой ориентированный ациклический граф (DAG). Каждая вершина соответствует состоянию, а каждое направленное ребро – детерминированному переходу, вызванному символом этого алфавита.
Состояние в SA соответствует классу эквивалентности подстрок с совпадающими конечными позициями в историческом наборе данных, то есть классу эквивалентности endpos. Кроме того, каждое состояние имеет суффиксную ссылку – структурный указатель на другое состояние, представляющее самый длинный суффикс подстрок, принадлежащих другому классу endpos. Почему эта сложная дискретная математика важна для розничного трейдера, работающего с геометрическими паттернами? Потому что поиск точной последовательности в глубоких исторических массивах с помощью обычных вложенных циклов for может иметь временную сложность O(n^2). В среде советника MQL5, обрабатывающего данные несколько раз за заданный интервал, сканирование со сложностью O(n^2) может легко заблокировать поток исполнения или исчерпать ресурсы VPS.
Однако при использовании алгоритма Блюмера суффиксный автомат строит весь исторический индекс в реальном времени за строго линейное время O(n) при максимуме 2n - 1 состояний и 3n - 4 переходов. Если затем выполнить запрос к SA с недавней ценовой последовательностью T, обход DAG обеспечивает время запроса O(|T|). Это позволяет сразу выделить самую длинную совпадающую последовательность из истории цен и получить почти идеальное категориальное совпадение паттерна без нагрузки на ресурсы сервера.
Автокодировщик
В то время как автомат управляет дискретным сопоставлением последовательностей, автокодировщик отвечает за непрерывное обнаружение аномалий в геометрических паттернах. По сути, автокодировщик – это нейронная сеть прямого распространения без учителя, предназначенная для изучения функции тождества. Она пытается воспроизводить те же данные, которые получает, но вынуждена делать это через сжатый скрытый слой. Предположим, что x – это входной вектор, нормализованный как последовательный массив последних необработанных цен. Нейронная сеть разделена на две симметричные части: проход кодировщика и проход декодировщика. Кодировщик сжимает необработанные рыночные паттерны в скрытый слой h, размер которого меньше размера входного слоя. Такое обязательное уменьшение размерности не позволяет сети просто запоминать входные данные. Благодаря этому сеть изучает основные геометрические структуры рыночных данных. Формальное определение кодировщика выглядит следующим образом:

Где:
- W(e) – матрица кодировщика размером k × m;
- b(e) – вектор смещений;
- sigma(z) – нелинейная сигмоидная функция активации.
Затем проход декодировщика пытается восстановить исходные рыночные паттерны из сильно сжатого слоя h. Он определяется следующим образом:

Где:
- x – восстановленный выходной вектор;
- W(d) – матрица декодировщика размера m × k;
- b(d) – смещение декодировщика.
Для проактивного выявления структурных сломов в рыночной среде рассчитывается ошибка восстановления с помощью средней квадратической ошибки между реальным рыночным входом и восстановленными сетью данными по следующей формуле:


Реализация на MQL5
Разобрав теорию и математику, перейдем к реализации на MQL5. Для интеграции с системой Мастера MQL5 пользовательский класс, как и ранее, наследуется от соответствующего базового класса стандартной библиотеки. В данном случае создается класс управления капиталом CMoneySuffixAE, поэтому он наследуется от базового класса CExpertMoney. На этом в основном заканчивается сходство данного класса с другими встроенными классами управления капиталом. На примере функции Optimize() видно, как новый класс отказывается от реактивного подхода, основанного на прошлых результатах, в пользу строго проактивного геометрического подхода.
Мини-карта – что Optimize() делает шаг за шагом
Перед разбором кода приведен краткий обзор действий функции Optimize():
- Анализ внешнего рынка. Функция отказывается от отслеживания исторических убытков и вместо этого получает последние необработанные ценовые данные.
- Построение генетического индекса. Функция динамически строит суффиксный автомат на основе исторических данных.
- Формирование последовательности ДНК. Функция извлекает последние дискретные паттерны для автомата и непрерывные необработанные цены для автокодировщика.
- Выполнение запроса к автомату. Функция оценивает знакомство с недавней последовательностью и вычисляет коэффициент масштабирования размера лота на основе выбранного режима алгоритма.
- Прохождение через защитный фильтр. Функция вычисляет ошибку восстановления с помощью автокодировщика, чтобы существенно снизить риск, если рыночная структура нечитаема.
Отказ от подхода "зеркала заднего вида"
Кратко рассмотрим основную логику встроенного класса CMoneySizeOptimized. В его функции Optimize() выполняется следующая процедура:
if(m_decrease_factor > 0) { //--- select history for access HistorySelect(0, TimeCurrent()); int orders = HistoryDealsTotal(); int losses = 0; CDealInfo deal; for(int i = orders - 1; i >= 0; i--) { deal.Ticket(HistoryDealGetTicket(i)); if(deal.Ticket() == 0) { Print("CMoneySizeOptimized::Optimize: HistoryDealGetTicket failed, no trade history"); break; } //--- check symbol if(deal.Symbol() != m_symbol.Name()) continue; //--- check profit double profit = deal.Profit(); if(profit > 0.0) break; if(profit < 0.0) losses++; } if(losses > 1) lot = NormalizeDouble(lot - lot * losses / m_decrease_factor, 2); }
Здесь показана структура запаздывающего индикатора. Явно запрашивается история счета с помощью HistorySelect(), перебираются тикеты закрытых ордеров и подсчитываются последовательные отрицательные результаты, чтобы решить, следует ли уменьшить объем входной сделки. Это может потребовать, чтобы трейдер, работающий с геометрическими паттернами, понес убытки, прежде чем функция станет полезной. При сравнении с приведенной ниже функцией нового класса CMoneySuffixAE::Optimize() видны следующие изменения:
class CMoneySuffixAE : public CExpertMoney { protected: CSuffixAutomaton m_sa; CAutoEncoder m_ae; int m_history_length; int m_dna_window; int m_algo_mode; bool m_use_ae; public: CMoneySuffixAE(void); ~CMoneySuffixAE(void); virtual double CheckOpenLong(double price,double sl); virtual double CheckOpenShort(double price,double sl); protected: double Optimize(double lots); };
Класс использует два механизма. Первый механизм – суффиксный автомат. Он работает с дискретным представлением движения цены. Более высокая цена закрытия обозначается как U, более низкая – как D, а неизменная цена закрытия – как F. В реализации они кодируются целыми числами.
int char_code = (diff > 0) ? 0 : ((diff < 0) ? 1 : 2); // 0=U, 1=D, 2=F
Второй механизм – автокодировщик. Он работает с нормализованными необработанными ценами и действует как структурный фильтр. Если недавняя форма цены все еще читаема, автокодировщик должен восстановить ее с малой ошибкой. Если рынок становится хаотичным, ошибка восстановления возрастает, а размер лота уменьшается.
В остальной части статьи пошагово рассматривается реализация функции Optimize(). Поэтому далее пояснения идут прямо по ходу кода, а не представлены как большое теоретическое введение.
Шаг 1 – получение рыночной структуры вместо истории сделок
Встроенный класс начинает работу с HistorySelect(). Пользовательский класс начинает работу с CopyClose().
double CMoneySuffixAE::Optimize(double lots) { double lot = lots; if(m_history_length > 0) { double closes[]; ArraySetAsSeries(closes, true); if(CopyClose(m_symbol.Name(), PERIOD_CURRENT, 0, m_history_length + m_dna_window, closes) == m_history_length + m_dna_window) { // further processing continues here } } return lot; }
Это изменение является главным концептуальным сдвигом в классе. Размер лота больше не корректируется по последним нескольким закрытым сделкам счета. Вместо этого класс напрямую считывает ценовую структуру текущего символа. Благодаря этому модель управления капиталом смотрит вовне, на рынок, а не внутрь, на собственную историю сделок.
Для систем на основе моментума предыдущая реализация использовала дерево Фенвика с индикатором On-Balance Volume и одномерной CNN. Это имело смысл, поскольку основной вопрос касался накопленного объемного давления. Здесь ситуация иная. Геометрического трейдера прежде всего интересует не то, какой объем движет рынком. Трейдер оценивает, остается ли текущая последовательность Price Action структурно знакомой.
Шаг 2 – построение исторической "ДНК цены"
После получения цен закрытия класс сбрасывает автомат и начинает преобразовывать историческое движение цены в дискретный алфавит.
m_sa.Reset(); for(int i = m_dna_window; i < m_history_length + m_dna_window - 1; i++) { double diff = closes[i] - closes[i + 1]; int char_code = (diff > 0) ? 0 : ((diff < 0) ? 1 : 2); // 0=U, 1=D, 2=F m_sa.Extend(char_code); }
Здесь суффиксный автомат заменяет собой дерево Фенвика. Дерево Фенвика было полезно, когда требовалось эффективно поддерживать накопительные суммы. Оно работало как бухгалтер: позволяло быстро определить, какой объем давления накопился.
Суффиксный автомат работает иначе. Он скорее похож на генетический индекс. Его не интересуют накопительные итоги. Он анализирует последовательности. Упрощенная строка "ДНК цены" может выглядеть так:
U D U U F D D U F U
Для геометрического трейдера это ближе к фактической торговой задаче. Система определяет, появлялась ли текущая последовательность движения цены ранее и насколько точно она соответствует исторической структуре. Простой исторический поиск может стать вычислительно затратным, если реализовать его вложенными циклами по большим массивам. Суффиксный автомат избегает этого, создавая компактную структуру, к которой можно эффективно выполнять запросы.
Шаг 3 – извлечение текущего сетапа
Автомат строится на основе исторических данных. Теперь классу требуется текущий паттерн, непосредственно предшествующий торговому сигналу. Для этого одновременно заполняются два массива.
int recent_dna[]; double recent_raw[]; ArrayResize(recent_dna, m_dna_window); ArrayResize(recent_raw, m_dna_window); double max_price = closes[0]; double min_price = closes[0]; for(int i = 0; i < m_dna_window; i++) { double diff = closes[m_dna_window - 1 - i] - closes[m_dna_window - i]; recent_dna[i] = (diff > 0) ? 0 : ((diff < 0) ? 1 : 2); recent_raw[i] = closes[m_dna_window - 1 - i]; if(recent_raw[i] > max_price) max_price = recent_raw[i]; if(recent_raw[i] < min_price) min_price = recent_raw[i]; }
Массив recent_dna – это дискретная версия сетапа. Он передается суффиксному автомату и отвечает на вопрос: "Имеет ли текущая направленная последовательность исторический аналог?"
Массив recent_raw сохраняет фактическую форму цены. Затем он передается автокодировщику и отвечает на другой вопрос: "Даже если последовательность кажется знакомой, достаточно ли чиста текущая рыночная структура, чтобы ей доверять?"
Это разделение важно. Дискретная последовательность может выглядеть знакомой, тогда как лежащее в ее основе ценовое поведение уже нестабильно. Например, во время макроэкономического шока паттерн может по-прежнему давать ту же последовательность U-D-U-D, но форма, диапазон и волатильность движения уже могут быть аномальными. Суффиксный автомат оценивает знакомство с последовательностью. Автокодировщик оценивает структурную целостность.
Шаг 4 – запрос к суффиксному автомату
После подготовки недавней последовательности "ДНК цены" класс запрашивает у автомата самое длительное историческое совпадение.
int longest_match = m_sa.GetLongestMatch(recent_dna); double dna_score = (double)longest_match / (double)m_dna_window; double scale_factor = 1.0; switch(m_algo_mode) { case 1: scale_factor = AlgorithmMode1_Linear(dna_score); break; case 2: scale_factor = AlgorithmMode2_Conservative(dna_score); break; case 3: scale_factor = AlgorithmMode3_Aggressive(dna_score); break; case 4: scale_factor = AlgorithmMode4_MeanReversion(dna_score); break; } lot = NormalizeDouble(lot * scale_factor, 2);
Результат нормализуется в dna_score – значение в диапазоне от 0,0 до 1,0. Значение, близкое к нулю, означает, что текущая последовательность в основном незнакома. Значение, близкое к единице, означает, что недавний сетап имеет сильное историческое совпадение. Эта оценка становится первым сигналом для расчета размера позиции.
Разные трейдеры, работающие с геометрическими паттернами, могут по-разному интерпретировать знакомство с паттерном. Трейдер, работающий по пробоям, может захотеть увеличить размер позиции при сильном подтверждении известного паттерна. Контртрендовый трейдер может предпочесть противоположный подход, когда паттерн выглядит слишком очевидным или исчерпанным. По этой причине класс предоставляет несколько режимов алгоритма.
Шаг 5 – преобразование знакомства с паттерном в риск
Режимы алгоритма представляют собой лишь кривые управления капиталом. Они сами по себе не формируют точки входа. Сигнал входа по-прежнему поступает от советника, собранного с помощью Мастера MQL5. Режимы просто преобразуют dna_score в множитель.
double CMoneySuffixAE::AlgorithmMode1_Linear(double score) { return 0.8 + 1.2 * score; } double CMoneySuffixAE::AlgorithmMode2_Conservative(double score) { return 0.5 + MathSqrt(score) * 0.75; } double CMoneySuffixAE::AlgorithmMode3_Aggressive(double score) { return 0.5 + 2.5 * score * score; } double CMoneySuffixAE::AlgorithmMode4_MeanReversion(double score) { if(score <= 0.0) return 1.0; return MathMin(3.0, 0.5 / score); }
Первый режим – линейный. Это наиболее прямое толкование оценки "ДНК цены": незнакомые структуры уменьшают размер позиции, а знакомые увеличивают его.
Второй режим – консервативный. Он больше подходит трейдерам, которым нужно подтверждение паттерна, но не требуется агрессивное увеличение лота. Кривая квадратного корня рано реагирует на умеренную уверенность, сохраняя общее масштабирование сдержанным.
Третий режим – агрессивный. Он существенно увеличивает размер позиции при совпадениях с высокой степенью уверенностью и предназначен для ситуаций, когда трейдер хочет заметно наращивать размер позиции только при сильном совпадении паттерна.
Четвертый режим использует обратную логику. Он предназначен для стратегий возврата к среднему или контртрендовых интерпретаций. Вместо увеличения позиции при идеальном знакомстве с паттерном предполагается, что чрезмерно очевидная структура может быть исчерпана.
Шаг 6 – добавление фильтра автокодировщика
Суффиксный автомат дает оценку знакомства с паттерном, но имеет одно ограничение: он видит дискретное направление, а не непрерывное искажение. Именно здесь добавляется автокодировщик.
if(m_use_ae) { double norm_range = (max_price - min_price == 0) ? 1.0 : (max_price - min_price); for(int i = 0; i < m_dna_window; i++) recent_raw[i] = (recent_raw[i] - min_price) / norm_range; double ae_coefficient = m_ae.Calculate(recent_raw); lot = NormalizeDouble(lot * ae_coefficient, 2); }
Сначала класс нормализует недавнее окно необработанных цен в диапазон [0, 1]. Это обеспечивает нейронной сети стабильный масштаб входных данных. Затем нормализованная ценовая структура передается в автокодировщик.
Автокодировщик сжимает входные данные в более компактное скрытое представление и пытается восстановить исходные данные из этой сжатой формы. Если недавняя рыночная структура напоминает структуру, которую сеть способна представить, ошибка восстановления должна быть малой. Если рынок стал хаотичным, ошибка восстановления должна возрасти.
h = σ(W e x + b e )
x′ = σ(W d h + b d )
MSE = (1 / n) × Σ(x i − x′ i )2
C = 1 / (1 + MSE)
Когда ошибка восстановления мала, C остается близким к 1,0, поэтому решение суффиксного автомата о размере лота в основном сохраняется. Когда ошибка восстановления возрастает, C стремится к нулю, а размер позиции уменьшается.
Это дает классу второй уровень защиты. Автомат оценивает, знакома ли последовательность. Автокодировщик оценивает, достаточно ли текущая структура чиста для торговли с нормальным уровнем риска.
Шаг 7 – проверка ограничений брокера
После того как оба механизма скорректировали размер лота, класс все равно должен соблюдать ограничения объема, установленные брокером.
double stepvol = m_symbol.LotsStep(); lot = stepvol * NormalizeDouble(lot / stepvol, 0); double minvol = m_symbol.LotsMin(); if(lot < minvol) lot = minvol; double maxvol = m_symbol.LotsMax(); if(lot > maxvol) lot = maxvol; return lot;
Этот заключительный блок не относится к логике модели, но необходим для исполнения сделок. Независимо от того, как суффиксный автомат или автокодировщик масштабируют позицию, полученный размер лота должен соответствовать шагу объема символа и оставаться в пределах минимального и максимального лимитов брокера.
На этом этапе полный рабочий процесс Optimize() можно резюмировать следующим образом:
1. Считываются последние и исторические цены закрытия. 2. Историческое движение преобразовывается в "ДНК цены". 3. Строится суффиксный автомат. 4. Текущий сетап преобразуется в недавнюю "ДНК цены". 5. У автомата запрашивается самое длительное историческое совпадение. 6. Оценка совпадения преобразуется в множитель лота. 7. При необходимости структура необработанных цен проводится через автокодировщик. 8. Размер лота уменьшается при высокой ошибке восстановления. 9. Результат нормируется в соответствии с ограничениями брокера.
Полная логика Optimize()
Основную процедуру можно рассматривать как единый пайплайн. Порядок важен: сначала суффиксный автомат предлагает множитель на основе знакомства с исторической последовательностью, и только затем автокодировщик может уменьшить этот множитель, если текущая структура искажена.
double CMoneySuffixAE::Optimize(double lots) { double lot = lots; if(m_history_length > 0) { double closes[]; ArraySetAsSeries(closes, true); //--- Pull enough bars for both the historical index and the current DNA window if(CopyClose(m_symbol.Name(), PERIOD_CURRENT, 0, m_history_length + m_dna_window, closes) == m_history_length + m_dna_window) { m_sa.Reset(); //--- 1. Build the Suffix Automaton from historical Price DNA for(int i = m_dna_window; i < m_history_length + m_dna_window - 1; i++) { double diff = closes[i] - closes[i + 1]; int char_code = (diff > 0) ? 0 : ((diff < 0) ? 1 : 2); // 0=U, 1=D, 2=F m_sa.Extend(char_code); } //--- 2. Extract the current setup in both discrete and raw form int recent_dna[]; double recent_raw[]; ArrayResize(recent_dna, m_dna_window); ArrayResize(recent_raw, m_dna_window); double max_price = closes[0]; double min_price = closes[0]; for(int i = 0; i < m_dna_window; i++) { double diff = closes[m_dna_window - 1 - i] - closes[m_dna_window - i]; recent_dna[i] = (diff > 0) ? 0 : ((diff < 0) ? 1 : 2); recent_raw[i] = closes[m_dna_window - 1 - i]; if(recent_raw[i] > max_price) max_price = recent_raw[i]; if(recent_raw[i] < min_price) min_price = recent_raw[i]; } //--- 3. Query the automaton and convert the match into a score int longest_match = m_sa.GetLongestMatch(recent_dna); double dna_score = (double)longest_match / (double)m_dna_window; //--- 4. Convert the DNA score into a lot multiplier double scale_factor = 1.0; switch(m_algo_mode) { case 1: scale_factor = AlgorithmMode1_Linear(dna_score); break; case 2: scale_factor = AlgorithmMode2_Conservative(dna_score); break; case 3: scale_factor = AlgorithmMode3_Aggressive(dna_score); break; case 4: scale_factor = AlgorithmMode4_MeanReversion(dna_score); break; } lot = NormalizeDouble(lot * scale_factor, 2); //--- 5. Use the Autoencoder as a structural gatekeeper if(m_use_ae) { double norm_range = (max_price - min_price == 0) ? 1.0 : (max_price - min_price); for(int i = 0; i < m_dna_window; i++) recent_raw[i] = (recent_raw[i] - min_price) / norm_range; double ae_coefficient = m_ae.Calculate(recent_raw); lot = NormalizeDouble(lot * ae_coefficient, 2); } } } //--- 6. Validate broker limits double stepvol = m_symbol.LotsStep(); lot = stepvol * NormalizeDouble(lot / stepvol, 0); double minvol = m_symbol.LotsMin(); if(lot < minvol) lot = minvol; double maxvol = m_symbol.LotsMax(); if(lot > maxvol) lot = maxvol; return lot; }
Этот порядок важен, поскольку знакомая последовательность не всегда безопасна. В дискретной форме паттерн все еще может выглядеть знакомым, тогда как непрерывная рыночная структура уже нарушается.
Анализ после оптимизации
Чтобы проверить вклад каждой части модели, класс тестировался в двух конфигурациях на GBPUSD на таймфрейме H4. Период оптимизации охватывал 2025 год, а форвардное тестирование – период с 1.01.2026 по 1.05.2026.
В первом запуске использовался только суффиксный автомат. Автомат работал в режиме 1, а фильтр автокодировщика был отключен.
m_algo_mode = 1 m_use_ae = false
Этот тест проверяет, может ли одно лишь знакомство с последовательностью дать рабочий сигнал для управления капиталом. Форвардное тестирование дало следующий отчет.

Результат показывает, что автомат может формировать прибыльный базовый результат. Это важно, поскольку корректировка лота не основывалась на недавних убытках счета. Она основывалась на том, имела ли текущая "ДНК цены" историческое сходство. Однако кривая средств счета оставалась волатильной, а просадка приближалась к 11%.
Эта слабость соответствует ранее рассмотренному ограничению. Суффиксный автомат может распознавать знакомство с последовательностью, но не обязательно распознает структурный хаос. Он может показать, что недавняя последовательность похожа на историческую, но не может полностью оценить, не исказил ли текущую рыночную среду внезапный шок.
Во втором запуске был добавлен автокодировщик.
m_algo_mode = 1 m_use_ae = true
Теперь размер позиции сначала корректировался суффиксным автоматом, а затем фильтровался коэффициентом восстановления, вычисленным автокодировщиком.

Результат этого форвардного тестирования был более стабильным: просадка средств счета была почти вдвое меньше, чем в запуске только с автоматом. Это не доказывает универсальную устойчивость модели. Тест проводился на одном символе и в одном ограниченном временном окне. До рассмотрения возможности внедрения требуются дополнительные тесты на разных символах, таймфреймах, спредах и рыночных режимах.
Тем не менее, это сравнение полезно. Это указывает на то, что два механизма не являются избыточными. Суффиксный автомат обеспечивает оценку знакомства с паттерном. Автокодировщик обеспечивает защиту от структурных нарушений.
Практические выводы
Основной вывод из этой реализации состоит в том, что управление капиталом не обязательно должно основываться только на истории сделок счета. Встроенный класс CMoneySizeOptimized уменьшает объем после серии убытков. Этот подход прост и практичен, но реактивен. Прежде чем класс отреагирует, рынок должен сначала нанести ущерб.
Класс CMoneySuffixAE стремится реагировать раньше. Он использует текущую рыночную структуру, а не историю закрытых сделок. Для систем на основе моментума могут быть оправданны методы, использующие объем, например дерево Фенвика с индикатором OBV. Для геометрических систем или систем на основе паттернов знакомство с последовательностью может быть более значимым. Именно здесь становится полезен суффиксный автомат.
Однако одного лишь знакомства с последовательностью недостаточно. Рынок все еще может формировать узнаваемые направленные фрагменты, даже когда его более широкая структура нарушается. Автокодировщик добавляется для выявления таких искажений по ошибке восстановления.
Поэтому полученная модель является условной. Если текущая последовательность знакома, а структура чиста, класс может сохранить или увеличить объем риска. Если последовательность незнакома или структура становится нечитаемой, класс может сократить объем риска до того, как обычно среагирует счетчик убытков.
Заключение
В этой статье создан пользовательский класс управления капиталом для Мастера MQL5 под названием CMoneySuffixAE. Вместо уменьшения размера позиции после серии убыточных сделок класс напрямую анализирует текущую рыночную структуру. Он преобразует исторический Price Action в дискретную "ДНК цены", использует суффиксный автомат для оценки знакомства текущего сетапа, а затем применяет автокодировщик как структурный фильтр.
В результате создана проактивная модель управления капиталом для трейдеров, работающих с геометрическими паттернами, чьи системы зависят от узнаваемых ценовых структур, а не от накопленного объемного давления. Тесты показывают, что суффиксный автомат может предоставить полезный сигнал для расчета размера позиции на основе последовательности, а автокодировщик может помочь снизить просадку, сокращая объем риска в периоды структурной нестабильности.
Основная идея проста: в системах на основе паттернов модель управления рисками не должна ждать, пока история счета подтвердит, что что-то пошло не так. Иногда рыночная структура первой подает предупреждение.
| имя | описание |
|---|---|
| wz_93.mqh | Советник, собранный Мастером MQL5. Ссылки приведены для новых читателей. |
| MoneySizeOptimized.mqh | Встроенный образец класса управления капиталом |
| MoneySuffixAE.mqh | Пользовательский класс управления капиталом, реализованный в статье |
| r1.set | Входные настройки советника на первом прогоне |
| r2.set | Входные настройки советника на втором прогоне |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/22842
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Интеграция MQL5 с пакетами обработки данных (Часть 9): Адаптивная оценка волатильности на основе энтропии
От начального до среднего уровня: Классы (III)
Моделирование рынка: Position View (XVI)
Моделирование рынка: Position View (XV)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования