Возможности Мастера MQL5, которые вам нужно знать (Часть 81): Использование паттернов Ишимоку и ADX Уайлдера в обучении на основе β-VAE-инференса
Введение
Почти все трейдеры признают, что рынки движутся в циклах оптимизма и пессимизма. Однако готовых инструментов, которые стабильно выявляют эти циклы и пригодны для прибыльной торговли, крайне мало. В последнее время на мировых рынках преобладают медвежьи настроения: внезапные распродажи и слабые отскоки происходят все чаще. В таких условиях механические стратегии, основанные на запаздывающих индикаторах, неизбежно дают ложные сигналы: волатильность выбивает трейдеров из позиций, которые в более спокойной обстановке могли бы развиться в ожидаемом направлении.
Нехватка готовых решений показывает необходимость кастомизации. Здесь особенно выделяются торговые платформы, оснащенные интегрированной средой разработки (IDE). Они не только обеспечивают исполнение сделок и построение графиков институционального уровня, но и могут иметь встроенного мастера создания торговых систем. В MetaTrader такой мастер представляет собой среду, которая позволяет трейдерам быстро собирать советники, даже не разрабатывая сложную торговую логику с нуля. Главное преимущество Мастера – возможность интеграции пользовательских классов сигналов. Благодаря этому трейдеры могут напрямую применять передовые методы машинного обучения в автоматизированных стратегиях.
Среди современных методов машинного обучения вариационные автокодировщики (VAE) привлекают внимание способностью сжимать шумные многомерные данные в структурированные скрытые представления. В отличие от обычного автокодировщика, β-VAE вводит контролируемый штраф, побуждающий скрытый слой выделять декомпозированные, содержательно значимые признаки, а не запоминать исходные данные. В трейдинге это позволяет выделять существенные свойства паттернов из потоков технических индикаторов и снижает чувствительность к шуму.
Однако ключевая идея этой статьи в том, что VAE особенно эффективны при работе с двоично закодированными признаками. Вместо непрерывных значений индикаторов или масштабированных данных из пайплайнов задаем четкие событийные условия. Пересекла ли линия Tenkan-sen линию Kijun-sen снизу вверх? Если да, присваиваем 1, иначе 0. Закрылась ли цена ниже облака? Если да, снова присваиваем 1, иначе 0. Превышает ли ADX 25, подтверждая сильный тренд? Если да, также присваиваем 1, иначе 0.
Рассмотрим паттерны 0, 1 и 5 для пары индикаторов Ишимоку и ADX. На графике они могут выглядеть так.
Паттерн 0
Медвежий сигнал: цена пересекает Senkou Span A сверху вниз, что указывает на пробой облака Kumo вниз. Сигнал подтверждается значением ADX не ниже 25. 
Паттерн 1
Медвежий сигнал: линия Tenkan-Sen пересекает Kijun-Sen сверху вниз и закрывается ниже нее, указывая на краткосрочную смену импульса в сторону снижения. Значение ADX должно быть не ниже 20. 
Паттерн 5
Бычий сигнал: цена отскакивает от Tenkan-Sen, а значение ADX не ниже 25. 
В основе реализации для этой статьи лежит предположение, что двоичные кодировки образуют компактные векторы, отражающие наличие или отсутствие важных паттернов в каждый момент времени. При обработке в VAE они формируют гораздо более четкую структуру сигналов, чем пайплайны с нормализованными значениями с плавающей точкой. В наших тестах одного этого изменения оказалось достаточно, чтобы улучшить обучение и получить более сильные сигналы при тестировании.
В этой статье пошагово показано, как реализовать эту систему. Мы объединим индикатор Ишимоку с ADX Уайлдера, сформируем двоичные векторы признаков, обучим β-VAE на исторических рыночных данных, а затем экспортируем обученную модель в формат ONNX для использования в MQL5. Наконец, мы рассмотрим результаты Тестера стратегий, чтобы оценить работу двоичных кодировок по сравнению с недавно изученными пайплайнами на основе непрерывных значений.
Мастер MQL5
Мастер MQL5 позволяет трейдерам быстро собирать советники из готовых блоков – сигналов, модулей управления капиталом, правил выхода и трейлинг-стопов – без написания типового служебного кода. Для более опытных трейдеров важна возможность добавлять пользовательские классы сигналов в советник, созданный с помощью Мастера. Класс сигнала определяет, какие сделки следует открывать и когда их нужно закрывать. MetaQuotes предоставляет стандартные сигналы, например на основе скользящих средних или RSI. Однако пользовательские сигналы значительно расширяют возможности Мастера. С помощью пользовательского класса сигнала ко встроенным стандартным паттернам можно подключать модели машинного обучения, вероятностные прогнозы или, как здесь, β-VAE. Это помогает трейдерам искать не только альфу, но и формировать портфели со стабилизированной волатильностью.
Одна из ключевых возможностей – добавление пользовательских классов сигналов. Встроенные сигналы могут использовать MA или RSI, а пользовательские классы – интегрировать модели машинного обучения, вероятностные прогнозы или VAE. Мастер управляет ордерами и размером позиций, а пользовательская логика обеспечивает гибкость, помогает отфильтровывать ложные сигналы и строить стратегии, учитывающие контекст.
Пайплайны и двоичные входные данные
В статье, предшествовавшей предыдущей, мы рассматривали, как пайплайны предварительной обработки в стиле SCIKIT-LEARN помогают нормализовать признаки модели перед обучением и инференсом. Идея была проста: последовательно применять заполнение пропусков, масштабирование и преобразование, чтобы значения всех индикаторов попадали в сопоставимые диапазоны. Для подготовки данных Ишимоку и ADX Уайлдера перед подачей в модель использовали масштабирование по минимуму и максимуму, стандартизацию и робастное масштабирование. Однако, как мы увидели, у этого подхода оказалось два недостатка при торговле.
Во-первых, снизилась четкость сигналов. Масштабирование непрерывных значений сгладило именно те условия, которые требовалось выделить или распознать. Например, пробой облака Ишимоку вверх – однозначное событие типа да/нет – после нормализации превращался в разницу значений с плавающей точкой. По сути, модели приходилось заново изучать то, что трейдерам и так очевидно: моменты пересечения линий и пробоя уровней.
Во-вторых, мы столкнулись с низкой стабильностью обучения. Полученные непрерывные признаки, как правило, усиливали шум. Случайные рыночные колебания без четкой причинной связи практически несущественны, однако преобразователи масштабирования придавали им такое же значение. В результате обучение сходилось медленнее, а при форвардном тестировании инференс давал менее надежные результаты.
По-видимому, решением стало изменение подхода. Вместо того чтобы заставлять модели интерпретировать величину различий, мы стали отмечать наличие или отсутствие определенных условий. В этом и состоит суть преобразования паттернов индикаторов в булевы (двоичные) входные данные. Например, если Tenkan-Sen пересекает Kijun-Sen при значении ADX выше 20, это отмечается как бычий сигнал данного паттерна и ему присваивается 1. Во всех остальных случаях присваивается 0. Аналогично, если цена закрытия пробивает Senkou Span A вниз, а ADX удерживается выше 25, этому паттерну присваивается 1. Во всех остальных случаях присваивается 0.
Эти булевы кодировки представлены компактным вектором сигналов "паттерн присутствует / паттерн отсутствует" для каждого ценового бара. Таким образом, вместо пайплайнов, формирующих масштабированные значения с плавающей точкой, функция GetFeatures из приведенного ниже кода β-VAE на Python напрямую формирует булевы векторы.
def GetFeatures(functions, *args, **kwargs) -> np.ndarray: features_per_func = [] n_rows = None for f in functions: out = f(*args, **kwargs) a = np.asarray(out) if a.ndim == 1: row = SetRow(a) block = row elif a.ndim == 2: rows = [SetRow(a[i, :]) for i in range(a.shape[0])] block = np.concatenate(rows, axis=0) else: raise ValueError("Feature function returned array with ndim "+str(a.ndim)) if n_rows is None: n_rows = block.shape[0] elif block.shape[0] != n_rows: raise ValueError("Inconsistent number of rows across feature functions.") features_per_func.append(block) return np.concatenate(features_per_func, axis=1) def GetStates(df): diffs = df['close'].diff() df['states'] = np.select( condlist=[diffs > 0, diffs < 0], choicelist=[1.0, -1.0], default=0.0 ) return df[['states']]
Стоит отметить, что в предыдущих статьях мы не объединяли сигнальные паттерны и строили отдельную модель для каждого из них. В этой статье объединяем три изучаемых паттерна в одну модель. Только три, а не исходные десять. Это означает, что мы разрабатываем и тестируем только одну модель.
По-видимому, эффект проявился сразу. VAE больше не нужно определять, какие части числовой шкалы соответствуют значимым событиям, а какие соответствуют шуму. Вместо этого VAE использует двоичные флаги состояния, которые более интуитивно понятны для трейдеров. Это изменение должно повысить стабильность как при тестировании на исторических данных, так и при последующей торговле в реальных условиях. В текущей рыночной среде, где ожидается значительная коррекция на рынке акций и высокая волатильность, двоичное представление лучше различает шум и достоверные сигналы продолжения движения.
Таким образом, отказ от пайплайнов в пользу двоичного кодирования событий не только упрощает предварительную обработку, но и дает β-VAE входные данные, которые она способна эффективно сжимать и представлять.
Вариационный автокодировщик типа бета
После формирования двоичных векторов признаков их передают в модель. Возможность Python обрабатывать этот и подобные этапы пакетами – одна из причин его высокой эффективности при обучении. Кроме того, использование тензоров при обратном распространении требует дополнительной памяти для хранения вычисленных градиентов на каждом прямом проходе. Наша модель VAE способна обучаться компактным представлениям повторяющихся рыночных структур. Это одно из ключевых преимуществ β-VAE.
Стандартный автокодировщик сжимает входные данные, преобразуя их в представление скрытого слоя. Этот слой также называют скрытым (латентным) пространством. Затем автокодировщик восстанавливает исходные входные данные. Вариационный автокодировщик развивает этот подход, делая скрытое пространство вероятностным. Скрытое пространство моделируется как распределение, а не как набор фиксированных значений. Поэтому при обучении кодировщик вычисляет среднее значение и дисперсию. Из этого распределения в ходе репараметризации извлекается случайная выборка.
β-VAE развивает описанную выше концепцию обычного VAE за счет добавления еще одного механизма. В функции потерь β-VAE умножает член KL-дивергенции на коэффициент β. Это усиливает регуляризацию и побуждает модель выделять декомпозированные, содержательно значимые признаки, а не запоминать входные данные. В трейдинге это важно, поскольку скрытое пространство лучше кодирует ключевые паттерны, такие как пробой облака и продолжение тренда, не приравнивая шум к полезным паттернам.
Модель включает следующие ключевые компоненты:
- Кодировщик – принимает двоичный вектор входных признаков и пропускает его через полносвязные слои. На выходе формируются параметры распределения: среднее значение z_mean и логарифм дисперсии z_logvar.
- Репараметризация – из скрытого гауссовского распределения выбирается значение z. В данном случае z – подразумеваемый результат, полученный из входных признаков. При обучении кодировщик формирует две матрицы весов между видимым и скрытым слоями, что обеспечивает стандартное кодирование входных данных.
- Декодер – восстанавливает исходный двоичный входной вектор по значению z. Благодаря этому скрытое пространство выделяет основную структуру исходных входных данных.
- Прогнозная head-часть y по латентному вектору z – головка прогнозирования при обучении с учителем, которая сопоставляет z с оценочным значением y. В нашем случае это прогноз направленности ценового движения: бычьей, медвежьей или боковой.
Функция потерь, используемая для этого VAE, объединяет три составляющие. Во-первых, потеря реконструкции, измеряемая бинарной кросс-энтропией между восстановленными выходными данными и исходными двоичными входными признаками. Во-вторых, KL-дивергенция, штрафующая отклонение скрытых распределений от стандартного гауссовского априорного распределения. Здесь применяется β-множитель. В-третьих, используется потеря головки при обучении с учителем. В нашем случае это среднеквадратичная ошибка (MSE) между прогнозируемым итоговым значением y и фактическим значением y.
Особенность входного слоя VAE в том, что к обычным входным признакам индикаторов добавляется отдельное входное измерение для ожидаемого выхода. На входе ей присваивается нейтральное значение 0,5. После прохождения через скрытое пространство и возврата ко входу значение y представляет следующее ценовое движение, которое следует за шестью ранее переданными входными признаками либо сопоставляется с ними. Таким образом, модель работает в двух режимах, и каждый из них формирует свой результат. Ниже приведен листинг реализации β-VAE на Python:
# ----------------------------- β-VAE (inference simplified to VAE-only) ----------------------------- class BetaVAEUnsupervised(nn.Module): """ Encoder: features -> (mu, logvar) Decoder: z -> x_hat **Inference (now VAE-only):** latent z is mapped to y via an internal head. All former infer modes (ridge/knn/kernel/lwlr/mlp) are bypassed. """ def __init__(self, feature_dim, latent_dim, k_neighbors=5, beta=4.0, recon='bce', infer_mode='vae', ridge_alpha=1e-2, kernel_bandwidth=1.0): super().__init__() self.latent_dim = latent_dim self.k_neighbors = k_neighbors self.beta = beta self.recon = recon self.infer_mode = 'vae' # force VAE-only self.ridge_alpha = float(ridge_alpha) self.kernel_bandwidth = float(kernel_bandwidth) # Encoder self.feature_encoder = nn.Sequential( nn.Linear(feature_dim, 256), nn.ReLU(), nn.Linear(256, 128), nn.ReLU(), nn.Linear(128, latent_dim * 2) ) # Decoder self.decoder = nn.Sequential( nn.Linear(latent_dim, 128), nn.ReLU(), nn.Linear(128, 256), nn.ReLU(), nn.Linear(256, feature_dim) ) # New: latent→y head (supervised head trained with MSE) self.y_head = nn.Sequential( nn.Linear(latent_dim, 128), nn.ReLU(), nn.Linear(128, 1) ) def encode(self, features): h = self.feature_encoder(features) z_mean, z_logvar = torch.chunk(h, 2, dim=1) return z_mean, z_logvar def reparameterize(self, mean, logvar): std = torch.exp(0.5 * logvar) eps = torch.randn_like(std) return mean + eps * std def decode(self, z): return self.decoder(z) def predict_from_latent(self, z): # VAE-only mapping return self.y_head(z) def forward(self, features, y=None): mu, logvar = self.encode(features) z = self.reparameterize(mu, logvar) x_logits = self.decode(z) y_hat = self.predict_from_latent(z) if y is not None: return {'z': z, 'z_mean': mu, 'z_logvar': logvar, 'x_logits': x_logits, 'y_hat': y_hat} else: return {'y': y_hat} # Reconstruction + KL def beta_vae_loss(features, x_logits, mu, logvar, beta=4.0, recon='bce'): if recon == 'bce': recon_loss = F.binary_cross_entropy_with_logits(x_logits, features, reduction='sum') / features.size(0) else: recon_loss = F.mse_loss(torch.sigmoid(x_logits), features, reduction='mean') * features.size(1) kl = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp()) / features.size(0) loss = recon_loss + beta * kl return loss, recon_loss.detach(), kl.detach()
В функции прямого прохода можно получить либо значение y, соответствующее следующему ценовому движению, либо это значение вместе с вектором распределения, содержащим среднее значение и дисперсию z. При экспорте в ONNX, поскольку этап обучения уже завершен, выводится только значение y для следующего ценового движения: все, что выше 0.5f, указывает на бычье направление, а все, что ниже, – на медвежье.
Объединение трех составляющих функции потерь при обучении побуждает модель сжимать двоичные признаки, сохраняя способность прогнозировать направление цены. Обучение выполняется с оптимизатором Adam в течение нескольких эпох. Для нашей модели использовали 50 эпох. На каждом шаге обучения признаки кодируются, а среднее значение и дисперсия используются для выборки z; декодер восстанавливает исходные признаки; скрытый слой передает данные в головку для прогноза следующего состояния рынка; если подано входное значение y, вычисляется функция потерь; наконец, обратное распространение обновляет веса как VAE, так и головки.
В цикле обучения используется bootstrap-процедура, которая позволяет эффективно проходить по набору данных и выводить сводки потерь для каждой эпохи. Выводимые сводки позволяют отслеживать, насколько VAE соблюдает баланс между сжатием и выделением независимых признаков, а также точность прогноза следующего состояния, формируемого головкой.
Экспорт в MQL5
Как знают постоянные читатели, обучение на Python – лишь половина пути. Чтобы использовать модели, разработанные на языке, отличном от MQL5, в советнике, собранном Мастером, требуется связующее звено. Таким связующим звеном, как уже было показано в предыдущих статьях, служит формат ONNX – Open Neural Network Exchange. Этот связующий формат предоставляет стандартизированный способ представления моделей, обученных на Python, для использования на различных платформах, помимо MetaTrader. При использовании в MQL5 этот файл может иметь размер от нескольких сотен килобайт до 128 МБ, согласно последним тестам автора. Модель можно загружать из изолированной файловой системы либо подключать как скомпилированный ресурс. Главное – можно эффективно обучать сложные модели с помощью PyTorch, проверять их в Python, а затем развертывать для использования в советниках без переписывания логики модели. Основную часть этого процесса выполняет функция export_onnx_y. Ее листинг приведен ниже:
import torch import torch.nn as nn import onnx import onnxruntime as ort def export_onnx_y(model: nn.Module, feature_dim: int, output_path: str, opset: int = 14): """ Export the VAE so that ONNX takes ONLY `features` and returns the inferred y. No latent (z, mu, logvar) are exported. No `y` input is used. """ model.eval() # Wrapper that returns a plain tensor (y_hat), suitable for ONNX export class _YOnly(nn.Module): def __init__(self, m: nn.Module): super().__init__() self.m = m def forward(self, features: torch.Tensor) -> torch.Tensor: mu, logvar = self.m.encode(features) z = self.m.reparameterize(mu, logvar) y_hat = self.m.predict_from_latent(z) return y_hat # shape: [N, 1] wrapped = _YOnly(model) dummy_features = torch.randn(1, feature_dim, dtype=torch.float32) torch.onnx.export( wrapped, dummy_features, output_path, input_names=["features"], output_names=["y_hat"], dynamic_axes={ "features": {0: "batch_size"}, "y_hat": {0: "batch_size"}, }, opset_version=opset, do_constant_folding=True, ) # Validate and print IO for sanity onnx_model = onnx.load(output_path) onnx.checker.check_model(onnx_model) print(f"ONNX model '{output_path}' exported and validated (features -> y_hat).") sess = ort.InferenceSession(output_path) for i in sess.get_inputs(): print(f"input: {i.name}, shape={i.shape}, type={i.type}") for o in sess.get_outputs(): print(f"output: {o.name}, shape={o.shape}, type={o.type}")
Рассмотренная выше экспортная функция создает компактную, готовую к использованию ONNX-модель. Она выполняет прямой проход по предварительно вычисленным признакам двух индикаторов и возвращает единственное значение y без дополнительных отладочных входных и выходных данных. Сначала VAE PyTorch переводится в режим eval, чтобы отключить случайное исключение нейронов (dropout) и зафиксировать параметры пакетной нормализации. Затем признаки кодируются для получения среднего значения и дисперсии, после чего с помощью описанной выше репараметризации формируется скрытый вектор z. Затем z передается в головку модели predict_from_latent, после чего возвращается значение y размерности [batch, 1]. Эта обертка создается для обученной модели, чтобы экспортируемый граф включал только минимально необходимые операции прямого прохода.
Для трассировки графа создается всего один фиктивный входной тензор размерности [1, 6]. Затем вызывается функция torch.onnx.export с явно заданными именами и динамическими осями для размерности пакета, чтобы экспортированная модель могла принимать пакеты любого размера. Параметру opset присваивается значение 14, а также включается свертка констант, чтобы упростить интерпретацию графа.
Даже после экспорта в Python необходимо сразу проверить файл, повторно импортировав его и сверив размерности входного и выходного слоев. Для этого файл загружается с помощью onnx.load, затем проверяется через onnx.checker.check_model на корректность и открывается в подходящей сессии инференса. Выводятся полученные сигнатуры входного и выходного слоев, необходимые при переносе модели в MetaTrader. Выбранная экспортная модель делает VAE внешне похожим на сеть, обученную с учителем. Однако это по-прежнему VAE: для отсутствующего входного значения y автоматически используется нейтральное значение, затем модель выполняет полный цикл от входа через скрытое пространство и обратно и определяет недостающее значение по обученным весам.
После экспорта файл ONNX необходимо сразу проверить в MQL5. Вместо обращения к файлу ONNX в изолированной файловой системе, как и в предыдущих статьях, подключаем этот файл в качестве ресурса. Однако этот вариант ограничивает максимальный размер экспортируемой модели. Раньше предельный размер файла ресурса составлял около 256 МБ, но в последнее время, по-видимому, снизился примерно вдвое – до 128 МБ. Это еще требует подтверждения, однако ONNX-файлы, не подключенные в качестве ресурсов и хранящиеся в изолированной файловой системе, могут не иметь таких ограничений. Такие ограничения могут быть особенно важны для трейдеров, рассчитывающих на использование MQL5 VPS.
Инференс на основе неполных признаков для прогнозирования
Обучение модели – лишь один из этапов; ее использование в реальной торговле часто оказывается совсем другой задачей. После экспорта β-VAE в формат ONNX модель используется для прогнозирования и в нашем случае подключается как ресурс. В Python проверяем, как этот инференс будет работать в MQL5: передаем набор синтетических двоичных данных в функцию infer_example. Это позволяет проверить, будут ли заранее заданные бычьи или медвежьи показания индикаторов интерпретированы ожидаемым образом.
# ----------------------------- Inference demo (updated) ----------------------------- def infer_example(model, dataloader=None, num_examples=5, prob_one_in_pair=0.6, seed=None, partial_rows=None): """ If partial_rows is provided, we zero-fill missing inputs and run a full VAE cycle to produce y that pairs with that incomplete input. Otherwise, we keep the previous synthetic-pairs demo using the dataloader. """ model.eval() with torch.no_grad(): if partial_rows is not None: device = next(model.parameters()).device X = _zero_fill_rows(partial_rows, feature_dim, device) preds = model(X) print("Inference with partial inputs (zero-filled):") for i in range(X.size(0)): yhat = float(preds['y'][i].item()) print(f"X[{i}] first 8: {X[i, :8].tolist()} -> y_hat={yhat:.6f}") return # ----- legacy synthetic example (kept) ----- if dataloader is None: raise ValueError("dataloader required when partial_rows is None") batch = next(iter(dataloader)) N = min(num_examples, batch["features"].shape[0]) feat_dim = batch["features"].shape[1] assert feat_dim >= 6 and feat_dim % 2 == 0, "feature_dim must be even and >= 6" pairs = 3 device = next(model.parameters()).device if seed is not None: torch.manual_seed(seed) X = torch.zeros((N, feat_dim), device=device) any_one = torch.bernoulli(torch.full((N, pairs), float(prob_one_in_pair), device=device)).bool() side = torch.bernoulli(torch.full((N, pairs), 0.5, device=device)).long() row_idx = torch.arange(N, device=device).unsqueeze(1).expand(N, pairs) base = (torch.arange(pairs, device=device).unsqueeze(0).expand(N, pairs) * 2) col_idx = base + side sel_rows = row_idx[any_one]; sel_cols = col_idx[any_one] X[sel_rows, sel_cols] = 1.0 preds = model(X) print(" Example Inference (3 exclusive pairs):") for i in range(N): yhat = float(preds['y'][i].item()) print(f"X[{i}]={X[i, :6].int().tolist()}{'...' if feat_dim > 6 else ''} -> ŷ={yhat:.5f}")
Рынок редко формирует полный вектор четких сигналов на каждом ценовом баре. Нередко пересечение Tenkan-Sen и Kijun-Sen уже сформировалось, однако пробой облака еще не произошел. Чтобы лучше смоделировать такую ситуацию, пример инференса поддерживает неполные входные данные: отсутствующим значениям по умолчанию присваивается ноль. Также симуляция исключает одновременную подачу в модель бычьих и медвежьих условий, поскольку заданные признаки индикаторов этого не допускают.
Результаты Тестера стратегий
Как говорится, все проверяется практикой. Строго говоря, для трейдеров это также включает управление торговым счетом, в том числе планирование вывода средств, которое может моделировать MetaTrader. Однако здесь сосредоточимся на прибыльности модели при тестировании в MQL5. β-VAE обучили на двоично закодированных признаках, сформированных по значениям индикаторов EURUSD на 4-часовом таймфрейме с 01.07.2023 по 01.07.2024. Затем модель экспортировали для форвардного тестирования в MetaTrader с 01.07.2024 по 01.07.2025. Результаты форвардного тестирования, с обычными оговорками, оказались лучше, чем в предыдущей статье, где в модели обучения с подкреплением использовались пайплайны.
Здесь необходимо учитывать много факторов: разные подходы (инференс и обучение с подкреплением), разные типы моделей (β-VAE и TD3), а также ограничение тестового периода одним годом. Кроме того, не применялись более сложные методы регуляризации, которые позволили бы сбалансировать целевые данные так, чтобы число бычьих и медвежьих состояний ценового движения было примерно одинаковым. Поэтому во всех отчетах указаны только сделки на покупку. Эти и другие более сложные вопросы здесь подробно не рассматриваются. Однако код MQL5 приложен, а общий подход на Python описан, поэтому трейдеры могут развить эту работу дальше.
Поскольку условия тестирования почти сопоставимы с предыдущей статьей, полезно сравнить результаты модели с двоичными входными признаками и модели, использующей пайплайны. При использовании пайплайнов в стиле SCIKIT-LEARN модель получала непрерывные значения, основанные на расстояниях и разностях между значениями индикаторов Ишимоку и ADX. Хотя результаты обучения выглядели многообещающе благодаря снижению потерь, форвардное тестирование показало обратное. В дальнейшем кривая средств счета также оставалась нестабильной, а соотношение риска и вознаграждения было неблагоприятным. Та же проблема сохраняется и при более удачных результатах с двоичными входными векторами, отчасти потому, что не используется стоп-лосс.
Кроме того, при хаотичных отскоках в трех отдельных тестах паттернов 0, 1 и 5 возникало много ложноположительных сигналов. Ранее эти тесты были независимыми, в отличие от этой статьи, где показания индикаторов объединены во входные данные одной модели VAE. Это еще одно важное отличие от предыдущей статьи.
Подход с двоичным кодированием, напротив, по-видимому, дает более четкие результаты: средства счета стабильно растут, сделок немного, и все они оказались точными по направлению. Это наблюдается в том же периоде тестирования. Хотя годичный период тестирования ограничен и стратегия запускается без корректного управления стопами, сравнение модели с масштабированием через пайплайн и модели с двоичными входными данными указывает на преимущества последнего подхода. Возможно, преимущество обеспечивается за счет устранения шума, связанного с непрерывными значениями с плавающей точкой, при переходе к двоичным данным.
Заключение
В этой статье мы показали, как расширить возможности Мастера за пределы стандартной встроенной библиотеки сигналов с помощью обучения на основе инференса – в данном случае вариационного автокодировщика типа бета, обученного на булевых паттернах индикаторов. Кодируя условия Ишимоку и ADX Уайлдера в двоичные векторы формата "да / нет", удалось снизить шум в рыночных данных, тогда как при использовании пайплайновых преобразований он, по-видимому, лишь усиливался. За ограниченный двухлетний период – год обучения и год тестирования – удалось получить стабильный процесс обучения, более чистые скрытые представления и некоторое улучшение результатов в Тестере стратегий. Прежде чем делать более определенные выводы, необходимо провести тестирование на более длительных периодах и реальных тиковых данных брокера.
| имя | описание |
|---|---|
| 81b.mq5 | Советник, собранный Мастером, в заголовке которого перечислены включенные файлы |
| SignalWZ_81b.mqh | Файл пользовательского класса сигнала, в который встроено обучение на основе инференса |
| 81_.onnx | Модель, экспортированная в формат ONNX. Подключена в качестве ресурса |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/19781
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
От начального до среднего уровня: Очереди, списки и деревья (II)
Автоматизация классических рыночных методов в MQL5 (Часть 3): Стадийный анализ Стэна Вайнштейна
Теория графов: Алгоритм Форда—Фалкерсона для оценки ликвидности и пропускной способности рынка
Автоматизация торговых стратегий на MQL5 (Часть 34): Система пробоя линии тренда с оценкой качества соответствия по коэффициенту R²
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования

