English Русский Español Deutsch
preview
MQL5とデータ処理パッケージの統合(第3回):データ可視化の強化

MQL5とデータ処理パッケージの統合(第3回):データ可視化の強化

MetaTrader 5 | 13 1月 2025, 12:12
103 0
Hlomohang John Borotho
Hlomohang John Borotho

はじめに

金融市場のトレーダーは、価格変動や取引量、テクニカル指標、経済ニュースなど、膨大なデータを理解するという課題に日々直面しています。現代市場のスピードと複雑さの中で、従来の手法を用いてこれらのデータを効果的に解釈することはますます困難になっています。従来のチャートだけでは十分な洞察を得られず、その結果、機会を逃したり、決断のタイミングを誤ったりすることがあります。さらに、トレンドの把握、反転の検出、潜在的リスクの早期特定といったタスクが重なり、その難しさは一層増しています。情報に基づいたデータ主導の意思決定を目指すトレーダーにとって、データから重要な洞察を効率的に引き出せないことは、利益の損失やリスクの増加を招く重大な問題となり得ます。

データ可視化の高度化は、こうした課題に対処するために、生の財務データをより直感的かつインタラクティブな視覚的表現へと変換します。例えば、ダイナミックなローソク足チャートやテクニカル指標のオーバーレイ、リターンのヒートマップといったツールにより、トレーダーは市場状況を従来以上に深く、実用的に理解することが可能になります。また、トレンド、相関関係、異常値を際立たせる視覚要素を統合することで、トレーダーは機会を迅速に発見し、より的確な意思決定を行えるようになります。このように高度化されたアプローチは、データ解釈の複雑さを軽減し、トレーダーがスピーディーに変化する金融市場の中で、より自信を持って効率的に行動できるよう支援します。


履歴データの収集

from datetime import datetime
import MetaTrader5 as mt5
import pandas as pd
import pytz

# Display data on the MetaTrader 5 package
print("MetaTrader5 package author: ", mt5.__author__)
print("MetaTrader5 package version: ", mt5.__version__)

# Configure pandas display options
pd.set_option('display.max_columns', 500)
pd.set_option('display.width', 1500)

# Establish connection to MetaTrader 5 terminal
if not mt5.initialize():
    print("initialize() failed, error code =", mt5.last_error())
    quit()

# Set time zone to UTC
timezone = pytz.timezone("Etc/UTC")
# Create 'datetime' objects in UTC time zone to avoid the implementation of a local time zone offset
utc_from = datetime(2024, 1, 2, tzinfo=timezone)
utc_to = datetime.now(timezone)  # Set to the current date and time

# Get bars from XAUUSD H1 (hourly timeframe) within the specified interval
rates = mt5.copy_rates_range("XAUUSD", mt5.TIMEFRAME_H1, utc_from, utc_to)

# Shut down connection to the MetaTrader 5 terminal
mt5.shutdown()

# Check if data was retrieved
if rates is None or len(rates) == 0:
    print("No data retrieved. Please check the symbol or date range.")
else:
    # Display each element of obtained data in a new line (for the first 10 entries)
    print("Display obtained data 'as is'")
    for rate in rates[:10]:
        print(rate)

    # Create DataFrame out of the obtained data
    rates_frame = pd.DataFrame(rates)
    # Convert time in seconds into the 'datetime' format
    rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s')

    # Save the data to a CSV file
    filename = "XAUUSD_H1_2nd.csv"
    rates_frame.to_csv(filename, index=False)
    print(f"\nData saved to file: {filename}")

履歴データを取得するには、まずmt5.initialize()関数を使用してMetaTrader 5端末への接続を確立します。この処理は、Pythonパッケージが実行中のMetaTrader 5プラットフォームと直接通信するために不可欠です。コード内で開始日と終了日を指定し、データ抽出に必要な時間範囲を設定します。datetimeオブジェクトはUTCタイムゾーンで作成され、異なるタイムゾーン間での一貫性が確保されます。スクリプトではその後、mt5.copy_rates_range()関数を使用して、2024年1月2日から現在の日時までのXAUUSD銘柄に関する過去の時間足データを取得します。

履歴データを取得した後は、不要な接続を回避するためにmt5.shutdown()を使用してMetaTrader 5端末から安全に切断します。取得されたデータは最初に生データ形式で表示され、データ抽出が正しく行われたことを確認します。その後、このデータをpandasのDataFrameに変換し、操作や分析を容易にします。さらに、Unixタイムスタンプを読み取り可能な日時形式に変換することで、データが適切に構造化され、さらなる処理や分析に対応できる状態にします。このアプローチを用いることで、トレーダーは過去の市場動向を分析し、過去の実績に基づいた情報に基づいて取引判断を下すことが可能になります。

filename = "XAUUSD_H1_2nd.csv"
rates_frame.to_csv(filename, index=False)
print(f"\nData saved to file: {filename}")

私のオペレーティングシステムはLinuxのため、受信したデータをファイルに保存する必要があります。しかし、Windowsをお使いの方は、以下のスクリプトを使用することで、簡単にデータを取得することができます。

from datetime import datetime
import MetaTrader5 as mt5
import pandas as pd
import pytz

# Display data on the MetaTrader 5 package
print("MetaTrader5 package author: ", mt5.__author__)
print("MetaTrader5 package version: ", mt5.__version__)

# Configure pandas display options
pd.set_option('display.max_columns', 500)
pd.set_option('display.width', 1500)

# Establish connection to MetaTrader 5 terminal
if not mt5.initialize():
    print("initialize() failed, error code =", mt5.last_error())
    quit()

# Set time zone to UTC
timezone = pytz.timezone("Etc/UTC")
# Create 'datetime' objects in UTC time zone to avoid the implementation of a local time zone offset
utc_from = datetime(2024, 1, 2, tzinfo=timezone)
utc_to = datetime.now(timezone)  # Set to the current date and time

# Get bars from XAUUSD H1 (hourly timeframe) within the specified interval
rates = mt5.copy_rates_range("XAUUSD", mt5.TIMEFRAME_H1, utc_from, utc_to)

# Shut down connection to the MetaTrader 5 terminal
mt5.shutdown()

# Check if data was retrieved
if rates is None or len(rates) == 0:
    print("No data retrieved. Please check the symbol or date range.")
else:
    # Display each element of obtained data in a new line (for the first 10 entries)
    print("Display obtained data 'as is'")
    for rate in rates[:10]:
        print(rate)

    # Create DataFrame out of the obtained data
    rates_frame = pd.DataFrame(rates)
    # Convert time in seconds into the 'datetime' format
    rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s')

    # Display data directly
    print("\nDisplay dataframe with data")
    print(rates_frame.head(10))

また、何らかの理由で履歴データを取得できない場合は、以下の手順でMetaTrader 5プラットフォーム上で手動で取得することが可能です。まず、MetaTraderプラットフォームを起動し、MetaTrader 5のペイン/パネル上部にある[ツール]>[オプション]に移動します。次に、チャートオプションのセクションに進みます。そこで、ダウンロードしたいチャートのバーの数を選択します。一定期間にどれだけのバーが存在するかは予測できないため、「無制限」のオプションを選択することをお勧めします。これにより、日付に基づいてデータを扱う際にも問題なく対応できます。

その後、実際のデータをダウンロードする必要があります。手順としては、[表示]>[銘柄]に進み、[仕様]タブに移動します。その後、ダウンロードしたいデータの種類に応じて、[Bars]または[Ticks]を選択します。次に、ダウンロードしたい履歴データの開始日と終了日を入力し、リクエストボタンをクリックします。これにより、データがダウンロードされ、.csv形式で保存されます。


Jupyter LabでのMetaTrader 5データの可視化

MetaTrader 5の履歴データをJupyter Labにロードするには、まずデータがダウンロードされたフォルダを見つける必要があります。Jupyter Labを開いたら、そのフォルダに移動してファイルにアクセスします。その後、データをロードし、列名を確認する手順に進みます。列名を確認することは、データを正しく管理し、不適切な列名の使用によるエラーを防ぐために重要です。

以下はPythonのコードです。

import pandas as pd

# assign variable to the historical data
file_path = '/home/int_junkie/Documents/ML/predi/XAUUSD.m_H1_2nd.csv'

data = pd.read_csv(file_path, delimiter='\t')

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



過去のデータは2024年1月2日から現在のデータまでです。

以下はPythonのコードです。

# Convert the <DATE> and <TIME> columns into a single datetime column
data['<DATETIME>'] = pd.to_datetime(data['<DATE>'] + ' ' + data['<TIME>'], format='%Y.%m.%d %H:%M:%S')

# Drop the original <DATE> and <TIME> columns
data = data.drop(columns=['<DATE>', '<TIME>'])

# Convert numeric columns from strings to appropriate float types
numeric_columns = ['<OPEN>', '<HIGH>', '<LOW>', '<CLOSE>', '<TICKVOL>', '<VOL>', '<SPREAD>']
data[numeric_columns] = data[numeric_columns].apply(pd.to_numeric)

# Set datetime as index for easier plotting
data.set_index('<DATETIME>', inplace=True)

# Let's plot the close price and tick volume to visualize the trend
import matplotlib.pyplot as plt

# Plot closing price and tick volume
fig, ax1 = plt.subplots(figsize=(12, 6))

# Close price on primary y-axis
ax1.set_xlabel('Date')
ax1.set_ylabel('Close Price', color='tab:blue')
ax1.plot(data.index, data['<CLOSE>'], color='tab:blue', label='Close Price')
ax1.tick_params(axis='y', labelcolor='tab:blue')

# Tick volume on secondary y-axis
ax2 = ax1.twinx()  
ax2.set_ylabel('Tick Volume', color='tab:green')  
ax2.plot(data.index, data['<TICKVOL>'], color='tab:green', label='Tick Volume')
ax2.tick_params(axis='y', labelcolor='tab:green')

# Show the plot
plt.title('Close Price and Tick Volume Over Time')
fig.tight_layout()
plt.show()

終値とティックボリューム

上のプロットは、2つの主要指標を時系列で示しています。

  1. 終値(青):チャート上で各時間の終値を表しています。これにより、金価格(XAU/USD)の上昇トレンドや下降トレンドを含む経時的な変動を観察することができます。
  2. ティックボリューム(緑):各時間内の価格変動の回数を示しています。ティックボリュームの急上昇は、市場の活況やボラティリティの増加に関連していることが多いです。たとえば、ティックボリュームが高い時間帯は、大きな価格変動を伴い、重要なイベントや市場心理の変化を示唆する場合があります。

それでは、このデータをさらに詳しく掘り下げてみましょう。

1. トレンド分析

# Calculating moving averages: 50-period and 200-period for trend analysis
data['MA50'] = data['<CLOSE>'].rolling(window=50).mean()
data['MA200'] = data['<CLOSE>'].rolling(window=200).mean()

# Plot close price along with the moving averages
plt.figure(figsize=(12, 6))

# Plot close price
plt.plot(data.index, data['<CLOSE>'], label='Close Price', color='blue')

# Plot moving averages
plt.plot(data.index, data['MA50'], label='50-Period Moving Average', color='orange')
plt.plot(data.index, data['MA200'], label='200-Period Moving Average', color='red')

plt.title('Close Price with 50 & 200 Period Moving Averages')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend(loc='best')
plt.grid(True)
plt.show()


このプロットは、終値と50期間および200期間の移動平均線を示しています。

  1. 終値(青線):各期間の終了時点での実際の価格を表しています。
  2. 50期間移動平均線(オレンジ線):50期間の価格データを平滑化する短期移動平均線です。この線が終値より上にクロスした場合、上昇トレンドの可能性を示唆し、下にクロスした場合は下降トレンドの兆しとなります。
  3. 200期間移動平均線(赤線):長期的な移動平均線で、全体的な市場トレンドを把握するために使用します。価格変動に対して反応が遅いため、50期間移動平均線とのクロスオーバーは、長期的なトレンド反転を示す重要なシグナルとなります。

重要なポイントはゴールデンクロスであり、50期間の移動平均線が200期間の移動平均線を上回った場合、強気のトレンドが始まる可能性を示唆します。逆に、デッドクロスは50期間移動平均線が200期間移動平均線を下回る場合、弱気のトレンドを示唆することがあります。

次に、価格帯(高値と安値の差)を計算してボラティリティを分析し、それを可視化します。

2. ボラティリティ分析

# Calculate the price range (High - Low)
data['Price_Range'] = data['<HIGH>'] - data['<LOW>']

# Calculate Bollinger Bands
# Use a 20-period moving average and 2 standard deviations
data['MA20'] = data['<CLOSE>'].rolling(window=20).mean()
data['BB_upper'] = data['MA20'] + 2 * data['<CLOSE>'].rolling(window=20).std()
data['BB_lower'] = data['MA20'] - 2 * data['<CLOSE>'].rolling(window=20).std()

# Plot the price range and Bollinger Bands along with the close price
plt.figure(figsize=(12, 8))

# Plot the close price
plt.plot(data.index, data['<CLOSE>'], label='Close Price', color='blue')

# Plot Bollinger Bands
plt.plot(data.index, data['BB_upper'], label='Upper Bollinger Band', color='red', linestyle='--')
plt.plot(data.index, data['BB_lower'], label='Lower Bollinger Band', color='green', linestyle='--')

# Fill the area between Bollinger Bands for better visualization
plt.fill_between(data.index, data['BB_upper'], data['BB_lower'], color='gray', alpha=0.3)

# Plot the price range on a separate axis
plt.figure(figsize=(12, 6))
plt.plot(data.index, data['Price_Range'], label='Price Range (High-Low)', color='purple')

plt.title('Bollinger Bands and Price Range (Volatility Analysis)')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend(loc='best')
plt.grid(True)
plt.show()



上記の出力から、以下の内容を実行しました。

  1. 価格帯の計算:各時間帯の価格帯は、最高価格(<High>から最低価格(<LOW>)を引くことで計算されます。これは、各時間帯の値動きの程度を把握し、その時間帯のボラティリティを測定するのに役立ちます。
  2. `data['Price-Range'] = data['<HIGH>'] - data['<LOW>'] :その結果、Price-Range列には、各時間内で価格がどの程度変動したかが示されます。
  3. ボリンジャーバンドの計算:ボリンジャーバンドは、価格変動の程度に関する洞察を得るために計算されます。MA-20は終値の20期間移動平均です。ボリンジャーバンドの中間線の役割を果たします。BB-upperは上限バンド、BB-lowerは下限バンドを表します。これは、20期間の移動平均の上下にある2つの標準偏差として計算されます。価格が上限バンドに近づく場合、市場が買われすぎの可能性があることを示唆します。同様に、下限バンドに近づく場合、市場が売られすぎの可能性があることを示します。
  4. 価格とボリンジャーバンドの視覚化:終値(青線)は、各期間の実際の終値を示しています。上限ボリンジャーバンド(赤の破線)と下限ボリンジャーバンド(緑の破線)は、移動平均線の上下に形成されるボラティリティバンドを表しています。グレーの網掛け部分は、上限バンドと下限バンドに挟まれたエリアで、価格がこの範囲内で動くと予想されるボラティリティの幅を視覚的に表現しています。
  5. 価格帯別プロット:2つ目のプロットでは、ボラティリティの時間的な変動を別個に示しています。紫色の線で表された価格帯は、各時間内でのボラティリティの大きさを示します。このプロットにおける大きなスパイクは、ボラティリティが特に増加した期間を意味します。
強化学習モデルの作成
    import pandas as pd
    import talib
    from stable_baselines3 import DQN
    from stable_baselines3.common.env_checker import check_env
    
    # Verify the environment
    check_env(env)
    
    # Initialize and train the DQN model
    model = DQN('MlpPolicy', env, verbose=1)
    model.learn(total_timesteps=10000)
    
    # Save the trained model
    model.save("trading_dqn_model")
    
    # Load and preprocess the data as before
    data = pd.read_csv('XAUUSD_H1_Data-V.csv', delimiter='\t')
    data['<DATETIME>'] = pd.to_datetime(data['<DATE>'] + ' ' + data['<TIME>'], format='%Y.%m.%d %H:%M:%S')
    data = data.drop(columns=['<DATE>', '<TIME>'])
    numeric_columns = ['<OPEN>', '<HIGH>', '<LOW>', '<CLOSE>', '<TICKVOL>', '<VOL>', '<SPREAD>']
    data[numeric_columns] = data[numeric_columns].apply(pd.to_numeric)
    data.set_index('<DATETIME>', inplace=True)
    
    # Calculate Bollinger Bands (20-period moving average with 2 standard deviations)
    data['MA20'] = data['<CLOSE>'].rolling(window=20).mean()
    data['BB_upper'] = data['MA20'] + 2 * data['<CLOSE>'].rolling(window=20).std()
    data['BB_lower'] = data['MA20'] - 2 * data['<CLOSE>'].rolling(window=20).std()
    
    # Calculate percentage price change and volume change as additional features
    data['Pct_Change'] = data['<CLOSE>'].pct_change()
    data['Volume_Change'] = data['<VOL>'].pct_change()
    
    # Fill missing values
    data.fillna(0, inplace=True)

    上記のコードでは、stable-baseline3のDQN (Deep Q-Network)アルゴリズムを使用して、XAU/USD(金)市場の取引決定を行う強化学習(RL)モデルのために金融データを準備しています。まず、過去のデータを読み込み処理し、ボリンジャーバンド(移動平均と価格のボラティリティに基づくテクニカル指標)を計算します。そして、価格や出来高の変化率などの特徴量を追加します。環境が検証され、DQNモデルは10,000タイムステップで訓練され、その後モデルは将来の使用のために保存されます。最後に、欠損データはゼロで埋められ、スムーズなモデル学習が可能となります。

    import gym
    from gym import spaces
    import numpy as np
    
    class TradingEnv(gym.Env):
        def __init__(self, data):
            super(TradingEnv, self).__init__()
            
            # Market data and feature columns
            self.data = data
            self.current_step = 0
            
            # Define action and observation space
            # Actions: 0 = Hold, 1 = Buy, 2 = Sell
            self.action_space = spaces.Discrete(3)
            
            # Observations (features: Bollinger Bands, Price Change, Volume Change)
            self.observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(5,), dtype=np.float32)
            
            # Initial balance and positions
            self.balance = 10000  # Starting balance
            self.position = 0  # No position at the start (0 = no trade, 1 = buy, -1 = sell)
        
        def reset(self):
            self.current_step = 0
            self.balance = 10000
            self.position = 0
            return self._next_observation()
        
        def _next_observation(self):
            # Get the current market data (Bollinger Bands, Price Change, Volume Change)
            obs = np.array([
                self.data['BB_upper'].iloc[self.current_step],
                self.data['BB_lower'].iloc[self.current_step],
                self.data['Pct_Change'].iloc[self.current_step],
                self.data['Volume_Change'].iloc[self.current_step],
                self.position
            ])
            return obs
        
        def step(self, action):
            # Execute the trade based on action and update balance and position
            self.current_step += 1
            
            # Get current price
            current_price = self.data['<CLOSE>'].iloc[self.current_step]
            
            reward = 0  # Reward initialization
            done = self.current_step == len(self.data) - 1  # Check if we're done
            
            # Buy action
            if action == 1 and self.position == 0:
                self.position = 1
                self.entry_price = current_price
            
            # Sell action
            elif action == 2 and self.position == 1:
                reward = current_price - self.entry_price
                self.balance += reward
                self.position = 0
            
            # Hold action
            else:
                reward = 0
            
            return self._next_observation(), reward, done, {}
        
        def render(self, mode='human', close=False):
            # Optional: Print the current balance and position
            print(f"Step: {self.current_step}, Balance: {self.balance}, Position: {self.position}")
    
    # Create the trading environment
    env = TradingEnv(data)

    上記のコードでは、gymライブラリを使用して取引環境TradingEnvクラスを定義し、過去の市場データに基づいた取引環境をシミュレートしています。この環境では、ホールド、買い、売りの3つのアクションが可能です。観測空間には、5つの特徴量(ボリンジャーバンド、価格の変化率、出来高の変化率、現在の取引ポジション)が含まれています。エージェントは1万ユニットの残高とポジションなしの状態でスタートします。各ステップで、選択されたアクションに基づき、環境はエージェントのポジションと残高を更新し、利益のある取引に対して報酬を計算し、データの次のステップに進みます。環境はリセットして新しいエピソードを開始したり、取引プロセスの現在の状態をレンダリングすることも可能です。この環境は強化学習モデルの訓練に使用されます。

    import gymnasium as gym
    from gymnasium import spaces
    import numpy as np
    from stable_baselines3 import DQN
    from stable_baselines3.common.env_checker import check_env
    
    # Define the custom Trading Environment
    class TradingEnv(gym.Env):
        def __init__(self, data):
            super(TradingEnv, self).__init__()
            
            # Market data and feature columns
            self.data = data
            self.current_step = 0
            
            # Define action and observation space
            # Actions: 0 = Hold, 1 = Buy, 2 = Sell
            self.action_space = spaces.Discrete(3)
            
            # Observations (features: Bollinger Bands, Price Change, Volume Change)
            self.observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(5,), dtype=np.float32)
            
            # Initial balance and positions
            self.balance = 10000  # Starting balance
            self.position = 0  # No position at the start (0 = no trade, 1 = buy, -1 = sell)
    
        def reset(self, seed=None, options=None):
            # Initialize the random seed
            self.np_random, seed = self.seed(seed)
            
            self.current_step = 0
            self.balance = 10000
            self.position = 0
            
            # Return initial observation and an empty info dictionary
            return self._next_observation(), {}
    
        def _next_observation(self):
            # Get the current market data (Bollinger Bands, Price Change, Volume Change)
            obs = np.array([
                self.data['BB_upper'].iloc[self.current_step],
                self.data['BB_lower'].iloc[self.current_step],
                self.data['Pct_Change'].iloc[self.current_step],
                self.data['Volume_Change'].iloc[self.current_step],
                self.position
            ], dtype=np.float32)  # Explicitly cast to float32
            return obs
        
        def step(self, action):
            self.current_step += 1
            current_price = self.data['<CLOSE>'].iloc[self.current_step]
            
            reward = 0
            done = self.current_step == len(self.data) - 1
            truncated = False  # Set to False unless there's an external condition to end the episode early
            
            # Execute the action
            if action == 1 and self.position == 0:
                self.position = 1
                self.entry_price = current_price
            
            elif action == 2 and self.position == 1:
                reward = current_price - self.entry_price
                self.balance += reward
                self.position = 0
            
            # Return next observation, reward, terminated, truncated, and an empty info dict
            return self._next_observation(), reward, done, truncated, {}
    
        def render(self, mode='human', close=False):
            print(f"Step: {self.current_step}, Balance: {self.balance}, Position: {self.position}")
        
        def seed(self, seed=None):
            self.np_random, seed = gym.utils.seeding.np_random(seed)
            return self.np_random, seed
    
    # Assuming your data is already prepared (as a DataFrame) and includes Bollinger Bands and other necessary features
    # Create the environment
    env = TradingEnv(data)
    
    # Verify the environment
    check_env(env)
    
    # Train the model using DQN
    model = DQN('MlpPolicy', env, verbose=1)
    model.learn(total_timesteps=10000)
    
    # Save the trained model
    model.save("trading_dqn_model")
    

    以下は出力です。

    モデル

    次に、強化学習のためにgymnasiumを使用して、エージェントが過去の市場データに基づいて取引決定を学習するカスタム取引環境を定義します。 この環境では、ホールド、買い、売りの3つのアクションが可能で、ボリンジャーバンド、価格変化率、出来高変化率、現在のポジションなどの5つの観測が含まれています。エージェントは1万ドルの残高があり、未決済のポジションがない状態からスタートします。環境内の各ステップはエージェントを前進させ、その位置、バランスを更新し、成功した取引の報酬を計算します。環境はstable-baseline3のcheck-Env()関数を使って検証され、DQN (Deep Q-Network)モデルは最適な取引戦略を学習するために10,000タイムステップ学習されます。学習されたモデルは、将来自動売買システムで使用するために保存されます。

    # Unpack the observation from the reset() method
    obs, _ = env.reset()
    
    # Loop through the environment steps
    for step in range(len(data)):
        # Predict the action based on the observation
        action, _states = model.predict(obs)
        
        # Step the environment
        obs, rewards, done, truncated, info = env.step(action)
        
        # Render the environment (print the current state)
        env.render()
    
        # Check if the episode is done
        if done or truncated:
            print("Testing completed!")
            break
    

    初めのエクイティ

    終わりのエクイティ

    その結果、学習済みモデルによって実行された取引戦略は取引期間中にわずかな利益をもたらし、残高は10,000ドルから10,108ドルに増加しました。これは、モデルが利益の出る取引を特定できたことを示唆していますが、108ドル(1.08%の利益)という利益率は比較的控えめです。

    この結果は、モデルが慎重または低頻度の取引を行っていることを示唆するか、あるいは戦略がより高いリターンを得るために十分に最適化されていないことを示している可能性があります。さらに長期間のテストや、モデルパラメータ(報酬関数、特徴選択、取引実行ロジックなど)の調整を含む評価が、モデルのパフォーマンス向上に役立つ可能性があります。また、取引コストやリスク管理などの要因を考慮して、戦略が長期的に利益を確保できることが重要です。

    MQL5ですべてをまとめる

    MQL5を学習済みモデルを実行するPythonスクリプトに接続するためには、MQL5とPython間の通信チャネルを設定する必要があります。ここでは、一般的に使用されるソケットサーバを利用します。
    //+------------------------------------------------------------------+
    //|                                                   EnhancedML.mq5 |
    //|                                  Copyright 2024, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2024, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    
    //+------------------------------------------------------------------+
    //|                          Includes                                |
    //+------------------------------------------------------------------+
    #include <WinAPI\winapi.mqh>
    #include <Trade\Trade.mqh>
    CTrade              trade;

    まず、システムレベルの操作のためのWindows APIライブラリ(winapi.mqh)と、取引管理のための取引ライブラリ(trade.mqh)が含まれています。次にCTradeクラスのインスタンスをtradeと名付け宣言します。

    //+------------------------------------------------------------------+
    //|                          Global Vars                             |
    //+------------------------------------------------------------------+
    int stopLoss = 350;
    int takeProfit = 500;
    
    string Address = "127.0.0.1";
    int port = 9999;
    int socket = SocketCreate();
    
    double Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

    Addressを127.0.0.1(ローカルホスト)に、portをソケット通信に使用する9999に設定します。SocketCreate()関数はソケットを初期化してsocket変数にソケットハンドルを格納し、Pythonサーバとの通信を可能にします。

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit(){
    
        if (!SocketConnect(socket, Address, port, 1000)){  
            Print("Successfully connected to ", Address, ":", port);
        } else {
            Print("Connection to ", Address, ":", port, " failed, error ", GetLastError());
            return INIT_FAILED;
        }
    
       return(INIT_SUCCEEDED);
    }

    SocketConnect()関数は、作成したソケットを、与えられたアドレス(ローカルホスト)とポート9999にあるPythonサーバに接続しようとします。ミリ秒単位のタイムアウトは1000です。

    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick(){
       
       
       uint len=SocketIsReadable(socket);
    
       char buffer[16];
       int bytes = SocketRead(socket, buffer, len, 23);
       
       if (bytes > 0){
       
          string action_str = CharArrayToString(buffer);
          
          int action = StringToInteger(action_str);
          //int action = atoi(buffer);
          
          // Execute a trade based on action
          if(action == 1){
             //buy trade
             MBuy();
             Print("Buy action received...");
          } else if(action == 2){
             //sell trade
             MSell();
             Print("Sell action received...");
          }
       }
       
    }

    MQL5のOnTick()関数は、ソケット接続を通じて各マーケットティックで受信した取引指示を処理するように設計されています。まず、SocketIsReadable()を使用してソケットにデータがあるか確認し、データの長さを返します。データが存在する場合、SocketRead()関数でデータをバッファに読み込み、読み込んだバイト数をbytesに格納します。データを受信した後、バッファは文字列に変換され、その後整数(action)に変換されます。actionの値に基づいて、この関数は対応する取引を実行します。action == 1の場合はMBuy()を呼び出して買い取引を実行し、action == 2の場合はMSell()を呼び出して売り取引を実行します。各取引アクション後にprint文が受け取ったアクション(買いまたは売り)をログに記録します。要するに、この関数はソケット経由で売買コマンドを受信し、それに応じて自動的に取引を実行します。

    //+------------------------------------------------------------------+
    //|                        Buy Function                              |
    //+------------------------------------------------------------------+
    void MBuy(){
    
       static int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
    
       double Lots = 0.02;
       double sl = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - stopLoss, digits);
       double tp = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK) + takeProfit * _Point, digits);
       trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, Lots, Ask, sl, tp);
    }

    以下は、買い取引を開始する関数です。

    //+------------------------------------------------------------------+
    //|                         Sell Function                            |
    //+------------------------------------------------------------------+
    void MSell(){
       static int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
    
       double Lots = 0.02;
       double sl = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + stopLoss, digits);
       double tp = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID) - takeProfit * _Point, digits);
       trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, Lots, Bid, sl, tp);
    }

    以下は、売り取引を開始する関数です。


    Pythonソケットサーバスクリプト(取引モデルサーバ)

    Pythonスクリプトは学習済みモデルをロードし、MQL5からの接続をリッスンするソケットサーバをセットアップします。データを受信すると、予測を立て、取引アクションを送り返します。

    import socket
    import numpy as np
    from stable_baselines3 import DQN
    
    # Load the trained model
    model = DQN.load("trading_dqn_model")
    
    # Set up the server
    HOST = '127.0.0.1'  # Localhost (you can replace this with your IP)
    PORT = 9999         # Port to listen on
    
    # Create a TCP/IP socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind((HOST, PORT))
    server_socket.listen(1)
    
    print(f"Server listening on {HOST}:{PORT}...")
    
    while True:
        # Wait for a connection
        client_socket, client_address = server_socket.accept()
        print(f"Connection from {client_address}")
        
        # Receive data from MQL5 (price data sent by EA)
        data = client_socket.recv(1024).decode('utf-8')
        
        if data:
            print(f"Received data: {data}")
            
            # Convert received data to a numpy array
            observation = np.fromstring(data, sep=',')  # Assumes comma-separated price data
    
            # Make prediction using the model
            action, _ = model.predict(observation)
            
            # Send the predicted action back to MQL5
            client_socket.send(str(action).encode('utf-8'))
        
        # Close the client connection
        client_socket.close()
    

    Pythonスクリプトをtrading-model-server.pyまたは任意の名前で保存します。端末かコマンドプロンプトを開き、モデルを保存したディレクトリとtrading-model-server.pyに移動し、以下を実行して接続を確立します。

    python trading_model_server.py
    


    結論

    まとめると、私たちは機械学習とMQL5を統合し、過去のデータに基づいて取引判断を自動化する包括的な取引システムを開発しました。最初にXAU/USDの履歴データを読み込み、前処理を行い、ボリンジャーバンドを計算し、価格や出来高の変化といったその他の主要な特徴を実装しました。次に、強化学習、特にDeep Q-Network (DQN)を使用して、データのパターンに基づいて売買アクションを予測するモデルを訓練しました。訓練されたモデルはソケット通信システムを通じてMQL5に接続され、取引プラットフォームとPythonベースの意思決定モデルの間でリアルタイムのやり取りが可能となりました。これにより、モデルの予測に基づいて取引が自動的に実行され、システム全体がアルゴリズム取引の強力なツールとなりました。

    データの可視化と機械学習の統合を強化することで、トレーダーにより深い洞察と情報に基づいた意思決定を提供し、大きな利益をもたらすことができます。市場のトレンド、ボラティリティ、主要なパターンを分析することで、システムは取引の最適なエントリーポイントとエグジットポイントを特定できます。データ駆動型モデルに基づく取引執行の自動化により、人的エラーや感情的バイアスが減少し、より一貫性のある戦略的な取引が可能になります。全体として、このアプローチはトレーダーに過去のデータを活用してパフォーマンスを向上させる洗練されたツールを提供し、反復的な取引作業を自動化することで時間を節約します。

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

    添付されたファイル |
    Enhanced.ipynb (2101.04 KB)
    EnhancedML.mq5 (3.68 KB)
    XAUUSD_H1_2nd.csv (281.65 KB)
    MQL5取引ツールキット(第3回):未決注文管理EX5ライブラリの開発 MQL5取引ツールキット(第3回):未決注文管理EX5ライブラリの開発
    MQL5のコードやプロジェクトで、包括的な未決注文管理EX5ライブラリを開発して実装する方法を学びましょう。本記事では、広範な未決注文管理EX5ライブラリを作成する手順を紹介し、それをインポートおよび実装する方法を、取引パネルまたはグラフィカルユーザーインターフェース(GUI)の構築を通じて解説します。このEA注文パネルを使用すれば、チャートウィンドウ上のGUIから、指定されたマジックナンバーに関連する未決注文を直接オープン、監視、削除することが可能です。
    古典的な戦略を再構築する(第10回):AIはMACDを強化できるか? 古典的な戦略を再構築する(第10回):AIはMACDを強化できるか?
    MACDインジケーターを経験的に分析し、インジケーターを含む戦略にAIを適用することで、EURUSDの予測精度が向上するかどうかをテストします。さらに、インジケーター自体が価格より予測しやすいのか、またインジケーターの値が将来の価格水準を予測できるのかも同時に評価します。これにより、AI取引戦略にMACDを統合することに投資する価値があるかどうかを判断するための情報を提供します。
    MQL5で取引管理者パネルを作成する(第4回):ログインセキュリティ層 MQL5で取引管理者パネルを作成する(第4回):ログインセキュリティ層
    悪意のある人物が取引管理者室に侵入し、世界中の何百万ものトレーダーに貴重な洞察を伝えるために使用されるコンピューターと管理パネルにアクセスしたと想像してください。このような侵入は、誤解を招くメッセージの不正送信や、意図しないアクションをトリガーするボタンのランダムクリックなど、悲惨な結果につながる可能性があります。このディスカッションでは、MQL5のセキュリティ対策と、これらの脅威から保護するために管理パネルに実装した新しいセキュリティ機能について説明します。セキュリティプロトコルを強化することで、通信チャネルを保護し、グローバルな取引コミュニティの信頼を維持することを目指しています。この記事のディスカッションでさらに詳しい情報を見つけてください。
    ニューラルネットワークが簡単に(第96回):マルチスケール特徴量抽出(MSFformer) ニューラルネットワークが簡単に(第96回):マルチスケール特徴量抽出(MSFformer)
    長期的な依存関係と短期的な特徴量の効率的な抽出と統合は、時系列分析において依然として重要な課題です。正確で信頼性の高い予測モデルを作成するためには、それらを適切に理解し、統合することが必要です。