English
preview
知っておくべきMQL5ウィザードのテクニック(第81回): β-VAE推論学習で一目均衡表とADX-Wilderのパターンを利用する

知っておくべきMQL5ウィザードのテクニック(第81回): β-VAE推論学習で一目均衡表とADX-Wilderのパターンを利用する

MetaTrader 5統合 |
15 0
Stephen Njuki
Stephen Njuki

はじめに


ほとんどのトレーダーは、市場が楽観と悲観のサイクルで動くことを受け入れていますが、利益を生むのに十分な一貫性でこれらのサイクルを捉えるアウトオブザボックスのツールはほとんど存在しません。近年、世界の市場は弱気傾向を強めており、急激な売りと浅い反発が頻発しています。このような環境では、遅行指標に基づく機械的な戦略は誤シグナルを出しやすく、ボラティリティによって、穏やかな状況下であれば通ったであろう取引が巻き戻されてしまいます。

アウトオブザボックスのソリューションが不足していることは、カスタマイズの必要性を示しています。ここで、IDEを備えた取引プラットフォームの利点が際立ちます。これらのプラットフォームは、機関投資家レベルの実行力とチャート機能を提供するだけでなく、システム構築ウィザードを備えていることが多いです。MetaTraderの例にあるウィザードは、トレーダーが複雑なロジックを一からコーディングせずとも、エキスパートアドバイザー(EA)を迅速に組み立てられるフレームワークとして機能します。ウィザードの真価は、カスタムシグナルクラスを統合できる点にあります。これにより、トレーダーは高度な機械学習技術を自動売買戦略に直接組み込むことが可能です。

現代の機械学習手法の中で、変分オートエンコーダ(VAE)は、高次元かつノイズの多いデータを構造化された潜在表現に圧縮できる能力で注目されています。単純なオートエンコーダとは異なり、β-VAEは隠れ層が入力データを単に記憶するのではなく、意味のある特徴を分離して抽出するよう制御ペナルティを導入します。金融取引においては、テクニカル指標のストリームからパターンの本質を抽出し、ノイズへの耐性を高めることにつながります。

しかし、最も重要なポイントであり本記事のテーマでもありますが、VAEはバイナリ符号化された特徴量に対して特に有効です。連続値やスケーリングされたパイプラインを入力する代わりに、明確でイベント駆動型の条件を定義します。たとえば、一目均衡表の転換線が基準線を上抜けた場合は1、そうでなければ0とします。終値が雲を下抜けた場合も1、そうでなければ0です。さらに、ADXが25以上で強いトレンドを確認できた場合も1、そうでなければ0とします。

今回扱うパターンは、一目均衡表とADXの組み合わせにおけるPattern_0、Pattern_1、Pattern_5です。チャート上では次のように現れることがあります。

Pattern_0

価格が先行スパンAを下抜け、雲の下方へのブレイクアウトを示す弱気シグナルです。このシグナルは、ADXが少なくとも25であることによって裏付けられます。

p0


Pattern_1

転換線が基準線を上から下にクロスし、基準線の下で終値を迎えた場合に発生する弱気シグナルです。これは短期的なモメンタムの変化を示し、売りの方向への動きを示唆します。ADXは少なくとも20である必要があります。

p1


Pattern_5

価格が転換線で反発し、ADXが少なくとも25以上である場合に発生する強気シグナルです。

p5


本記事での実装の前提は、これらのバイナリ符号化が各時点で重要なパターンが存在するか否かを表すコンパクトなベクトルを形成する、というものです。これをVAEに入力することで、正規化された連続値のパイプラインと比べて、はるかにクリーンな信号構造が得られます。ここでのテストでは、この調整だけでも学習プロセスが改善され、テスト時により強いシグナルを生成できることが確認されました。

本記事では、このシステムの実装方法を順を追って解説します。一目均衡表とワイルダーのADXを組み合わせ、バイナリ特徴ベクトルを生成し、過去の市場データでβ-VAEを学習させます。その後、学習済みモデルをMQL5内で使用できるONNX形式にエクスポートします。最後に、戦略テスターの結果を確認し、これらのバイナリ符号化が、先に試した連続値パイプラインと比較してどのようなパフォーマンスを示したかを示します。


MQL5ウィザード

MQL5ウィザードを使うと、トレーダーは事前定義されたブロック(シグナル、マネーマネジメント、決済、トレーリングストップなど)からEAを素早く組み立てることができ、定型コードを手書きする必要はありません。 より高度なトレーダーにとって特に重要な機能のひとつが、ウィザードで作成したEAにカスタムシグナルクラスを挿入できる点です。初心者向けに説明すると、シグナルクラスとは、どの取引を開くべきか、いつ決済すべきかといったロジックを設定する部分です。MetaQuotesは移動平均線(MA)やRSIなどの標準シグナルを提供していますが、カスタムシグナルを使うことでウィザードの機能を大幅に拡張できます。カスタムシグナルクラスを使えば、機械学習モデルや確率予測、今回のようにβ-VAEを、組み込みの標準シグナルパターンに組み込むことが可能です。これにより、単なるアルファの追求にとどまらず、ボラティリティを安定させたポートフォリオ構築も狙えます。

重要なポイントは、カスタムシグナルクラスを挿入できることです。標準のシグナルにはMAやRSIが含まれますが、カスタムクラスでは機械学習モデル、確率予測、あるいはVAEを統合できます。ウィザードは注文管理やポジションサイズを処理し、カスタムロジックが柔軟性を提供することで、誤シグナルのフィルタリングや文脈を考慮した戦略設計を可能にします。


パイプラインとバイナリ入力

前回の記事では、SCIKIT-LEARNスタイルの前処理パイプラインを用いて、モデルの特徴量を学習前や推論前に正規化する方法について検討しました。その考え方は単純で、欠損値補完、スケーリング、変換ステップを体系的に適用し、すべてのインジケーター値を比較可能な範囲に収めるというものです。Min-Maxスケーリング、標準化、ロバストスケーリングなどの手法を組み合わせて、一目均衡表とADX-Wilderのデータをモデルに入力する前に準備しました。しかし、実際に取引に応用すると、このアプローチには二つの弱点があることがわかりました。

第一に、シグナルの明確さが失われる点です。連続値のスケーリングによって、強調したい条件や特定したい事象が平滑化されてしまいました。たとえば、一目均衡表の雲を上抜けするブレイクアウトという明確な真偽イベントが、正規化後には浮動小数点の差分として希釈されてしまいます。要するに、モデルはトレーダーにとって明らかであるはずの、クロスオーバーやブレイクアウトの瞬間を再学習しなければならなくなっていました。

第二に、学習中の安定性が低下しました。出力された連続値の特徴量はノイズを増幅する傾向がありました。市場のランダムな変動は実務上は重要でないにもかかわらず、スケーラーによって同等の重要性を持たされてしまったのです。その結果、学習の収束が遅くなり、ライブ環境での推論精度も低下しました。

この問題を解決するには、視点を変える必要がありました。モデルに差分の大きさを解釈させるのではなく、特定の条件が「成立しているかどうか」をマークするようにしました。これが、インジケーターのパターンを真偽値またはバイナリ入力に変換するという考え方です。たとえば、転換線が基準線を上から下にクロスし、かつADXが20以上であれば、あるパターンに対する強気シグナルとして1を割り当てます。それ以外は0です。同様に、終値が先行スパンAを下抜けし、ADXが25以上であれば、そのパターンに対して1を割り当て、他の条件は0とします。

こうして得られたバイナリエンコーディングは、各プライスバーごとにパターンON/OFF信号のクリーンなベクトルとして表現されます。つまり、従来のパイプラインでスケーリングされた浮動小数点を生成する代わりに、VAEのPythonコードのget-features関数は直接このバイナリベクトルを組み立てます。

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']]

注目すべき点として、過去の記事では各シグナルパターンを個別のモデルとして扱い、パターンを組み合わせなかったのに対し、本記事では3つの研究対象パターンを1つのモデルに統合しています。元の10パターンではなく、この3つだけです。つまり、1つのモデルを開発し、検証する形になります。

この変更の効果はすぐに現れました。VAEは数値スケールのどの部分が意味のあるイベントに対応するかを再学習する必要がなくなり、オン/オフフラグで直接学習できるようになりました。これはトレーダーの直感により近い表現です。バックテストにおいても、この設計変更は安定性向上に寄与し、ポストテストのライブ運用でも有利に働きます。特に、市場環境が大きな株式の調整を伴い、変動が激しくなる場合には、バイナリ表現がノイズと有効な継続シグナルの区別をより正確にモデル化できます。

パイプラインを避け、バイナリイベントコーディングを採用することで、前処理が簡素化されるだけでなく、β-VAEに対して本来意図された形で圧縮・表現可能な入力を提供できるのです。


β変分オートエンコーダ

ブール値で表現された特徴量ベクトルが定義できたら、次にそれをモデルに入力します。Pythonはこのようなバッチ処理を効率的におこなえるため、学習時の処理が非常に高速です。さらに、バックプロパゲーションでテンソルを用いることで、各フォワードパスにおける勾配情報のバッファリングやストレージも提供されます。私たちのVAEモデルは、市場で繰り返し現れる構造をコンパクトに表現する能力を持つよう設計されています。これがβ-VAEの重要な特徴です。

標準的なオートエンコーダは、入力データを隠れ層(潜在空間)に圧縮し、その後元の形に復元します。変分オートエンコーダ(VAE)はさらに進化させ、潜在空間を確率的に扱います。つまり、この隠れ層は固定値ではなく分布としてモデル化されます。エンコーダは学習中に平均値と分散を出力し、この分布からランダムサンプルを選択する「再パラメータ化(reparameterization)」をおこないます。

β-VAEはさらに一歩進み、損失関数中のKLダイバージェンス項にβを掛けます。これにより正則化が強化され、モデルは入力データを単純に記憶するのではなく、意味のある特徴を分離して学習することが促されます。取引においては、この点が特に重要です。潜在空間は、雲のブレイクアウトやトレンド継続などの重要なパターンを効率的に符号化できるようになり、重要でない変動まで等価に扱ってしまうことを避けることができます。

私たちのモデルは以下の主要コンポーネントで構成されています。

  • エンコーダ:ブール入力ベクトルを受け取り、全結合層を通して潜在空間分布の表現を出力します。ここで出力されるのは平均値(z_mean)と分散の対数(z_logvar)です。
  • 再パラメータ化:潜在ガウス分布からzをサンプリングします。zは入力特徴量の「暗黙的な出力」となります。前述の通り、また広く知られている通り、エンコーダは可視層(入力)と隠れ層(潜在層)の間に一対の重み行列を学習し、任意の関連する入力データを「標準的に」エンコードできるように学習します。
  • デコーダ:次に、zから元の入力ブールベクトルの復元を試みます。これにより潜在空間は入力データの核心的構造を捉え、重要なパターンを集中して表現するように強制されます。
  • 潜在 - yヘッド:最後に、潜在変数zから予測値yを出力する教師付きのヘッドがあります。本記事での場合、このyはプライスアクションの予測を示し、強気、弱気、または横ばいのいずれかを表します。

このVAEで用いる損失関数は、3つの損失値を統合しています。まず、再構成損失は、復元出力と元のブール入力特徴量との間でバイナリクロスエントロピーとして計測されます。次に、潜在分布が標準ガウス分布から逸脱することをペナルティ化するKLダイバージェンスがあります。ここでβ倍数が適用されます。最後に、ヘッドの教師付き損失があり、これは予測yの出力と実際のyの出力との間の平均二乗誤差(MSE)として計算されます。

私たちのVAE入力層は特殊で、通常のインジケーター入力特徴量に加え、期待される出力用の次元を追加しています。入力時、この次元には中立値0.5を割り当てます。潜在空間を経て入力に戻った後、y出力は先に提供された6つの入力特徴量に対応する次のプライスアクション(欠落している次の価格変動)を示します。つまり、このモデルは2つのモードで動作し、それぞれ異なる出力を提供します。以下はPythonによるβ-VAE実装コードです。

# ----------------------------- β-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のみを出力します。このyは次のプライスアクションを示し、0.5fを上回る場合は強気、下回る場合は弱気と判定されます。 

3つの損失値を統合することにより、モデルは二値特徴を圧縮しつつ、価格方向の予測能力を維持するように学習されます。学習はAdamオプティマイザを用いて複数エポックにわたり実行されます。今回のモデルでは50エポックで学習をおこないました。各学習ステップでは、まず特徴量がエンコードされ、その際に潜在変数zの平均と分散がサンプリングされます。次にデコーダが元の特徴量を復元し、潜在層からの出力はヘッドに渡されて次の市場状態を予測します。yの入力が与えられていれば損失が計算され、学習として扱われます。そして最後に、バックプロパゲーションによってVAEのみならずヘッドの重みも更新されます。

学習ループはブートストラップ方式で実行され、データセットを効率的に反復処理しつつ、各エポックごとの損失サマリを出力します。この出力により、VAEが圧縮と分離(disentanglement)のバランスをどの程度達成しているか、またヘッドが提供する次の状態に対する予測精度をモニタリングすることができます。


MQL5へのエクスポート

ご存じの通り、Pythonでの学習は旅の半分にすぎません。MQL5以外の言語で開発したモデルを、ウィザードで組み立てたEA内で使用するには橋渡しが必要です。過去の記事でも示した通り、これがONNX (Open Neural Network Exchange)の役割です。この「橋渡し」により、Pythonで学習したモデルをMetaTrader以外のさまざまなプラットフォームでも利用できる標準化された形式で表現できます。MQL5にエクスポートすると、このファイルは数百キロバイトから、私が以前のテストで確認した範囲では最大128MBに達することもありますが、サンドボックスファイルシステム経由やリソースコンパイルを通じて各種ランタイムで読み込むことが可能です。要点としては、PyTorchで高度なモデルを効率的に学習させ、Pythonで検証した後、EAで再実装せずにそのまま利用できるということです。このプロセスは主に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ヘッドを構築します。これにより、事前計算された2つのインジケーターの特徴量を入力として受け取り、余計なデバッグ出力や追加入力なしで単一のy値を出力するためのフォワードパスが可能になります。処理の最初に、PyTorch VAEをevalモードに切り替え、ドロップアウトやバッチ正規化の挙動を固定します。その後、特徴量をエンコードして平均と分散を取得し、先述のリパラメータ化トリックで潜在ベクトルzをサンプリングします。このzをモデルのpredict-from-latentヘッドに通し、最終的に[batch,1]の形でy値を返します。このラッパーは、エクスポートされたグラフに最小限のフォワードパス機能を含めるために、学習済みモデルの周りに実装されます。

グラフ追跡用に[1,6]形状のダミー入力テンソルを1つ作成します。その後、torch.onnx.exportモジュールを呼び出し、明示的な名前付けとバッチ次元に対する動的軸指定をおこなうことで、任意のバッチサイズを受け付けられるモデルにします。opsetパラメータはデフォルトで14に設定し、定数折りたたみも有効化してグラフの解釈を容易にしています。 

Python内でも、エクスポート後にすぐファイルを検証する必要があります。具体的には、ファイルを再インポートし、入力層および出力層のサイズに関するチェックをおこないます。まずonnx.loadモジュールを用いてファイルを読み込み、onnx.checker.check_modelでモデルが有効かどうかを確認し、適切な推論セッションで開けるかを検証します。その後、検出された入力層および出力層のシグネチャを出力します。これらはMetaTraderに取り込む際に重要な情報となります。今回選択したエクスポートモデルは、VAEモデルをほぼ教師あり学習ネットワークのように簡略化しています。しかし前述の通り、このモデルはVAEであり、欠損するy入力を中立値0で自動補完し、入力から潜在層を経て再構築、再び入力に戻す完全なサイクルを実行したうえで、VAEの学習済み重みに基づき欠損値を推論します。

エクスポート後のONNXファイルは、MQL5内でも即座に検証する必要があります。サンドボックス内のONNXファイルを参照する代わりに、これまで通りリソースとしてファイルをインポートする方法を採用しています。しかし、この方法にはエクスポートできるモデルサイズに関する制約があります。以前はリソースファイルの上限が約256MBでしたが、最近ではその半分の128MB程度に減少したようです。まだ完全には確認できていませんが、サンドボックス内に配置される非リソース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}")

市場では、すべてのローソク足において完全に揃ったクリーンなシグナルベクトルが得られることは稀です。多くの場合、転換線と基準線のクロスは明確であっても、雲のブレイクアウトは発生していないことがあります。これをより現実的にシミュレートするために、推論例では部分的な入力をサポートしており、欠損値にはデフォルトとして0を使用します。また、シミュレーションにおいては、買いの条件と売りの条件が同時にモデルに入力されないよう注意が払われています。これは、指標の特徴量の定義上、両方を同時に持つことが不可能だからです。


ストラテジーテスターの結果

よく言われるように「結果がすべて」です。トレーダーの立場では、本来これには出金計画を含む口座管理まで含まれますが、ここではMQL5上でのモデルの収益性に焦点を当てます。この検証では、EUR/USDの4時間足を対象に、2023.07.01〜2024.07.01の期間に取得した指標データをブール値でエンコードし、β-VAEを学習させました。その後、このモデルを2024.07.01〜2025.07.01の期間でフォワードウォークテストとしてMetaTrader上で評価しています。一般的な注意事項は前提としつつも、結果は前回記事でパイプラインを用いた強化学習モデルを使用した場合より良好でした。 

r015

c015


この検証には複数の要因が関係しています。学習手法の違い(推論学習と強化学習)、モデル構造の違い(β-VAEとTD3)、そしてテスト期間が1年に限定されている点などです。また、売りと買いのプライスアクション数を均等化するための高度な正則化手法は導入していません。その結果、レポート上では買いのみが表示されています。これらの点は本稿では詳細に掘り下げていませんが、MQL5のコードは添付しており、Python側の設計思想も示しているため、読者自身で発展させることが可能です。

とはいえ、前回記事とほぼ同一条件の検証環境であるため、バイナリ特徴量入力モデルとパイプライン入力モデルの比較は有益です。SCIKit-Learn風のパイプラインを用いていた場合、一目均衡表とADXの差分や距離といった連続値がモデル入力となっていました。学習段階では損失は順調に低下しましたが、フォワードウォークでは期待とは逆の結果となり、エクイティカーブも不安定で、リスクリワードレシオの低い挙動を示しました。この問題は、今回のバイナリ入力モデルにおいても完全に解消されているわけではありません。というのも、本検証ではストップロスを使用していないためです。

また、過去の検証ではPattern_0、Pattern_1、Pattern_5を個別にテストしており、レンジ的な戻り局面では多くの誤検出が発生していました。本記事ではそれとは異なり、これら3つのパターンに対応するインジケーターの判定結果を単一のVAEモデルへの入力として統合しています。この点も、前回の記事との重要な相違点の一つです。

その結果、バイナリエンコーディングによるアプローチでは、トレード回数は少ないものの、精度の高いエントリーが維持され、エクイティは一貫して上昇する傾向を示しました。同一の1年間テストで、ストップ管理なしという条件下ではありますが、連続値をスケーリングするパイプライン型モデルと比較すると、ブール値入力の有効性が明確に表れています。これは、はい/いいえ形式の入力によって連続値に含まれるノイズを排除できたことが、結果としてモデル性能の向上につながった可能性を示唆しています。


結論

本記事では、これまでと同様に、ウィザードが標準で提供するシグナルライブラリを超えて拡張可能であることを示しました。今回は推論学習を組み込み、その具体例として、ブール値でエンコードしたインジケーターパターンを学習するβ変分オートエンコーダを使用しています。一目均衡表およびWilderのADX条件をはい/いいえのバイナリベクトルとして表現することで、パイプラインTransformerを用いた場合に見られたノイズの増幅とは異なり、市場データに含まれるノイズを抑制できたと考えられます。学習期間1年、テスト期間1年という合計2年間の限定的な検証ではありますが、学習プロセスは安定しており、潜在空間の表現もより明瞭になり、ストラテジーテスターにおいても一定の性能改善が確認できました。とはいえ、より長期間の検証や、ブローカー提供のリアルティックデータを用いたテストをおこなうことが、より確度の高い結論を導くためには不可欠です。

名前 説明
81b.mq5 ヘッダに参照ファイルを示すウィザード組み立てEA
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)
EAのサンプル EAのサンプル
一般的なMACDを使ったEAを例として、MQL4開発の原則を紹介します。
MQL5でのAI搭載取引システムの構築(第3回):スクロール対応の単一スレッド型チャットUIへのアップグレード MQL5でのAI搭載取引システムの構築(第3回):スクロール対応の単一スレッド型チャットUIへのアップグレード
本記事では、MQL5で構築したChatGPT統合プログラムを、タイムスタンプ付きの会話履歴管理と動的スクロール機構を備えた、単一スレッド型チャット指向のUIへとアップグレードします。本システムはJSON解析を用いてマルチターンのメッセージを管理し、スクロールバー表示モードの切り替えやホバーエフェクトをサポートすることで、実装面と操作性の両面からユーザー体験を向上させます。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
プライスアクション分析ツールキットの開発(第43回):ローソク足の確率とブレイクアウト プライスアクション分析ツールキットの開発(第43回):ローソク足の確率とブレイクアウト
MQL5ネイティブで開発されたCandlestick Probability EAは、ローソク足データをリアルタイムかつ銘柄別の確率情報へと変換する、軽量で実用的な分析ツールです。本EAは、バー確定時にピンバー、包み足、および十字線といったパターンを分類し、ATRを考慮したフィルタリングや、任意でブレイクアウト確認をおこないます。さらに、各パターンについて、純粋なフォロー率および出来高加重フォロー率を算出することで、特定の銘柄や時間足における典型的な結果を把握することが可能です。チャート上のマーカー、コンパクトなダッシュボード、インタラクティブな切り替え機能により、検証作業や分析対象への集中を容易にします。また、詳細なCSVログをエクスポートできるため、オフラインでの検証や追加分析にも対応しています。本EAを活用することで、確率プロフィールの構築、戦略の最適化をおこない、ローソク足パターン認識を定量的な優位性へと変換できます。