
MQL5でのカスタム市場レジーム検出システムの構築(第1回):インジケーター
はじめに
金融市場は常に変化し続けており、強いトレンド、レンジ相場(横ばい)、そして混沌とした高ボラティリティ局面が次々に入れ替わります。これは、アルゴリズムトレーダーにとって大きな課題です。トレンド相場で好成績を収める戦略も、レンジ相場では機能しないことが多く、逆に低ボラティリティ向けの戦略は、急激な変動により大きな損失を招くこともあります。それにもかかわらず、多くの取引システムは、市場の挙動が時間を通じて一貫しているという暗黙の前提に基づいて設計されています。
この前提と市場の現実との間にある根本的なギャップこそが、戦略のパフォーマンス低下というよくある問題を引き起こしているのです。システムがバックテストや初期運用では好成績を残しても、市場環境が変化するとパフォーマンスが急落する。そんな場面に直面したトレーダーは、戦略を破棄して一からやり直すか、市場が再び自分の手法に合うまで損失に耐えるかという、難しい選択を迫られます。
では、もっと優れたアプローチがあるとしたらどうでしょう?もし取引システムが現在の市場レジーム(相場環境)を客観的に認識し、それに応じて戦略を柔軟に適応できたとしたら?本記事ではまさにその実現を目指し、MQL5を用いて市場環境を明確に分類する「市場レジーム検出システム(Market Regime Detection System)」を構築します。これを基盤に、適応型の取引戦略を展開していきます。
本連載の終わりには、以下を含む市場レジーム検出システムの完全実装を手に入れることができます。- 市場を客観的に分類するための堅牢な統計的基盤
- トレンド・レンジ・高ボラティリティといった相場環境を識別する、カスタム市場レジーム検出クラス
- レジームの変化をチャート上に視覚化するカスタムインジケーター
- 現在のレジームに応じて適切な戦略を自動で選択する適応型エキスパートアドバイザー(第2回)
- 実際の取引に即したパラメータ設定や最適化の例(第2回)
このシステムは、既存のアルゴリズム戦略を強化したい経験豊富なトレーダーにも、より堅牢な戦略構築を目指す初心者の方にも、有用なものです。変化の激しい金融市場を乗り越えるための、実践的かつ強力なツールとなるでしょう。
市場レジームについて
実装の詳細に入る前に、市場レジームとは何か、そしてそれがトレーダーにとってなぜ重要なのかを理解することが重要です。市場は常に一様に動くわけではなく、異なる行動パターンや「レジーム」の間を移行していきます。これらのレジームは価格の動きに大きな影響を与え、それに伴って取引戦略の成績も大きく変わります。
市場レジームとは
市場レジームとは、価格変動の特定の統計的性質によって特徴付けられる、市場行動の明確に異なるパターンのことです。市場レジームの分類には様々な方法がありますが、ここでは取引戦略の開発に最も関連性が高い3つの主要なタイプに注目します。- トレンドレジーム:市場が強い方向性を持って動き、平均回帰がほとんど見られません。価格は一方向に継続的に動き、押し戻しも浅い傾向があります。統計的には、トレンド相場はリターンの自己相関が正であり、一方向の動きが次の動きも同じ方向になる可能性が高いことを示します。
- レンジレジーム:市場はサポートとレジスタンスの間で振動し、強い平均回帰傾向を持ちます。価格はどちらかの方向にブレイクアウトするのではなく、定義された範囲内で跳ね返る傾向があります。統計的には、レンジ相場はリターンの自己相関が負であり、上昇の後には下降が来やすく、逆もまた然りです。
- 高ボラティリティレジーム:市場は大きく、不規則な価格変動を経験し、方向性が明確でない状態です。これは不確実性、ニュースイベント、市場のストレス期に多く見られます。統計的には、高ボラティリティ相場はリターンの標準偏差が高く、自己相関パターンは予測不可能です。
現在の市場がどのレジームにあるかを理解することは、取引の意思決定において非常に重要な文脈を提供します。トレンド相場に最適化された戦略はレンジ相場での成績が悪くなる可能性が高く、逆にレンジ相場向けの平均回帰戦略は強いトレンドが発生すると大きな損失を招くことがあります。
従来のインジケーターでは不十分な理由
多くのテクニカル指標は、特定の価格パターンや市場の状態を捉えるために設計されており、市場レジームを分類することを目的としていません。以下はその例です。- 移動平均線やMACDはトレンドを検出するのに有効ですが、「トレンド相場」と「ボラティリティが高いだけの相場」とを区別することはできません。
- RSIやストキャスティクスはレンジ相場で効果を発揮しますが、トレンドが発生している場面では誤ったシグナルを出すことが多くなります。
- ボリンジャーバンドはボラティリティに適応しますが、レジームの転換を明確に識別するものではありません。
レジーム検出の統計的基盤
効果的な市場レジーム検出システムを構築するには、市場の挙動を客観的に分類するための統計的指標を活用する必要があります。このプロジェクトで使用する主な統計概念は以下のとおりです。- 自己相関:時系列データとその遅延バージョンとの相関を測定します。正の自己相関はトレンド傾向を、負の自己相関は平均回帰傾向(レンジ)を示します。
- ボラティリティ:通常はリターンの標準偏差で測定され、価格変動の激しさを表します。ボラティリティの急上昇は、レジームの変化を示唆することがあります。
- トレンドの強さ:自己相関の絶対値、線形回帰の傾き、あるいはADXのような専用インジケーターを用いて定量化できます。
これらの統計指標を組み合わせることで、市場レジームを客観的かつ堅牢に分類できるフレームワークを構築することが可能になります。次のセクションでは、これらの概念をMQL5のコードに実装し、実際の「市場レジーム検出システム」を構築していきます。
統計基盤の構築
このセクションでは、市場レジーム検出システムに必要な統計的コア要素を実装していきます。ここでは、レジーム分類に必要なすべての数学的計算を処理する、堅牢なCStatisticsクラスを作成します。
CStatisticsクラス
私たちのレジーム検出システムの土台となるのは、価格データに対して様々な計算を実行できる高機能な統計クラスです。以下では、このクラスの主要な構成要素について解説していきます。
//+------------------------------------------------------------------+ //| Class for statistical calculations | //+------------------------------------------------------------------+ class CStatistics { private: double m_data[]; // Data array for calculations int m_dataSize; // Size of the data array bool m_isSorted; // Flag indicating if data is sorted double m_sortedData[]; // Sorted copy of data for percentile calculations public: // Constructor and destructor CStatistics(); ~CStatistics(); // Data management methods bool SetData(const double &data[], int size); bool AddData(double value); void Clear(); // Basic statistical methods double Mean() const; double StandardDeviation() const; double Variance() const; // Range and extremes double Min() const; double Max() const; double Range() const; // Time series specific methods double Autocorrelation(int lag) const; double TrendStrength() const; double MeanReversionStrength() const; // Percentile calculations double Percentile(double percentile); double Median(); };
このクラスは、価格データを分析し、現在の市場レジームを判別するための統計関数を網羅的に提供します。それでは、主要なメソッドのいくつかを詳しく見ていきましょう。
コンストラクタとデストラクタ
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CStatistics::CStatistics() { m_dataSize = 0; m_isSorted = false; ArrayResize(m_data, 0); ArrayResize(m_sortedData, 0); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CStatistics::~CStatistics() { Clear(); }
コンストラクタとデストラクタは、クラスの初期化とクラスの初期化解除に役立ちます。 コンストラクタはメンバー変数と配列を初期化し、デストラクタはClearメソッドを呼び出して適切なクリーンアップを確実におこないます。適切な初期化とクリーンアップの徹底は、MQL5におけるメモリリークの防止や安定した動作の確保において非常に重要です。
データ管理メソッド
次に、データを設定、追加、消去できるデータ管理メソッドを実装しましょう。
bool CStatistics::SetData(const double &data[], int size) { if(size <= 0) return false; m_dataSize = size; ArrayResize(m_data, size); for(int i = 0; i < size; i++) m_data[i] = data[i]; m_isSorted = false; return true; } bool CStatistics::AddData(double value) { m_dataSize++; ArrayResize(m_data, m_dataSize); m_data[m_dataSize - 1] = value; m_isSorted = false; return true; } void CStatistics::Clear() { m_dataSize = 0; ArrayResize(m_data, 0); ArrayResize(m_sortedData, 0); m_isSorted = false; }
SetDataメソッドは、データ全体を新しい配列に置き換えることができます。これは、過去の価格データを処理する際に便利です。一方、AddDataメソッドは、1つの値を既存のデータに追加するためのもので、新しい価格データが到着したときに段階的な更新を行う際に役立ちます。Clearメソッドは、オブジェクトを初期状態にリセットし、割り当てられたメモリを解放します。
なお、データが変更されるたびに「m_isSorted = false」と設定していることに注目してください。このフラグは、パーセンタイル計算の際にのみ必要に応じてソートをおこなうことで、パフォーマンスの最適化に役立ちます。
基本的な統計メソッド
次に、平均、標準偏差、分散を計算するための基本的な統計メソッドを実装していきましょう。
double CStatistics::Mean() const { if(m_dataSize <= 0) return 0.0; double sum = 0.0; for(int i = 0; i < m_dataSize; i++) sum += m_data[i]; return sum / m_dataSize; } double CStatistics::StandardDeviation() const { if(m_dataSize <= 1) return 0.0; double mean = Mean(); double sum = 0.0; for(int i = 0; i < m_dataSize; i++) sum += MathPow(m_data[i] - mean, 2); return MathSqrt(sum / (m_dataSize - 1)); } double CStatistics::Variance() const { if(m_dataSize <= 1) return 0.0; double stdDev = StandardDeviation(); return stdDev * stdDev; }
これらのメソッドは、標準的な統計の公式に基づいて実装されています。Meanメソッドは、すべてのデータ点の平均値を計算します。StandardDeviationメソッドは、データが平均からどの程度散らばっているか、つまり分布のばらつき(散布度)を測定します。これは、ボラティリティの高い市場レジームを識別する上で非常に重要です。Varianceメソッドは標準偏差の二乗で、同じくデータの分散性を示す指標です。
また、データが空である場合や、要素数が1つだけのケースにも注意して、ゼロを返すように設計されています。このような防御的プログラミングにより、十分なデータが揃っていない状況でもエラーを回避し、プログラムの安定性を確保することができます。範囲と極値のメソッド
//+------------------------------------------------------------------+ //| Calculate minimum value in the data | //+------------------------------------------------------------------+ double CStatistics::Min() const { if(m_dataSize <= 0) return 0.0; double min = m_data[0]; for(int i = 1; i < m_dataSize; i++) if(m_data[i] < min) min = m_data[i]; return min; } //+------------------------------------------------------------------+ //| Calculate maximum value in the data | //+------------------------------------------------------------------+ double CStatistics::Max() const { if(m_dataSize <= 0) return 0.0; double max = m_data[0]; for(int i = 1; i < m_dataSize; i++) if(m_data[i] > max) max = m_data[i]; return max; } //+------------------------------------------------------------------+ //| Calculate range (max - min) of the data | //+------------------------------------------------------------------+ double CStatistics::Range() const { return Max() - Min(); }
これらのメソッドは、データの分布に関する追加情報を提供します。MinメソッドとMaxメソッドはそれぞれデータセット内の最小値と最大値を見つけ、Rangeメソッドはそれらの差を計算します。これらの値は、市場の値幅や急激な変動の兆候を判断する材料として使われます。
時系列特有メソッド
次に、レジーム検出に不可欠な時系列特有のメソッドを実装していきましょう。
double CStatistics::Autocorrelation(int lag) const { if(m_dataSize <= lag || lag <= 0) return 0.0; double mean = Mean(); double numerator = 0.0; double denominator = 0.0; for(int i = 0; i < m_dataSize - lag; i++) { numerator += (m_data[i] - mean) * (m_data[i + lag] - mean); } for(int i = 0; i < m_dataSize; i++) { denominator += MathPow(m_data[i] - mean, 2); } if(denominator == 0.0) return 0.0; return numerator / denominator; } double CStatistics::TrendStrength() const { // Use lag-1 autocorrelation as a measure of trend strength double ac1 = Autocorrelation(1); // Positive autocorrelation indicates trending behavior return ac1; } double CStatistics::MeanReversionStrength() const { // Negative autocorrelation indicates mean-reverting behavior double ac1 = Autocorrelation(1); // Return the negative of autocorrelation, so positive values // indicate stronger mean reversion return -ac1; }
Autocorrelationメソッドは、データ系列とその遅延した系列との相関を計算します。これは、トレンド相場とレンジ相場を区別するための強力な指標です。正の自己相関(0より大きい値)はトレンド傾向を示し、負の自己相関(0より小さい値)は平均回帰(レンジ)傾向を示します。
TrendStrengthメソッドは、ラグ1の自己相関をトレンドの強さの直接的な指標として利用します。値が高く正のほど、トレンドが強いことを意味します。一方、MeanReversionStrengthメソッドは自己相関の符号を反転させて返し、正の値は平均回帰傾向が強いことを示します。
これらのメソッドは、私たちのレジーム検出システムの統計的基盤を形成し、市場の挙動を客観的に測定することでレジーム分類に活用されます。
パーセンタイル計算
最後に、パーセンタイルと中央値を計算するメソッドを実装しましょう。
double CStatistics::Percentile(double percentile) { if(m_dataSize <= 0 || percentile < 0.0 || percentile > 100.0) return 0.0; // Sort data if needed if(!m_isSorted) { ArrayResize(m_sortedData, m_dataSize); for(int i = 0; i < m_dataSize; i++) m_sortedData[i] = m_data[i]; ArraySort(m_sortedData); m_isSorted = true; } // Calculate position double position = (percentile / 100.0) * (m_dataSize - 1); int lowerIndex = (int)MathFloor(position); int upperIndex = (int)MathCeil(position); // Handle edge cases if(lowerIndex == upperIndex) return m_sortedData[lowerIndex]; // Interpolate double fraction = position - lowerIndex; return m_sortedData[lowerIndex] + fraction * (m_sortedData[upperIndex] - m_sortedData[lowerIndex]); } double CStatistics::Median() { return Percentile(50.0); }
Percentileメソッドは、データの中で指定されたパーセンテージ以下に位置する値を計算します。まずデータをソート(未ソートの場合)し、その後線形補間を使って正確なパーセンタイル値を求めます。Medianメソッドは、50パーセンタイル、つまりデータセットの中央値を返す便利な関数です。
ここで注目すべきは、m_isSortedフラグによる最適化です。このフラグのおかげで、複数のパーセンタイルを計算する場合でもデータのソートは一度だけおこなわれます。これは、MQL5コードのパフォーマンスを向上させる慎重な実装例と言えます。
こうしてCStatisticsクラスが完成し、価格データの分析や市場レジーム検出に役立つ強力なツール群が揃いました。次のセクションでは、この基盤をもとに市場レジーム検出器クラスを構築していきます。
市場レジーム検出器(Market Regime Detector)の実装
統計的基盤が整ったので、システムの中核となるコンポーネントである市場レジーム検出器を構築します。このクラスは、先ほど実装した統計指標を活用して、市場状況を特定のレジームに分類します。
市場レジームの列挙
まず、システムが識別する市場レジームの種類を定義します。これを全コンポーネントで共有できるように、MarketRegimeEnum.mqhという別ファイルに列挙型を作成します。
// Define market regime types enum ENUM_MARKET_REGIME { REGIME_TRENDING_UP = 0, // Trending up regime REGIME_TRENDING_DOWN = 1, // Trending down regime REGIME_RANGING = 2, // Ranging/sideways regime REGIME_VOLATILE = 3, // Volatile/chaotic regime REGIME_UNDEFINED = 4 // Undefined regime (default) };
この列挙は、システムが検出できる5つの市場レジームを定義します。現在の市場の状態を表すために、実装全体でこれらの値を使用します。
CMarketRegimeDetectorクラス
市場レジーム検出器クラスは、これまでに構築した統計ツールとレジーム分類のロジックを組み合わせたものです。その構造を詳しく見ていきましょう。
class CMarketRegimeDetector { private: // Configuration int m_lookbackPeriod; // Period for calculations int m_smoothingPeriod; // Period for smoothing regime transitions double m_trendThreshold; // Threshold for trend detection double m_volatilityThreshold; // Threshold for volatility detection // Data buffers double m_priceData[]; // Price data buffer double m_returns[]; // Returns data buffer double m_volatility[]; // Volatility buffer double m_trendStrength[]; // Trend strength buffer double m_regimeBuffer[]; // Regime classification buffer // Statistics objects CStatistics m_priceStats; // Statistics for price data CStatistics m_returnsStats; // Statistics for returns data CStatistics m_volatilityStats; // Statistics for volatility data // Current state ENUM_MARKET_REGIME m_currentRegime; // Current detected regime // Helper methods void CalculateReturns(); void CalculateVolatility(); void CalculateTrendStrength(); ENUM_MARKET_REGIME DetermineRegime(); public: // Constructor and destructor CMarketRegimeDetector(int lookbackPeriod = 100, int smoothingPeriod = 10); ~CMarketRegimeDetector(); // Configuration methods void SetLookbackPeriod(int period); void SetSmoothingPeriod(int period); void SetTrendThreshold(double threshold); void SetVolatilityThreshold(double threshold); // Processing methods bool Initialize(); bool ProcessData(const double &price[], int size); // Access methods ENUM_MARKET_REGIME GetCurrentRegime() const { return m_currentRegime; } string GetRegimeDescription() const; double GetTrendStrength() const; double GetVolatility() const; // Buffer access for indicators bool GetRegimeBuffer(double &buffer[]) const; bool GetTrendStrengthBuffer(double &buffer[]) const; bool GetVolatilityBuffer(double &buffer[]) const; };
このクラスは、市場レジームを検出するために必要なすべての機能をカプセル化します。それぞれのメソッドを実装してみましょう。
コンストラクタとデストラクタ
まず、コンストラクタとデストラクタを実装しましょう。
CMarketRegimeDetector::CMarketRegimeDetector(int lookbackPeriod, int smoothingPeriod) { // Set default parameters m_lookbackPeriod = (lookbackPeriod > 20) ? lookbackPeriod : 100; m_smoothingPeriod = (smoothingPeriod > 0) ? smoothingPeriod : 10; m_trendThreshold = 0.2; m_volatilityThreshold = 1.5; // Initialize current regime m_currentRegime = REGIME_UNDEFINED; // Initialize buffers ArrayResize(m_priceData, m_lookbackPeriod); ArrayResize(m_returns, m_lookbackPeriod - 1); ArrayResize(m_volatility, m_lookbackPeriod - 1); ArrayResize(m_trendStrength, m_lookbackPeriod - 1); ArrayResize(m_regimeBuffer, m_lookbackPeriod); // Initialize buffers with zeros ArrayInitialize(m_priceData, 0.0); ArrayInitialize(m_returns, 0.0); ArrayInitialize(m_volatility, 0.0); ArrayInitialize(m_trendStrength, 0.0); ArrayInitialize(m_regimeBuffer, (double)REGIME_UNDEFINED); } CMarketRegimeDetector::~CMarketRegimeDetector() { // Free memory (not strictly necessary in MQL5, but good practice) ArrayFree(m_priceData); ArrayFree(m_returns); ArrayFree(m_volatility); ArrayFree(m_trendStrength); ArrayFree(m_regimeBuffer); }
コンストラクタは、すべてのメンバー変数と配列をデフォルト値で初期化します。統計的に有意な結果を得るため、ルックバック期間が少なくとも20バー以上であること、および平滑化期間が正の値であることを確認するパラメータの検証も含まれています。デストラクタでは、配列に割り当てられたメモリを解放します。MQL5には自動的なガベージコレクションがあるとはいえ、こうした明示的なクリーンアップは良い習慣です。
設定メソッド
次に、ユーザーが検出器の動作をカスタマイズできるようにする設定メソッドを実装していきましょう。
void CMarketRegimeDetector::SetLookbackPeriod(int period) { if(period <= 20) return; m_lookbackPeriod = period; // Resize buffers ArrayResize(m_priceData, m_lookbackPeriod); ArrayResize(m_returns, m_lookbackPeriod - 1); ArrayResize(m_volatility, m_lookbackPeriod - 1); ArrayResize(m_trendStrength, m_lookbackPeriod - 1); ArrayResize(m_regimeBuffer, m_lookbackPeriod); // Re-initialize Initialize(); } void CMarketRegimeDetector::SetSmoothingPeriod(int period) { if(period <= 0) return; m_smoothingPeriod = period; } void CMarketRegimeDetector::SetTrendThreshold(double threshold) { if(threshold <= 0.0) return; m_trendThreshold = threshold; } void CMarketRegimeDetector::SetVolatilityThreshold(double threshold) { if(threshold <= 0.0) return; m_volatilityThreshold = threshold; }
これらのメソッドにより、ユーザーは特定の取引商品や時間枠に合わせて検出器のパラメータをカスタマイズできます。SetLookbackPeriodメソッドは、すべての内部バッファのサイズを新しい期間に合わせて変更するため、特に重要です。他のメソッドは、入力値を検証した後、対応するパラメータを更新するだけです。
初期化と処理のメソッド
次に、初期化とデータ処理のメソッドを実装します。
bool CMarketRegimeDetector::Initialize() { // Initialize buffers with zeros ArrayInitialize(m_priceData, 0.0); ArrayInitialize(m_returns, 0.0); ArrayInitialize(m_volatility, 0.0); ArrayInitialize(m_trendStrength, 0.0); ArrayInitialize(m_regimeBuffer, (double)REGIME_UNDEFINED); // Reset current regime m_currentRegime = REGIME_UNDEFINED; return true; } bool CMarketRegimeDetector::ProcessData(const double &price[], int size) { if(size < m_lookbackPeriod) return false; // Copy the most recent price data for(int i = 0; i < m_lookbackPeriod; i++) m_priceData[i] = price[size - m_lookbackPeriod + i]; // Calculate returns, volatility, and trend strength CalculateReturns(); CalculateVolatility(); CalculateTrendStrength(); // Determine the current market regime m_currentRegime = DetermineRegime(); // Update regime buffer for indicator display for(int i = 0; i < m_lookbackPeriod - 1; i++) m_regimeBuffer[i] = m_regimeBuffer[i + 1]; m_regimeBuffer[m_lookbackPeriod - 1] = (double)m_currentRegime; return true; }
Initializeメソッドは、すべてのバッファと現在のレジームをデフォルト値にリセットします。ProcessDataメソッドは検出器の心臓部であり、新しい価格データを処理し、レジームの分類を更新します。まず最新の価格データをコピーし、次にリターン、ボラティリティ、トレンドの強さを計算し、最後に現在の市場レジームを判定します。また、インジケーター表示用のレジームバッファも更新し、値をシフトして新しいレジームのためのスペースを確保します。
計算メソッド
レジーム検出に使用する統計指標を計算するための計算メソッドを実装していきましょう。
void CMarketRegimeDetector::CalculateReturns() { for(int i = 0; i < m_lookbackPeriod - 1; i++) { // Calculate percentage returns if(m_priceData[i] != 0.0) m_returns[i] = (m_priceData[i + 1] - m_priceData[i]) / m_priceData[i] * 100.0; else m_returns[i] = 0.0; } // Update returns statistics m_returnsStats.SetData(m_returns, m_lookbackPeriod - 1); } void CMarketRegimeDetector::CalculateVolatility() { // Use a rolling window for volatility calculation int windowSize = MathMin(20, m_lookbackPeriod - 1); for(int i = 0; i < m_lookbackPeriod - 1; i++) { if(i < windowSize - 1) { m_volatility[i] = 0.0; continue; } double sum = 0.0; double mean = 0.0; // Calculate mean for(int j = 0; j < windowSize; j++) mean += m_returns[i - j]; mean /= windowSize; // Calculate standard deviation for(int j = 0; j < windowSize; j++) sum += MathPow(m_returns[i - j] - mean, 2); m_volatility[i] = MathSqrt(sum / (windowSize - 1)); } // Update volatility statistics m_volatilityStats.SetData(m_volatility, m_lookbackPeriod - 1); } void CMarketRegimeDetector::CalculateTrendStrength() { // Use a rolling window for trend strength calculation int windowSize = MathMin(50, m_lookbackPeriod - 1); for(int i = 0; i < m_lookbackPeriod - 1; i++) { if(i < windowSize - 1) { m_trendStrength[i] = 0.0; continue; } double window[]; ArrayResize(window, windowSize); // Copy data to window for(int j = 0; j < windowSize; j++) window[j] = m_returns[i - j]; // Create temporary statistics object CStatistics tempStats; tempStats.SetData(window, windowSize); // Calculate trend strength using autocorrelation m_trendStrength[i] = tempStats.TrendStrength(); } // Update price statistics m_priceStats.SetData(m_priceData, m_lookbackPeriod); }これらのメソッドは、レジーム検出に使用される主要な統計指標を計算します。
- CalculateReturnsは、価格データからパーセンテージのリターンを算出します。これは生の価格データよりも統計分析に適しています。
- CalculateVolatilityは、ローリングウィンドウ方式を用いて、各時点でのリターンの標準偏差を計算し、市場のボラティリティを測定します。
- CalculateTrendStrengthもローリングウィンドウ方式を用いますが、各ウィンドウに対して一時的なCStatisticsオブジェクトを生成し、そのTrendStrengthメソッドで自己相関に基づくトレンドの強さを求めます。
このようなローリングウィンドウによる計算は、ルックバック期間全体を一括で処理する方法よりも、市場状況の変化に敏感かつ正確に対応できます。
レジームの分類
システムの中核となるのがDetermineRegimeメソッドです。
ENUM_MARKET_REGIME CMarketRegimeDetector::DetermineRegime() { // Get the latest values double latestTrendStrength = m_trendStrength[m_lookbackPeriod - 2]; double latestVolatility = m_volatility[m_lookbackPeriod - 2]; // Get the average volatility for comparison double avgVolatility = 0.0; int count = 0; for(int i = m_lookbackPeriod - 22; i < m_lookbackPeriod - 2; i++) { if(i >= 0) { avgVolatility += m_volatility[i]; count++; } } if(count > 0) avgVolatility /= count; else avgVolatility = latestVolatility; // Determine price direction double priceChange = m_priceData[m_lookbackPeriod - 1] - m_priceData[m_lookbackPeriod - m_smoothingPeriod - 1]; // Classify the regime if(latestVolatility > avgVolatility * m_volatilityThreshold) { // Highly volatile market return REGIME_VOLATILE; } else if(MathAbs(latestTrendStrength) > m_trendThreshold) { // Trending market if(priceChange > 0) return REGIME_TRENDING_UP; else return REGIME_TRENDING_DOWN; } else { // Ranging market return REGIME_RANGING; } }このメソッドでは、階層的な分類手法を用いて市場の状態を判定しています。
- 最初に、直近のボラティリティが過去20バーの平均ボラティリティを上回っているかをチェックします。閾値を超えていれば、市場は高ボラティリティと分類されます。
- 市場が高ボラティリティでない場合は、トレンドの強さを絶対値で評価し、設定された閾値値を超えていればトレンドと見なします。その後、平滑化期間内の価格変化に基づいて上昇トレンドか下降トレンドかを判断します。
- ボラティリティもトレンドも検出されなかった場合、市場はレンジ相場として分類されます。
この階層的な判定手法により、「トレンドよりもボラティリティを優先的に評価」する構造となっています。これは、トレンドフォロー戦略がボラティリティの高い局面に弱いという特性を考慮した設計です。
アクセスメソッド
最後に、現在の市場レジームに関する情報を提供するアクセスメソッドを実装しましょう。
string CMarketRegimeDetector::GetRegimeDescription() const { switch(m_currentRegime) { case REGIME_TRENDING_UP: return "Trending Up"; case REGIME_TRENDING_DOWN: return "Trending Down"; case REGIME_RANGING: return "Ranging"; case REGIME_VOLATILE: return "Volatile"; default: return "Undefined"; } } double CMarketRegimeDetector::GetTrendStrength() const { if(m_lookbackPeriod <= 2) return 0.0; return m_trendStrength[m_lookbackPeriod - 2]; } double CMarketRegimeDetector::GetVolatility() const { if(m_lookbackPeriod <= 2) return 0.0; return m_volatility[m_lookbackPeriod - 2]; } bool CMarketRegimeDetector::GetRegimeBuffer(double &buffer[]) const { if(ArraySize(buffer) < m_lookbackPeriod) ArrayResize(buffer, m_lookbackPeriod); for(int i = 0; i < m_lookbackPeriod; i++) buffer[i] = m_regimeBuffer[i]; return true; } bool CMarketRegimeDetector::GetTrendStrengthBuffer(double &buffer[]) const { int size = m_lookbackPeriod - 1; if(ArraySize(buffer) < size) ArrayResize(buffer, size); for(int i = 0; i < size; i++) buffer[i] = m_trendStrength[i]; return true; } bool CMarketRegimeDetector::GetVolatilityBuffer(double &buffer[]) const { int size = m_lookbackPeriod - 1; if(ArraySize(buffer) < size) ArrayResize(buffer, size); for(int i = 0; i < size; i++) buffer[i] = m_volatility[i]; return true; }これらのメソッドは、現在のレジームとその特徴にアクセスするための機能を提供します。
- GetRegimeDescriptionは、現在の市場レジームを人間が読みやすい説明文として返します。
- GetTrendStrengthとGetVolatilityは、それぞれ直近のトレンド強度とボラティリティの値を返します。
- GetRegimeBuffer、GetTrendStrengthBuffer、GetVolatilityBufferは、内部バッファの内容を外部配列にコピーします。これにより、インジケーターとしてチャートに表示することが可能になります。
これでCMarketRegimeDetectorクラスが完成し、市場レジームを検出するための強力なツールが整いました。次のセクションでは、これを活用してレジームをチャート上に視覚的に表示するカスタムインジケーターを作成します。
レジーム可視化のためのカスタムインジケーターの作成
市場レジーム検出器クラスが完成したので、今度は検出されたレジームをチャート上に直接表示するカスタムインジケーターを作成しましょう。これにより、トレーダーはレジームの変化を直感的に把握し、戦略を柔軟に適応させることができます。
MarketRegimeIndicator(市場レジームインジケーター)
私たちのカスタムインジケーターは、現在の市場レジーム、トレンド強度、およびボラティリティをチャート上に直接表示します。実装は次のとおりです。#property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3 // Include the Market Regime Detector #include "MarketRegimeEnum.mqh" #include "MarketRegimeDetector.mqh" // Indicator input parameters input int LookbackPeriod = 100; // Lookback period for calculations input int SmoothingPeriod = 10; // Smoothing period for regime transitions input double TrendThreshold = 0.2; // Threshold for trend detection (0.1-0.5) input double VolatilityThreshold = 1.5; // Threshold for volatility detection (1.0-3.0) // Indicator buffers double RegimeBuffer[]; // Buffer for regime classification double TrendStrengthBuffer[]; // Buffer for trend strength double VolatilityBuffer[]; // Buffer for volatility // Global variables CMarketRegimeDetector *Detector = NULL;インジケーターは、市場レジームの異なる側面を格納・表示するために3つのバッファを使用します。
- RegimeBuffer:現在のレジームを数値で保持
- TrendStrengthBuffer:トレンド強度の値を保持
- VolatilityBuffer:ボラティリティ値を保持
インジケーターの初期化
OnInit関数は、インジケーターバッファを設定し、市場レジーム検出器を生成します。
int OnInit() { // Set indicator buffers SetIndexBuffer(0, RegimeBuffer, INDICATOR_DATA); SetIndexBuffer(1, TrendStrengthBuffer, INDICATOR_DATA); SetIndexBuffer(2, VolatilityBuffer, INDICATOR_DATA); // Set indicator labels PlotIndexSetString(0, PLOT_LABEL, "Market Regime"); PlotIndexSetString(1, PLOT_LABEL, "Trend Strength"); PlotIndexSetString(2, PLOT_LABEL, "Volatility"); // Set indicator styles PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE); PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE); PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_LINE); // Set line colors PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrBlue); PlotIndexSetInteger(2, PLOT_LINE_COLOR, clrRed); // Set line styles PlotIndexSetInteger(1, PLOT_LINE_STYLE, STYLE_SOLID); PlotIndexSetInteger(2, PLOT_LINE_STYLE, STYLE_SOLID); // Set line widths PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 1); PlotIndexSetInteger(2, PLOT_LINE_WIDTH, 1); // Create and initialize the Market Regime Detector Detector = new CMarketRegimeDetector(LookbackPeriod, SmoothingPeriod); if(Detector == NULL) { Print("Failed to create Market Regime Detector"); return INIT_FAILED; } // Configure the detector Detector.SetTrendThreshold(TrendThreshold); Detector.SetVolatilityThreshold(VolatilityThreshold); Detector.Initialize(); // Set indicator name IndicatorSetString(INDICATOR_SHORTNAME, "Market Regime Detector"); return INIT_SUCCEEDED; }この関数はいくつかの重要なタスクを実行します。
- インジケーターバッファを対応する配列にバインドする
- インジケーターの視覚的なプロパティ(ラベル、スタイル、色)を設定する
- ユーザー指定のパラメータで市場レジーム検出器を生成・設定する
SetIndexBufferや各種のPlotIndexSetXXX関数の使用は、MQL5インジケーター開発における標準的な手法です。これらの関数により、インジケーターがチャート上にどのように表示されるかを細かく設定します。
インジケーターの計算
OnCalculate関数は価格データを処理し、インジケーターバッファを更新します。
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { // Check if there's enough data if(rates_total < LookbackPeriod) return 0; // Process data with the detector if(!Detector.ProcessData(close, rates_total)) { Print("Failed to process data with Market Regime Detector"); return 0; } // Get the regime buffer Detector.GetRegimeBuffer(RegimeBuffer); // Get the trend strength buffer Detector.GetTrendStrengthBuffer(TrendStrengthBuffer); // Get the volatility buffer Detector.GetVolatilityBuffer(VolatilityBuffer); // Display current regime in the chart corner string regimeText = "Current Market Regime: " + Detector.GetRegimeDescription(); string trendText = "Trend Strength: " + DoubleToString(Detector.GetTrendStrength(), 4); string volatilityText = "Volatility: " + DoubleToString(Detector.GetVolatility(), 4); Comment(regimeText + "\n" + trendText + "\n" + volatilityText); // Return the number of calculated bars return rates_total; }この関数は以下の処理をおこないます。
- 計算に十分なデータがあるかを確認する
- 市場レジーム検出器を使って価格データを処理する
- レジーム、トレンド強度、ボラティリティのバッファを取得する
- 現在のレジーム情報をチャートの隅に表示する
- 計算済みのバー数を返す
OnCalculate関数は、新しい価格データが利用可能になった時やチャートがスクロールされた時にプラットフォームから呼び出されます。インジケーターバッファを更新し、その結果がチャート上に表示される役割を担っています。
インジケーターのクリーンアップ
OnDeinit関数は、インジケーターが削除されたときに適切なクリーンアップを保証します。
void OnDeinit(const int reason) { // Clean up if(Detector != NULL) { delete Detector; Detector = NULL; } // Clear the comment Comment(""); }
この関数は、メモリリークを防ぐために市場レジーム検出器オブジェクトを削除し、チャート上のコメントを消去します。不要になったリソースを確実に解放することは、MQL5プログラミングにおいて適切なクリーンアップとして非常に重要です。
インジケーターの解釈
市場レジームインジケーターを使用する場合、トレーダーは次の点に注意する必要があります。- レジームライン:現在の市場レジームを表すラインです。数値はさまざまなレジームに対応します。
- 0:上昇トレンド
- 1:下降トレンド
- 2:レンジ
- 3:高ボラティリティ
- 4:未定義
- トレンド強度ライン:青いラインで、トレンドの強さを示します。正の値が大きいほど強い上昇トレンド、負の値が大きいほど強い下降トレンド、0付近の値はトレンドが弱いか存在しないことを示します。
- ボラティリティライン:赤いラインで、現在のボラティリティ水準を表します。このラインの急上昇はレジーム変化の前兆であり、取引のチャンスやリスクの警告となります。
- チャートコメント:チャートの左上に現在のレジーム、トレンド強度、ボラティリティの数値が表示され、参照しやすくなっています。
これらの要素をモニターすることで、トレーダーは現在の市場レジームを素早く把握し、戦略を適切に調整できます。たとえば、トレンドフォロー戦略はトレンド相場で効果的ですが、レンジ相場では平均回帰戦略の方が適しています。ボラティリティが高い相場では、ポジションサイズを縮小するか、市場から一時的に離れることも検討すべきです。
現在の市場がレンジ相場であることが、チャートを見ても明確に確認できます。
結論
本記事では、アルゴリズム取引における大きな課題の一つである「変化する市場環境への適応」に焦点を当て、その解決に向けたアプローチを段階的に紹介してきました。市場は常に一定の状態を保つわけではなく、異なる「レジーム」が入れ替わるという前提に立ち、そうした変化を検出できる包括的な市場レジーム検出システムの構築を目指しました。次回の記事では、この検出結果に基づき、取引戦略をどのように適応・自動化していくかについて、より実践的な観点から解説していきます。
問題から解決までの道のり
まず、多くの取引システムが抱えている根本的な問題、すなわち「市場の状態を客観的に認識し、それに応じて戦略を変化させる仕組みが存在しない」点を明らかにしました。従来の戦略やインジケーターは、特定の市場環境に最適化されているため、相場の変化によってパフォーマンスが急激に悪化することが少なくありません。ある環境では良好な成績を出す戦略が、別の環境では大きな損失を生む——これはトレーダーが日々直面している現実です。
こうした課題に対して、本記事では一から市場環境を分類・検出する堅牢なシステムを構築するというアプローチを取りました。まずは、自己相関やボラティリティといった統計的な指標を用いて、市場の動向を客観的に把握するための土台を築きました。次に、それらの指標を活用して、トレンド相場・レンジ相場・高ボラティリティ相場を識別する専用の「市場レジーム検出器」クラスを開発しました。
さらに、この仕組みを視覚的にわかりやすくするため、チャート上に市場レジームの変化を表示するカスタムインジケーターも実装しました。これにより、トレーダーは現在の市場環境を一目で判断できるようになります。さらに、検出されたレジームに応じて、取引戦略を自動的に切り替える適応型EAを構築するための基礎についても取り上げました。
次回の記事では、このシステムを実際の取引に導入する際の実践的な考慮点、つまり、パラメータの最適化、レジーム遷移への対処法、既存戦略との統合などに踏み込んでいきます。これらの内容は、構築した市場レジーム検出システムをご自身の取引戦略に効果的かつ自動的に組み込むための指針として役立つはずです。それまでの間、今回ご紹介したインジケーターを手動で活用し、レジーム判定がどのように機能するかを実際にご体験ください。
ファイルの概要
この記事で作成されたすべてのファイルの概要は次のとおりです。ファイル名 | 説明 |
---|---|
MarketRegimeEnum.mqh | システム全体で使用される市場レジームの列挙型の定義 |
CStatistics.mqh | 市場レジーム検出のための統計計算クラス |
MarketRegimeDetector.mqh | 市場レジーム検出器のコア実装 |
MarketRegimeIndicator.mq5 | チャート上でレジームを視覚化するためのカスタムインジケーター |
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/17737
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
あなたのコードはコンパイルされません... IsStrongSignal(value)がありません...
どのファイルを参照していますか?
MarketRegimeIndicator をコンパイルしようとすると、24個のエラーと1個の警告が出ます:
'MarketRegimeIndicator.mq5' 1
file 'C: \Usersrauma\AppData\RoamingMetaQuotesTerminal10CE948A1DFC9A8C27E56E827008EBD4 ❃MQL5Include ❃MarketRegimeEnum.mqh' not found MarketRegimeIndicator.mq5 14 11
file 'C:³³Users³³AppData³³Roaming³³MetaQuotes³³Terminal³³ 10CE948A1DFC9A8C27E56E827008EBD4³³MQL5³³Include³³MarketRegimeDetector.mqh' not found MarketRegimeIndicator.mq5 15 11
'CMarketRegimeDetector' - 予期しないトークン、おそらく型が見つからない? MarketRegimeIndicator.mq5 29 1
'*' - セミコロンが予期されたもの MarketRegimeIndicator.mq5 29 23
'Detector' - 宣言されていない識別子 MarketRegimeIndicator.mq5 64 5
'CMarketRegimeDetector' - 型のない宣言 MarketRegimeIndicator.mq5 64 20
'CMarkRegimeDetector' - 期待されるクラス型 MarketRegimeIndicator.mq5 64 20
関数が定義されていません MarketRegimeIndicator.mq5 64 20
'new' - 'void' 型の式が不正 MarketRegimeIndicator.mq5 64 16
'=' - 不正な操作の使用 MarketRegimeIndicator.mq5 64 14
'Detector' - 宣言されていない識別子 MarketRegimeIndicator.mq5 65 8
'==' - 不正な操作の使用 MarketRegimeIndicator.mq5 65 17
'Detector' - 宣言されていない識別子 MarketRegimeIndicator.mq5 72 5
'Detector' - 宣言されていない識別子 MarketRegimeIndicator.mq5 73 5
'Detector' - 宣言されていない識別子 MarketRegimeIndicator.mq5 74 5
'Detector' - 宣言されていない識別子 MarketRegimeIndicator.mq5 101 9
';' - 予期しないトークン MarketRegimeIndicator.mq5 103 68
'(' - アンバランスな左括弧 MarketRegimeIndicator.mq5 101 7
空の管理対象 文が見つかりました MarketRegimeIndicator.mq5 103 68
'Detector' - 宣言されていない識別子 MarketRegimeIndicator.mq5 133 8
'!=' - 不正な操作の使用 MarketRegimeIndicator.mq5 133 17
'Detector' - 宣言されていない識別子 MarketRegimeIndicator.mq5 135 16
'Detector' - 期待されるオブジェクトポインタ MarketRegimeIndicator.mq5 135 16
'Detector' - 宣言されていない識別子 MarketRegimeIndicator.mq5 136 9
'=' - 不正な操作の使用 MarketRegimeIndicator.mq5 136 18
24 エラー、1 警告 25 2
Market regime indicatorをコンパイルしようとすると、24のエラーと1つの警告が表示される:
'MarketRegimeIndicator.mq5' 1
file 'C: \Usersersrauma\AppData\Roaming\MetaQuotesTerminal\10CE948A1DFC9A8C27E56E827008EBD4 ❃MQL5 ❃Include ❃MarketRegimeEnum.mqh' not found MarketRegimeIndicator.mq5 14 11
file 'C:¥Users¥PetaQuotes¥AppData¥Roaming¥MetaQuotes¥Terminal¥10CE948A1DFC9A8C27E56E827008EBD4¥MQL5Include¥MarketRegimeDetector.mqh' not found MarketRegimeIndicator.mq5 15 11
The indicator searches for these files in the folderC:¥Users¥MetaQuotes¥AppData¥Roaming¥MetaQuotes¥Terminal¥10CE948A1DFC9A8C27E56E827008EBD4¥MQL5¥Include
どのファイルを参照していますか?
の472行目
のことかと思います
'IsStrongSignal' - undeclared identifier MarketRegimeDetector.mqh 472 16