English Deutsch
preview
古典的な戦略を再構築する(第20回):現代のストキャスティクス

古典的な戦略を再構築する(第20回):現代のストキャスティクス

MetaTrader 5 |
16 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

ストキャスティクスは、市場における潜在的な反転ポイントを特定するために従来から広く用いられてきた、よく知られたテクニカル指標です。古典的な定義では、一定の価格レンジ内における価格変動の勢いを測定するものであり、価格が極端な水準に達した際には、市場が買われすぎ、あるいは売られすぎの状態にあると解釈されます。この伝統的な枠組みでは、価格はいずれ平均的な水準へと回帰するという前提のもと、買われすぎでは売り、売られすぎでは買いの機会を探すという逆張り戦略が採用されます。

この手法は長年にわたり有効に機能してきましたが、本記事ではストキャスティクスが持つ、これまで十分に注目されてこなかった別の側面に焦点を当てます。具体的には、厳密な平均回帰指標としてではなく、むしろトレンドフォローの文脈においても有効に機能し得る可能性を検討します。わずかな解釈ルールの調整によって、このオシレーターを市場の主要トレンドを捉えるための手法として再利用できることを示します。

これを裏付けるために、指標のシグナルを再検証し、古典的なルールに疑問を投げかけます。そして、買われすぎの局面で買い、売られすぎの局面で売るという代替的な枠組みを導入します。この考え方は一見すると直感に反するかもしれません。しかし本記事で示すように、ストキャスティクスは一般に考えられているよりもはるかに汎用性が高く、単一の解釈に限定されるものではありません。

過去の分析においても、オシレーターは生の価格変動よりも予測可能性が高いことが確認されました。この洞察は、指標をさらに深く分析する動機となりました。

これを十分に検証するため、本記事ではストキャスティクスに基づく5つの異なる戦略バージョンを提示します。指標から最大限の価値を引き出す最後の試みは成功しませんでしたが、この結果は重要な点を示しています。すなわち、5つの戦略のうち4つが良好な結果を示したという事実です。このことは、多くのトレーダーが既に理解していると考えている指標について、私たちが実際にどの程度理解しているのかを再考させるものです。

複数の戦略バージョンを評価するため、いくつかのパラメータはすべてのバージョンで一貫している必要があります。各バージョンは一度に1件のみ取引を実行するため、収益性の差は取引ルールの変更によるもののみになります。すべての戦略は同一のポジションサイズを使用し、バックテストは2021年から2025年までの同一の過去データ期間で実施されます。 

図1:戦略のすべてのバージョンで使用するバックテスト期間

最後に、各バージョンは実運用での約定遅延を再現するため、ランダム化された実行遅延条件下でテストされます。

図2:戦略のテスト条件は、実際の市場環境を模倣したものである


ベースラインの確立

まず最初に、議論に必要な重要なグローバル変数を定義することから始めます。

//+------------------------------------------------------------------+
//|                                          Stochastic Strategy.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
int      stoch_handler,atr_handler;
double   stoch_main_reading[],stoch_signal_reading[],atr_reading[];
double   bid,ask;

次に、ポジション管理を支援するために、Tradeライブラリをインポートします。

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade Trade;

取引アプリケーションを初めて初期化する際に、戦略に必要なテクニカル指標を定義します。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Setup our technical indicators
atr_handler    = iATR(Symbol(),PERIOD_D1,14);
stoch_handler  = iStochastic(Symbol(),PERIOD_D1,5,3,3,MODE_SMA,STO_LOWHIGH);
//---
   return(INIT_SUCCEEDED);
  }

取引アプリケーションが使用されなくなった時点で、そのアプリケーションが依拠していたテクニカル指標を解放します。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   IndicatorRelease(atr_handler);
   IndicatorRelease(stoch_handler);
  }

新しい価格水準が受信されるたびに、適切な指標バッファとグローバル変数が更新されます。まず、ストキャスティクスの伝統的な解釈、すなわち売られすぎの状態で買い、買われすぎの状態で売るという解釈を実装します。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Keep track of the time
datetime        current_time   = iTime(Symbol(),PERIOD_D1,0);
static datetime time_stamp;
   
   if(current_time != time_stamp)
      {
         //--- Update the time
         time_stamp = current_time;
         
         //--- Update our technical indicators
         CopyBuffer(stoch_handler,0,0,1,stoch_main_reading);
         CopyBuffer(stoch_handler,1,0,1,stoch_signal_reading);
         CopyBuffer(atr_handler,0,0,1,atr_reading);
         ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
         bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
         
         //--- Trading rules
         if(PositionsTotal() == 0)
            {
               if(stoch_main_reading[0] < 20) Trade.Buy(0.01,Symbol(),ask,(ask - (atr_reading[0]*2)),(ask + (atr_reading[0]*2)));
               
               if(stoch_main_reading[0] > 80) Trade.Sell(0.01,Symbol(),bid,(bid + (atr_reading[0]*2)),(bid - (atr_reading[0]*2)));
            }
      }
   
  }
//+------------------------------------------------------------------+

このベンチマークから得られるエクイティカーブは変動が激しく、提案された戦略の妥当性に対する信頼性は低くなります。通常、このような戦略は放棄されることになりますが、私たちは、まだ改善の余地があると考えます。

図3:ストキャスティクスの従来の解釈によって生成されるエクイティカーブは信頼性に欠けるように見える

さらに、この戦略は取引の49%で損失を出し、バックテスト期間中に投資家の資産価値の減少を招きました。

図4:古典的戦略の詳細な統計には、改善の余地がかなり残されている


ベースラインを超えて改良する

本記事で提案する解決策では、この指標をトレンド識別のためのツールとして再解釈します。あらゆる市場において特定される支配的なトレンドは、現在の市場価格が観測された市場の範囲に対してどの位置にあるかによって定義されます。まず、過去に観測された高値と安値を記録するためのベクトルを作成することから始めます。

vector   high,low;

次に、過去90日分のローソク足における取引レンジの中間値を算出します。90という値は、外国為替市場を支配する機関投資家の典型的な景気循環と一致するため、恣意的に選択されました。

//--- Calculate the middle of the trading range
high.CopyRates(Symbol(),PERIOD_D1,COPY_RATES_HIGH,0,90);
low.CopyRates(Symbol(),PERIOD_D1,COPY_RATES_LOW,0,90);
double mid = ((high.Mean() + low.Mean())/2);

ポジションが開いていない場合は、まず買われすぎの価格水準で買いポジションを取ることを試みます。さらに、終値が観測された高値と安値の範囲の中間値を上回ることを確認することで、さらなる裏付けを得る必要があります。

//--- Trading rules
if(PositionsTotal() == 0)
   {
      if((stoch_main_reading[0] > 80) && (iClose(Symbol(),PERIOD_D1,0) > mid)) Trade.Buy(0.01,Symbol(),ask,(ask - (atr_reading[0]*2)),(ask + (atr_reading[0]*2)));
      
      if((stoch_main_reading[0] < 20) && (iClose(Symbol(),PERIOD_D1,0) < mid)) Trade.Sell(0.01,Symbol(),bid,(bid + (atr_reading[0]*2)),(bid - (atr_reading[0]*2)));
   }

こうした状況下で実現したエクイティカーブは、当初の変動が激しく低迷していたベンチマークと比較して、大幅な上昇を示しています。

図5:改良したストキャスティクスの適用によって生成されたエクイティカーブ

得られた詳細な統計データは、著しい改善を示しています。総純利益は、前年同期のマイナス26.22ドルと比較して、184.35ドルへと大幅に増加しました。さらに、勝率も最初の試みの49%から現在では55%に上昇しました。

図6:改良した戦略から得られた詳細な統計データは、加えた変更が有意義であったことを示している


より高いパフォーマンスレベルを目指して

より短い時間足でローソク足分析を注意深くおこなうことで、戦略に有意義な改善を加えることができます。その根拠は、日足チャートで観察されるトレンドは、より短い時間足の始値と終値の差にも反映されるはずであり、それによって有意義なエントリーポイントを特定できるという点にあります。

vector  open,close;

1時間足チャートでは、直近12本のローソク足を参考に、その日の主要なトレンドを判断します。 

//--- Calculate the current trend on the lower time frame
 open.CopyRates(Symbol(),PERIOD_H1,COPY_RATES_OPEN,0,12);
 close.CopyRates(Symbol(),PERIOD_H1,COPY_RATES_CLOSE,0,12);
これらの改良された取引ルールを組み合わせると、3つの要件からなるフィルターが形成されます。それぞれの要件は、市場において単一の支配的なトレンドが強く働いている可能性を示唆しています。
//--- Trading rules
if(PositionsTotal() == 0)
   {
      if((stoch_main_reading[0] > 80) && (iClose(Symbol(),PERIOD_D1,0) > mid) && (open.Mean() < close.Mean())) Trade.Buy(0.01,Symbol(),ask,(ask - (atr_reading[0]*2)),(ask + (atr_reading[0]*2)));
               
      if((stoch_main_reading[0] < 20) && (iClose(Symbol(),PERIOD_D1,0) < mid) && (open.Mean() > close.Mean())) Trade.Sell(0.01,Symbol(),bid,(bid + (atr_reading[0]*2)),(bid - (atr_reading[0]*2)));
   }

この改良された戦略によって得られるエクイティカーブは、当初の試みで得られたギザギザで変動の激しいエクイティカーブとは対照的に、より滑らかに伸びるようになりました。

図7:改良した戦略は、以前の最高スコアを大きく上回る成果を上げている

総純利益はさらに増加し223ドルとなり、シャープレシオは前回の0.69から0.88に改善しました。取引総数は123件から118件に減少しました。効率性が向上した明確な指標は、より少ない労力で同じ目標を達成できる能力です。加えた変更は、この目的を達成することに成功したようです。さらに、勝率も過去最高の56%に上昇しました。

図8:ストキャスティクス戦略の第3版によって得られた詳細な結果


ストキャスティックからの取引ルールのアルゴリズム的発見

これまでの議論では、取引の実行を導くために、取引ルールと市場フィルターを手動で定義してきました。このプロセスは創造的思考や市場の理解を深めるうえでは役立ちますが、人間の直感だけで到達できる範囲には限界があります。

市場データの中には、人間の論理ではすぐには直感的に理解できなかったり、容易に認識できなかったりする、意味のあるパターン、ルール、意思決定ロジックが存在する可能性があります。この可能性を探るため、次に、追加のルール、具体的にはストキャスティクスを解釈するためのルールを発見するためのアルゴリズムによる手法に注目します。

そのためには、過去のEUR/USD市場データとストキャスティクスの値を取得するMQL5スクリプトを作成します。このデータはCSV形式でエクスポートされます。このデータセットには、標準的な始値、高値、安値、終値(OHLC)の価格フィードに加え、ストキャスティクスのバッファである%K(メイン)ラインと%D(シグナル)ラインが含まれます。

最後に、データセットを充実させるために、ストキャスティクスのデータに対して手動による特徴量エンジニアリングをおこないます。これには、%K線と%D線の中間点を計算すること、主要な値と80および20の閾値レベルとの距離を測定すること、および指標の挙動を把握するのに役立つ追加の観測値を導き出すことが含まれます。これらの特徴量を組み合わせることで、ストキャスティクスの高次元表現が形成され、より高度なアルゴリズム解析が可能になります。

//+------------------------------------------------------------------+
//|                                          Fetch Data Stochastic 2 |
//|                                      Copyright 2020, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs

//--- File name
string file_name = Symbol() + " Stochastic Strategy.csv";

int stoch_handler = iStochastic(Symbol(),PERIOD_CURRENT,5,3,3,MODE_EMA,STO_LOWHIGH);
double stoch_main[],stoch_signal[];
double stoch_o,stoch_h,stoch_l;

//--- Amount of data requested
input int size = 365;

//+------------------------------------------------------------------+
//| Our script execution                                             |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Write to file
   int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,",");

//--- 
   CopyBuffer(stoch_handler,0,0,size,stoch_main);
   stoch_o = stoch_main[0];
   stoch_h = stoch_main[0];
   stoch_l = stoch_main[0];
   ArraySetAsSeries(stoch_main,true);
   CopyBuffer(stoch_handler,1,0,size,stoch_signal);
   ArraySetAsSeries(stoch_signal,true);
   
   for(int i=size;i>=1;i--)
     {
      if(i == size)
        {
        
         FileWrite(file_handle,
                  //--- Time
                  "Time",
                   //--- OHLC
                   "Open",
                   "High",
                   "Low",
                   "Close",
                   //--- Stochastic Readings
                   "Stochastic Main",
                   "Stochastic Signal",
                   //--- Feature Engineering Stochastic Oscilator
                   "Stoch Main - Signal",
                   "Stoch M-S Mid",
                   "Stoch - 80",
                   "Stoch - 20",
                   "Stoch O",
                   "Stoch H",
                   "Stoch L",
                   "Stoch O-C",
                   "Stoch H-C",
                   "Stoch L-C"
                  );
        }

      else
        {
        
        //--- Set features
        stoch_h = (stoch_h < stoch_main[i]) ? stoch_main[i] : stoch_h;
        stoch_l = (stoch_l > stoch_main[i]) ? stoch_main[i] : stoch_l;
        
         FileWrite(file_handle,
                   iTime(_Symbol,PERIOD_CURRENT,i),
                   //--- OHLC
                   iOpen(_Symbol,PERIOD_CURRENT,i),
                   iHigh(_Symbol,PERIOD_CURRENT,i),
                   iLow(_Symbol,PERIOD_CURRENT,i),
                   iClose(_Symbol,PERIOD_CURRENT,i),
                   //--- Stochastic Readings
                   stoch_main[i],
                   stoch_signal[i],
                   //--- Stochastic Feature Engineering
                   stoch_main[i] - stoch_signal[i],
                   ((stoch_main[i] + stoch_signal[i])/2),
                   (stoch_main[i] - 80),
                   (stoch_main[i] - 20),
                   stoch_o,
                   stoch_h,
                   stoch_l,
                   stoch_o - stoch_main[i],
                   stoch_h - stoch_main[i],
                   stoch_l - stoch_main[i]
                   );
        }
     }
//--- Close the file
   FileClose(file_handle);
  }
//+------------------------------------------------------------------+


Pythonでの市場データ分析

過去の市場データのCSVファイルを書き出したので、次にPythonでデータを分析します。まず、必要なPythonライブラリをインポートします。

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

次に、作成したCSVファイルを読み込みます。

data = pd.read_csv("./EURUSD Stochastic Strategy.csv")
data

MetaTrader 5のバックテスト用に確保されているテストセットから、学習用の観測データセットを分離します。

train = data.iloc[:-(365 * 5),:]
test = data.iloc[-(365 * 5):,:]

必要な機械学習ライブラリを読み込みます。 

from sklearn.linear_model    import LinearRegression
from sklearn.model_selection import cross_val_score,TimeSeriesSplit

予測期間を定義します。

HORIZON = 5

検討対象となる各モデルの精度を評価するのに役立つ、時系列交差検証オブジェクトを作成します。

tscv = TimeSeriesSplit(n_splits=10,gap=HORIZON)

データセットにラベルを付けましょう。私たちの関心は、メインストキャスティクスの値を予測することにあります。

data['Target'] = data['Stochastic Main'].shift(-HORIZON)
data = data.iloc[:-HORIZON,:]

従来のOHLC列を使用した場合、新しく開発したストキャスティクスの特徴量を使用した場合、そして最後に両者を組み合わせた場合の精度を比較してみましょう。

X_classic = data.iloc[:,1:7].columns
X_new     = data.iloc[:,7:-1].columns
X_all     = data.iloc[:,1:-1].columns
y         = 'Target'

モデルに入力するデータを変更する際に観測された精度レベルを記録しておきます。

scores = []

誤差の変化が選択した入力値に起因することを確実にするため、基となるモデルは同じままにしておきます。

model = LinearRegression()

利用可能な入力値の組み合わせごとに、それぞれの精度を記録します。

scores.append(np.mean(np.abs(cross_val_score(model,data.loc[:,X_classic],data.loc[:,y],cv=tscv,scoring='neg_mean_squared_error'))))
scores.append(np.mean(np.abs(cross_val_score(model,data.loc[:,X_new],data.loc[:,y],cv=tscv,scoring='neg_mean_squared_error'))))
scores.append(np.mean(np.abs(cross_val_score(model,data.loc[:,X_all],data.loc[:,y],cv=tscv,scoring='neg_mean_squared_error'))))

可能なさまざまな入力セットから得られた精度レベルをプロットすると、私たちが設計したカスタムストキャスティクスの特徴量が、確認できる中で最も低い誤差レベルを生み出していることが明確にわかります。

sns.barplot(np.abs(scores),color='black')
plt.axhline(np.min(scores),linestyle=':',color='red')
plt.xticks([0,1,2],['OHLC Features','Custom Features','All Features'])


図9:生成されたカスタム特徴量は、ストキャスティクスのメインバッファを最適に予測するのに役立った

ほとんどの動的プロセスでは、記録されたすべての変数が等しく情報量を持つとは限りません。どの変数が最も多くの情報を含んでいるかを特定することで、今後の特徴量エンジニアリングの取り組みを方向づけることができ、最も影響力のある特徴量の、より豊富で多様なバリエーションを生成することに集中できるようになります。これを定量化するために、相互情報量回帰分析を用います。相互情報量(MI)は、線形関係と非線形関係の両方を捉える統計的依存性の尺度であり、2つの変数間のあらゆる形態の依存性を評価するのに適しています。

from sklearn.feature_selection import mutual_info_regression

統計的検定を実施します。このテストには、私たちが生成した新しいストキャスティクスの特徴量と、現在選択したターゲットが必要です。

scores = mutual_info_regression(data.loc[:,X_new],data.loc[:,'Target'])

今回生成した10個のカスタム特徴量のうち、ターゲットと有意な関係がまったくないと思われるのはわずか3個だけのようです。これは、MIスコアの列がほぼ0であることから推測されます。したがって、MQL5スクリプトで生成したデータから、アルゴリズム的に意味のある関係性を発見したように思われます。

sns.barplot(scores,color='black')
plt.axhline(np.mean(scores),color='red',linestyle=':')


図10:観測されたMIスコアは、構築されたデータセットが有意義であり、学習可能な関係性を持っていることを示している


ONNXへのエクスポート

これで、学習済みの統計モデルをOpen Neural Network Exchange (ONNX)形式にエクスポートする準備が整いました。ONNXは、元の学習フレームワークやプログラミング言語を必要とせずに機械学習モデルを展開することを可能にし、プラットフォームや環境を超えて移植できるようにします。まず、このプロセスに必要なONNXライブラリを読み込みます。

import onnx
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType

モデルの入力形状を指定します。

initial_types = [('float input',FloatTensorType([1,len(X_new)]))]

訓練データを用いてモデルを適合させます。

model = GradientBoostingRegressor()

model.fit(train.loc[:,X_new],train.iloc[:,-1])

モデルをONNXプロトタイプとして保存します。 

onnx_proto = convert_sklearn(model,initial_types=initial_types,target_opset=12)

ONNXプロトタイプをONNXファイルとしてディスクに保存します。

onnx.save(onnx_proto,'EURUSD Stochastic GBR AI.onnx')


改善策の実施

次に、取引戦略を修正して、ONNXファイルを組み込んでみましょう。

//+------------------------------------------------------------------+
//|                                               Stochastic AI.mq5  |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#resource "\\Files\\EURUSD Stochastic GBR AI.onnx" as const uchar onnx_proto[];

アプリケーション内でONNXモデルを扱うために使用するグローバル変数を定義します。

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
vectorf  model_inputs,model_outputs;
long     model;

システム定数を指定します。これらの定数は、モデルが持つ入力と出力の数を指定します。

//+------------------------------------------------------------------+
//| System Definitions                                               |
//+------------------------------------------------------------------+
#define MODEL_INPUT_SHAPE  10
#define MODEL_OUTPUT_SHAPE 1

アプリケーションが初期化されると、テクニカル指標を読み込み、学習セットで生成したカスタムストキャスティクスの特徴量(史上最高値や史上最安値など)も追跡します。次に、ONNXモデルを設定し、適切に設定され、正常に動作していることを確認します。 

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Setup our indicators
   atr_handler     = iATR("EURUSD",PERIOD_D1,14);
   stoch_handler   = iStochastic(Symbol(),PERIOD_CURRENT,5,3,3,MODE_EMA,STO_LOWHIGH);
   stoch_o = 22.69153;
   stoch_h = 98.551023;
   stoch_l = 1.372058;

//--- Setup the ONNX model
   model = OnnxCreateFromBuffer(onnx_proto,ONNX_DATA_TYPE_FLOAT);

//--- Define the model parameter shape
   ulong input_shape[] = {1,MODEL_INPUT_SHAPE};
   ulong output_shape[] = {1,MODEL_OUTPUT_SHAPE};

   if(!OnnxSetInputShape(model,0,input_shape))
     {
      Print("ONNX Model Error: Incorrect Input Shape ",GetLastError());
      return(INIT_FAILED);
     }

   if(!OnnxSetOutputShape(model,0,output_shape))
     {
      Print("ONNX Model Error: Incorrect Output Shape ",GetLastError());
      return(INIT_FAILED);
     }

   model_inputs = vectorf::Zeros(MODEL_INPUT_SHAPE);
   model_outputs = vectorf::Zeros(MODEL_OUTPUT_SHAPE);

   if(model != INVALID_HANDLE)
     {
      return(INIT_SUCCEEDED);
     }

//---
   return(INIT_FAILED);
  }

取引アプリケーションが使用されなくなった時点で、使用しなくなったテクニカル指標とONNXモデルを解放します。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Free up memory we are no longer using when the application is off
   IndicatorRelease(atr_handler);
   IndicatorRelease(stoch_handler);
   OnnxRelease(model);
  }

新たな価格水準が得られた場合には、当社独自の取引ルールに基づいて取引をおこない、さらに統計モデルによって学習された取引シグナルを補足的に使用します。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- When price levels change

   datetime current_time = iTime("EURUSD",PERIOD_D1,0);
   static datetime  time_stamp;

//--- Update the time
   if(current_time != time_stamp)
     {
     
      time_stamp = current_time;

      //--- Calculate the middle of the trading range produced over the last business cycle
      high.CopyRates(Symbol(),PERIOD_D1,COPY_RATES_HIGH,0,90);
      low.CopyRates(Symbol(),PERIOD_D1,COPY_RATES_LOW,0,90);
      double mid = ((high.Mean() + low.Mean())/2);

      //--- Calculate the current trend on the lower time frame
      open.CopyRates(Symbol(),PERIOD_H1,COPY_RATES_OPEN,0,12);
      close.CopyRates(Symbol(),PERIOD_H1,COPY_RATES_CLOSE,0,12);

      //--- Fetch indicator current readings
      CopyBuffer(atr_handler,0,0,1,atr_reading);
      CopyBuffer(stoch_handler,0,0,1,stoch_main);
      CopyBuffer(stoch_handler,1,0,1,stoch_signal);

      //--- Setting model inputs
      stoch_h = (stoch_h < stoch_main[0]) ? stoch_main[0] : stoch_h;
      stoch_l = (stoch_l > stoch_main[0]) ? stoch_main[0] : stoch_l;

      model_inputs[0] = (float)(stoch_main[0] - stoch_signal[0]);
      model_inputs[1] = (float)(((stoch_main[0] + stoch_signal[0])/2));
      model_inputs[2] = (float)((stoch_main[0] - 80));
      model_inputs[3] = (float)((stoch_main[0] - 20));
      model_inputs[4] = (float)(stoch_o);
      model_inputs[5] = (float)(stoch_h);
      model_inputs[6] = (float)(stoch_l);
      model_inputs[7] = (float)(stoch_o - stoch_main[0]);
      model_inputs[8] = (float)(stoch_h - stoch_main[0]);
      model_inputs[9] = (float)(stoch_l - stoch_main[0]);

      ask = SymbolInfoDouble("EURUSD",SYMBOL_ASK);
      bid = SymbolInfoDouble("EURUSD",SYMBOL_BID);

      //--- If we have no open positions
      if(PositionsTotal() == 0)
        {

         if(!(OnnxRun(model,ONNX_DATA_TYPE_FLOAT,model_inputs,model_outputs)))
           {
            Comment("Failed to obtain a forecast from our model: ",GetLastError());
           }

         else
           {
            Comment("Forecast: ",model_outputs);

            //--- Trading rules
            if((model_outputs[0] > stoch_main[0]) && (stoch_main[0] > 80) && (iClose(Symbol(),PERIOD_D1,0) > mid) && (open.Mean() < close.Mean()))
              {
               //--- Buy signal
               Trade.Buy(0.01,"EURUSD",ask,ask-(atr_reading[0] * 2),ask+(atr_reading[0] * 2),"");
              }

            else
               if((model_outputs[0] < stoch_main[0]) && (stoch_main[0] < 20) && (iClose(Symbol(),PERIOD_D1,0) < mid) && (open.Mean() > close.Mean()))
                 {
                  //--- Sell signal
                  Trade.Sell(0.01,"EURUSD",bid,bid+(atr_reading[0] * 2),bid-(atr_reading[0] * 2),"");
                 }
           }
        }
     }
  }
//+------------------------------------------------------------------+

最後に、MQL5で作成したすべてのシステム定義を未定義にします。これは開発者にとって良い習慣です。

#undef MODEL_INPUT_SHAPE
#undef MODEL_OUTPUT_SHAPE

改良した戦略によって生成されたエクイティカーブを観察すると、システムにノイズが多すぎるように思われます。曲線は以前のような滑らかな上昇傾向を失い、今では私たちが望むよりも不安定な状態になっています。 

図11:最終的に採用した取引戦略から得られたエクイティカーブには、ノイズの多いシグナルが含まれているようにみえる

この戦略は依然として収益性があるものの、そのパフォーマンスは以前のピークである223ドルから大幅に低下しています。これは、ストキャスティクスに統計的取引戦略の基礎としての価値がないという意味ではなく、むしろ、実践者にとってより慎重かつ厳密な方法論が必要であることを示しています。さらに、この戦略では買いはおこなわず、売りに偏った傾向が見られたことも分かります。

本連載を初めて読む方にとっては、これらの結果は意外に思えるかもしれません。統計モデルを開発する過程で、カスタムストキャスティクスの特徴量を用いることで、誤差指標に明らかな改善が見られることがわかりました。 しかし、以前からの読者の方には、このパターンに見覚えがあるでしょう。

姉妹連載「機械学習の限界を克服する(第1回):相互運用可能な指標の欠如」で紹介した通り、モデルの学習に使用される統計指標は、実際の取引の目的を反映していないことが多くなります。したがって、統計誤差の改善は、必ずしも取引成績の向上につながるわけではなありません。

実際には、現代の統計モデリングは、試行錯誤に似たプロセスであることが多くなります。したがって、綿密な分析をおこなったにもかかわらず取引成績が振るわなかった場合、それを自身のスキル不足の表れと解釈すべきではありません。関連記事へのリンクはこちらです。現時点では、売買システムに意図せず過剰なノイズが混入した可能性が高いと結論づけるのが妥当であり、したがって、アプリケーションの中で最もパフォーマンスの高いバージョンであるバージョン3に戻すことにします。

図12:ストキャスティクス取引戦略の最終版に関する詳細な統計分析



結論

本記事では、古典的なテクニカル指標を従来の用途を超えて再利用する方法を示しました。読者は、馴染みのある戦略を異なる分析視点から見直すことで新たな価値を生み出す方法、そして試行錯誤を繰り返す思慮深いプロセスを通して新たなパラダイムや取引ルールが生まれる方法についての洞察を得ることができます。結局のところ、MetaTrader 5ターミナルにあるすべてのテクニカル指標には、まだ活用されていない潜在能力が秘められています。課題は、目に見えないままになっている有意義な解釈を自動的に見つけ出すことにあります。

ファイル名 ファイルの説明
Stochastic_Strategy.mq5  従来のストキャスティクス戦略は、バックテストの結果、利益を上げられないことが判明した。
Stochastic_Strategy_2.mq5 最初に開発したアプリケーション。明確なトレンドの方向性を示すために日足の変動幅に依存する。
Stochastic_Strategy_3.mq5 これまでの変更に加え、より短い時間足の分析を取り入れた、最も収益性の高い取引アプリケーションバージョン。
Stochastic_AI.mq5 取引戦略の中で2番目に収益性の高いバージョン。シグナルにノイズが多すぎるように思われました。
Stochastic_Strategy.ipynb  過去のEUR/USD市場データとカスタム特徴量を分析するために作成したJupyter Notebook
Fetch_Data_Stochastic_2.mq5 私たちが作成したMQL5スクリプトは、OHLC EURUSDデータと、ストキャスティクスに関するその他のカスタム観測データを取得するものです。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/20530

共和分株式による統計的裁定取引(第9回):バックテストポートフォリオのウェイト更新 共和分株式による統計的裁定取引(第9回):バックテストポートフォリオのウェイト更新
本記事では、共和分関係にある銘柄を通じた統計的裁定取引を利用する平均回帰ベースの戦略において、ポートフォリオのウェイト更新をバックテストするためにCSVファイルを使用する方法について説明します。データベースへのローリングウィンドウ固有ベクトル比較(RWEC, Rolling Windows Eigenvector Comparison)の結果入力から、バックテストレポートの比較までを網羅します。その一方で、各RWECパラメータの役割と、それが全体的なバックテスト結果に与える影響を詳しく説明し、相対的なドローダウンの比較がこれらのパラメータをさらに改善するのにどのように役立つかを示します。
MQL5でカスタムインジケーターを作成する(第1回):Canvasグラデーションを使用したピボットベースのトレンドインジケーターの構築 MQL5でカスタムインジケーターを作成する(第1回):Canvasグラデーションを使用したピボットベースのトレンドインジケーターの構築
本記事では、ユーザーが定義した期間にわたって高速ピボットラインと低速ピボットラインを計算し、これらのラインに対する価格の位置に基づいてトレンドの方向を検出し、矢印でトレンドの開始を知らせるとともに、必要に応じて現在のバーを超えてラインを延長するピボットベースのトレンドインジケーターを、MQL5で作成します。このインジケーターは、カスタマイズ可能な色で表示される個別の上昇線と下降線、トレンドの変化に応じて色が変わる点線の高速線、そしてトレンド領域の強調表示を強化するためのCanvasオブジェクトを使用した、線間のオプションのグラデーション塗りつぶしによる動的な可視化をサポートしています。
MQL5でカスタムインジケーターを作成する(第2回):Canvasと針のメカニクスを使ったゲージ型RSIインジケーターの構築 MQL5でカスタムインジケーターを作成する(第2回):Canvasと針のメカニクスを使ったゲージ型RSIインジケーターの構築
本記事では、MQL5でゲージ型のRSIインジケーターを開発します。このインジケーターは、RSIの値を円形のスケール上の動く針で可視化し、買われすぎと売られすぎのレベルを色分けした範囲と、カスタマイズ可能な凡例を備えています。Canvasクラスを使用して、円弧、目盛り、扇形などの要素を描画し、新しいRSIデータに基づいて滑らかに更新されるようにします。
ラリー・ウィリアムズの『市場の秘密』(第1回):MQL5でスイングストラクチャーインジケーターを構築する ラリー・ウィリアムズの『市場の秘密』(第1回):MQL5でスイングストラクチャーインジケーターを構築する
MQL5でラリー・ウィリアムズ式の市場構造インジケーターを構築するための実践的なガイドです。バッファの設定、スイングポイントの検出、チャートの設定、そしてトレーダーがテクニカル市場分析でこのインジケーターをどのように活用できるかについて解説します。