English Deutsch
preview
PythonとMQL5を使用した特徴量エンジニアリング(第3回):価格の角度(2)極座標

PythonとMQL5を使用した特徴量エンジニアリング(第3回):価格の角度(2)極座標

MetaTrader 5 |
256 1
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

価格レベルの変化を角度の変化へと変換しようとする関心は衰えていません。本連載の前回の記事でも述べたように、価格レベルの変化を意味のある角度として表現するには、多くの課題を克服する必要があります。

コミュニティのディスカッションやフォーラムでよく指摘される制限のひとつは、このような計算に意味のある解釈が伴っていないことです。経験豊富なコミュニティメンバーの中には、角度は2本の線の間に存在するものであるため、価格の変化によって角度を求めようとすることには物理的な意味がないと説明する方もいます。 

こうした現実世界での解釈の困難さは、価格レベルの変化によって生じる角度を計算しようとするトレーダーにとって克服すべき数多くの課題のひとつにすぎません。前回の記事では、形成される角度が価格レベルの比率として意味を持つように、x軸における時間を代用することでこの問題の解決を試みました。しかし、その探索の過程で、この変換をおこなうとデータセットが「無限大」の値で埋め尽くされることがあるとわかりました。以前の内容を簡単に復習されたい方は、こちらの記事へのリンクをご覧ください。

価格レベルの変化を角度の変化に変換する試みには多くの課題が存在し、また明確な現実的意味が伴っていないため、この分野に関する体系的な情報は限られています。 

そこで今回は、まったく新しい視点からこの問題に取り組みます。初回の試みに用いたツールと比較して、より数学的に洗練され、堅牢なアプローチを採用します。極座標にすでに精通している方は、「MQL5を始める」セクションに直接進み、これらの数学的ツールがMQL5でどのように実装されているかをご確認ください。 

それ以外の方は、まず極座標とは何かを理解し、MetaTrader 5端末において価格変化によって形成される角度をどのように計算し、それをどのように取引に活用するかについての直感を身につけていきましょう。すなわち:

  1. 私たちが提案する解決策は、現実世界で物理的な意味を持ちます。
  2. また、以前に直面した無限値や未定義値の問題も解決します。


極座標とは何か?そしてそれはどのように役立つのか?

GPSテクノロジーやシンプルなスプレッドシートアプリケーションを使用する際、私たちは「デカルト座標」と呼ばれる座標系を利用しています。これは、通常2本の直交する軸によって平面上の点を表すための数学的なシステムです。 

デカルト座標系では、任意の1点は(x, y)という座標のペアで表されます。ここでxは原点からの水平方向の距離、yは原点からの垂直方向の距離を示します。 

一方、周期的な要素や円運動を含むプロセスを研究する場合には、デカルト座標よりも極座標のほうが適していることがあります。極座標とは、基準点からの距離と、基準方向から反時計回りに測った角度を用いて平面上の点を表す、まったく異なる座標系です。  

金融市場は、ほぼ周期的に繰り返されるパターンを示す傾向があります。したがって、これらを極座標で表現することが適している可能性があります。実際、価格レベルの変化から求めたい角度は、価格レベルを極座標のペアとして表すことによって、自然に導き出せるようです。 

極座標は(r, θ)のペアで表されます。

  • r:基準点(原点)からの半径距離
  • θ:基準方向から反時計回りに測定した角度

MQL5の行列およびベクトルAPIに実装されている三角関数を利用することで、価格の変化を、角度としてスムーズに変換することが可能になります。 

この目標を達成するためには、まず本稿全体で使用する用語に慣れる必要があります。最初に、変換対象となるxおよびyの入力値を定義します。この解説では、xを銘柄の始値、yを終値と設定します。

スクリーンショット1

図1:極点に変換する直交座標点を定義する

xおよびyの入力を定義したところで、次に極座標のペアの最初の要素である「r」、すなわち原点からの半径距離を計算する必要があります。 

スクリーンショット2

図2:(x, y)からrを計算するための閉じた式

幾何学的に表現すると、極座標は円を記述するものとしてイメージすることができます。x軸とrの間に形成される角度がθです。つまり、極座標は、xおよびyを直接使用する代わりに、rとθを使うことも同じくらい有益であると提案しているのです。xおよびyが図3のように描かれていると仮定すると、半径距離rは、xとyを直角三角形の辺と見なして、ピタゴラスの定理を適用することで計算できます。

スクリーンショット3

図3:極座標は円上の点を表すものとして可視化できる

rとxの間の角度であるθは、私たちが求める現実世界での意味を満たしています。この単純な例では、θは始値と終値の変化によって形成されるトレンドの方向に相関しています。θは、終値を始値で割った値の逆正接(アークタンジェント)を計算することで求められます。これは下図に示されています。

スクリーンショット4

図4:始値xと終値yからθを計算する

任意の極座標(r, θ)が与えられた場合、以下の2つの式を使って元の価格レベルに簡単に変換することができます。

スクリーンショット5

図5:極座標を直交座標に戻す方法

これまでに4つの式を説明しましたが、そのうちθを含むのは後ろの3つの式だけです。最初にrを計算する式はθとは無関係です。後ろの3つの式にはθが含まれており、それらは簡単に微分することができます。これらの三角関数の導関数はよく知られており、オンラインや基礎的な微積分の教科書で簡単に確認できます。 

これら3つの導関数を追加の入力として用い、角度の変化とそれに対応する価格レベルの変化との関係をコンピューターに学習させる予定です。


MQL5を始める

それでは始めましょう。まず、MetaTrader 5端末から過去の市場データを取得し、価格変化によって形成される角度を計算するための変換を行うスクリプトをMQL5で作成する必要があります。

最初に、作成するCSVファイルの名前を定義し、取得するバーの本数を指定します。取得するバーの本数はブローカーによって異なる場合があるため、このパラメータはスクリプトの入力として設定しています。

#property copyright "Gamuchirai Zororo Ndawana"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs

//---File name
string file_name = _Symbol + " " + " Polar Coordinates.csv";

//---Amount of data requested
input int size = 100;
int size_fetch = size + 100;

スクリプトが実行されると、価格レベルとそれに対応する角度の変化を書き出すためのファイルハンドラを作成します。

void OnStart()
  {
      //---Write to file
       int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,",");
       
    for(int i=size;i>0;i--){
      if(i == size){
            FileWrite(file_handle,"Time","Open","High","Low","Close","R","Theta","X Derivatie","Y Derivative","Theta Derivative");
      }
      
      else{

それでは、図2で説明した式を使ってrを計算していきましょう。

double r = MathSqrt(MathPow(iOpen(_Symbol,PERIOD_CURRENT,i),2) + MathPow(iClose(_Symbol,PERIOD_CURRENT,i),2));

θは、yとxの比の逆正接によって計算されます。これはMQL5のAPIで実装されています。

double theta = MathArctan2(iClose(_Symbol,PERIOD_CURRENT,i),iOpen(_Symbol,PERIOD_CURRENT,i));

上記の図5で示したx(始値)を計算する式を思い出してください。この式をθについて微分することで、始値の一階導関数を求めることができます。ご存じの通り、cos()の導関数は「-sin()」です。

double derivative_x = r * (-(MathSin(theta)));

三角関数の導関数が既に分かっているため、yの導関数も同様に計算することができます。

double derivative_y = r * MathCos(theta);

最後に、θの一階導関数についても既に分かっています。ただし、この三角関数はMQL5のAPIに直接実装されていないため、代わりに数学的な恒等式を使用し、適切なMQL5関数を使って代用します。

double derivative_theta = (1/MathPow(MathCos(theta),2));

角度の計算が完了したので、次はデータの書き出し処理に進みましょう。

           FileWrite(file_handle,iTime(_Symbol,PERIOD_CURRENT,i),
                                 iOpen(_Symbol,PERIOD_CURRENT,i),
                                 iHigh(_Symbol,PERIOD_CURRENT,i),
                                 iLow(_Symbol,PERIOD_CURRENT,i),
                                 iClose(_Symbol,PERIOD_CURRENT,i),
                                 r,
                                 theta,
                                 derivative_x,
                                 derivative_y,
                                 derivative_y
                                 );
      } 
    }
    
    FileClose(file_handle);
  }
//+---------


データの分析

データがCSV形式で書き出されたので、これを使って角度に基づく取引をコンピューターに学習させていきましょう。開発プロセスを効率化するために、いくつかのPythonライブラリを使用します。

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

翌日に価格レベルが上昇したのか、それとも下落したのかを記録するために、データにラベルを付けましょう。

data = pd.read_csv("EURUSD  Polar Coordinates.csv")
data["UP DOWN"] = 0
data.loc[data["Close"] < data["Close"].shift(-1),"UP DOWN"] = 1
data

これが私たちが求めている取引シグナルです。元のデータセットにおいて、Rとθの両方が増加した場合、価格が下落することは一度もないという点に注目してください。この条件でpandasにクエリを実行しても、何も返ってきません。これは、極座標ペア(Rとθ)が現実世界において意味を持つことを示しています。Rとθの将来値が分かっていれば、将来の価格レベルが分かっているのと同じことなのです。

data.loc[(data["R"] < data["R"].shift(-1)) & (data['Theta'] < data['Theta'].shift(-1)) & (data['Close'] > data['Close'].shift(-1))

同様に、逆の条件で同じクエリを実行してみましょう。つまり、Rとθが増加しているにもかかわらず、その後の価格が下落したケースを探すということです。この場合も、pandasは該当するデータが0件であることを返します。

data.loc[(data["R"] > data["R"].shift(-1)) & (data['Theta'] > data['Theta'].shift(-1)) & (data['Close'] < data['Close'].shift(-1))]

したがって、私たちの取引シグナルは、将来のRとθの値が現在の値よりも大きくなるとコンピューターが予測したときに形成されます。次に進みましょう。価格データを極座標上の円の点として可視化してみます。図6に示されているように、データの分離は依然として容易ではないことが分かります。

data['Theta_rescaled'] = (data['Theta'] - data['Theta'].min()) / (data['Theta'].max() - data['Theta'].min()) * (2 * np.pi)
data['R_rescaled'] = (data['R'] - data['R'].min()) / (data['R'].max() - data['R'].min()) 

# Create the polar plot
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})

# Plot data points on the polar axis
ax.scatter(data['Theta_rescaled'], data['R_rescaled'],c=data["UP DOWN"], cmap='viridis', edgecolor='black', s=100)

# Add plot labels
ax.set_title("Polar Plot of OHLC Points")
plt.colorbar(plt.cm.ScalarMappable(cmap='viridis'), ax=ax, label='1(UP) | O(DOWN)')

plt.show()

スクリーンショット6

図6:価格データを極座標上の円の点として可視化する

念のため、値にnullが含まれていないかを簡単に確認しましょう。

data.isna().any()

スクリーンショット7

図7:値がnullかどうかを確認する


データのモデリング

すべての値が定義されていて問題ありません。次に、データにラベルを付ける必要があります。私たちの目的は、将来のθとrの値を予測することなので、それらをターゲットとして設定します。

LOOK_AHEAD = 1
data['R Target'] = data['R'].shift(-LOOK_AHEAD)
data['Theta Target'] = data['Theta'].shift(-LOOK_AHEAD)
data.dropna(inplace=True)
data.reset_index(drop=True,inplace=True)

最後の2年間のデータはテスト用に使いたいため、除外することを念頭に置いてください。

#Let's entirely drop off the last 2 years of data
_ = data.iloc[-((365 * 2) + 230):,:]
data = data.iloc[:-((365 * 2) + 230),:]
data

スクリーンショット8

図8:過去2年間のデータを除いたデータセット

それでは、手元のデータを使ってコンピューターを学習させましょう。今回は、相互作用効果の学習に特に優れているため、モデルとして勾配ブースティング木(Gradient Boosted Tree)を使用します。

from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split,TimeSeriesSplit,cross_val_score

次に、時系列データの分割用オブジェクトを定義しましょう。

tscv = TimeSeriesSplit(n_splits=5,gap=LOOK_AHEAD)

入力とターゲットを定義します。

X = data.columns[1:-5]
y = data.columns[-2:]

データを学習用とテスト用の半分ずつに分割しましょう。

train , test = train_test_split(data,test_size=0.5,shuffle=False)

学習用とテスト用の分割を用意します。

train_X = train.loc[:,X]
train_y = train.loc[:,y]

test_X = test.loc[:,X]
test_y = test.loc[:,y]

学習用とテスト用の分割を標準化する必要があります。

mean_scores = train_X.mean()
std_scores = train_X.std()

データをスケーリングします。

train_X = ((train_X - mean_scores) / std_scores)
test_X = ((test_X - mean_scores) / std_scores)

モデルを初期化します。

model = GradientBoostingRegressor()

結果を保存するテーブルを準備します。

results = pd.DataFrame(index=["Train","Test"],columns=["GBR"])

モデルを使ってrを予測するために学習させます。

results.iloc[0,0] = np.mean(np.abs(cross_val_score(model,train_X,train_y["R Target"],cv=tscv)))
results.iloc[1,0] = np.mean(np.abs(cross_val_score(model,test_X,test_y["R Target"],cv=tscv)))
results
GBR
Train 0.76686
Test   0.89129

モデルを使ってθを予測するために学習させます。

results.iloc[0,0] = np.mean(np.abs(cross_val_score(model,train_X,train_y["Theta Target"],cv=tscv)))
results.iloc[1,0] = np.mean(np.abs(cross_val_score(model,test_X,test_y["Theta Target"],cv=tscv)))
results
GBR
Train 0.368166
Test 0.110126


ONNXへのエクスポート

必要なライブラリをロードします。

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

モデルを初期化します。

r_model = GradientBoostingRegressor()
theta_model = GradientBoostingRegressor()

データセット全体の標準化スコアをCSV形式で保存します。

mean_scores = data.loc[:,X].mean()
std_scores = data.loc[:,X].std()

mean_scores.to_csv("EURUSD Polar Coordinates Mean.csv")
std_scores.to_csv("EURUSD Polar Coordinates Std.csv")

データセット全体を正規化します。

data[X] = ((data.loc[:,X] - mean_scores) / std_scores)

スケール処理したデータに対してモデルを学習させます。

r_model.fit(data.loc[:,X],data.loc[:,'R Target'])
theta_model.fit(data.loc[:,X],data.loc[:,'Theta Target'])

入力形状を定義します。

initial_types = [("float_input",FloatTensorType([1,len(X)]))]

保存するONNXプロトタイプを準備します。

r_model_proto = skl2onnx.convert_sklearn(r_model,initial_types=initial_types,target_opset=12)
theta_model_proto = skl2onnx.convert_sklearn(theta_model,initial_types=initial_types,target_opset=12)

ONNXファイルを保存します。

onnx.save(r_model_proto,"EURUSD D1 R Model.onnx")
onnx.save(theta_model_proto,"EURUSD D1 Theta Model.onnx")


MQL5を始める

これで、取引アプリケーションを構築する準備が整いました。

//+------------------------------------------------------------------+
//|                                              EURUSD Polar EA.mq5 |
//|                                               Gamuchirai Ndawana |
//|                    https://www.mql5.com/ja/users/gamuchiraindawa |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Ndawana"
#property link      "https://www.mql5.com/ja/users/gamuchiraindawa"
#property version   "1.00"

//+------------------------------------------------------------------+
//| System Constants                                                 |
//+------------------------------------------------------------------+
#define ONNX_INPUTS 9                                              //The total number of inputs for our onnx model
#define ONNX_OUTPUTS 1                                             //The total number of outputs for our onnx model
#define TF_1  PERIOD_D1                                            //The system's primary time frame
#define TRADING_VOLUME 0.1                                         //The system's trading volume

ONNXモデルをシステムリソースとしてロードします。

//+------------------------------------------------------------------+
//| System Resources                                                 |
//+------------------------------------------------------------------+
#resource "\\Files\\EURUSD D1 R Model.onnx" as uchar r_model_buffer[];
#resource "\\Files\\EURUSD D1 Theta Model.onnx" as uchar theta_model_buffer[];

グローバル変数を定義します。これらの変数の一部は、データの標準化やONNXモデルの予測結果の保存などに使用します。

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
double mean_values[] = {1.1884188643844635,1.1920754015799868,1.1847545720868993,1.1883860236998025,1.6806588395310122,0.7853854898794739,-1.1883860236998025,1.1884188643844635,1.1884188643844635};
double std_values[]  = {0.09123896995032886,0.09116171300874902,0.0912656190371797,0.09120265318308786,0.1289537623737421,0.0021932437785043796,0.09120265318308786,0.09123896995032886,0.09123896995032886};
double current_r,current_theta;
long r_model,theta_model;
vectorf r_model_output = vectorf::Zeros(ONNX_OUTPUTS);
vectorf theta_model_output = vectorf::Zeros(ONNX_OUTPUTS);
double bid,ask;
int ma_o_handler,ma_c_handler,state;
double ma_o_buffer[],ma_c_buffer[];

取引ライブラリを読み込みます。

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

私たちの取引アプリケーションは主にイベントハンドラで構成されています。アプリケーションのライフサイクルの各段階で、それぞれの目的に応じた専用の関数を呼び出して処理をおこないます。たとえば、初期化時にはテクニカル指標を設定し、新しい価格データが利用可能になったときには、それらの指標の値を更新します。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   if(!setup())
     {
      Comment("Failed To Load Corretly");
      return(INIT_FAILED);
     }

   Comment("Started");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   OnnxRelease(r_model);
   OnnxRelease(theta_model);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   update();
  }
//+------------------------------------------------------------------+

ONNXモデルから予測を取得します。モデルが期待する形式やサイズのデータを正しく受け取るように、すべてのモデル入力はfloat型にキャストしていますのでご注意ください。

//+------------------------------------------------------------------+
//| Get a prediction from our models                                 |
//+------------------------------------------------------------------+
void get_model_prediction(void)
  {
//Define theta and r
   double o = iOpen(_Symbol,PERIOD_CURRENT,1);
   double h = iHigh(_Symbol,PERIOD_CURRENT,1);
   double l = iLow(_Symbol,PERIOD_CURRENT,1);
   double c = iClose(_Symbol,PERIOD_CURRENT,1);
   current_r = MathSqrt(MathPow(o,2) + MathPow(c,2));
   current_theta = MathArctan2(c,o);

   vectorf model_inputs =
     {
      (float) o,
      (float) h,
      (float) l,
      (float) c,
      (float) current_r,
      (float) current_theta,
      (float)(current_r * (-(MathSin(current_theta)))),
      (float)(current_r * MathCos(current_theta)),
      (float)(1/MathPow(MathCos(current_theta),2))
     };

//Standardize the model inputs
   for(int i = 0; i < ONNX_INPUTS;i++)
     {
      model_inputs[i] = (float)((model_inputs[i] - mean_values[i]) / std_values[i]);
     }

//Get a prediction from our model
   OnnxRun(r_model,ONNX_DATA_TYPE_FLOAT,model_inputs,r_model_output);
   OnnxRun(theta_model,ONNX_DATA_TYPE_FLOAT,model_inputs,theta_model_output);

//Give our prediction
   Comment(StringFormat("R: %f \nTheta: %f\nR Forecast: %f\nTheta Forecast: %f",current_r,current_theta,r_model_output[0],theta_model_output[0]));
  }

新しい価格が提示されるたびにシステムを更新します。

//+------------------------------------------------------------------+
//| Update system state                                              |
//+------------------------------------------------------------------+
void update(void)
  {
   static datetime time_stamp;
   datetime current_time = iTime(_Symbol,TF_1,0);

   bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
   ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   if(current_time != time_stamp)
     {
      CopyBuffer(ma_o_handler,0,0,1,ma_o_buffer);
      CopyBuffer(ma_c_handler,0,0,1,ma_c_buffer);
      time_stamp = current_time;
      get_model_prediction();
      manage_account();
      if(PositionsTotal() == 0)
         get_signal();
     }
  }

口座を管理します。取引で損失が出ている場合は、できるだけ早くその取引を終了したいと考えています。また、取引を開始した後でも、移動平均線のクロスがそのポジションへの信頼を損なうような形になった場合には、直ちに取引を終了します。

//+------------------------------------------------------------------+
//| Manage the open positions we have in the market                  |
//+------------------------------------------------------------------+
void manage_account()
  {
   if(AccountInfoDouble(ACCOUNT_BALANCE) < AccountInfoDouble(ACCOUNT_EQUITY))
     {
      while(PositionsTotal() > 0)
         Trade.PositionClose(Symbol());
     }

   if(state == 1)
     {
      if(ma_c_buffer[0] < ma_o_buffer[0])
         Trade.PositionClose(Symbol());
     }

   if(state == -1)
     {
      if(ma_c_buffer[0] > ma_o_buffer[0])
         Trade.PositionClose(Symbol());
     }
  }

テクニカル指標やONNXモデルなどのシステム変数を設定します。

//+------------------------------------------------------------------+
//| Setup system variables                                           |
//+------------------------------------------------------------------+
bool setup(void)
  {
   ma_o_handler = iMA(Symbol(),TF_1,50,0,MODE_SMA,PRICE_CLOSE);
   ma_c_handler = iMA(Symbol(),TF_1,10,0,MODE_SMA,PRICE_CLOSE);

   r_model = OnnxCreateFromBuffer(r_model_buffer,ONNX_DEFAULT);
   theta_model = OnnxCreateFromBuffer(theta_model_buffer,ONNX_DEFAULT);

   if(r_model == INVALID_HANDLE)
      return(false);
   if(theta_model == INVALID_HANDLE)
      return(false);

   ulong input_shape[] = {1,ONNX_INPUTS};
   ulong output_shape[] = {1,ONNX_OUTPUTS};

   if(!OnnxSetInputShape(r_model,0,input_shape))
      return(false);
   if(!OnnxSetInputShape(theta_model,0,input_shape))
      return(false);

   if(!OnnxSetOutputShape(r_model,0,output_shape))
      return(false);
   if(!OnnxSetOutputShape(theta_model,0,output_shape))
      return(false);

   return(true);
  }

取引シグナルがあるかどうかを確認します。まずは、移動平均線のクロスオーバーストラテジーの方向性を確認します。その後、ONNXモデルによって与えられた予測を確認します。したがって、移動平均線のクロスオーバーが弱気のシグナルを示していても、rおよびθのONNXモデルが強気のシグナルを示している場合は、両方のシステムが一致するまでポジションは取りません。

//+------------------------------------------------------------------+
//| Check if we have a trading signal                                |
//+------------------------------------------------------------------+
void get_signal(void)
  {
   if(ma_c_buffer[0] > ma_o_buffer[0])
     {
      if((r_model_output[0] < current_r) && (theta_model_output[0] < current_theta))
        {
         return;
        }
        
      if((r_model_output[0] > current_r) && (theta_model_output[0] > current_theta))
        {
         Trade.Buy(TRADING_VOLUME * 2,Symbol(),ask,0,0);
         Trade.Buy(TRADING_VOLUME * 2,Symbol(),ask,0,0);
         state = 1;
         return;
        }
        
      Trade.Buy(TRADING_VOLUME,Symbol(),ask,0,0);
      state = 1;
      return;
     }

   if(ma_c_buffer[0] < ma_o_buffer[0])
     {
      if((r_model_output[0] > current_r) && (theta_model_output[0] > current_theta))
        {
         return;
        }

     if((r_model_output[0] < current_r) && (theta_model_output[0] < current_theta))
        {
         
         Trade.Sell(TRADING_VOLUME * 2,Symbol(),bid,0,0);
         Trade.Sell(TRADING_VOLUME * 2,Symbol(),bid,0,0);
         state = -1;
         return;
        }

      Trade.Sell(TRADING_VOLUME,Symbol(),bid,0,0);
      state = -1;
      return;
     }
  }

使用していないシステム定数を未定義にします。

//+------------------------------------------------------------------+
//| Undefine system variables we don't need                          |
//+------------------------------------------------------------------+
#undef ONNX_INPUTS
#undef ONNX_OUTPUTS
#undef TF_1
//+------------------------------------------------------------------+


システムのテスト

では、システムのテストを開始しましょう。データ準備の段階で、2022年1月1日以降のデータを除外したことを思い出してください。これは、バックテストがこれまで一度も見たことのないデータに対して、私たちの戦略がどれだけうまく機能するかを反映させるためです。

スクリーンショット9

図9:バックテストの設定

次に、初期口座設定を指定します。 



図10:サンプル外データを使った重要なバックテストのための設定(2つ目)

新しいシステムによって生成された取引シグナルのエクイティカーブを確認できます。戦略は$5000の初期残高からスタートし、最終的には約$7000の残高で終了しました。これは良好な結果であり、今後も戦略の探究と再定義を続けていく励みになります。

スクリーンショット11

図11:角度変換によって生成されたシグナルを使った取引のバックテスト結果

結果を詳しく分析してみましょう。私たちの戦略は、サンプル外データにおいて88%の精度を達成しました。これは非常に有望な結果であり、読者にとっては、今回のアプリケーションで示したMetaTrader 5ターミナルの機能と能力を拡張することで、自身のアプリケーションを構築するための良い出発点となるかもしれません。または、私たちのフレームワークをあくまで参考として活用し、取引戦略自体を完全に独自のものに置き換えるという選択肢も考えられます。

スクリーンショット12

図12:バックテストの結果を詳細に分析する


結論

本日紹介したソリューションは、価格水準の変化を角度の変化に意味のある形で変換することによって見出される潜在的な優位性を、どのように実現するかを示しました。私たちの手法は、この課題に取り組むためのシンプルで洗練されたフレームワークを提供し、取引ロジックと数学的ロジックの最良の融合を可能にします。さらに、価格を直接予測しようとして行き詰まる一般的な市場参加者とは異なり、読者は今や、価格そのものを知るのと同じくらい有用でありながら、価格よりも一貫して予測しやすい代替のターゲットを持つことができます。

添付ファイル 詳細
Polar Fetch Data 価格データを取得して極座標に変換するためのカスタマイズされたスクリプト
EURUSD Polar EA 検出された角度の変化から生成されたシグナルを取引するEA
EURUSD D1 R Model Rの将来の値を予測するONNXモデル
EURUSD D1 Theta Model θの将来の値を予測するONNXモデル
EURUSD Polar Coordinates MQL5スクリプトで取得したデータを分析するために使用したJupyter Notebook

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

最後のコメント | ディスカッションに移動 (1)
Too Chee Ng
Too Chee Ng | 27 3月 2025 において 10:41
好き
MQL5で取引管理者パネルを作成する(第9回):コード編成(I) MQL5で取引管理者パネルを作成する(第9回):コード編成(I)
このディスカッションでは、大規模なコードベースを扱う際に直面する課題について掘り下げます。MQL5におけるコード構成のベストプラクティスを紹介し、取引管理パネルのソースコードの可読性と拡張性を向上させるための実践的なアプローチを実装します。また、他の開発者がアルゴリズム開発で活用できる再利用可能なコードコンポーネントの開発も目指しています。ぜひ最後までお読みいただき、ご意見をお寄せください。
プライスアクション分析ツールキットの開発(第11回):Heikin Ashi Signal EA プライスアクション分析ツールキットの開発(第11回):Heikin Ashi Signal EA
MQL5は、ユーザーの好みに合わせてカスタマイズ可能な自動売買システムを開発するための無限の可能性を提供します。複雑な数値計算も実行できることをご存知でしょうか。この記事では、自動売買戦略として日本の平均足手法を紹介します。
ログレコードをマスターする(第5回):キャッシュとローテーションによるハンドラの最適化 ログレコードをマスターする(第5回):キャッシュとローテーションによるハンドラの最適化
この記事では、ハンドラへのフォーマッタ追加、実行サイクルを管理するためのCIntervalWatcherクラスの導入、キャッシュとファイルローテーションによる最適化、さらにパフォーマンステストおよび実用的な使用例を通じて、ログライブラリをさらに改善します。これらの機能強化により、さまざまな開発シナリオに柔軟に対応可能な、効率的でスケーラブルなロギングシステムが実現します。
トレンドフォロー戦略のためのLSTMによるトレンド予測 トレンドフォロー戦略のためのLSTMによるトレンド予測
長・短期記憶(LSTM: Long Short-Term Memory)は、長期的な依存関係を捉える能力に優れ、勾配消失問題にも対処できる、時系列データ処理に特化した再帰型ニューラルネットワーク(RNN: Recurrent Neural Network)の一種です。本記事では、LSTMを活用して将来のトレンドを予測し、トレンドフォロー型戦略のパフォーマンスを向上させる方法について解説します。内容は、主要な概念と開発の背景の紹介、MetaTrader 5からのデータ取得、そのデータを用いたPythonでのモデル学習、学習済みモデルのMQL5への統合、そして統計的なバックテストに基づく結果の分析と今後の展望までを含みます。