English Русский 中文 Español Deutsch Português
preview
MQL5とPythonで自己最適化EAを構築する

MQL5とPythonで自己最適化EAを構築する

MetaTrader 5 |
768 3
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

概要

アルゴリズム取引の開発者にとって、予測が困難な市場の変動に適応することは大きな課題です。市場環境が変化するたびに、使用する戦略も変える必要があります。例えば、市場がレンジ相場のときは平均回帰戦略が最適かもしれませんが、強いトレンドが発生した際にはトレンドフォロー戦略がより適しています。

通常、私たちは単一の取引戦略を実装し、それをすべての市場環境で適用しようとしますが、これは安定した成果を保証するものではありません。また、複数の戦略を1つのプログラムに組み込み、ユーザーが手動で最適な戦略を選べるようにすることも可能ですが、これは完全な解決策とはいえません。

そこで、変化する市況に応じて自律的に最適な戦略を選び、切り替えるEAを設計することが重要となります。このためには、市場のトレンドや平均回帰の強度を定量的に測定する方法が必要です。EAがこれらの動きを的確に評価できれば、適切な戦略を自動的に選択することが可能になります。

この記事では、遷移行列を用いて市場の動向をモデル化し、トレンドフォロー戦略と平均回帰戦略のどちらを選択すべきかを判断する方法について説明します。まず、遷移行列の基本的な概念を理解し、これらの数学的ツールを活用して、より賢明な意思決定をおこなうインテリジェントな取引アルゴリズムを構築する手法を探ります。


はじめに:アンドレイ・マルコフとは誰か?

19世紀は、アレクサンダー・グラハム・ベルによる電話の発明や、トーマス・エジソンによる電球の発明、グリエルモ・マルコーニによるラジオの開発など、革新的な発見が数多くありました。しかし、アルゴリズム開発者としての私たちにとって、これらの発明以上に重要なのは、ロシアの優れた数学者アンドレイ・マルコフの貢献です。

マルコフ

図1:若き日のアンドレイ・マルコフの写真

マルコフは、完全にランダムなプロセスをモデル化する必要がある数々の問題に取り組みました。これは、市場のダイナミクスにおける予測不可能性に対処する現代の課題にも通じます。彼が定式化した「マルコフ連鎖」は、ランダムな現象を扱う際に非常に有用な枠組みです。これを直感的に理解してみましょう。

例えば、あなたが70年以上にわたりドイツでバスサービスを提供している公共交通機関の経営者だとしましょう。現在、バスの増台を検討しており、どの目的地にバスを増やすべきか、またどの目的地はさらなる投資に値しないかを決定しなければなりません。

この問題をマルコフ連鎖として捉えることで、意思決定のプロセスを簡素化できます。次の図では、同社が70年の歴史の中で完了したすべての旅が、マルコフ連鎖として視覚化されています。


マルコフモデル

図2:ある運送会社の架空のマルコフモデルと、顧客によってランダムに使用されるルート

上のマルコフ連鎖を解釈してみましょう。このマルコフ連鎖モデルを解釈してみましょう。例えば、フランクフルトで搭乗した乗客のうち、40%がミュンヘンで下車し、残りの60%はケルンへ向かうことがわかります。さらに、ケルンに到着した乗客の30%はフランクフルトに戻り、70%はベルリンに移動します。このようにして、顧客が最も頻繁に利用するルートが視覚的に示されます。

また、このモデルからは、直行便のない目的地も明らかになります。つまり、過去70年の間に、ある2つの都市間を直接移動するニーズがほとんどなかったことを示しています。経営者として、フランクフルトからベルリンへの直行バスを追加するよりも、フランクフルトからケルンのような人気路線を強化する方が利益を見込めるという判断ができるのです。

ここでの重要なポイントは、遷移行列がある状態(都市)から別の状態へ移行する確率を示していることです。マルコフの理論によれば、次にどの状態に遷移するかは、現在の状態だけに依存します。これは、システムの変化を予測し、次に発生する可能性の高い状態を理解するのに役立ちます。金融市場にこの遷移行列を適用するには、まず市場が取り得るすべての状態を定義します。



戦略の構築 市場状態の定義

市場状態を定義する効果的な方法の1つは、テクニカル指標を使用することです。ここでは、MetaTrader 5端末で銘柄に移動平均を適用する例を挙げます。この方法により、以下のように市場の状態を分類することができます。ローソク足が移動平均線より上で終値をつけた場合、その状態を「UP」と定義します(図の「1」)。一方で、ローソク足が移動平均線より下で終値をつけた場合、その状態を「DOWN」と定義します(図の「2」)。


市場状態の定義

図3:市場状態を1か2のいずれかで表した模式図

マルコフ連鎖を用いて、市場が移動平均線の上で取引を終え、その後移動平均線の下で取引を終えるまでの推移をモデル化することができます。言い換えれば、移動平均線と終値の関係をマルコフ連鎖で表現することで、「あるローソク足が移動平均を上回って引けた場合、次のローソク足も移動平均を上回って引ける確率はどれくらいか?」という問いに答えることが可能になります。例えば、この確率が0.5を超える場合、次のローソク足も移動平均線の上で終わる可能性が高く、市場はトレンドフォロー戦略に適していると考えられます。逆に、この確率が0.5未満であれば、市場は平均回帰戦略に適している可能性が高いと推測できます。


はじめに:最初の遷移行列の構築

まず、MetaTrader 5端末との通信とデータ分析のための標準的なPythonライブラリをインポートします。

#Import packages
import pandas as pd
import numpy as np
import MetaTrader5 as mt5
from   datetime import datetime
import pandas_ta as ta
import time

次に、ログイン認証情報を定義し、取引したい銘柄や使用したい時間枠など、関心のある他のグローバル変数を指定します。 

#Account login details
login = 123456789
password = "Enter Your Password"
server = "Enter Your Server"
symbol = "EURUSD"
#What timeframe are we working on?
timeframe = mt5.TIMEFRAME_M1
#This data frame will store the most recent price update
last_close = pd.DataFrame()
#We may not always enter at the price we want, how much deviation can we tolerate?
deviation = 100
#The size of our positions
volume = 0
#How many times the minimum volume should our positions be
lot_multiple = 1

これでログインできます。

#Login
if(mt5.initialize(login=login,password=password,server=server)):
    print("Logged in successfully")
else:
    print("Failed to login")
ログイン成功です。

次に、取引量を定義します。

#Setup trading volume
symbols = mt5.symbols_get()
for index,symbol in enumerate(symbols):
    if symbol.name == "EURUSD":
        print(f"{symbol.name} has minimum volume: {symbol.volume_min}")
        volume = symbol.volume_min * lot_multiple
EURUSDの最小出来高は0.01です。
MetaTrader 5端末から必要なデータ量を指定します。
#Specify date range of data to be collected
date_start = datetime(2020,1,1)
date_end = datetime.now()

データを取得したら、次に遷移行列を計算し、EUR USD市場がどのように推移するかを確認します。

#Fetch market data
market_data = pd.DataFrame(mt5.copy_rates_range("EURUSD",timeframe,date_start,date_end))
market_data["time"] = pd.to_datetime(market_data["time"],unit='s')
#Add simple moving average technical indicator
market_data.ta.sma(length=20,append=True)
#Delete missing rows
market_data.dropna(inplace=True)
#Inspect the data frame
market_data

データフレーム

図4:現在の形式でのデータフレーム

次に、2本のローソク足の間にどれだけのスペースが必要かを定義します。この例では、「現在のローソク足が移動平均線の上で終値をつけた場合、次のローソク足も移動平均線の上で終値をつける確率は?」という質問に答えることに焦点を当てています。より長期的な移行確率に関心がある場合、このパラメータを大きく調整し、特定の戦略の要件に合わせることが重要です。

#Define how far ahead we are looking
look_ahead = 1

遷移行列の計算は簡単です。

  1. まず、可能なすべての状態を定義します(ここでは単純な2つの状態であるUPとDOWN)。
  2. それぞれの状態に当てはまるローソク足の数を数えます。
  3. UP状態のローソク足全体の中で、次もUP状態になったローソク足の割合を計算します。
  4. DOWN状態のローソク足全体の中で、次もDOWN状態になったローソク足の割合を計算します。
#Count the number of times price was above the moving average
up = market_data.loc[market_data["close"] > market_data["SMA_20"]].shape[0]

#Count the number of times price was below the moving average
down = market_data.loc[market_data["close"] < market_data["SMA_20"]].shape[0]

#Count the number of times price was above the moving average and remained above it
up_and_up = (market_data.loc[( (market_data["close"] > market_data["SMA_20"]) & (market_data["close"].shift(-look_ahead) > market_data["SMA_20"].shift(-look_ahead)) )].shape[0]) / up

#Count the number of times price was below the moving average and remained below it
down_and_down = (market_data.loc[( (market_data["close"] < market_data["SMA_20"]) & (market_data["close"].shift(-look_ahead) < market_data["SMA_20"].shift(-look_ahead)) )].shape[0]) / down
そして、データをデータフレームにまとめます。
transition_matrix = pd.DataFrame({
    "UP":[up_and_up,(1-down_and_down)],
    "DOWN":[(1-up_and_up),down_and_down]
},index=['UP','DOWN'])

遷移行列を見てみましょう。

transition_matrix

遷移行列

図5:遷移行列

現在のローソク足が移動平均線より上で引けた場合、次のローソク足が移動平均線より上で引ける確率は88%、次のローソク足が移動平均線より下で引ける確率は12%です。これは、この市場では価格が頻繁に反転しないことを示しており、トレンドフォロー戦略が適している可能性を示しています。したがって、市場はトレンドフォロー戦略に適合する可能性があります。 

遷移行列ができたので、次はこの遷移行列を用いて、特定の証券を買うべきか売るべきかを判断するアルゴリズムの残りの部分を構築していきます。 

まず、端末から現在の価格データを取得し、テクニカル指標を計算する関数を定義します。

def get_prices():
    start = datetime(2024,6,1)
    end   = datetime.now()
    data  = pd.DataFrame(mt5.copy_rates_range("EURUSD",timeframe,start,end))
    #Add simple moving average technical indicator
    data.ta.sma(length=20,append=True)
    #Delete missing rows
    data.dropna(inplace=True)
    data['time'] = pd.to_datetime(data['time'],unit='s')
    data.set_index('time',inplace=True)
    return(data.iloc[-1,:])

次に、現在の市場の状態を取得する関数を定義します。

def get_state(current_data):
    #Price is above the moving average, UP state
    if(current_data["close"]  > current_data["SMA_20"]):
        return(1)
    #Price is below the moving average, DOWN state
    elif(current_data["close"] < current_data["SMA_20"]):
        return(2)

最後に、現在の市場の状態と移行確率が与えられたときに、アクションを選択する関数を定義します。

def get_action(current_state):
    if(current_state == 1):
        if(transition_matrix.iloc[0,0] > transition_matrix.iloc[0,1]):
            print("The market is above the moving average and has strong trends, buy")
            print("Opening a BUY position")
            mt5.Buy("EURUSD",volume)
        elif(transition_matrix.iloc[0,0] < transition_matrix.iloc[0,1]):
            print("The market is above the moving average and has strong mean reverting moves, sell")
            print("Opening a sell position")
            mt5.Sell("EURUSD",volume)
    elif(current_state == 2):
        if(transition_matrix.iloc[1,0] > transition_matrix.iloc[1,1]):
            print("The market is below the moving average and has strong mean reverting moves, buy")
            print("Opening a BUY position")
            mt5.Buy("EURUSD",volume)
        elif(transition_matrix.iloc[1,0] < transition_matrix.iloc[1,1]):
            print("The market is below the moving average and has strong trends, sell")
            print("Opening a sell position")
            mt5.Sell("EURUSD",volume)

これで、アルゴリズムが実際に動いているのを見ることができます。

while True:
    #Get data on the current state of our terminal and our portfolio
    positions = mt5.positions_total()
    #If we have no open positions then we can open one
    if(positions == 0):
        get_action(get_state(get_prices()))
    #If we have finished all checks then we can wait for one day before checking our positions again
    time.sleep(60)

市場が移動平均線を下回り、強いトレンドがある場合は売ります。

売りポジションを建てます。

初期市況

図6:取引アルゴリズムによって選択された取引


翌日の取引

図7:翌日の取引アルゴリズムによって選択された取引

遷移行列については、まだ言えることがたくさんあります。しかし、これはこのトピックを紹介するための良い入門書です。議論を終える前に、遷移行列に影響を与える変数や、必要に応じて遷移行列をどのように操作できるかについても触れることが重要です。


銘柄
遷移行列に影響を与える最初の変数は、選択する銘柄です。例えば、他のすべての変数をそのままにして、単に新しい銘柄「Boom 1000 Index」を選択すると、遷移行列は異なる結果を示すことになります。



UP DOWN
UP 0.926 0.074
DOWN 0.043 0.957

ご覧のように、EURUSDを銘柄として選択したとき、移動平均線の上に2本のローソク足が連続する確率は88%でしたが、今回選択した新しい銘柄「Boom 1000 Index」では、移動平均線の上に2本のローソク足が連続する確率は93%に上昇しました。したがって、選択の銘柄が遷移行列に与える影響は否定できません。

テクニカル指標のパラメータ

テクニカル指標は、市場状態を簡単に定義するのに役立ちます。移動平均の期間を変えることで、遷移行列に大きな影響を与えることができます。ポイントを簡単に説明するために、EURUSDのモデルに戻りましょう。最初の例では20期間の移動平均を使用しましたが、今回は2期間の移動平均を使用します。他の変数はすべて同じに保たれています。


UP DOWN
UP 0.456 0.544
DOWN 0.547 0.453

遷移確率が、どちらの方向にも50/50に収束していることに注目してください。これは、移動平均の期間が長くなるにつれて、遷移確率が50/50の可能性から離れていくことを示唆しています。

ローソク足の隙間

これまでの議論では、連続する2本のローソク足の関係にのみ焦点を当てていました。しかし、ローソク足の間隔を広げると、遷移行列も変わります。再びEURUSDのモデルに戻り、今回は2本のローソク足の間隔を100に広げます。つまり、2本のローソク足の間隔を除けば、他の変数はすべて同じになります。


UP DOWN
UP 0.503 0.497
DOWN 0.507 0.493


推奨 

マルコフ連鎖の設計に絶対的な「正しい」方法や「間違っている」方法はありませんが、アプリケーションを議論と一致させるためには、マルコフ連鎖を構築する際に以下に概説する設計パターンに従うことが必須です。
transition_matrix = pd.DataFrame({
    "UP":["UP AND UP","UP AND DOWN"],
    "DOWN":["DOWN AND UP","DOWN AND DOWN"]
},index=['UP','DOWN'])

私たちの遷移行列は、トレンドに従うべきか、トレンドに逆らうべきかを素早く示すように設計されています。

トレンドフォロー戦略は、主対角線が最大の確率を含んでいるときに最もうまく機能する可能性があります。これは、市場がトレンドを拾い、そのトレンドを維持する傾向があることを意味します。

トレンドフォロー

図8:トレンドに従う推移行列

逆に、平均回帰戦略は、非対角線に最大の確率が含まれる場合に最も効果的に機能する可能性があります。これは、市場が均衡レベルに戻る傾向があることを意味します。

平均回帰

図9:平均回帰する遷移行列

さらに、最も大きな確率が最下段にある場合、これは市場が弱気であることを意味します。


弱気市場

図10:弱気遷移行列


最後に、最大の確率が最上段にある場合、これは市場が強気であることを意味します。

強気市場

図11:強気の遷移行列


MQL5の実装

MQL5を使ってストラテジーを実装し、実際の市場データを使って戦略を徹底的にテストします。

まず、必要なライブラリを読み込みます。

//+------------------------------------------------------------------+
//|                                          Transition Matrices.mq5 |
//|                                       Gamuchirai Zororo Ndawana. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Zororo Ndawana."
#property link      "https://www.mql5.com"
#property version   "1.00"

//+------------------------------------------------------------------+
//|Overview                                                          |
//+------------------------------------------------------------------+
/*
This expert advisor will demonstrate how we can use transition matrices to build
self optimizing expert advisors. We will use the transition matrix to decide whether
we should employ trend following, or mean reverting trading strategies.

Gamuchirai Zororo Ndawana
Friday 19 July 2024, 10:09
Selebi Phikwe
Botswana
*/

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

次に、エンドユーザーが編集できる入力パラメータを定義します。

//+------------------------------------------------------------------+
//| Inputs                                                            |
//+------------------------------------------------------------------+
input int fetch = 5; //How much historical data should we fetch?
input int look_ahead = 1; //Our forecast horizon

input int ma_period = 20; //The MA Period
input int rsi_period = 20; //The RSI Period
input int wpr_period = 20; //The Williams Percent Range Period

input int  lot_multiple = 20; //How big should the lot sizes be
input double sl_width = 0.4; //Stop loss size

次に、アプリケーション全体で必要となるグローバル変数があります。

//+------------------------------------------------------------------+
//|Global variables                                                  |
//+------------------------------------------------------------------+
double minimum_volume;//Smallest lot size
double ask_price;//Ask
double bid_price;//Bid
int ma_handler,rsi_handler,wpr_handler;//The handlers for our technical indicators
vector ma_readings(fetch);//MA indicator values
vector rsi_readings(fetch);//RSI indicator values
vector wpr_readings(fetch);//WPR indicator values
vector price_readings(fetch);//The vector we will use to store our historical price values
matrix transition_matrix = matrix::Zeros(2,2);//The matrix to store our observations on price's transition behavior
bool transition_matrix_initialized = false;//This flag will instruct the application to initialize the transition matrix
double up_and_up = 0;//These variables will keep count of the price transitions
double up_and_down = 0;
double down_and_up = 0;
double down_and_down = 0;
double total_count = (double) fetch - look_ahead;//This variable will store the total number of observations used to calculate the transition matrix
double trading_volume;//This is our desired trading size
vector market_behavior = vector::Zeros(4);//Transition matrix interpretations

EAの初期化関数を定義します。この関数は、ユーザーが有効な入力を渡したことを確認し、テクニカル指標を設定します。

//+------------------------------------------------------------------+
//| Initialization Function                                          |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initialize the technical indicator
   ma_handler = iMA(_Symbol,PERIOD_CURRENT,ma_period,0,MODE_EMA,PRICE_CLOSE);
   rsi_handler = iRSI(_Symbol,PERIOD_CURRENT,rsi_period,PRICE_CLOSE);
   wpr_handler = iWPR(_Symbol,PERIOD_CURRENT,wpr_period);
   minimum_volume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   trading_volume = minimum_volume * lot_multiple;
//--- Look ahead cannot be greater than fetch
   if(look_ahead > fetch)
     {
      Comment("We cannot forecast further into the future than thr to total amount of  data fetched.\nEither fetch more data or forecast nearer to the present.");
      return(INIT_FAILED);
     }
//--- End of initialization
   return(INIT_SUCCEEDED);
  }

初期化が解除されるたびに従うプロシージャも必要です。

//+------------------------------------------------------------------+
//| Expert de-initialization function                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Remove technical indicators
   IndicatorRelease(rsi_handler);
   IndicatorRelease(wpr_handler);
   IndicatorRelease(ma_handler);
//--- Remove Expert Advisor
   ExpertRemove();
  }

また、テクニカル指標を更新し、現在の市場価格を取得する機能も作ります。

//+------------------------------------------------------------------+
//|This function will update our technical indicator values          |
//+------------------------------------------------------------------+
void update_technical_indicators(void)
  {
//--- Update bid and ask price
   ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   bid_price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
//--- Update each indicator value, we only need the most recent reading
   rsi_readings.CopyIndicatorBuffer(rsi_handler,0,0,1);
   wpr_readings.CopyIndicatorBuffer(wpr_handler,0,0,1);
   ma_readings.CopyIndicatorBuffer(ma_handler,0,0,1);
  }

テクニカル指標の数値の解釈は、常に推移行列によって測定される市場の動きに左右されることを忘れないでください。

//+------------------------------------------------------------------+
//|This function will find an entry opportunity based on our signals |                                                                  |
//+------------------------------------------------------------------+
void find_entry(void)
  {
//--- Store the index of our largest entry
   ulong max_arg = market_behavior.ArgMax();

//--- First we have to know the behavior of the market before we decide to buy or sell
   if(max_arg == 0)
     {
      //--- This means that the market is bullish and we should probably only take buy oppurtunities
      Comment("The observed transition matrix can only be generated by a bullish market");
      bullish_sentiment(0);
     }
   else
      if(max_arg == 1)
        {
         //--- This means that the market is bearish and we should probably only take sell oppurtunities
         Comment("The observed transition matrix can only be generated by a bearish market");
         bearish_sentiment(0);
        }
      else
         if(max_arg == 2)
           {
            //--- This means that the market trends and we should probably join either side of the trend
            Comment("The observed transition matrix can only be generated by a trending market");
            bearish_sentiment(0);
            bullish_sentiment(0);
           }
         else
            if(max_arg == 3)
              {
               //--- This means that the market is mean reverting and we should probably play against the trends on either side
               Comment("The observed transition matrix can only be generated by a mean reverting market");
               bearish_sentiment(-1);
               bullish_sentiment(-1);
              }
  }

買い注文を実行する関数が必要です。

//+----------------------------------------------------------------+
//|This function will look for oppurtunities to buy                |
//+----------------------------------------------------------------+
void bullish_sentiment(int f_flag)
  {
//--- This function analyses the market for bullish sentiment using our technical indicator
//--- It has only 1 parameter, a flag denoting whether we should interpret the indicators in a trend following fashion
//--- or a mean reverting fashion. For example 0 means interpret the indicators in a trend following fashion.
//--- Therefore if we call the function and pass 0, RSI readings above 50 will trigger buy orders.
//--- However if -1 was passed then RSI readings below 50 will trigger buy orders.
//--- First make sure we have no open positions
   if(PositionsTotal() > 0)
     {
      return;
     }

//--- Interpret the flag
   if(f_flag == 0)
     {
      //--- The flag is telling us to follow the trend
      if((rsi_readings[0] > 50) && (wpr_readings[0] > -50))
        {
         Trade.Buy(trading_volume,_Symbol,ask_price,(ask_price - sl_width),(ask_price + sl_width),"Transition Matrix Order");
        }
     }
   else
      if(f_flag == -1)
        {
         //--- The flag is telling us to bet against the trend
         if((rsi_readings[0] < 50) && (wpr_readings[0] < -50))
           {
            Trade.Buy(trading_volume,_Symbol,ask_price,(ask_price - sl_width),(ask_price + sl_width),"Transition Matrix Order");
           }
        }

  }

この関数は、売り注文を実行してくれます。市場が平均回帰的であれば、指標を「逆」に解釈することを思い出してください。

//+-------------------------------------------------------------+
//|This function will help us find oppurtunities to sell        |
//+-------------------------------------------------------------+
void bearish_sentiment(int f_flag)
  {
//--- This function analysises the market for bearish sentiment using our technical indicator
//--- It has only 1 parameter, a flag denoting whether we should interpret the indicators in a trend following fashion
//--- or a mean reverting fashion. For example 0 means interpret the indicators in a trend following fashion.
//--- Therefore if we call the function and pass 0, RSI readings below 50 will trigger sell orders.
//--- However if -1 was passed then RSI readings above 50 will trigger sell orders.
//--- First make sure we have no open positions
   if(PositionsTotal() > 0)
     {
      return;
     }
//--- Interpret the flag
   if(f_flag == 0)
     {
      //--- Now we know how to interpret our technical indicators
      if((rsi_readings[0] < 50) && (wpr_readings[0] < -50))
        {
         Trade.Sell(trading_volume,_Symbol,bid_price,(bid_price + sl_width),(bid_price - sl_width),"Transition Matrix Order");
        }
     }
   else
      if(f_flag == -1)
        {
         //--- Now we know how to interpret our technical indicators
         if((rsi_readings[0] > 50) && (wpr_readings[0] > -50))
           {
            Trade.Sell(trading_volume,_Symbol,bid_price,(bid_price + sl_width),(bid_price - sl_width),"Transition Matrix Order");
           }
        }
  }

また、遷移行列が準備され、上で説明した手順に従って計算されるようにする関数も定義しておきましょう。

//+---------------------------------------------------------------+
//|This function will initialize our transition matrix            |
//+---------------------------------------------------------------+
void initialize_transition_matrix(void)
  {
//--- We need to update our historical price readings and our MA readings
   ma_readings.CopyIndicatorBuffer(ma_handler,0,1,fetch);
   price_readings.CopyRates(_Symbol,PERIOD_CURRENT,COPY_RATES_CLOSE,1,fetch);

//--- Now let us update our transition matrix
   for(int i = 0; i < fetch - look_ahead; i++)
     {
      //--- Did price go from being above the MA but end up beneath the MA?
      if((price_readings[i] > ma_readings[i]) && (price_readings[i + look_ahead] < ma_readings[i + look_ahead]))
        {
         up_and_down += 1;
        }
      //--- Did price go from being above the MA and remain above it?
      else
         if((price_readings[i] > ma_readings[i]) && (price_readings[i + look_ahead] > ma_readings[i + look_ahead]))
           {
            up_and_up += 1;
           }

         //--- Did price go from being below the MA but end up above it?
         else
            if((price_readings[i] < ma_readings[i]) && (price_readings[i + look_ahead] > ma_readings[i + look_ahead]))
              {
               down_and_up += 1;
              }

            //--- Did price go from being below the MA and remain below it?
            else
               if((price_readings[i] < ma_readings[i]) && (price_readings[i + look_ahead] < ma_readings[i + look_ahead]))
                 {
                  down_and_down += 1;
                 }
     }

//--- Let us see our counts so far
   Print("Up and up: ",up_and_up,"\nUp and down: ",up_and_down,"\nDown and up: ",down_and_up,"\nDown and down: ",down_and_down);
   double sum_of_counts = up_and_up + up_and_down + down_and_up + down_and_down;
   Print("Sum of counts: ",(sum_of_counts),"\nObservations made: ",total_count,"\nDifference:[the difference should always be 0] ",(total_count - sum_of_counts));
//--- Now we will calculate the transition matrix
//--- The matrix position (0,0) stores the probaility that after making a move up, the market will continue rising
//--- The matrix position (0,1) stores the probability that after making a move down, price will reverse and start rising
//--- The matrix position (1,0) stores the probability that after making a move up, price will reverse and start falling
//--- The matrix position (1,1) stores the probabilty that after making a move down, price will continue falling
   transition_matrix[0][0] = up_and_up / (up_and_up + up_and_down);
   transition_matrix[0][1] = down_and_up / (up_and_up + up_and_down);
   transition_matrix[1][0] = up_and_down / (down_and_up + down_and_down);
   transition_matrix[1][1] = down_and_down / (down_and_up + down_and_down);
//--- Show the transition matrix
   Print("Our transition Matrix");
   Print(transition_matrix);
//--- Now we need to make sense of the transition matrix
   analyse_transition_matrix();
//--- Now we need to update the flag
   transition_matrix_initialized = true;
  }

遷移行列を解釈するヘルパー関数も必要です。

//+-------------------------------------------------------------+
//|This function will analyse our transition matrix             |
//+-------------------------------------------------------------+
void analyse_transition_matrix(void)
  {
//--- Check if the market is bullish
   if((transition_matrix[0][0] > transition_matrix[1][0])&&(transition_matrix[0][1] > transition_matrix[1][1]))
     {
      market_behavior[0] = 1;
     }
//--- Check if the market is bearish
   else
      if((transition_matrix[0][1] > transition_matrix[1][0])&&(transition_matrix[1][1] > transition_matrix[1][0]))
        {
         market_behavior[1] = 1;
        }
      //--- Check if the market trends
      else
         if(transition_matrix.Trace() > 1)
           {
            market_behavior[2] = 1;
           }
         //--- Check if the market is mean reverting
         else
            if(transition_matrix.Trace() < 1)
              {
               market_behavior[3] = 1;
              }
  }
//+------------------------------------------------------------------+

OnTickハンドラは、上で説明したすべての関数が適切なタイミングで呼び出されるようにします。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- First we must check if our transition matrix has been initialized
   if(!transition_matrix_initialized)
     {
      initialize_transition_matrix();
     }
//--- Otherwise our transition matrix has been initialized
   else
     {
      //--- Update technical indicator values
      update_technical_indicators();
      //--- If we have no open positions we will use our tranistion matrix to help us interpret our technical indicators
      if(PositionsTotal() == 0)
        {
         find_entry();
        }
     }
  }
//+------------------------------------------------------------------+

遷移行列

図12:MQL5で計算された遷移行列

EAのテスト

図13:EAがAUDJPYペアを取引している


結論

の記事では、変化する市況に適応するためのアルゴリズム取引におけるマルコフ連鎖の応用について考察しました。まず、マルコフ連鎖の基本概念を紹介し、それが市場のダイナミクスといったランダム過程のモデル化においていかに有用であるかを説明しました。次に、移動平均線などのテクニカル指標を用いて市場の状態を定義し、マルコフ連鎖を活用して市場の推移を分析する具体的な方法を示しました。この手法により、将来の市場動向の確率を予測し、トレンドフォロー戦略と平均回帰戦略のどちらを採用すべきかを判断できるようになります。このアプローチを通じて、意思決定能力を強化したインテリジェントな取引アルゴリズムを構築し、ダイナミックに変化する市場での取引パフォーマンスの向上を目指しています。


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

最後のコメント | ディスカッションに移動 (3)
Sibusiso Steven Mathebula
Sibusiso Steven Mathebula | 26 7月 2024 において 03:38
上記の記事では、マトリックスとベクトルを使用して、必ずしも従来のニューラルネットワークアプローチを使用せずに取引戦略を 最適化しています。つまり、EAの自己最適化に活性化関数やニューロンは必要ないということです。私は訂正される可能性が高いです。私は間違いなく間違っているかもしれないし、本当にひどく間違っているかもしれない。
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana | 27 7月 2024 において 11:14
Sibusiso Steven Mathebula 取引戦略を 最適化しています。つまり、EAの自己最適化に活性化関数やニューロンは必要ないということです。私は訂正される可能性が高いです。僕は間違いなく間違っているかもしれないし、本当にひどく間違っているかもしれない。
おい、SIbusiso、Ujani Budi?

まあ、何事にもいろいろなやり方があるのはご存知の通りだ。私がここで説明したアプローチは、確実な結果を早く得るためのものだ。しかし、何事にも代償はつきものです。あなたが観察する遷移行列は、どれだけ多くのデータを取得したかに大きく影響されます。しかし、より多くのデータを取得するにつれて、遷移行列は安定し、変化しなくなります(収束します)。

このように言っておきましょう。遷移行列とNNアプローチは、まったく別の問題を解決しています。彼らは別の質問に答えているのです。遷移行列は何かを予測するものではなく、過去に起こったことを単に要約して教えてくれるものであり、未来に起こりそうなことを教えてくれるものではない。

一方、NNは将来何が起こりそうかを教えてくれる。1つのEAでその両方を使うことは可能だ。
thiezo
thiezo | 1 1月 2025 において 07:09
こんにちは、がむしゃらさん。この記事は私に直接語りかけてくれて、私たちの心を開いてくれたことに感謝します。私はコーディングの初心者で、あなたのような記事を読み、コーディングすることで学んでいます。私の最大の課題はPythonです。バックテストをして 自分のEAにアイデアを取り入れることができるため、取引がテーマであればより早く学ぶことができます。Pythonを学べる場所を教えてください。私はMQL5バージョンしかコーディングしていませんが、私が直面している問題は、'max_arg'が0のままであるため、EAが強気のままであることです。私の限られた理解力では、いくつかのパラメーターを操作してみましたが、コードが買いと売りを同時に置くポイントで止まってしまいました。重要なディテールを見逃しているかもしれません。もしあなたのコードがあなたの側で正しく機能するのであれば、私のコピーしたコードや修正したコードを送ることができます。もしかしたら、問題を発見できるかもしれません。私は休暇中で、オフラインで作業しているため、ダウンロードしたデータを使用しています。それが問題を引き起こしているのでしょうか?あなたの仕事ぶりには感謝していますし、記事も素晴らしいです。私はSA出身ですが、tsanoさんには感謝の言葉しかありません。
Candlestick Trend Constraintモデルの構築(第7回):EA開発モデルの改良 Candlestick Trend Constraintモデルの構築(第7回):EA開発モデルの改良
今回は、エキスパートアドバイザー(EA)開発のための指標の詳細な準備について掘り下げていきます。議論の中では、現行バージョンの指標にさらなる改良を加えることで、その精度と機能性の向上を図ります。さらに、前バージョンがエントリポイントの識別に限られていた制約に対応するため、新たにエグジットポイントを特定する機能を導入します。
時系列の非定常性の指標としての2標本コルモゴロフ–スミルノフ検定 時系列の非定常性の指標としての2標本コルモゴロフ–スミルノフ検定
この記事では、最も有名なノンパラメトリック同質性検定の1つである2標本のコルモゴロフ–スミルノフ検定について考察します。モデルデータと実際の相場の両方が分析されています。また、この記事では非定常性指標(iスミルノフ距離)の構築例も紹介しています。
データサイエンスと機械学習(第27回):MetaTrader 5取引ボットにおける畳み込みニューラルネットワーク(CNN)に価値はあるか? データサイエンスと機械学習(第27回):MetaTrader 5取引ボットにおける畳み込みニューラルネットワーク(CNN)に価値はあるか?
畳み込みニューラルネットワーク(CNN)は、画像や映像のパターンを検出する能力に優れていることで有名で、さまざまな分野に応用されています。この記事では、金融市場の価値あるパターンを識別し、MetaTrader 5取引ボットのための効果的な取引シグナルを生成するCNNの可能性を探ります。このディープマシンラーニングの手法を、よりスマートな取引判断のためにどのように活用できるかを見てみましょう。
パラボリックSARを使ってトレーリングストップを追加する方法 パラボリックSARを使ってトレーリングストップを追加する方法
取引戦略を作成する際には、さまざまな保護ストップのオプションをテストする必要があります。その中で、価格に追随してストップロスレベルをダイナミックに引き上げる方法が考えられます。その最有力候補として、パラボリックSAR指標が挙げられます。これ以上シンプルで視覚的にわかりやすい指標はないでしょう。