English Deutsch
preview
MQL5とデータ処理パッケージの統合(第1回):高度なデータ分析と統計処理

MQL5とデータ処理パッケージの統合(第1回):高度なデータ分析と統計処理

MetaTrader 5トレーディングシステム | 2 10月 2024, 10:39
322 0
Hlomohang John Borotho
Hlomohang John Borotho

はじめに

金融市場は膨大な量のデータを生成しており、テクニカル分析だけに基づいてデータを分析するのは困難です。手作業でのテクニカル分析では、トレーダーはデータ内のパターン、トレンド、アノマリーを効果的に分析し、解釈することができません。一方、Jupyter Labのような高度なデータ分析パッケージを使用することで、トレーダーは複雑な統計分析、機械学習、データの可視化をおこなうことが可能になります。これにより、有益な取引機会を特定し、市場行動や季節的傾向を理解し、将来の値動きを予測する助けとなります。


履歴データの収集

始めるには、MetaTrader 5から.csv形式で保存された履歴データが必要です。MetaTraderプラットフォームを起動し、MetaTrader 5のパネルの上部にある[ツール]>[オプション]に移動し、[チャート]オプションに移動します。次に、ダウンロードするチャートのバー数を選択します。バーの数は無制限にするのが一番です。日付を扱うことになるし、ある期間にいくつのバーがあるかわからないからです。

チャートオプション

その後、実際のデータをダウンロードする必要があります。そのためには、 [表示] に移動し、次に[銘柄] に移動して、[仕様] タブに移動し、ダウンロードするデータの種類に応じて[バー] または [ティック] に移動するだけです。ダウンロードする履歴データの開始日と終了日を入力し、リクエストボタンをクリックすると、データがダウンロードされ、.csv形式で保存されます。

履歴データ

以上の手順で、MetaTrader取引プラットフォームから履歴データをダウンロードすることができました。次に、分析用のJupyter Lab環境をダウンロードして設定する必要があります。Jupyter Labをダウンロードして設定するには、公式の webサイトにアクセスし、簡単な手順に従ってダウンロードできます。使用しているOSの種類に応じてpipcondabrewなど、さまざまなインストール方法が用意されています。


Jupyter LabでMetaTrader 5の履歴データをロードする

MetaTrader 5の履歴データをJupyter Labに正常にロードするには、データをダウンロードしたフォルダを把握しておく必要があります。その後、Jupyter Labでそのフォルダに移動するだけです。まず、データをロードして列名を確認する必要があります。列名を正しく処理し、誤った列名を使用した場合に発生する可能性のあるエラーを回避するために、列名をしっかりと検査することが重要です。

前処理

データ分析に入る前に、データを前処理する必要があります。

1. 日時の解析:日付列を日付-時刻形式に変換します。

2. 欠測値の処理:欠損値を処理します。

3. 特徴量エンジニアリング:必要であれば、新しい特徴量を作成します。

pythonコード

import pandas as pd

# Load historical data
file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv'
data = pd.read_csv(file_path)

# Display the first few rows and column names
print(data.head())
print(data.columns)

出力

出力

出力から、列の中にいくつかの特殊文字<>があり、ファイルがタブ区切りであることを示す\tが存在することがわかります。これで、正しい列名で履歴データをロードする手順に進むことができます。

以下のコードでは次のようにします。

1. ライブラリをインポートする

  • PandasはPythonの強力なデータ操作ライブラリです。
  • テクニカル分析ライブラリ(TA lib)は、金融市場データのテクニカル分析に使用されます。

2. 履歴データを読み込む

  • File-pathは、履歴データを含むCSVファイルの場所を指定するために使用されます。
  • Pd.readはCSVファイルをpandasのData-Frameに読み込みます。区切り文字の「\t」は、ファイルがタブ区切りであることを示します。

3. データを表示する

  • Data.headはData-frameの最初の数行を表示し、内容を確認します。
  • Data.columnsはデータフレームの構造を確認するために列名を表示します。

4. データを日付順に並べ替える

  • Date.sortは、<DATE>列に基づいてデータフレームを並び替えます。これにより、データが時系列順になります。

5. RSIを計算する

  • TA.RSIは、<CLOSE>価格列を使用して相対力指数(RSI)を計算します。
  • Time-periodは14期間のRSIを指定します。
  • 計算された値は、データフレームの「RSI」という新しい列に格納されます。

6. 更新されたデータを表示する

  • Data.headは、RSI計算を検証するために、更新されたデータフレームの最初の数行を表示します。
Pythonコード
import pandas as pd
import talib as ta

# Load historical data
file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv'
data = pd.read_csv(file_path, delimiter='\t')

# Display the first few rows and column names to verify
print(data.head())
print(data.columns)

# Ensure data is sorted by the correct date column
data.sort_values('<DATE>', inplace=True)

# Calculate RSI using the '<CLOSE>' price column
data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14)

# Display the first few rows to verify
print(data.head())

出力

出力2

出力から、RSI列の下にNAN値があることがわかります。NAN値を正しく処理する必要があります。RSI関数は、指標を計算するために最小限のデータポイントを必要とします。初期にはNAN値を返すかもしれません。NAN値を正しく扱うには、列<CLOSE>のデータ型を知る必要があります。Data-Frameと列へのアクセスが正しいことを確認します。以下のPythonコードは、NAN値をどのように扱うかを示しています。

import pandas as pd
import talib as ta

# Load the historical data
file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv'
data = pd.read_csv(file_path, delimiter='\t')data['<CLOSE>'] = data['<CLOSE>'].astype(float)


# Verify the column names
print(data.columns)

# Convert the column to the correct data type if necessary
data['<CLOSE>'] = data['<CLOSE>'].astype(float)

# Calculate the RSI
data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14)

# Display the RSI values
print(data[['<CLOSE>', 'RSI']].tail(20))

出力

取扱NAN値

NAN値が正しく処理されていることがわかります。(ロックバック期間により予想される)初期期間にNAN値が表示された場合は、以下のように処理を進めることができます。

data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14)
data['RSI'] = data['RSI'].fillna(0)  # or use any other method to handle NaN values


探索的データ分析

探索的データ分析(EDA)の主な目的は、データの主要な特徴を要約することで、その根本的な構造を理解することです。EDAを実施する際には、パターンを発見し、データ内の傾向や関係を特定します。また、異常や外れ値、さらなる調査が必要とされる異常な観察結果を検出します。さらに、後の分析に影響を与える可能性のあるデータに関する仮定をチェックします。そして、データの欠損値、エラー、不整合など、対処が必要な問題を探し出し、データのクリーニングをおこないます。 

次のpythonスクリプトを使用してEDAをおこないます。

import seaborn as sns
import matplotlib.pyplot as plt
import warnings

warnings.filterwarnings("ignore")

for i in data.select_dtypes(include="number").columns:
    sns.histplot(data=data, x=i)
    plt.show()

上記のコードから、必要なライブラリをすべてインポートした後、クリーンな出力を得るために警告を無視することから始めます。「data.select_dtypes(include="number")」は数値列のみを含むデータフレームを返し、columnsはこれらの列の名前を返します。そして、データからヒストグラムをプロットします。上のコードからプロットされたヒストグラムを以下に示します。

始値

   

高値

安値

終値

TICKVOL

RSI

統計処理の操作の後、収集したデータに対してモデルの学習を進めることができます。目標は、集めた履歴データから予測を立てられるようにすることです。ここでは、RSI指標だけを使って予測します。その前に、データ間にどのような関係があるのかを知る必要があります。これは相関行列を実行することによっておこないます。その関係が、正の相関があるのか、負の相関があるのか、あるいは相関がないのかを解釈できるようにしなければなりません。

  • 正の相関:相関係数が1に近い値を示す場合、2つの変数の間に強い正の相関が存在することを意味します。例えば、「始値」と「終値」の相関が1に近い場合、これらの値は同じ方向に動く傾向があります。
  • 負の相関:相関係数が-1に近い値を示す場合、強い負の相関が存在します。例えば、「出来高」と「価格」の相関が-1に近い場合、出来高が増加すると価格が下がる傾向があることを示しています。
  • 相関関係なし:相関係数が0に近い値を示す場合、2つの変数間に相関がないことを意味します。

ヒートマップ

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import r2_score, mean_squared_error
import talib as ta  # Technical Analysis library

# Load the data
file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv'
data = pd.read_csv(file_path, delimiter='\t')

# Exploratory Data Analysis (EDA)
print(data.info())
print(data.describe())

# Visualize the closing price
plt.figure(figsize=(12, 6))
plt.plot(data['<CLOSE>'])
plt.title('XAUUSD Closing Price')
plt.xlabel('<DATE>')
plt.ylabel('Price')
plt.show()

# Feature Engineering
data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14)

# Drop rows with missing values
data.dropna(inplace=True)

# Define target variable
data['Target'] = data['<CLOSE>'].shift(-1)
data.dropna(inplace=True)

# Split the data
X = data[['RSI']]  # Only use RSI as the feature
y = data['Target']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)

# Model Development
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train, y_train)

# Predictions
y_pred = model.predict(X_test)

# Evaluate the model
mse = mean_squared_error(y_test, y_pred)
print(f'Mean Squared Error: {mse}')

# Visualize the predictions
plt.figure(figsize=(12, 6))
plt.plot(y_test.index, y_test, label='Actual Values')
plt.plot(y_test.index, y_pred, label='Predicted Values')
plt.xlabel('Samples')
plt.ylabel('TARGET_CLOSE')
plt.title('Actual vs Predicted Values')
plt.legend()
plt.show()


これがモデルを訓練したときの出力です。

  実勢価格

予測と実際

データ分析の結果、予測値が実際の値と一致していないことが確認されました。これは、モデルが根底にあるパターンを効果的に捉えていないことを示唆しています。この問題にはいくつかの原因が考えられます。

1. 訓練データの不足:データセットが小さい場合、モデルは十分な情報を得られず、パターンを正確に学習できない可能性があります。

2. 過剰適合:モデルが学習データを過度に記憶し、汎化できていない可能性があります。これは、モデルが過度に複雑であるか、データに適切に一般化されていないことが原因でよく発生します。

3. 過少適合:モデルが単純すぎて、データに存在するパターンを捉えられない場合があります。これは、モデルの複雑さが不十分であったり、重要な特徴量が欠けている場合に起こります。

4. 特徴量エンジニアリング:選択した特徴量やエンジニアリングが不十分だと、モデルが予測をおこなうための必要な関連情報を十分に持たない可能性があります。

問題を診断し、解決するためのステップ

1. データをチェックする:データがクリーンで一貫性があり、適切に前処理されていることを確認します。

2. モデルの性能を評価する:平均二乗誤差(MSE)や平均絶対誤差(MAB)などの行列を使用します。

3. 特徴量エンジニアリングの改善:テクニカル指標、ラグ値、その他の関連する金融指標など、さまざまな機能を試します。

4. ハイパーパラメータを調整する:グリッドサーチやランダムサーチのようなテクニックを使って、モデルに最適なハイパーパラメータを見つけます。


MQL5ですべてをまとめる

モデルを保存した後、予測用のpythonスクリプトを作成する必要があります。このスクリプトはモデルを読み込み、予測をおこないます。

import joblib
import sys
import os
  • Joblib:シリアライズされた機械学習モデルをロードするために使用されます。
  • Sys:「システム」の略で、コマンドライン引数へのアクセスを提供します。
  • OS:「オペレーティングシステム」の略で、ファイルの存在を確認し、ファイル操作を実行するために使用されます。
model_path = sys.argv[/home/int_junkie/Documents/ML/random_forest_model.pkl]
features_path = sys.argv[/home/int_junkie/Documents/ML/features.txt]
  • Sys.argv[1]:最初のコマンドライン引数は、モデルファイルへのパスです。
  • Sys.argv[2]:2番目のコマンドライン引数は、特徴量ファイルへのパスです。
print(f"Model path: {model_path}")
print(f"Features path: {features_path}")

デバッグ情報を出力します。デバッグ用にモデルファイルと特徴量ファイルのパスを表示します。

if not os.path.exists(model_path):
    print(f"Error: Model file not found at {model_path}")
    sys.exit(1)

if not os.path.exists(features_path):
    print(f"Error: Features file not found at {features_path}")
    sys.exit(1)
  • モデルファイルと特徴量ファイルが存在するかどうかをチェックします。
  • どちらかが存在しない場合、エラーメッセージを表示し、ステータスコード1でスクリプトを終了します。
model = joblib.load(model_path)
  • 指定された'model-path'から学習済みの機械学習モデルをロードします。
with open(features_path, 'r') as f:
    features = [float(line.strip()) for line in f]
  • 読み込むために機能ファイルを開きます。
  • 各行を読み込み、先頭や末尾の空白を取り除き、ファイルをfloatに変換して特徴量リストに格納します。
prediction = model.predict([features])[0]
  • ロードされたモデルを使用して、ロードされた特徴量に基づいて予測をおこないます。
  • モデル.予測予測値のリスト(この場合は単一の予測値)を返し、[0]は最初の予測値を抽出します。
print(prediction)
  • 予測をストランド出力にプリントします。この出力は、MQL5などの別のスクリプトやプログラムによって取り込まれ、取引の意思決定をおこなうことができます。


MQL5

OnInit()でモデルをロードします。

int OnInit(){
   // Load the model and feature names
   string modelPath = "/home/int_junkie/Documents/ML/random_forest_model.pkl";
   string featurePath = "/home/int_junkie/Documents/ML/features.txt";
   
   // Your code to load the model (use appropriate library for pkl files)

   // Initialize the features
   double features[];
   int fileHandle = FileOpen(featurePath, FILE_READ | FILE_TXT);
   if (fileHandle != INVALID_HANDLE)
     {
      string line;
      while(!FileIsEnding(fileHandle))
        {
         line = FileReadString(fileHandle);
         ArrayResize(features, ArraySize(features) + 1);
         features[ArraySize(features) - 1] = StringToDouble(line);
        }
      FileClose(fileHandle);
     }

   return(INIT_SUCCEEDED);
  }

OnTick()でpythonファイルから予測を読み込みます。

void OnTick(){
   // Declare static variables to retain values across function calls
   static bool isNewBar = false;
   static int prevBars = 0;
   
   // Get the current number of bars
   int newbar = iBars(_Symbol, _Period);
   
   // Check if the number of bars has changed
   if (prevBars == newbar) {
       // No new bar
       isNewBar = false;
   } else {
       // New bar detected
       isNewBar = true;
       // Update previous bars count to current
       prevBars = newbar;
   }
   
   // Update the features based on current data
   double features[];
   ArrayResize(features, 1);
   features[0] = iClose(Symbol(), 0, 0);
   
   // Write the features to a file
   int fileHandle = FileOpen("/home/int_junkie/Documents/ML/features.txt", FILE_WRITE | FILE_TXT);
   if (fileHandle != INVALID_HANDLE)
     {
      for (int i = 0; i < ArraySize(features); i++)
        {
         FileWrite(fileHandle, DoubleToString(features[i]));
        }
      FileClose(fileHandle);
     }
   else
     {
      Print("Error: Cannot open features file for writing");
      return;
     }
     
      // Call the Python script to get the prediction
   string command = "python /home/int_junkie/Documents/ML/predict.py /home/int_junkie/Documents/ML/random_forest_model.pkl /home/int_junkie/Documents/ML/features.txt";
   int result = ShellExecuteA(command);
   if(result != 0)
     {
      Print("Error: ShellExecuteA failed with code ", result);
      return;
     }

   // Read the prediction from a file
   Sleep(1000); // Wait for the Python script to complete
   fileHandle = FileOpen("/home/int_junkie/Documents/ML/prediction.txt", FILE_READ | FILE_TXT);
   if (fileHandle != INVALID_HANDLE)
     {
      string prediction = FileReadString(fileHandle);
      FileClose(fileHandle);

      double pred_value = StringToDouble(prediction);

      // Generate trading signals based on predictions
      double some_threshold = 0.0; // Define your threshold
      if (pred_value > some_threshold)
        {
         // Buy signal
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),Digits());
         double sl =  Ask - stopLoss * _Point;
         double tp =  Ask + takeProfit * _Point;
         trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, lotsize, Ask, sl, tp, "ML");
        }
      else if (pred_value < some_threshold)
        {
         // Sell signal
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),Digits());
         double sl = Bid + stopLoss * _Point;
         double tp = Bid - takeProfit * _Point;
         trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, lotsize, Bid, sl, tp, "ML");
        }
     }
   else
     {
      Print("Error: Cannot open prediction file for reading");
     }
  }

OnTickで、変数commandは、特徴量ファイルとモデルファイルを引数としてpythonスクリプトを実行する文字列コマンドを準備します。「ShellExecuteA (command)」は、ShellExecuteAを使ってpythonスクリプトを実行します。「Sleep (1000)」は、pythonスクリプトの実行が完了するまで1秒間待ちます。その後、予測ファイルを開いて読みます。次に、予測ファイルが正常に開いたかどうかをチェックします。正常に開いている場合、予想を読みます。もしそうでなければ、エラーを表示します。閾値変数は、取引の意思決定に使用されます。予測値より大きければ買いシグナル、予測値より小さければ、売りシグナルが発生します。


結論

要約すると、MetaTrader 5取引プラットフォームからデータを収集し、そのデータをJupyter Labで分析・統計処理に利用しました。Jupyter Labでの分析結果を基に、MQL5で取引判断を下すプロセスを統合しました。この統合により、MQL5の限られた分析や可視化機能を補完し、戦略開発やデータの処理効率を向上させます。また、取引における高度なデータ分析と統計処理が可能な柔軟かつ強力な環境を提供します。

結論として、MQL5とJupyter Labを連携させることで、従来の取引プラットフォームでは難しかった高度なデータ分析や統計処理が可能となり、取引戦略の開発・最適化に大きく貢献します。これにより、データ主導のダイナミックな金融取引の分野において、競争力を高めることができます。

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

添付されたファイル |
jupyterpackage.ipynb (323.31 KB)
features.txt (0 KB)
MQL5でボリンジャーバンド取引戦略を実装する:ステップごとのガイド MQL5でボリンジャーバンド取引戦略を実装する:ステップごとのガイド
ボリンジャーバンド売買戦略に基づくMQL5での自動売買アルゴリズム実装のためのステップごとのガイドです。トレーダーに役立つEAの作成に基づく詳細なチュートリアルです。
独自のLLMをEAに総合する(第5部): LLMを使った取引戦略の開発とテスト(I) - 微調整 独自のLLMをEAに総合する(第5部): LLMを使った取引戦略の開発とテスト(I) - 微調整
今日の人工知能の急速な発展に伴い、言語モデル(LLM)は人工知能の重要な部分となっています。私たちは、強力なLLMをアルゴリズム取引に統合する方法を考える必要があります。ほとんどの人にとって、これらの強力なモデルをニーズに応じて微調整(ファインチューニング)し、ローカルに展開して、アルゴリズム取引に適用することは困難です。本連載では、この目標を達成するために段階的なアプローチをとっていきます。
古典的な戦略をPythonで再構築する(第3回):高値更新と安値更新の予測 古典的な戦略をPythonで再構築する(第3回):高値更新と安値更新の予測
本連載では、古典的な取引戦略を実証的に分析し、AIを用いてそれらの改善が可能かどうかを検証します。本日の議論では、線形判別分析モデルを用いて高値更新と安値更新の予測に挑戦します。
ソケットを使ったツイッターのセンチメント分析 ソケットを使ったツイッターのセンチメント分析
この革新的な取引ボットは、MetaTrader 5とPythonを統合し、リアルタイムのソーシャルメディアセンチメント分析を活用して自動売買の意思決定をおこないます。特定の金融商品に関連するツイッターのセンチメントを分析することで、ボットはソーシャルメディアのトレンドを実用的な取引シグナルに変換します。ソケット通信によるクライアントサーバーアーキテクチャを採用しており、MT5の取引機能とPythonのデータ処理能力とのシームレスな相互作用を実現しています。このシステムは、クオンツファイナンスと自然言語処理の組み合わせが持つ可能性を示し、代替データソースを活用したアルゴリズム取引への最先端アプローチを提供します。このボットは将来性を示す一方で、より高度なセンチメント分析技術やリスク管理戦略の改善といった今後強化すべき分野も明らかにしています。