English Русский Deutsch
preview
PythonとMQL5でロボットを開発する(第1回):データ前処理

PythonとMQL5でロボットを開発する(第1回):データ前処理

MetaTrader 5トレーディングシステム | 20 8月 2024, 09:39
15 0
Yevgeniy Koshtenko
Yevgeniy Koshtenko

はじめに

市場はますます複雑になっています。今日、それはアルゴリズムの戦いに変わりつつあります。取引高の95%以上はロボットによって生み出されています。 

次のステップは機械学習です。これらは強力なAIではありませんが、単純な線形アルゴリズムでもありません。機械学習モデルは困難な状況でも利益を上げることができます。機械学習を取引システムに応用するのは興味深いものです。ニューラルネットワークのおかげで、取引ロボットはビッグデータを分析し、パターンを見つけ、値動きを予測します。


データの収集、処理、サンプルの拡大、特徴量エンジニアリング、モデルの選択と訓練、Pythonを使った取引システムの作成、取引のモニタリングといった、取引ロボットの開発サイクルについて見ていきます。

Pythonでの作業には、機械学習の分野でのスピード、特徴量の選択と生成能力といった利点があります。ONNXにモデルをエクスポートするには、Pythonとまったく同じ特徴量生成ロジックが必要ですが、これは容易ではありません。Pythonを使用したオンライン取引を選択したのはこのためです。

タスクの設定とツールの選択

このプロジェクトの目標は、取引するための収益性の高い持続可能な機械学習モデルをPythonで作成することです。作業の段階は次の通りです。

  • MetaTrader 5からデータを収集し、主な機能を読み込む
  • データ拡張によるサンプル拡大  
  • ラベルによるデータのマークアップ
  • 特徴量エンジニアリング:生成、クラスタリング、選択
  • MLモデルの選択と訓練(アンサンブルも可)
  • メトリクスによるモデルの評価
  • 収益性評価のためのテスター開発  
  • 新たなデータに基づいて好結果を出す
  • Python MQL5による取引アルゴリズムの実装
  • MetaTrader 5との統合
  • モデルの改善

ツール:Python MQL5、スピードと機能性を追求したPythonのMLライブラリ

目標と目的を明確にしました。記事の枠内で以下の作業をおこないます。

  • MetaTrader 5からデータを収集し、主な機能を読み込む
  • データ拡張によるサンプル拡大  
  • ラベルによるデータのマークアップ
  • 特徴量エンジニアリング:生成、クラスタリング、選択

環境の設定とインポート:データ収集

まず、MetaTrader 5で履歴データを取得する必要があります。コードは、端末ファイルへのパスを初期化メソッドに渡すことで、取引プラットフォームへの接続を確立します。

ループ内で、mt5.copy_rates_range()メソッドを使用して、商品、時間枠、開始日と終了日をパラメータとして指定し、データを取得します。失敗した試行回数のカウンタと、安定した接続までの遅延があります。

この関数は、mt5.shutdown()メソッドを使用してプラットフォームから切断することで終了します。

これは、プログラム内でさらに呼び出すための別の関数です。 

スクリプトを実行するには、以下のライブラリをインストールする必要がります。

  1. NumPy:多次元配列と数学関数を扱うためのライブラリ

  2. Pandas:表や時系列を扱うための便利なデータ構造を提供するデータ分析ツール

  3. Random:乱数を生成し、シーケンスからランダムな要素を選択するモジュール

  4. Datetime:日付と時刻を操作するためのクラスと関数を提供

  5. MetaTrader5:MetaTrader 5取引端末とのインタラクション用ライブラリ

  6. Time:時間とプログラムの実行遅延を扱うモジュール

  7. Concurrent.futures:並列タスクと非同期タスクを実行するためのツール(将来的に、多通貨の並列処理に必要になる)

  8. Tqdm:反復処理の実行時に進捗状況を表示するためのライブラリ(将来的に、多通貨の並列処理に必要になる)

  9. Train_test_split:機械学習モデルを学習する際に、データセットを訓練セットとテストセットに分割する関数

pipを使用して以下のコマンドを実行すればインストールできます。

pip install numpy pandas MetaTrader5 concurrent-futures tqdm sklearn matplotlib imblearn
import numpy as np
import pandas as pd
import random
from datetime import datetime
import MetaTrader5 as mt5
import time
import concurrent.futures
from tqdm import tqdm
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from sklearn.utils import class_weight
from imblearn.under_sampling import RandomUnderSampler

# GLOBALS
MARKUP = 0.00001
BACKWARD = datetime(2000, 1, 1)
FORWARD = datetime(2010, 1, 1)
EXAMWARD = datetime(2024, 1, 1)
MAX_OPEN_TRADES = 3
symbol = "EURUSD"

def retrieve_data(symbol, retries_limit=300):
    terminal_path = "C:/Program Files/MetaTrader 5/Arima/terminal64.exe"

    attempt = 0
    raw_data = None

    while attempt < retries_limit:
        if not mt5.initialize(path=terminal_path):
            print("MetaTrader initialization failed")
            return None

        instrument_count = mt5.symbols_total()
        if instrument_count > 0:
            print(f"Number of instruments in the terminal: {instrument_count}")
        else:
            print("No instruments in the terminal")

        rates = mt5.copy_rates_range(symbol, mt5.TIMEFRAME_H1, BACKWARD, EXAMWARD)
        mt5.shutdown()

        if rates is None or len(rates) == 0:
            print(f"Data for {symbol} not available (attempt {attempt+1})")
            attempt += 1
            time.sleep(1)
        else:
            raw_data = pd.DataFrame(rates[:-1], columns=['time', 'open', 'high', 'low', 'close', 'tick_volume'])
            raw_data['time'] = pd.to_datetime(raw_data['time'], unit='s')
            raw_data.set_index('time', inplace=True)
            break

    if raw_data is None:
        print(f"Data retrieval failed after {retries_limit} attempts")
        return None

    # Add simple features
    raw_data['raw_SMA_10'] = raw_data['close'].rolling(window=10).mean()
    raw_data['raw_SMA_20'] = raw_data['close'].rolling(window=20).mean()
    raw_data['Price_Change'] = raw_data['close'].pct_change() * 100

    # Additional features
    raw_data['raw_Std_Dev_Close'] = raw_data['close'].rolling(window=20).std()
    raw_data['raw_Volume_Change'] = raw_data['tick_volume'].pct_change() * 100

    raw_data['raw_Prev_Day_Price_Change'] = raw_data['close'] - raw_data['close'].shift(1)
    raw_data['raw_Prev_Week_Price_Change'] = raw_data['close'] - raw_data['close'].shift(7)
    raw_data['raw_Prev_Month_Price_Change'] = raw_data['close'] - raw_data['close'].shift(30)

    raw_data['Consecutive_Positive_Changes'] = (raw_data['Price_Change'] > 0).astype(int).groupby((raw_data['Price_Change'] > 0).astype(int).diff().ne(0).cumsum()).cumsum()
    raw_data['Consecutive_Negative_Changes'] = (raw_data['Price_Change'] < 0).astype(int).groupby((raw_data['Price_Change'] < 0).astype(int).diff().ne(0).cumsum()).cumsum()
    raw_data['Price_Density'] = raw_data['close'].rolling(window=10).apply(lambda x: len(set(x)))
    raw_data['Fractal_Analysis'] = raw_data['close'].rolling(window=10).apply(lambda x: 1 if x.idxmax() else (-1 if x.idxmin() else 0))
    raw_data['Price_Volume_Ratio'] = raw_data['close'] / raw_data['tick_volume']
    raw_data['Median_Close_7'] = raw_data['close'].rolling(window=7).median()
    raw_data['Median_Close_30'] = raw_data['close'].rolling(window=30).median()
    raw_data['Price_Volatility'] = raw_data['close'].rolling(window=20).std() / raw_data['close'].rolling(window=20).mean()

    print("\nOriginal columns:")
    print(raw_data[['close', 'high', 'low', 'open', 'tick_volume']].tail(100))

    print("\nList of features:")
    print(raw_data.columns.tolist())

    print("\nLast 100 features:")
    print(raw_data.tail(100))

    # Replace NaN values with the mean
    raw_data.fillna(raw_data.mean(), inplace=True)

    return raw_data

retrieve_data(symbol)

グローバル変数(スプレッドコストとブローカー手数料、訓練サンプルとテストサンプルの日付、最大取引数、銘柄)を定義します。

MetaTrader 5から相場をループで読み込みます。データはDataFrameに変換され、次の特徴量で強化されます。

  • 10および20期間のSMA移動平均
  • 価格変動 
  • 標準価格偏差
  • ボリューム変化
  • 日次/月次価格変動
  • 価格中央値

これらの特徴量は、価格に影響する要素を考慮するのに役立ちます。

DataFrameの列と最新のエントリに関する情報が表示されます。

最初の関数がどのように実行されたかを見てみましょう。

最初の関数

すべてが機能しています。このコードでは、見積もりを読み込み、特徴量を準備し読み込みました。コードの次の部分に移りましょう。


データ拡張でサンプルを拡大する

データ拡張とは、サンプルサイズを増やし、モデルの質を向上させるために、既存の学習例に基づいて新しい学習例を生成することです。データ増強は、限られたデータしかない時系列の予測に適しており、モデルの誤差を減らし、ロバスト性を高めます。

財務データの拡張方法

  • ノイズに対するロバスト性を確保するためのノイズ(ランダムな偏差)の追加
  • 異なる開発シナリオをモデル化するためのタイムシフト
  • 価格高騰/下落をモデル化するためのスケーリング
  • ソースデータの反転

DataFrameと各メソッドの新しい例の数を受け取る入力拡大関数を実装しました。さまざまなアプローチで新しいデータを生成し、元のDataFrameに連結します。

def augment_data(raw_data, noise_level=0.01, time_shift=1, scale_range=(0.9, 1.1)):
    print(f"Number of rows before augmentation: {len(raw_data)}")

    # Copy raw_data into augmented_data
    augmented_data = raw_data.copy()

    # Add noise
    noisy_data = raw_data.copy()
    noisy_data += np.random.normal(0, noise_level, noisy_data.shape)

    # Replace NaN values with the mean
    noisy_data.fillna(noisy_data.mean(), inplace=True)

    augmented_data = pd.concat([augmented_data, noisy_data])
    print(f"Added {len(noisy_data)} rows after adding noise")

    # Time shift
    shifted_data = raw_data.copy()
    shifted_data.index += pd.DateOffset(hours=time_shift)

    # Replace NaN values with the mean
    shifted_data.fillna(shifted_data.mean(), inplace=True)

    augmented_data = pd.concat([augmented_data, shifted_data])
    print(f"Added {len(shifted_data)} rows after time shift")

    # Scaling
    scale = np.random.uniform(scale_range[0], scale_range[1])
    scaled_data = raw_data.copy()
    scaled_data *= scale

    # Replace NaN values with the mean
    scaled_data.fillna(scaled_data.mean(), inplace=True)

    augmented_data = pd.concat([augmented_data, scaled_data])
    print(f"Added {len(scaled_data)} rows after scaling")

    # Inversion
    inverted_data = raw_data.copy()
    inverted_data *= -1

    # Replace NaN values with the mean
    inverted_data.fillna(inverted_data.mean(), inplace=True)

    augmented_data = pd.concat([augmented_data, inverted_data])
    print(f"Added {len(inverted_data)} rows after inversion")

    print(f"Number of rows after augmentation: {len(augmented_data)}")

    # Print dates by years
    print("Print dates by years:")
    for year, group in augmented_data.groupby(augmented_data.index.year):
        print(f"Year {year}: {group.index}")

    return augmented_data

このコードを呼び出すと、次のような結果が得られます。

2

データ拡張法を適用した結果、15万のH1プライスバーのオリジナルセットは、747,000本の文字列に拡張されました。機械学習における多くの権威ある研究は、合成例の生成による学習データ量の大幅な増加が、学習済みモデルの品質メトリクスにプラスの効果をもたらすことを示しています。私たちの場合も、この手法が望ましい効果をもたらすと期待しています。


データのラベル付け

データのラベル付けは、教師あり学習アルゴリズムの成功に不可欠です。これにより、モデルが学習するラベルをソースデータに与えることができます。ラベル付けされたデータは、精度を向上させ、汎化を改善し、訓練をスピードアップし、モデルの評価を容易にします。EURUSDの予測問題では、次の価格変動がスプレッドと手数料より大きいかどうかを示す「ラベル」バイナリ列を追加しました。これにより、モデルはスプレッドの再生とロールバックしない傾向のパターンを学習することができます。

    ラベル付けは、機械学習アルゴリズムが、生のままでは見えない複雑なパターンをデータから見つけ出すための重要な役割を果たしています。コードのレビューに移りましょう。

    def markup_data(data, target_column, label_column, markup_ratio=0.00002):
        data.loc[:, label_column] = np.where(data.loc[:, target_column].shift(-1) > data.loc[:, target_column] + markup_ratio, 1, 0)
    
        data.loc[data[label_column].isna(), label_column] = 0
    
        print(f"Number of markups on price change greater than markup: {data[label_column].sum()}")
    
        return data

    このコードを実行し、データに含まれるラベルの数を取得します。この関数はフレームを返します。すべてが機能しています。ちなみに、70万を超えるデータポイントのうち、価格がスプレッド以上に変動したのは7万件に過ぎません。

    3

    さて、もう1つのデータマークアップ機能があります。今回は実際の収益に近いです。


    ターゲットラベル関数

    def label_data(data, symbol, min=2, max=48):
        terminal_path = "C:/Program Files/MetaTrader 5/Arima/terminal64.exe"
    
        if not mt5.initialize(path=terminal_path):
            print("Error connecting to MetaTrader 5 terminal")
            return
    
        symbol_info = mt5.symbol_info(symbol)
        stop_level = 100 * symbol_info.point
        take_level = 800 * symbol_info.point
    
        labels = []
    
        for i in range(data.shape[0] - max):
            rand = random.randint(min, max)
            curr_pr = data['close'].iloc[i]
            future_pr = data['close'].iloc[i + rand]
            min_pr = data['low'].iloc[i:i + rand].min()
            max_pr = data['high'].iloc[i:i + rand].max()
    
            price_change = abs(future_pr - curr_pr)
    
            if price_change > take_level and future_pr > curr_pr and min_pr > curr_pr - stop_level:
                labels.append(1)  # Growth
            elif price_change > take_level and future_pr < curr_pr and max_pr < curr_pr + stop_level:
                labels.append(0)  # Fall
            else:
                labels.append(None)
    
        data = data.iloc[:len(labels)].copy()
        data['labels'] = labels
    
        data.dropna(inplace=True)
    
        X = data.drop('labels', axis=1)
        y = data['labels']
    
        rus = RandomUnderSampler(random_state=2)
        X_balanced, y_balanced = rus.fit_resample(X, y)
    
        data_balanced = pd.concat([X_balanced, y_balanced], axis=1)
    
        return data

    この関数は、取引利益に関する機械学習モデルを訓練するためのターゲットラベルを取得します。この関数はMetaTrader 5に接続し、銘柄情報を取得し、ストップ/テイクレベルを計算します。そして、各エントリポイントについて、ランダムな期間後に将来の価格が決定されます。価格の変化がテイクプロフィットを上回り、ストップに触れず、また上昇/下降の条件を満たした場合、それに応じて1.0/0.0マークが追加されます。それ以外の場合は、何も追加されません。ラベル付きデータのみを含む新しいデータフレームが作成されます。どれも平均値に置き換えられません。

    成長/下降ラベルの数が表示されます。

    すべてが意図したとおりに機能します。データとその派生物を受け取っています。データは2つの異なる関数で拡張され、大幅に補足され、マークされています。次のステップに進みましょう。クラスのバランス調整です。

    ところで、機械学習の経験豊富な読者であれば、最終的には回帰モデルではなく分類モデルを開発することになることは以前から理解していると思います。私は回帰モデルの方が好きです。分類モデルよりも、もう少し予測ロジックがあると思うからです。 

    次の手はクラスバランシングです。


    クラスバランシング:分類

    分類は、共通の特徴量に従って情報を構造化する人間の自然な能力に基づく分析の基本的な方法です。分類の最初の用途の1つは、地質学的特徴量に基づいて有望な地域を特定することです。

    コンピュータの出現により、分類は新たなレベルに達しましたが、細部の見かけの混沌からパターンを発見するという本質は変わりません。金融市場においては、価格ダイナミクスを成長/下落に分類することが重要です。しかし、クラスのバランスが崩れる可能性があります。トレンドが少なく、フラットが多いです。

    そのため、支配的な例を削除したり、希少な例を生成したりするクラスバランシング手法が用いられます。これにより、モデルは重要だが稀な価格変動パターンを確実に認識することができます。

    pip install imblearn

        data = data.iloc[:len(labels)].copy()
        data['labels'] = labels
    
        data.dropna(inplace=True)
    
        X = data.drop('labels', axis=1)
        y = data['labels']
    
        rus = RandomUnderSampler(random_state=2)
        X_balanced, y_balanced = rus.fit_resample(X, y)
    
        data_balanced = pd.concat([X_balanced, y_balanced], axis=1)

    これで、クラスのバランスが取れました。各クラス(値下げと落下)に同数のラベルを用意しています。

    データ予測で最も重要なこと、特徴量に話を移しましょう。


    機械学習における特徴量とは何か?

    属性と特徴量は、子供の頃から誰もが知っている基本的な概念です。オブジェクトを説明するとき、そのプロパティを列挙します。これらが属性です。このような特徴の集合によって、オブジェクトを完全に特徴付けることができます。

    データ分析でも同じことで、各観測は一連の数値的特徴量とカテゴリー特徴量によって記述されます。成功するためには、有益な特徴量を選択することが重要です。

    より高いレベルでは、研究対象をより適切に記述するために、初期パラメータから新しい派生特性が構築される特徴エンジニアリングがおこなわれます。

    こうして、オブジェクトをその特徴量によって記述することで世界を知るという人間の経験は、形式化と自動化のレベルで科学に移されます。


    自動特徴量抽出

    特徴量エンジニアリングとは、ソースデータを機械学習モデルを学習するための特徴量セットに変換することです。目標は、最も有益な特徴量を見つけることです。手動アプローチ(人が特徴量を選択する)と自動アプローチ(アルゴリズムを使用する)があります。

    ここでは自動アプローチを使用します。特徴量生成法を適用して、データから最適な特徴量を自動的に抽出してみましょう。次に、得られた集合から最も情報量の多いものを選択します。

    以下は、新しい特徴量を生成するメソッドです。 

    def generate_new_features(data, num_features=200, random_seed=1):
        random.seed(random_seed)
        new_features = {}
    
        for _ in range(num_features):
            feature_name = f'feature_{len(new_features)}'
    
            col1_idx, col2_idx = random.sample(range(len(data.columns)), 2)
            col1, col2 = data.columns[col1_idx], data.columns[col2_idx]
    
            operation = random.choice(['add', 'subtract', 'multiply', 'divide', 'shift', 'rolling_mean', 'rolling_std', 'rolling_max', 'rolling_min', 'rolling_sum'])
    
            if operation == 'add':
                new_features[feature_name] = data[col1] + data[col2]
            elif operation == 'subtract':
                new_features[feature_name] = data[col1] - data[col2]
            elif operation == 'multiply':
                new_features[feature_name] = data[col1] * data[col2]
            elif operation == 'divide':
                new_features[feature_name] = data[col1] / data[col2]
            elif operation == 'shift':
                shift = random.randint(1, 10)
                new_features[feature_name] = data[col1].shift(shift)
            elif operation == 'rolling_mean':
                window = random.randint(2, 20)
                new_features[feature_name] = data[col1].rolling(window).mean()
            elif operation == 'rolling_std':
                window = random.randint(2, 20)
                new_features[feature_name] = data[col1].rolling(window).std()
            elif operation == 'rolling_max':
                window = random.randint(2, 20)
                new_features[feature_name] = data[col1].rolling(window).max()
            elif operation == 'rolling_min':
                window = random.randint(2, 20)
                new_features[feature_name] = data[col1].rolling(window).min()
            elif operation == 'rolling_sum':
                window = random.randint(2, 20)
                new_features[feature_name] = data[col1].rolling(window).sum()
    
        new_data = pd.concat([data, pd.DataFrame(new_features)], axis=1)
    
        print("\nGenerated features:")
        print(new_data[list(new_features.keys())].tail(100))
    
        return data

    以下のパラメータに注意してください。

    random_seed=42

    random_seedパラメータは、新しい特徴量の生成結果の再現性のために必要です。generate_new_features関数は、ソースデータから新しい特徴量を作成します。入力:データ、特徴量数、シード

    Randomは与えられたシードで初期化されます。ループの中で:名前が生成され、2つの既存の特徴量と演算(加算、減算など)がランダムに選択されます。選択された操作の新しい特徴量が計算されます。

    生成後、元のデータに新しい特徴量が追加されます。自動生成された特徴量を持つ更新されたデータが返されます。

    このコードによって、機械学習の質を向上させるための新しい特徴量を自動的に作り出すことができます。

    では、コードを実行して結果を見てみましょう。

    5

    100個の新しい特徴量が生成されました。次の段階、つまり特徴量クラスタリングに移りましょう。


    特徴量のクラスタリング

    特徴量クラスタリングでは、類似した特徴量をグループ化し、その数を減らします。これにより、冗長なデータを取り除き、相関を減らし、過剰適合せずにモデルを単純化することができます。

    一般的なアルゴリズムには、k-means(クラスタ数が指定され、特徴量はセントロイドを中心にグループ化される)および階層クラスタリング(複数レベルのツリー構造)があります。

    特徴量をクラスタリングすることで、無駄な特徴量を整理し、モデルの効率を向上させることができます。

    特徴量クラスタリングのコードを見てみましょう。

    from sklearn.mixture import GaussianMixture
    
    def cluster_features_by_gmm(data, n_components=4):
        X = data.drop(['label', 'labels'], axis=1)
    
        X = X.replace([np.inf, -np.inf], np.nan)
        X = X.fillna(X.median())
    
        gmm = GaussianMixture(n_components=n_components, random_state=1)
    
        gmm.fit(X)
    
        data['cluster'] = gmm.predict(X)
    
        print("\nFeature clusters:")
        print(data[['cluster']].tail(100))
    
        return data

    特徴量クラスタリングにはGMM (Gaussian Mixture Model、混合ガウスモデル)アルゴリズムを使用します。基本的な考え方は、データが正規分布の混合として生成され、各分布が1つのクラスタであるということです。

    まず、クラスタ数を設定します。次にモデルの初期パラメータ(平均、共分散行列、クラスタ確率)を設定します。アルゴリズムは、これらのパラメータが変化しなくなるまで、最尤推定を用いてこれらのパラメータを周期的に再計算します。

    その結果、各クラスタの最終的なパラメータが得られ、これによって新しい特徴量がどのクラスタに属するかを決定できます。

    GMMは素晴らしいです。GMMはさまざまな仕事に使用され、クラスタの形状が複雑で境界が曖昧なデータに適しています。

    このコードでは、GMMを使用して特徴量をクラスタに分割します。元のデータを取得し、クラスラベルを削除します。指定された数のクラスタに分割するためにGMMを使用します。クラスタ番号は新しい列として追加されます。最後に、得られたクラスタの表が出力されます。

    クラスタリングコードを実行し、結果を見てみましょう。

    6

    最適な特徴量を選択する関数に移りましょう。


    最適な特徴量の選択

    from sklearn.feature_selection import RFECV
    from sklearn.ensemble import RandomForestClassifier
    import pandas as pd
    
    def feature_engineering(data, n_features_to_select=10):
        # Remove the 'label' column as it is not a feature
        X = data.drop(['label', 'labels'], axis=1)
        y = data['labels']
    
        # Create a RandomForestClassifier model
        clf = RandomForestClassifier(n_estimators=100, random_state=1)
    
        # Use RFECV to select n_features_to_select best features
        rfecv = RFECV(estimator=clf, step=1, cv=5, scoring='accuracy', n_jobs=-1, verbose=1,
                      min_features_to_select=n_features_to_select)
        rfecv.fit(X, y)
    
        # Return a DataFrame with the best features, 'label' column, and 'labels' column
        selected_features = X.columns[rfecv.get_support(indices=True)]
        selected_data = data[selected_features.tolist() + ['label', 'labels']]  # Convert selected_features to a list
    
        # Print the table of best features
        print("\nBest features:")
        print(pd.DataFrame({'Feature': selected_features}))
    
        return selected_data
    
    labeled_data_engineered = feature_engineering(labeled_data_clustered, n_features_to_select=10)

    この関数は、データから最もクールで有用な特徴量を抽出します。入力は、クラス特徴量とラベルを持つオリジナルデータと、必要な数の選択された特徴量です。

    まず、クラスラベルは特徴量選択に役立たないのでリセットされます。ランダムフォレストアルゴリズムは、特徴量のランダムなセットに対して決定木の束を構築するモデルです。

    すべての木を訓練した後、ランダムフォレストは各特徴量がどの程度重要で、どの程度分類に影響するかを評価します。これらの重要度スコアに基づいて、この関数は上位の特徴量を選択します。

    最後に、選択された特徴量がデータに追加され、クラスラベルが返されます。この関数は、選択された特徴量を含む表を出力し、更新されたデータを返します。

    なぜわざわざこのようなことをするのでしょうか。こうやって、プロセスを遅らせるだけの役立たずの特徴量を排除するのです。そのようなデータで訓練することで、モデルがより良い結果を示すように、最もクールな特徴量を残します。

    この関数を起動し、結果を見てみましょう。

    8

    価格予測のための最良の指標は、始値そのものであることが判明しました。上位には、移動平均、価格増分、標準偏差、日次および月次の価格変動に基づく特徴量が含まれています。自動生成された特徴量は参考にならないことが判明しました。

    このコードでは、重要な特徴量を自動的に選択することができ、モデルの性能と汎化能力を向上させることができます。


    結論

    将来の機械学習モデルの開発を見越したデータ操作コードを作成しました。その手順を簡単に振り返ってみましょう。

    • EURUSDの初期データをMetaTrader 5プラットフォームから抽出した。その後、サンプル数を大幅に増やすために、ノイズの付与、シフト、スケーリングといったランダムな操作で変換をおこなった。
    • 次のステップは、ストップロスとテイクプロフィットレベルを考慮しながら、特別なトレンドマーカー(上昇と下降)で価格値をマークすることだった。クラスのバランスをとるため、冗長な例は削除された。
    • 次に、複雑な特徴量エンジニアリングがおこなわれた。まず、元の時系列から数百の新しい特徴量が自動的に生成された。その後、冗長性を検出するために混合ガウスクラスタリングがおこなわれた。最後に、ランダムフォレストアルゴリズムを使用して、最も情報量の多い変数のサブセットをランク付けし、選択した。
    • その結果、最適な特徴量を持つ高質の強化データセットが生成された。これは、機械学習モデルのさらなる訓練と、MetaTrader 5に統合されたPythonによる取引戦略の開発の基礎となる。


    次回は、最適な分類モデルの選択、改良、交差検証の実装、Python/MQL5バンドル用のテスター関数の作成に焦点を当てます。

    MetaQuotes Ltdによってロシア語から翻訳されました。
    元の記事: https://www.mql5.com/ru/articles/14350

    添付されたファイル |
    synergy_ml_bot.py (11.74 KB)
    多通貨エキスパートアドバイザーの開発(第6回):インスタンスグループ選択の自動化 多通貨エキスパートアドバイザーの開発(第6回):インスタンスグループ選択の自動化
    取引戦略を最適化した後、パラメータのセットを受け取ります。これらを使用して、1つのEAに複数の取引戦略のインスタンスを作成することができます。以前は手動でおこないましたが、ここでは、このプロセスの自動化を試みます。
    ニューラルネットワークが簡単に(第81回):Context-Guided Motion Analysis (CCMR) ニューラルネットワークが簡単に(第81回):Context-Guided Motion Analysis (CCMR)
    これまでの作業では、常に環境の現状を評価しました。同時に、指標の変化のダイナミクスは常に「舞台裏」にとどまっていました。この記事では、連続する2つの環境状態間のデータの直接的な変化を評価できるアルゴリズムを紹介したいと思います。
    母集団最適化アルゴリズム:ボイドアルゴリズム 母集団最適化アルゴリズム:ボイドアルゴリズム
    この記事では、動物の群れ行動のユニークな例に基づいたボイドアルゴリズムについて考察しています。その結果、ボイドアルゴリズムは、「群知能(Swarm Intelligence)」の名の下に統合されたアルゴリズム群全体の基礎となった。
    取引戦略の開発を実践する 取引戦略の開発を実践する
    この記事では、独自の取引戦略の開発を試みます。どんな取引戦略も、何らかの統計的優位性に基づいていなければなりません。しかも、この利点は長く続くべきです。