English Deutsch 日本語
preview
Возможности Мастера MQL5, которые вам нужно знать (Часть 81): Использование паттернов Ишимоку и ADX Уайлдера в обучении на основе β-VAE-инференса

Возможности Мастера MQL5, которые вам нужно знать (Часть 81): Использование паттернов Ишимоку и ADX Уайлдера в обучении на основе β-VAE-инференса

MetaTrader 5Интеграция |
81 0
Stephen Njuki
Stephen Njuki

Введение


Почти все трейдеры признают, что рынки движутся в циклах оптимизма и пессимизма. Однако готовых инструментов, которые стабильно выявляют эти циклы и пригодны для прибыльной торговли, крайне мало. В последнее время на мировых рынках преобладают медвежьи настроения: внезапные распродажи и слабые отскоки происходят все чаще. В таких условиях механические стратегии, основанные на запаздывающих индикаторах, неизбежно дают ложные сигналы: волатильность выбивает трейдеров из позиций, которые в более спокойной обстановке могли бы развиться в ожидаемом направлении.

Нехватка готовых решений показывает необходимость кастомизации. Здесь особенно выделяются торговые платформы, оснащенные интегрированной средой разработки (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.

p0


Паттерн 1

Медвежий сигнал: линия Tenkan-Sen пересекает Kijun-Sen сверху вниз и закрывается ниже нее, указывая на краткосрочную смену импульса в сторону снижения. Значение ADX должно быть не ниже 20.

p1


Паттерн 5

Бычий сигнал: цена отскакивает от Tenkan-Sen, а значение ADX не ниже 25.

p5


В основе реализации для этой статьи лежит предположение, что двоичные кодировки образуют компактные векторы, отражающие наличие или отсутствие важных паттернов в каждый момент времени. При обработке в 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. Результаты форвардного тестирования, с обычными оговорками, оказались лучше, чем в предыдущей статье, где в модели обучения с подкреплением использовались пайплайны. 

r015

c015


Здесь необходимо учитывать много факторов: разные подходы (инференс и обучение с подкреплением), разные типы моделей (β-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

Прикрепленные файлы |
81b.mq5 (7.38 KB)
SignalWZ_81b.mqh (13.26 KB)
81_.onnx (3227.92 KB)
От начального до среднего уровня: Очереди, списки и деревья (II) От начального до среднего уровня: Очереди, списки и деревья (II)
Это статья, которую вам, уважаемый читатель, следует внимательно изучить. Это связано с типом материала, который будет в нём изложен. Хотя мы постарались изложить материал максимально просто и познавательно, представленная здесь информация, несомненно, может показаться довольно сложной для тех, кто только начинает изучать программирование. Тем не менее, это не повод для уныния или игнорирования того, что здесь объясняется, так как эта статья установит связь между двумя совершенно разными, хотя и тесно связанными темами.
Автоматизация классических рыночных методов в MQL5 (Часть 3): Стадийный анализ Стэна Вайнштейна Автоматизация классических рыночных методов в MQL5 (Часть 3): Стадийный анализ Стэна Вайнштейна
В статье представлен полноценный торговый советник, построенный на методе стадийного анализа Стэна Вайнштейна. Советник классифицирует рынок по одной из четырёх стадий, анализируя наклон и положение 30-недельной скользящей средней, а также характер движения цены и динамику объёма. Затем он открывает длинные позиции только при прорывах в Стадию 2, а короткие — при пробоях вниз в Стадию 4. В статье объясняется каждая стадия, способы её программного определения и то, почему преимущество метода создаёт именно дисциплина — торговля только на подходящей стадии.
Теория графов: Алгоритм Форда—Фалкерсона для оценки ликвидности и пропускной способности рынка Теория графов: Алгоритм Форда—Фалкерсона для оценки ликвидности и пропускной способности рынка
В статье представлен советник на MQL5, который адаптирует метод максимального потока Форда-Фалкерсона в фильтр ликвидности и пропускной способности. Рыночные структуры — максимумы/минимумы свингов, ценовой дисбаланс (FVG), ордер-блоки и пулы ликвидности — образуют ориентированный граф с пропускной способностью ребер, определяемой объемом, ценовой реакцией, расстоянием и качеством структуры. Максимальный поток позволяет оценить возможности ICT-сделок, отфильтровать слабые пути и использовать динамическое управление размером позиции для последовательного двухэтапного процесса принятия решений.
Автоматизация торговых стратегий на MQL5 (Часть 34): Система пробоя линии тренда с оценкой качества аппроксимации по коэффициенту R² Автоматизация торговых стратегий на MQL5 (Часть 34): Система пробоя линии тренда с оценкой качества аппроксимации по коэффициенту R²
В этой статье мы разработаем на MQL5 систему пробоя линии тренда, которая определяет линии поддержки и сопротивления по свинговым точкам (локальным экстремумам). Надежность линий будет оцениваться по коэффициенту детерминации R², а также по ограничениям на угол наклона. После проверки система сможет автоматически открывать сделки на пробое. Мы будем находить локальные максимумы и минимумы в пределах заданной глубины анализа, строить линии тренда с минимально допустимым количеством касаний и оценивать их надежность по коэффициенту детерминации R² и углу наклона.