
知っておくべきMQL5ウィザードのテクニック(第57回):移動平均とストキャスティクスを用いた教師あり学習
はじめに
MQLウィザードで組み立てられたエキスパートアドバイザー(EA)で実装できるシンプルなパターンの検討を続けます。この取り組みの主な目的は、常にアイデアの試運転や検証をおこなうことにあります。最終的な運用や実口座での使用には、長期間のテストを経たうえで手動で組み立てたEAを使うことができますが、ウィザードで構築したEAを使えば、初期のコードをほとんど書かずに素早くテストを実施できます。
現在、機械学習は大きな注目を集めており、本連載のこれまでの記事でもその特定の要素について取り上げてきました。本記事および今後の記事でも、こうした技術的な内容を引き続き扱いますが、今回はより広く知られた、定番のインジケーターパターンに焦点を当てます。
さらに、機械学習の文脈において、本連載では3つの主要な学習形式をそれぞれ個別の記事で取り上げる予定です。最初は「教師あり学習」を扱い、インジケーターパターンとしては、トレンド系指標とモメンタム系指標の組み合わせを取り上げます。今回は、移動平均とストキャスティクスを使用します。
教師あり学習では、各パターンを個別のニューラルネットワークに実装します。最近の記事でも述べたとおり、これらはMQL5よりもPythonで実装・学習させたほうが遥かに効率的です。Pythonを使えば、学習後に交差検証を簡単におこなうことができるため、各パターンについてこの検証を実施します。
Pythonでの交差検証は、テスト実行時の損失値を最後の学習エポックの損失値と比較することでおこないますが、これだけではネットワークの現在の重みやバイアスに関する検証としては不十分な場合があります。
そのため、MetaTrader 5のストラテジーテスターを使って、エクスポートしたONNX形式のネットワークによるウォークフォワードを実施します。今回の検証では、MetaTrader 5からPythonに送信される価格データ(x, yデータセット)はEUR/JPYの2023年分のものであり、これを用いて学習をおこないます。そしてウォークフォワードテストは、同じ通貨ペア EUR/JPYの2024 年分のデータを使用しておこないます。分析対象の時間枠は日足です。
移動平均(MA)とストキャスティクスを組み合わせることで、さまざまな売買シグナルが生成可能です。今回の検証では、これらの指標を組み合わせた際に利用できる代表的なシグナルパターンのうち、上位10種類のみを対象とします。
機械学習の種類とアプローチ
機械学習のパイプラインは、教師あり学習(Supervision)、強化学習(Reinforcement)、および推論(Inference)の3つの相互に関連するフェーズとして捉えることができます。それぞれの定義を振り返ってみましょう。
教師あり学習(別名:トレーニングフェーズ)とは、ラベル付きデータに基づいてモデルが過去のパターンを学習する段階です。ここでいうラベル付きデータとは、通常「x」とラベルされる入力(独立)データと、「y」とラベルされる予測対象の(従属)データから構成されます。
一方、強化学習(使用中の学習および最適化フェーズとも見なされます)は、モデルが環境との相互作用を通じて自らを洗練させ、長期的な報酬を最大化するように行動を最適化していく段階です。これは、ウォークフォワード中のバックプロパゲーションのようなものと捉えることもできます。
そして最後に、推論学習(別名:教師なし学習フェーズ)では、モデルが過去の学習結果をもとに一般化をおこない、新しいデータや問題にそれを適用していきます。
ここでは例示として、これら3つのフェーズが異なる問題領域でどのように結びつくかを見ていきます。具体的には、気象情報システムと金融時系列予測のケースを取り上げます。
教師あり学習:
教師あり学習の目的は、ラベル付きデータから予測モデルを構築することです。このプロセスではまず、過去のデータを収集し、前処理をおこないます。その後に特徴量抽出がおこなわれます。これは、過去データをニューラルネットワークや他の学習モデルに入力できるような範囲や形式に正規化する処理と捉えることができます。この処理の後、ラベル付きデータを用いてモデルをLSTM、XGBoost、あるいはTransformerで訓練します。ここでの「ラベル付け」とは、予測に使用される既知のデータ(独立変数、いわゆる前提データ)と、予測対象となるデータ(従属変数)に、それぞれ識別可能なラベルを付けることを意味します。この段階での初期目標は損失の最小化であり、予測値と実際の値の差をできるだけ小さくすることです。これは一般的に勾配降下法を用いて実現されます。
教師あり学習では交差検証がおこなわれることがあり、その結果によってモデルを本格的に運用するかどうかの判断材料となります。ただし、本記事で扱う3段階のステップにおいては、教師あり学習の最終段階は「モデル選定とハイパーパラメータの調整」となります。ここでは、最も効果的だった学習率やトレーニングのバッチサイズなどが選ばれます。さらに、活性化関数の種類やレイヤーのサイズといったネットワーク構造も、必要に応じてこの段階で最適化されます。
これを例として説明すると、気象予測システムの場合、入力データは過去の気温、湿度、気圧などであり、モデルとしてはランダムフォレストやCNN(畳み込みニューラルネットワーク)ベースの手法が使われる可能性があります。こうした手法により、将来の気温の傾向を予測するシステムを構築することが可能になります。 一方、金融時系列の予測においては、入力データは過去の価格データであり、これに特徴量エンジニアリングを施してインジケーター値を生成します。モデルにはLSTMが用いられ、損失関数にはMSE(平均二乗誤差)が使われます。このような手法により、過去の観測値を基に次の価格を予測する学習済みモデルを構築できます。
強化学習:
教師あり学習において、主にラベル付きの過去データで学習がおこなわれた後に問われるのは、そのモデルがライブデータや将来のデータに対しても適切に機能するか、つまり、学習時とは異なる将来の条件に適応できるかということです。これに答えるために強化学習が導入されます。静的なモデルは、動的な環境下ではうまく機能しないことが多く、強化学習はそうした状況における意思決定の最適化を支援します。
したがって、強化学習の目的は、フィードバックに基づいた意思決定を最適化することで予測の精度を向上させることにあります。言い換えると、たとえば価格変動を予測するニューラルネットワークがあるとすれば、次のステップとしては、これらの価格変動を「状態」として扱い、それに基づいた「方策ネットワーク」や「価値ネットワーク」を構築していくことになります。これにより、価格変動とトレーダーの行動を切り分け、「状態」と「行動」という枠組みを導入することができます。
この説明は金融時系列予測に基づいていますが、気象予測に当てはめると、状態と行動はそれぞれ降水量の予測値や主要作物の播種量などに置き換えることができます。金融予測においては、モデルの予測に基づいて取った行動の結果として得られる利益/損失が、より具体的で望まれる成果となります。気象予測の場合であれば、それは作物の収量になります。
強化学習のプロセスは、以前にも述べたように、利益や損失をより正確に見積もるための「Criticネットワーク(価値ネットワーク)」の訓練および更新に加え、方策ネットワークの重みやバイアスを微調整するためのデルタ(変化量)も提供します。
強化学習のプロセスをまとめると、まずエージェントと環境の設定から始まり、ここで状態、行動、報酬が定義されます。前述のように、これらは教師あり学習フェーズで訓練されたモデルから流れ出す、あるいは決定されるものです(上図のようなイラストで示される通りです)。その後、方策の学習に進みます。ここでは、Actorネットワークまたはそれに相当するアルゴリズムが最適化され、状態から適切な行動へのマッピングが学習されます。また、報酬に基づいた最適化の仕組みも存在し、これは価値関数アルゴリズムやCriticネットワークの形をとって、長期的な利益に基づいて予測を調整します。そして最後に、探索(新しい知識を得るためにさまざまな方策設定を試すこと)と活用(過去にうまくいった方法を継続すること)をバランスよくおこなうエージェントが構成されます。
天気予報や金融予測を例に、教師あり学習フェーズと強化学習の関係を示すとわかりやすくなります。天気予報における教師あり学習フェーズでは、初期モデルが気温や湿度などを入力として、降水量を予測します。私たちが求めるのはこの予測された降水量です。農作物の植え付けをおこなうかどうかはこの降水量に大きく依存するため、強化学習ではこの予測に対する解釈の判断層が導入されます。つまり、降水量の予測が「状態」として扱われるのです。これらの状態が、地域や時間帯によって異なる降水を示す場合、それを「植えるかどうか」という意思決定において最適化し、報酬(この場合は収穫量)を最大化する方向に学習します。このようにして、現実の天候変化に応じて自らを改善していく予測モデルが得られるわけです。教師あり学習が学校教育に例えられるとすれば、強化学習は現場での実践的訓練にあたります。
金融予測の場合、監督フェーズのモデルによって価格変動が予測され、これが「状態」として扱われます。これらの変動は、複数の時間枠を含んでいれば、多次元ベクトルとしても表現され得ます。「行動」はトレーダーやEAが取るもので、買う・売る・保持(中立)などがあります。そして、各状態(価格変化)に対して取られた行動に基づいて、Criticネットワークやアルゴリズムによって状態と行動を利益にマッピングします。その後、方策(状態と行動の対応関係)を、探索と活用のバランスを取りながら徐々に更新していき、教師あり学習フェーズで出力された予測(状態と見なされる)をよりよく解釈できるようにします。
ただし、この「現場での訓練」は、実際には非常にゆっくりとしか進みません。なぜなら、ライブデータは非常にゆっくりとしか得られないからです。たとえば、教師あり学習で過去10年分のデータを使った場合、強化学習で1年分の学習をしようとすると、それも実際に1年かかることになります。これにより、シミュレーションや、より高度な環境クラスが必要になるのです。
過去のデータを使ってライブデータの状況をシミュレーションすれば、強化学習のスピードを上げるだけでなく、より多くのデータをカバーすることができ、結果としてより頑健なモデルが得られます。
金融時系列予測においては、これはつまり、教師あり学習フェーズで使ったのと同じデータを用いて試験的な学習をおこない、状態(価格変化予測)に対して取られた行動に基づいて、利益を最大化するように強化学習ネットワークを最適化するということです。
推論:
教師あり学習で基礎的な予測をおこなうモデルと、強化学習でその予測の解釈と応用を改善するモデルの2つが揃ったところで、次に進むのが第3のフェーズ、すなわち「推論」です。この段階では、本質的にこれまでに得た知識とモデルを別の分野、あるいは私たちのケースで言えば別の取引銘柄に適用していきます。
これは、いわゆる「未知のデータに対する汎化」とも呼ばれるプロセスであり、学習済みの知識を現実の環境に応用しながら、時間とともにさらに洗練させ、ラベルのないデータの中にある未発見のパターンを検出していくことを目的とします。このフェーズでは、グラフ構造やオートエンコーダー、クラスタリングといった技術が非常に重要な役割を果たします。
この3つのフェーズ(教師あり学習 → 強化学習 → 推論)において、それぞれ次の段階へ進むにつれて計算負荷は減少していきます。これが、段階を分けて学習を進めることの本質的な意義でもあります。本連載ではこれまで、主に教師なし学習を用いた手法を取り上げてきましたが、近いうちに強化学習からの「引き継ぎ」として、教師なし学習がどのように活用できるかという観点から再検討していく予定です。強化学習は単独でも予測に使えるものですが、ここで示した段階的アプローチによって、他の学習モードと組み合わせることでより効率的になる可能性があることを強調しています。
移動平均(MA)とストキャスティクスを組み合わせることで、さまざまな売買シグナルが生成可能です。この2つのインジケーターの組み合わせから得られる代表的な10種類のシグナルパターンについても、トレーダーが実用的に利用できる形で検証します。
移動平均クロスオーバー + ストキャスティクスの買われすぎ・売られすぎ判定(Pattern-0)
トレンドフォロー系のインジケーターである移動平均(MA)と、モメンタム系のオシレーターであるストキャスティクスの組み合わせから成るこのパターンは、高い確率で有効なエントリーシグナルを提供する傾向があります。買いシグナルは、短期移動平均が長期移動平均を下から上へ突き抜ける「ゴールデンクロス」が発生し、上昇トレンドへの転換が示唆される場面で、同時にストキャスティクスが20以下にあることが条件となります。これは通常、資産が売られすぎで、価格が反転しやすいタイミングを意味します。一方で、売りシグナルは、短期移動平均が長期移動平均を上から下へ突き抜ける「デッドクロス」となります。これは、短期の移動平均線が長期の移動平均線を上から下へ突き抜ける現象であり、単体でも下落トレンドの可能性を示すサインです。この際、ストキャスティクスが80を超えていれば、買われすぎの状態にあると判断され、強い売りシグナルとなります。
インジケーターの値をネットワークの特徴量として利用するように、シグナルクラスのフォーマットを少し変更したことで、現在では以下のように単独パターンの関数を実装しています。
//+------------------------------------------------------------------+ //| Check for Pattern. | //+------------------------------------------------------------------+ double CSignal_MA_STO::IsPattern(int Index, ENUM_POSITION_TYPE T) { vectorf _x = Get(Index, m_time.GetData(X()), m_close, m_ma, m_ma_lag, m_sto); vectorf _y(1); _y.Fill(0.0); ResetLastError(); if(!OnnxRun(m_handles[Index], ONNX_NO_CONVERSION, _x, _y)) { printf(__FUNCSIG__ + " failed to get y forecast, err: %i", GetLastError()); return(double(_y[0])); } //printf(__FUNCSIG__+" y: "+DoubleToString(_y[0],2)); if(T == POSITION_TYPE_BUY && _y[0] > 0.5f) { _y[0] = 2.0f * (_y[0] - 0.5f); } else if(T == POSITION_TYPE_SELL && _y[0] < 0.5f) { _y[0] = 2.0f * (0.5f - _y[0]); } return(double(_y[0])); }
私たちは、移動平均とストキャスティクスの値を取得する関数を含んだファイル「57_X.mqh」をインクルードしています。これらの値は、ネットワークへの入力データとして使用されます。現在、最大で10種類のパターンを検討しており、それぞれに対して個別のネットワークを使用する予定です。したがって、このインクルードファイル内の関数「Get」も、各ネットワークに対応する最大10種類のデータセットを返すようになっています。
このGet関数は、vectorf(浮動小数点ベクトル)を返します。これはONNX形式のネットワークに簡単に入力できる形式であり、非常に扱いやすくなっています。この関数は、PythonでMetaTrader 5のインポートライブラリを使う場合の代替手段としても機能します。PythonのMetaTrader 5ライブラリを使う際には、通常、価格データをネットワークが受け取れるように正規化する必要がありますが、この関数はその処理をあらかじめ含んでいるため、その手間を省くことができます。Pythonでのネットワークは簡単で、次のように実装できます。
class SimpleNeuralNetwork(nn.Module): def __init__(self): super(SimpleNeuralNetwork, self).__init__() self.fc1 = nn.Linear(feature_size, 256) # Input layer to hidden layer 1 self.fc2 = nn.Linear(256, 256) # Hidden layer 1 to hidden layer 2 self.fc3 = nn.Linear(256, state_size) # Hidden layer 2 to output layer self.sigmoid = nn.Sigmoid() def forward(self, x): x = self.sigmoid(self.fc1(x)) # Activation for hidden layer 1 x = self.sigmoid(self.fc2(x)) # Activation for hidden layer 2 x = self.fc3(x) # Output layer (no activation) return x
パラメータ「feature_size」と「state_size」は、それぞれネットワークの入力サイズと出力サイズを表します。出力については、すべての10パターンで共通とし、0.0〜1.0の範囲の値をターゲットとします。この出力値に基づく解釈としては、0.5未満であれば価格が下落すると予測される、0.5以上1.0未満であれば価格が上昇すると予測される、そしてちょうど0.5であれば変化なしとみなされます。
ただし、特徴量の設定においては、入力のベクトルサイズが一定とは限りません。というのも、各パターンごとに条件の数が異なるためです。今回の最初のパターンでは、条件は4つあります。そのため、以下のように、Get関数の中で入力ベクトルをネットワークに割り当てることにします。
if(Index == 0) { if(CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(M_LAG.Handle(), 0, T, 2, _ma_lag) >= 2 && CopyBuffer(S.Handle(), 0, T, 1, _sto_k) >= 1) { _v[0] = ((_ma_lag[1] > _ma[1] && _ma_lag[0] < _ma[0]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[0] <= 20.0) ? 1.0f : 0.0f); _v[2] = ((_sto_k[0] >= 80.0) ? 1.0f : 0.0f); _v[3] = ((_ma_lag[1] < _ma[1] && _ma_lag[0] > _ma[0]) ? 1.0f : 0.0f); } }
この内容を分解して説明すると、上昇パターンの条件は2つで、それぞれのインジケーターに1つずつ対応しています。下降パターンについても同様です。入力ベクトルを設定する際は、それぞれの条件が満たされているかどうかを単純にチェックします。これらの条件は互いに反転関係(ミラー)またはブール的性質を持つため、同時に満たされる最大数は2つとなります。
それでも、すべての条件をベクトルに含めるのは、ネットワークにとって有用な情報となり得るからです。また、パターン0におけるこの4つの条件は、実際には6つに分割することも可能です。というのも、1つ目と4つ目の条件はそれぞれ2つの引数を取るためです。この記事の最後にソースコードを添付していますので、読者はこの点について自由に実験してみることができます。
Pythonでのトレーニングおよびテストの実行では、両者の損失値が以下のように記録されました。
Epoch 10/10, Train Loss: 0.2498
Test Loss: 0.2593
検証時の損失値は、第1エポック(具体的な数値は記載されていません)の初期損失よりは低いものの、第10エポックの損失値よりは高い状態です。トレーニングおよび検証に使用した価格データは、すべて2023年のデータです。そのため、2023年のデータを用いてPattern-0における適切なオープン/クローズ/パターンの閾値を最適化した後、2024年のデータを用いたウォークフォワードテストを実施しました。これにより、次のレポートが得られます。
ロングトレードのみに限定されてはいますが、ウォークフォワードは成功しています。ただし、ショートトレードも含めたウォークが可能かどうかを確認するには、より大規模なデータセットでのトレーニングが必要です。また、今回のウォークフォワードでは、2023年の80%の期間に基づいて学習された重みを使用しており、2023年全体を使ったわけではありません。それにもかかわらず、得られた重みは2024年全体にわたって適用されています。
MAクロス + ストキャスティクスによるトレンド確認(pattern-1)
次のパターンでは、移動平均線(MA)に対する価格のクロス判定と、ストキャスティクスの方向性を用いてロングおよびショートの条件を定義します。買いシグナルは、価格が移動平均線を上抜けし、かつストキャスティクスの%Kが%Dを上抜けする、または既に上にあるときに発生します。売りシグナルはその逆で、価格が移動平均線を下抜けし、%Kが%Dを下回っている場合です。
価格データをインポートし、ネットワークへの入力形式に整形した後の入力ベクトルは、以下に示すように、Get関数の出力と同様の構造になります。
else if(Index == 1) { if(C.GetData(T, 2, _c) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2 && CopyBuffer(S.Handle(), 1, T, 2, _sto_d) >= 2) { _v[0] = ((_c[1] < _ma[1] && _c[0] > _ma[0]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[1] < _sto_d[1] && _sto_k[0] > _sto_d[0]) ? 1.0f : 0.0f); _v[2] = ((_sto_k[1] > _sto_d[1] && _sto_k[0] < _sto_d[0]) ? 1.0f : 0.0f); _v[3] = ((_c[1] > _ma[1] && _c[0] < _ma[0]) ? 1.0f : 0.0f); } }
現在はサイズ4の入力ベクトルを使用していますが、前述のとおり、一部の条件は2つ以上の引数を取るため、実際には条件数より多くの情報が存在します。このケースでは、4つのすべての条件がそれぞれ2つの引数を取るため、入力ベクトルをサイズ8として構成することも可能です。
ネットワークのトレーニングは、以下のTrain関数によって実行されます。
# Train function def Train(model, train_loader, optimizer, loss_fn, epochs): device = T.device('cuda:0' if T.cuda.is_available() else 'cpu') model.to(device) for epoch in range(epochs): model.train() train_loss = 0.0 for batch_idx, (data, target) in enumerate(tqdm(train_loader, desc=f"Epoch {epoch + 1}/{epochs} (Train)")): # Step 1: Ensure proper tensor dimensions data = data.view(-1, 1, feature_size) # Step 2: Verify dimensions expected_shape = [feature_size] actual_shape = list(data.shape[2:]) if actual_shape != (expected_shape): raise RuntimeError(f"Invalid spatial dimensions after reshaping. Got {data.shape[2:]}, expected {[x_data.shape[1]]}") # Step 3: Move to device and forward pass data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) target = target.view(-1, 1, state_size) loss = loss_fn(output, target) # Step 4: Backpropagation loss.backward() optimizer.step() train_loss += loss.item() train_loss /= len(train_loader) print(f"Epoch {epoch + 1}/{epochs}, Train Loss: {train_loss:.4f}")
2023年を通じたTrain関数の実行結果は、以下の損失関数のログとして示されます。
Epoch 10/10, Train Loss: 0.2491
Test Loss: 0.2592
2024年のウォークフォワードテストでは、以下のようなレポート結果が得られました。
有望なウォークフォワード結果が得られましたが、ロングトレードに比べてショートトレードがやや少なめという課題があります。さらなるトレーニングでこの問題は改善できるでしょう。このパターンは、価格がMAをクロスすることをターゲットにしているため、トレンドの早期変化を捉えやすい特徴があります。Pattern-0のような2本のMAクロスよりも遅延が少ないはずです。また、ストキャスティクスを確認指標として用いることで、多くの偽のブレイクアウトを除外し、早すぎるエントリーを避ける効果もあります。
一方で、このパターンには弱点や制約も存在します。まず、特に期間の長いMAは遅延が大きいため、エントリータイミングが遅れやすい傾向があります。デフォルトでは日足の期間8を使用していますが、これはカスタムシグナルクラスで最適化可能なパラメータなので、ご自身で最適な値に調整してください。次に、ストキャスティクスは特にレンジ相場で不安定になることが知られており、偽の確認シグナルが多発しやすいという問題があります。また、強いトレンドがすでに形成されている場合、価格がMAをクロスするのを待っていると、早期のエントリー機会を逃しやすくなります。
インジケーター期間の最適化については、たとえばサブ20期間の短期MAは、50~200期間の長期MAより価格変動に対してより素早く反応します。したがって、ユーザーは自分が使う時間枠を考慮し、「素早い反応」を重視するのか、「安定性や確実性」を重視するのかを判断する必要があります。
ストキャスティクスについても同様で、(5,3,3)はより速い設定ですが、その分シグナルがノイジーになりやすいです。逆に(14,5,5)のような遅めの設定はより適切と考えられますが、これも使用する時間枠と照らし合わせて考慮すべきです。今回のテストで用いたカスタムシグナルクラスでは、移動平均線とストキャスティクスに標準的な期間設定を使用しています。 具体的には、移動平均の期間をNとすると、ストキャスティクスは(N,3,3)、遅行MAは2×Nの期間としています。
さらに、出来高の急増を使ってMAクロスやブレイクアウトの強さを確認することも可能です。サポートやレジスタンスのレベルを加味することで、パターンの信頼性をより高めることもできます。
移動平均の傾き+ストキャスティクスによるトレンド確認(Pattern-2)
このパターンは、移動平均線(MA)の傾きを使ってトレンドの方向を捉え、ストキャスティクスでモメンタムを確認します。買いシグナルは、移動平均線の傾きが上向きで、かつストキャスティクスが50を上回り上昇している場合に発生します。売りシグナルはその逆で、移動平均線の傾きが下向き、かつストキャスティクスが50を下回り下降している場合です。このような条件をチェックする入力ベクトルは、以下のようにGet関数で取得されます。
else if(Index == 2) { if(C.GetData(T, 2, _c) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2) { _v[0] = ((_ma[1] < _ma[0]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[1] < _sto_k[0] && _sto_k[1] >= 50.0) ? 1.0f : 0.0f); _v[2] = ((_sto_k[1] > _sto_k[0] && _sto_k[1] <= 50.0) ? 1.0f : 0.0f); _v[3] = ((_ma[1] > _ma[0]) ? 1.0f : 0.0f); } }
これは10個のパターンの中で初めて、2番目のストキャスティクスバッファ、つまり%Kを使用するパターンです。%Kは常にメインのバッファ(%D)より遅れて動くため、この%Kを使うことでオシレーターの傾きを確認するのに役立ちます。このデータのベクトルを、2023年のEUR/JPYの期間にわたってPythonにエクスポートした後、先に示したシンプルなネットワークを先述のTrain関数でトレーニングします。テストや交差検証は、Train関数と非常によく似た関数でおこない、こちらはTest関数と名付けています。そのPythonコードは以下の通りです。
# Test function def Test(model, test_loader, optimizer, loss_fn, epochs): device = T.device('cuda:0' if T.cuda.is_available() else 'cpu') model.to(device) with T.no_grad(): test_loss = 0.0 for batch_idx, (data, target) in enumerate(tqdm(test_loader, desc=f"Loss at {test_loss} in (Test)")): # Step 1: Ensure proper tensor dimensions data = data.view(-1, 1, feature_size) ... # Step 3: Move to device and forward pass data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) target = target.view(-1, 1, 1) #print(f"target: {target.shape}, plus output: {output.shape}") loss = loss_fn(output, target) test_loss += loss.item() test_loss /= len(test_loader) print(f"Test Loss: {test_loss:.4f}")
トレーニングおよびテストの損失スコアは、パターン0および1で示した内容と概ね一致しているため、ここでは割愛します。2024年のウォークフォワードテストをMetaTraderのストラテジーテスターで実施した結果、以下のレポートが得られました。
このパターンは、ロングポジションのみが開かれるため、片寄ったウォークフォワード結果となっています。より大規模なデータセットでのトレーニングにより、この問題は解消されるでしょう。Pattern-2は、レンジ相場のフィルターとしては非常に優れています。移動平均線の傾きにより、明確なトレンドがある場合にのみ取引がおこなわれるためです。トレンドの強さはストキャスティクスによって確認され、50の閾値を基準にした相対的な位置で現在のトレンドのモメンタムが検証されます。さらに、上記のパターンのようにMAクロスを待つ必要がないため、遅延が比較的少なくなり、トレンドの加速を捉えやすいというメリットもあります。
一方で、主な弱点としては、トレンド反転に対する反応が遅いことが挙げられます。移動平均線の傾きはトレンド反転後に変化するまでに時間がかかるためです。また、ストキャスティクスもこのような状況では遅いシグナルを出しやすく、既にトレンドが発生している場合にストキャスティクスの確認を待つと、早期のエントリー機会を逃すことがあります。総じて、このパターンはトレンドに強く依存しているため、横這い(レンジ)相場には不向きです。
このパターンにADX(平均方向性指数)などの追加フィルターを組み合わせることで、強いトレンドの確認が可能になります。また、プライスアクションのブレイクアウトやプルバックのチェックを加えることで、精度の向上が期待できます。実際の活用例としては、今回テストしているFX市場のほか、小さな調整の後の継続トレンドを捉えやすい株式市場、さらには金やビットコインのような資産におけるモメンタムを捉えるのに有効な仮想通貨やコモディティ市場などが挙げられます。
移動平均の反発+ストキャスティクスのダイバージェンス(Pattern-3)
このパターンは、移動平均線が動的なサポート・レジスタンスとして機能する「価格の反発」と、ストキャスティクスによるモメンタムのダイバージェンスを組み合わせて、
反転の兆候を事前に捉えることを目的としています。買いシグナルは、上向きに傾いた移動平均線(この場合はサポートとして機能)で価格が反発し、かつストキャスティクスが強気ダイバージェンスを示している時に発生します。具体的には、価格がより低い安値(安値更新)をつける一方で、オシレーターはより高い安値(安値切り上げ)を形成している状態です。
売りシグナルは、下向きに傾いた移動平均線(レジスタンスとして機能)で価格が反発して押し戻され、ストキャスティクスが弱気ダイバージェンスを示している場合です。価格はより高い高値(高値更新)をつけるものの、ストキャスティクスはより低い高値(高値切り下げ)を形成しています。このパターンのMQL5での実装および、パターン処理用ネットワークへの入力定義は以下のようにおこなわれます。
else if(Index == 3) { _v.Init(6); _v.Fill(0.0); if(C.GetData(T, 3, _c) >= 3 && CopyBuffer(M.Handle(), 0, T, 3, _ma) >= 3 && CopyBuffer(S.Handle(), 0, T, 3, _sto_k) >= 3) { _v[0] = ((_c[2] > _c[1] && _c[1] < _c[0] && _c[0] < _c[2] && _ma[1] >= _c[1]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[2] > _sto_k[1] && _sto_k[1] < _sto_k[0] && _sto_k[1] >= 40.0) ? 1.0f : 0.0f); _v[2] = ((_ma[2] > _ma[0]) ? 1.0f : 0.0f); _v[3] = ((_ma[2] < _ma[0]) ? 1.0f : 0.0f); _v[4] = ((_sto_k[2] < _sto_k[1] && _sto_k[1] > _sto_k[0] && _sto_k[1] <= 60.0) ? 1.0f : 0.0f); _v[5] = ((_c[2] < _c[1] && _c[1] > _c[0] && _c[0] > _c[2] && _ma[1] <= _c[1]) ? 1.0f : 0.0f); } }
トレーニングおよびテスト検証損失関数のスコアは、最初の2つのパターンで共有したログと大きな差はありません。テストの損失関数は、トレーニングの第1エポック時の損失よりは低いものの、第10エポック時の損失よりは高い状態です。2023年のデータでトレーニングをおこなった後、2024年のウォークフォワードテストのレポートは以下の通りです。
ポジティブなウォークフォワード結果は、ロングとショートのポジションがバランスよく開かれている場合に限り良いサインとなりますが、今回のケースはそうではありません。しかし、Pattern-3は早期の反転検知に優れており、ストキャスティクスのダイバージェンスが価格の動きに先駆けてトレンドの反転を知らせることが多いです。また、トレンドの追随を避け、遅いエントリーになる代わりに動的なサポート・レジスタンス付近でのエントリーを可能にします。さらに、トレンド相場でも有効であり、移動平均線の反発がトレンドの継続を示す一方で、ダイバージェンスが勢いの衰えを警告します。
一方で、強いトレンド相場では偽シグナルが出やすいという弱点があります。これは、こうした状況ではダイバージェンスだけでは反転の根拠として不十分なことが多いためです。また、移動平均線の遅れも影響し、MAの反発とダイバージェンスのタイミングが完全に一致しないことや、精度を高めるための確認が必要な点もデメリットと言えます。
Pattern-3は、移動平均線の付近でピンバーや包み足などのローソク足パターンと組み合わせることで、エントリーの精度を高めることができます。また、ボリュームスパイク(出来高の急増)を利用してダイバージェンスの確認を補強することも有効です。
移動平均による動的なサポート/レジスタンス + ストキャスティクスのクロスオーバー(Pattern-4)
Pattern-4の戦略は、トレンドフォロー(移動平均線を使って方向性とサポート/レジスタンスを判断)と、モメンタムの確認(ストキャスティクスのクロスオーバー)を組み合わせたものです。これにより、実用的な買い・売りシグナルを生成します。
買いシグナルは、価格が上向きの移動平均線の上で維持されており、かつストキャスティクスの%Kが%Dを上抜けしたときに発生します。この強気(上昇)パターンは、ロングポジションに必要な複数の条件を満たすことを目的としています。まず第一に、上向きの移動平均線が現在の市場トレンドを示しているため、トレンドを確認しながら取引をおこなうことができます。
第二に、ストキャスティクスのクロスオーバーがモメンタムに基づいたタイミングでのエントリーを提供します。これは、モメンタムの変化が新たな買い圧力の発生を示すことが多いためです。そのため、単に移動平均線に触れた時点での早すぎるエントリーを避けることができます。ストキャスティクスのクロスオーバーによって、モメンタムの存在が確認されてからエントリーするためです。その結果、誤ったシグナルは減少します。ただし、このパターンでは両方の条件が満たされるのを待つ必要があるため、初動のブレイクアウトを逃す可能性があり、エントリーはやや遅れる傾向があります。
売りシグナルは、価格が下向きの移動平均線の下に位置しており、ストキャスティクスの%Kが%Dを下回るクロスが発生したときに出現します。これは、強気のシグナルと同様にトレンドの確認を提供し、モメンタムに基づくエントリーやエグジットのタイミングとなります。早すぎるエントリーは避けられ、弱いシグナルは除外される仕組みです。ただし、レンジ相場では価格が一時的に移動平均線を突破することがあり、その後に再び下降トレンドに戻る場合があるため注意が必要です。これは特に重要な点で、多くの通貨ペアや銘柄において、下降トレンドの方が上昇トレンドよりもボラティリティが高い傾向があることが背景にあります。これをMQL5で次のように実装します。
else if(Index == 4) { if(C.GetData(T, 2, _c) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2 && CopyBuffer(S.Handle(), 1, T, 2, _sto_d) >= 2) { _v[0] = ((_sto_k[1] < _sto_d[1] && _sto_k[0] > _sto_d[0]) ? 1.0f : 0.0f); _v[1] = ((_ma[1] < _c[1] && _ma[0] < _c[0] && _ma[1] < _ma[0]) ? 1.0f : 0.0f); _v[2] = ((_ma[1] > _c[1] && _ma[0] > _c[0] && _ma[1] > _ma[0]) ? 1.0f : 0.0f); _v[3] = ((_sto_k[1] > _sto_d[1] && _sto_k[0] < _sto_d[0]) ? 1.0f : 0.0f); } }
このパターンを処理するネットワークへの入力ベクトルは、サイズ4の次元にバッチ処理されます。先に示した他のパターンと同様に、このベクトルは、各条件内の引数を個別に入力として明示することで、より詳細な構成にすることも可能です。Pattern-4だけを学習させたネットワークに対して、ウォークフォワードを実行した結果は以下の通りです。
このパターンはトレンドフォロー型の市場においては有効と考えられますが、レンジ相場では苦戦する可能性があります。ウォークフォワードの結果は良好でしたが、上で述べたようにロングトレードのみが実行されているため、ロングとショートの両方の取引がおこなえるかどうかを確認するためにも、より大規模なデータセットでのテストが重要です。
MA付近でのストキャスティクスの買われすぎ/売られすぎによる反転(Pattern-5)
Pattern-5では、移動平均線(MA)を動的なサポート/レジスタンスとして使ったトレンドフォローと、ストキャスティクスを用いたモメンタム反転の組み合わせによってシグナルを生成します。買いシグナルは、ストキャスティクスが20ラインを上抜け、かつ価格が移動平均線によって支えられている(反発している)ときに発生します。このパターンは、MAがサポートゾーンとして機能していることを確認する手段となり、価格が既存の上昇トレンド内で反発していることを示します。また、ストキャスティクスの20ライン上抜けは、弱まりつつある売り圧力と買い手の参入を示唆します。2つのインジケーターを組み合わせることで、早すぎるエントリーを避けられるだけでなく、高確率のシグナルを得ることができます。ただし、このパターンの潜在的な弱点としては、強い下降トレンドの中では、価格が一時的にMAで持ちこたえた後に、さらに下抜ける可能性があるという点が挙げられます。
売りシグナルは、ストキャスティクスが80ラインを下抜け、かつ価格が下向きの移動平均線の近くで推移しているときに発生します。この状況は、MAがレジスタンスとして機能していることの確認となり、価格がそれ以上上昇できずに抑えられていることを示します。また、ストキャスティクスの80ライン下抜けは、買い圧力の減退と売り手の参入を示す、買われすぎからの反転シグナルです。ここでも2つのインジケーターを併用することで、早すぎるエントリーやエグジットを避け、弱いプルバックをフィルターする効果があります。ただし、Pattern-5の売りシグナルにおける欠点としては、強い下降トレンド中に価格が一時的にMAの上で横ばいになった後、下抜けることがあるため、エグジットが遅れたり、取りこぼしが発生する可能性がある点です。
MQL5では、これを次のように実装します。
else if(Index == 5) { if(C.GetData(T, 3, _c) >= 3 && CopyBuffer(M.Handle(), 0, T, 3, _ma) >= 3 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2) { _v[0] = ((_sto_k[1] < 20.0 && _sto_k[0] > 20.0) ? 1.0f : 0.0f); _v[1] = ((_c[2] > _c[1] && _c[1] < _c[0] && _c[1] >= _ma[1]) ? 1.0f : 0.0f); _v[2] = ((_c[2] < _c[1] && _c[1] > _c[0] && _c[1] <= _ma[1]) ? 1.0f : 0.0f); _v[3] = ((_sto_k[1] > 80.0 && _sto_k[0] < 80.0) ? 1.0f : 0.0f); } }
この記事で紹介しているMQL5のパターン実装は、ニューラルネットワークに入力する入力ベクトルを設定または定義することを目的としています。この入力ベクトルは、単純に0と1の集合として定義されており、特定のロングまたはショートの条件が満たされた場合に1が立ちます。ロングの条件とショートの条件は別々のインデックスに対応しているため、ベクトル内のすべての要素が1になることは基本的にありません。これは、ロングとショートの条件が常にミラー関係にあるためです。
また、各インデックスにおける条件は、その条件の中の各引数をそれぞれ別々のインデックスに割り当てることで、さらに詳細化・拡張することも可能です。このアプローチを取ると、入力ベクトルはより長くなりますが、本稿ではこの点については未検証です。なお、トレーニングおよびテストは2023年のデータを用いおこなっており、以下はそれに基づいた2024年のウォークフォワード結果となります。次のレポートが得られます。
Pattern-5を用いたウォークフォワードは実行可能であることが確認できましたが、ひとつ注意点があります。実行されたのはロングトレードのみだということです。これは主に、限られた・小規模なデータセットで学習をおこなったことが原因で、ネットワークの出力がその偏ったサンプルに影響された結果と考えられます。この問題は、より大きなデータセットでトレーニングをおこなうことで改善できる可能性があります。
ゴールデンクロス/デッドクロス+ストキャスティクスによる確認(Pattern-6)
Pattern-6は、短期の移動平均線が遅行の移動平均線をクロスするゴールデンクロス/デッドクロスを使い、さらにモメンタムをストキャスティクスの50レベルのクロスで確認します。買いシグナルは、短期移動平均線が長期移動平均線を下から上にクロスするゴールデンクロスであり、同時にストキャスティクスが50以上で上昇している場合です。売りシグナルは、短期移動平均線が長期移動平均線を上から下にクロスするデッドクロスであり、同時にストキャスティクスが50以下で下降している場合です。Pattern-6のネットワークへの入力は、以下のように設定しています。
else if(Index == 6) { if(CopyBuffer(M.Handle(), 0, T, 2, _ma_lag) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_d) >= 2) { _v[0] = ((_ma_lag[1] > _ma[1] && _ma_lag[0] < _ma[0]) ? 1.0f : 0.0f); _v[1] = ((50.0 <= _sto_d[0] && _sto_k[0] > _sto_d[0]) ? 1.0f : 0.0f); _v[2] = ((50.0 >= _sto_d[0] && _sto_k[0] < _sto_d[0]) ? 1.0f : 0.0f); _v[3] = ((_ma_lag[1] > _ma[1] && _ma_lag[0] < _ma[0]) ? 1.0f : 0.0f); } }
今回使用したソースコードでは、_maというハンドルが短期移動平均線を指しており、トレーニングでは期間を8に設定しています。一方、長期移動平均線は_ma_lagというハンドルを使い、その期間は短期の2倍の16となっています。2023年のデータでトレーニングをおこなった後、2024年のウォークフォワードテストを実施した結果は以下の通りです。
Pattern-6は、ウォークフォワードテストにおいて、利益面だけでなくロングとショートのバランスの面でも失敗しています。しかし、十分なトレーニングとテストを重ねることで、この評価を覆す可能性もあります。
ストキャスティクス極端反転+移動平均トレンド確認(Pattern-7)
Pattern-7では、ストキャスティクスが極端なレベルで反転するタイミングと、価格の移動平均線に対する相対位置を組み合わせてエントリーシグナルを定義します。買いシグナルは、ストキャスティクスが10以下で反転上昇し始め、かつ価格が上向きの移動平均線の上にあるときに発生します。売りシグナルは、ストキャスティクスが90以上のレベルで反転下降し、かつ価格が下向きの移動平均線の下にあるときに発生します。このパターンは、以下のようにMQL5のネットワークにマッピングしています。
else if(Index == 7) { if(C.GetData(T, 2, _c) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 3, _sto_k) >= 3) { _v[0] = ((_ma[0] > _ma[1] && _c[0] > _ma[0]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[0] > _sto_k[1] && _sto_k[1] < _sto_k[2] && _sto_k[2] <= 10.0) ? 1.0f : 0.0f); _v[2] = ((_sto_k[0] < _sto_k[1] && _sto_k[1] > _sto_k[2] && _sto_k[2] >= 90.0) ? 1.0f : 0.0f); _v[3] = ((_ma[0] < _ma[1] && _c[0] < _ma[0]) ? 1.0f : 0.0f); } }
ここではストキャスティクスの%Kハンドルのみを厳密に使用しており、極端なレベルにあるときの強気(買い)条件では「n字型の反転」、弱気(売り)条件では「u字型の反転」をチェックしています。ウォークフォワードテストの結果は以下の通りです。
上記の結果から、このパターンも前年のテスト結果に基づく1年間のウォークフォワードがうまく機能していません。また、ショートトレードのみをおこなっています。ウォークフォワードで片側のトレードだけがおこなわれるのは懸念材料です。出力は0.0から1.0の範囲のスカラー浮動小数点値であり、これはすべての予測値が0.5未満だったことを意味します。この問題を改善するためには、より大きなデータセットでのテストやトレーニングが重要です。
ストキャスティクスのブレイクアウト+移動平均の確認(Pattern-8)
最後から2番目のパターンであるPattern-8は、ストキャスティクスの50レベルと価格と移動平均線のクロスを組み合わせています。買いシグナルは、ストキャスティクスが50の閾値を下から上へクロスして終値が上回り、同時に価格も同様に移動平均線を下から上にクロスして終値が上回ったときに発生します。売りシグナルはその逆で、オシレーターが50を上から下へクロスし、価格が移動平均線のサポートを下抜けたときに発生します。このパターンは、前回までの記事でおこなっていたように適用すると、あまり多くの取引を生み出さない可能性があります。しかし、今回は強気と弱気の両方のシグナルに対して、移動平均線とストキャスティクスの条件をそれぞれ別々にチェックしています。これはMQL5では次のように実行されます。
else if(Index == 8) { if(C.GetData(T, 2, _c) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2 && CopyBuffer(S.Handle(), 1, T, 2, _sto_d) >= 2) { _v[0] = ((_c[1] < _ma[1] && _c[0] > _ma[0]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[1] < 50.0 && _sto_k[0] > 50.0) ? 1.0f : 0.0f); _v[2] = ((_sto_k[1] > 50.0 && _sto_k[0] < 50.0) ? 1.0f : 0.0f); _v[3] = ((_c[1] > _ma[1] && _c[0] < _ma[0]) ? 1.0f : 0.0f); } }
Pattern-8のネットワークへの入力ベクトルは、強気(買い)または弱気(売り)の条件のいずれかを少なくともひとつ検出しやすくなっており、これがトレーニングの助けになるとともに、ネットワークを実際に運用する際の適応性にも寄与しています。これは、両方のインジケーターを処理するだけでなく、いずれか一方のインジケーターが対象のパターンを検出した場合でも予測をおこなうためです。ウォークフォワードの結果は以下の通りです。
このパターンはウォークフォワードテストをクリアできそうで、これは励みになる結果です。ただし、いくつか重要な注意点も挙げておきます。まず、これらのテストはテイクプロフィットのみを設定し、ストップロスは設定していません。確かにストップロスは必ずしも希望する価格での決済を保証しませんが、損失を抑えるための何らかの戦略は必要です。次に、トレーニングは直近の1年分のデータでおこない、テストはその翌年のデータを用いましたが、これは比較的短いテスト期間です。より長期間のテストや、高品質なブローカーデータを用いた検証が望まれます。
移動平均の収束+ストキャスティクスのブレイクアウト(Pattern-9)
最後のパターンであるPattern-9では、移動平均線の収束(スクイーズ)を利用して低ボラティリティ状態を検出し、そこからのブレイクアウトのタイミングをストキャスティクスのモメンタムで判断します。買いシグナルは、短期と長期の2本の移動平均線が一定期間にわたり収束したあとに、ストキャスティクスが50ラインを鋭く上抜けしてモメンタムの加速を示したときに発生します。売りシグナルでも同様の条件が適用されますが、いくつかの違いがあります。まず、買いシグナルのスクイーズでは短期移動平均線が長期移動平均線の上にあるのに対し、売りシグナルではその関係が逆になります。また、売りにおけるストキャスティクスは、50ラインを鋭く下抜けて下降するという点でも異なります(買いでは上抜け)。
この条件に対応したネットワークへの入力ベクトルは、以下のようにMQL5から取得します。
else if(Index == 9) { if(CopyBuffer(M.Handle(), 0, T, 3, _ma) >= 3 && CopyBuffer(M_LAG.Handle(), 0, T, 3, _ma_lag) >= 3 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2) { _v[0] = ((_ma_lag[0] < _ma[0] && fabs(fabs(_ma_lag[2] - _ma[2]) - fabs(_ma_lag[0] - _ma[0])) <= fabs(_ma[2] - _ma[0])) ? 1.0f : 0.0f); _v[1] = ((_sto_k[1] <= 45.0 && _sto_k[0] >= 55.0) ? 1.0f : 0.0f); _v[2] = ((_sto_k[1] >= 55.0 && _sto_k[0] <= 45.0) ? 1.0f : 0.0f); _v[3] = ((_ma_lag[0] > _ma[0] && fabs(fabs(_ma_lag[2] - _ma[2]) - fabs(_ma_lag[0] - _ma[0])) <= fabs(_ma[2] - _ma[0])) ? 1.0f : 0.0f); } }
そして、そのウォークフォワードの結果は次のとおりです。
Pattern-9も、ロングおよびショートの両方のトレードを出せてはいるものの、有効性には欠けるようです。均等な重み付けの観点から見ると、これ以上深く検討する価値はないかもしれません。というのも、上で紹介した他のいくつかのパターンでは、より良好なウォークフォワード結果が出ているからです。
結論
本記事では、パターンを組み合わせたり、特定のパターンを選別して統合的なシステムを構築したりすることには踏み込んでいません。これは、過去の記事(特に最後の記事)で見られたように非常に危険になりうる行為だからです。複数のパターンを同時に使用するには、各パターンに精通している必要があります。というのも、パターン同士が互いに取引を打ち消し合ってしまう可能性があるからです。マジックナンバーを用いて取引を個別に管理することである程度対処は可能ですが、証拠金制限などの課題は依然として残る可能性があります。次回の記事では、今回構築した内容をもとに、強化学習がどのように活用できるかについて掘り下げていきます。
ファイル | 説明 |
---|---|
57_0.onnx | pattern-0ネットワーク |
57_1.onnx | pattern-1ネットワーク |
57_2.onnx | pattern-2ネットワーク |
57_3.onnx | pattern-3ネットワーク |
57_4.onnx | pattern-4ネットワーク |
57_5.onnx | pattern-5ネットワーク |
57_6.onnx | pattern-6ネットワーク |
57_7.onnx | pattern-7ネットワーク |
57_8.onnx | pattern-8ネットワーク |
57_9.onnx | pattern-9ネットワーク |
SignalWZ_57.mqh | シグナルクラスファイル |
57_X.mqh | ネットワーク入力を処理するためのシグナルファイル |
wz_57.mq5 | EAの組み立てで使用されたファイルを表示するために含まれています |
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/17479
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。




- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
SignalWZ_57.mqhがありません。
ええ、私もSignalWZ_57.mqhファイルが見つからないという同じ問題に直面しました。