English Deutsch
preview
MQL5標準ライブラリエクスプローラー(第3回):エキスパート標準偏差チャネル

MQL5標準ライブラリエクスプローラー(第3回):エキスパート標準偏差チャネル

MetaTrader 5 |
95 1
Clemence Benjamin
Clemence Benjamin

内容



はじめに

探求者の目的は、物事をより深く理解し、実験を通して新しいアイデアを発見することです。前回は、この学習プロセスを簡略化するために、エキスパートアドバイザー(EA)にクラスを統合するための基礎と手順を示しました。主な目的は、クラスのインターフェースと役割を理解することでした。また、開発者コメントが学習プロセスをどれほど容易にするかも確認しました。

最後に、標準ライブラリのクラスを扱うための基本的なステップをまとめました。ここで際立った大きな利点の一つは、MQL5の充実したドキュメントが利用できることです。他のプログラミング言語と同様に、MQL5のドキュメントは包括的な知識ベースを形成しています。MQL5のウェブサイト全体(記事、フォーラム、本、コードベースを含む)が、アルゴリズム取引における実践的な課題解決の豊富な参照資料となります。各セクションは、開発者やトレーダーが実務上の課題を解決できるよう設計されています。

本記事の課題は、ボラティリティに基づくチャネルを用いた取引をおこなうEAを作成することです。その過程で、CChartObjectStdDevChannelクラスを学ぶことで、単に機能するEAを作成するだけでなく、標準ライブラリへの理解も深めることができます。既存コードからも常に学べることがあり、すべての実験が新しいアイデアの扉を開きます。

今日の課題をパズルだと考えてみてください。最終的な完成図がEAであり、私たちの役割は、前回の議論で得た概念やメモを活用して、ピースを組み合わせることです。

実装に入る前に、まずMQL5のドキュメントを使ってライブラリファイルを理解する方法を簡単にお見せします。これは実装を確認する前の重要な手順です。新しいモジュールを作成する開発者は、プログラマコメントに注目し、他者が開発を容易に理解できるよう明確なドキュメントを提供することが重要です。以下のコードスニペットでは、ハイライトされたコメントに注目してください。

//+------------------------------------------------------------------+
//|                                         ChartObjectsChannels.mqh |
//|                             Copyright 2000-2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
//| All channels.                                                    |
//+------------------------------------------------------------------+
#include "ChartObjectsLines.mqh"
//+------------------------------------------------------------------+
//| Class CChartObjectChannel.                                       |
//| Purpose: Class of the "Equidistant channel" object of chart.     |
//|          Derives from class CChartObjectTrend.                   |
//+------------------------------------------------------------------+
class CChartObjectChannel : public CChartObjectTrend
  {
public:
                     CChartObjectChannel(void);
                    ~CChartObjectChannel(void);
   //--- method of creating the object
   bool              Create(long chart_id,const string name,const int window,
                            const datetime time1,const double price1,
                            const datetime time2,const double price2,
                            const datetime time3,const double price3);
   //--- method of identifying the object
   virtual int       Type(void) const { return(OBJ_CHANNEL); }
  };

この記事を通して、読者は以下のことを習得できるようになります。

  1. CTradeクラスを用いた注文およびポジション管理の中核エンジンの適用方法
  2. ChartObjectsChannelsクラスライブラリを戦略ロジックおよびシグナル生成の基盤として使用する方法
  3. これらのクラスを実務的に統合して完全に機能するEAを作成する方法
  4. 取引戦略の開発および構造化の手法
  5. 効果的なリスク管理技術の実装
  6. ストラテジーテスターを用いたEAのパフォーマンス評価と最適化

ドキュメントやMQL5リファレンスを通じて、クラスの目的を理解する方法

第2回では、EAにクラスを統合するルーチンを紹介しました。しかし、ここでは異なるアプローチを取ります。MQL5のドキュメントを活用してライブラリクラスを迅速に理解する方法を示します。

強力な組み込みツールとして、MetaEditorから直接アクセス可能なMQL5リファレンスがあります。今回使用するChartObjectsChannelsライブラリに関しても、効率的にアクセスして探索する方法を示しました。

ライブラリのソースコード内で調べたいクラス(ここではCChartObjectChannel)を探します。クラス名を右クリックして[ヘルプにアクセス]を選択すると、MQL5リファレンスのウィンドウが開き、クラスに関する詳細情報を確認できます。

前述の通り、ドキュメントには簡潔な概要が提供されており、多くの場合これだけでクラスの目的を理解できます。しかし、クラスの真価を理解するためには、実際に手を動かして試すことが最も効果的です。

図1はMetaEditorでMQL5リファレンスを使用する方法を示し、図2は同じ目的でオンラインのMQL5ドキュメントを活用する代替アプローチを示しています。

図1:MQL5リファレンスの利用方法

図2:MQL5ドキュメントの利用方法



戦略の概要

アルゴリズムを開発する前に、多くのトレーダーはまずチャートオブジェクトを使って市場価格を視覚的に分析します。このアプローチは直感的に感じられます。なぜなら、サポートやレジスタンスを示す水平線、トレンドライン、チャネルなどのツールは、チャート上に直接簡単に配置できるからです。しかし、本当の課題は、これらの視覚的分析の概念をコードに変換する段階で生じます。特に、MQL5の開発環境にまだ慣れていない人にとってはハードルが高い部分です。

この議論では、そのギャップを埋めることを目指します。つまり、手動でおこなう分析のツールをMQL5標準ライブラリを用いたカスタムアルゴリズムに変換する方法を探ります。MQL5のフレームワークの強みは、オブジェクト指向構造と、標準クラスに付随する包括的なドキュメントにあります。これにより、開発者は、ゼロから作り直すことなく、既存のチャートベースのツールを容易に理解および拡張できます。

前のセクションで示した通り、ライブラリクラスの機能を理解する方法はいくつかあります。その中でも特に効率的なのが、MetaEditorの組み込みリファレンスを使用する方法です。各クラスのメンバーやメソッドに関する詳細なドキュメントが提供されており、特にChartObjectsChannels.mqhファイルのような複雑なコンポーネントを学ぶ際に有用です。

このファイルを確認すると、チャネル関連の複数のクラスが定義されていることが分かります。

  • CChartObjectChannel
  • CChartObjectRegression
  • CChartObjectStdDevChannel
  • CChartObjectPitchfork

今回の開発では、CChartObjectStdDevChannelクラスに注目します。このクラスは、価格が線形トレンドからどれだけ乖離しているかを測定する一般的に使用される分析ツールである標準偏差チャネルを表します。手動でチャート分析をおこなうための可視的な構造を提供するだけでなく、アルゴリズム戦略に組み込むためのプログラムインターフェースも備えています。

本記事での目的は、このチャネルオブジェクトをEAに統合し、価格の動きがチャネルの境界に対してどのように推移するかに基づいて、エントリーとエグジットのシグナルを生成することです。このプロセスを通じて、機能的なEAを構築するとともに、MQL5標準ライブラリが実務向け取引ツールの開発をどのように簡素化しているかをより深く理解できます。

要するに、標準偏差チャネルは手動でチャートに追加することも可能ですが、今回の目標はその作成、更新、解釈を自動化することです。つまり、慣れ親しんだ手動分析ツールを、体系的な取引ロジックの強力なコンポーネントに変換することを目指します。

標準偏差チャネル

図3:チャートへのStdDevチャネルの追加


標準偏差とその取引への応用を理解する

取引や定量分析において、標準偏差は、市場のボラティリティ、すなわち価格が平均値からどれだけ乖離する傾向があるかを測るための重要な統計概念です。この概念を理解することで、トレーダーは市場が落ち着いているのか活発なのかを判断でき、これが、リスク管理、ポジションサイズの決定、シグナルの有効性の確認に役立ちます。

1. 標準偏差の概念

標準偏差(σ)は、データが平均値の周りでどの程度散らばっているかを測定する指標です。価格分析においては、ある期間内の個々の価格が平均価格からどれだけ離れているかを定量化します。

数学的には次のように定義されます。

ここで

  • N:価格データ点の数(例:ローソク足やバーの数)
  • xᵢ:各価格データ(通常は終値)
  • x̄:同期間の平均価格
  • σが大きいほどボラティリティは高く、価格は平均値から大きく離れていることを意味します。
  • σが小さいほど安定しており、価格は平均値付近に集中していることを示します。

2. 取引への応用

実務では、トレーダーは標準偏差を用いてボラティリティを可視化し、取引機会を特定します。最も一般的な応用の一つが、ボラティリティチャネルです。その代表例が標準偏差チャネル(StdDev Channel)です。

(a) 標準偏差チャネル

このチャネルは、中央の基準線(通常はトレンドラインや線形回帰)と、基準線の上下に標準偏差の倍数分だけ離れた2本の外側線で構成されます。

計算式は次の通りです。

  • 上チャネル = 基準線+k×σ
  • 下チャネル = 基準線−k×σ

ここで 

kは偏差の倍率(一般的に1, 2, 3など)です。

例: 

k = 2とすると、正規分布を仮定した場合、価格変動の約95%を包含します。これらのチャネル境界は、トレーダーがボラティリティの範囲(エンベロープ)を視覚的に把握するために使われ、ブレイクアウト、プルバック、または価格の行き過ぎゾーンを検出するのに役立ちます。



実装

ChartObjectsChannels.mqhライブラリには、CChartObjectTrendを基底クラスとして派生した複数のチャネル系クラスが定義されており、標準偏差チャネル(StdDev Channel)、回帰チャネル(Regression Channel)、等間隔チャネル(Equidistant Channel)などがあります。これらのクラスはいずれも、チャート上で価格チャネルを表現および操作するための機能を提供します。ここでは、回帰線の概念と標準偏差による統計的な価格分布の評価を組み合わせたCChartObjectStdDevChannelに焦点を当てます。

CChartObjectStdDevChannelをEAに効果的に統合するには、プログラミングと分析の理解を組み合わせた実践的な開発手順に従うことが重要です。

目的
  • 標準ライブラリのCChartObjectStdDevChannelクラスを用いて、標準偏差チャネルをプログラムで作成および制御する。
  • 価格がチャネルに接触したり、ブレイクアウトした際にシグナルを自動生成する。
  • CTradeクラスを用いて注文管理ロジックを統合する。
  • チャート上の可視化と取引ロジックを組み合わせ、完全なEAを構築する。

概念的基礎

MQL5のすべてのチャネルオブジェクトは、CChartObjectTrendを継承しており、アンカーの設定、色指定、表示と非表示の切り替えなど、基本的なライン操作機能を備えています。さらに、CChartObjectChannelクラスは、この基盤に平行線構造を追加し、等間隔の価格チャネルを作成できるように拡張されています。

そして、CChartObjectStdDevChannelは、線形回帰線の周囲に統計的偏差帯(標準偏差バンド)を導入します。この上限線と下限線により、価格の行き過ぎや平均回帰が生じやすいゾーンを特定できます。この考え方は、ボラティリティを基にした取引戦略で一般的に使用されます。

まずコードを確認し、EAへの統合に必要なメソッドを特定します。次に、CChartObjectStdDevChannelをEAに組み込むための明確な手順を整理します。以下のコードスニペットの後に、統合手順の概要を表形式でまとめ、実装手順を理解しやすくしています。

なお、ここに示すのはヘッダファイルの抜粋の一部であり、CChartObjectStdDevChannelクラスに特化して解説するために限定しています。

//+------------------------------------------------------------------+
//| Class CChartObjectStdDevChannel.                                 |
//| Purpose: Class of the "Standrad deviation channel"               |
//|          object of chart.                                        |
//|          Derives from class CChartObjectTrend.                   |
//+------------------------------------------------------------------+
class CChartObjectStdDevChannel : public CChartObjectTrend
  {
public:
                     CChartObjectStdDevChannel(void);
                    ~CChartObjectStdDevChannel(void);
   //--- methods of access to properties of the object
   double            Deviations(void) const;
   bool              Deviations(const double deviation) const;
   //--- method of creating the object
   bool              Create(long chart_id,const string name,const int window,
                            const datetime time1,const datetime time2,const double deviation);
   //--- method of identifying the object
   virtual int       Type(void) const { return(OBJ_STDDEVCHANNEL); }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CChartObjectStdDevChannel::CChartObjectStdDevChannel(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CChartObjectStdDevChannel::~CChartObjectStdDevChannel(void)
  {
  }
//+------------------------------------------------------------------+
//| Create object "Standard deviation channel"                       |
//+------------------------------------------------------------------+
bool CChartObjectStdDevChannel::Create(long chart_id,const string name,const int window,
                                       const datetime time1,const datetime time2,const double deviation)
  {
   if(!ObjectCreate(chart_id,name,OBJ_STDDEVCHANNEL,window,time1,0.0,time2,0.0))
      return(false);
   if(!Attach(chart_id,name,window,2))
      return(false);
   if(!Deviations(deviation))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Get value of the "Deviations" property                           |
//+------------------------------------------------------------------+
double CChartObjectStdDevChannel::Deviations(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
//--- result
   return(ObjectGetDouble(m_chart_id,m_name,OBJPROP_DEVIATION));
  }
//+------------------------------------------------------------------+
//| Set value for the "Deviations" property                          |
//+------------------------------------------------------------------+
bool CChartObjectStdDevChannel::Deviations(const double deviation) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetDouble(m_chart_id,m_name,OBJPROP_DEVIATION,deviation));
  }
//+------------------------------------------------------------------+
//| Writing parameters of object to file                             |
//+------------------------------------------------------------------+
bool CChartObjectStdDevChannel::Save(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- write
   if(!CChartObjectTrend::Save(file_handle))
      return(false);
//--- write value of the "Deviations" property
   if(FileWriteDouble(file_handle,ObjectGetDouble(m_chart_id,m_name,OBJPROP_DEVIATION))!=sizeof(double))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Reading parameters of object from file                           |
//+------------------------------------------------------------------+
bool CChartObjectStdDevChannel::Load(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- read
   if(!CChartObjectTrend::Load(file_handle))
      return(false);
//--- read value of the "Deviations" property
   if(!ObjectSetDouble(m_chart_id,m_name,OBJPROP_DEVIATION,FileReadDouble(file_handle)))
      return(false);
//--- successful
   return(true);
  }

統合手順

ステップ 操作目的
1ChartObjectsChannels.mqhをインクルードするCChartObjectStdDevChannelを含む全ての標準チャネルクラスにアクセスする
2CChartObjectStdDevChannelを宣言する標準偏差チャネルオブジェクトの作成と操作の準備をおこなう
3Create()を呼び出す選択した価格と時間座標を使ってチャート上にチャネルを描画する
4Deviations()プロパティを設定するチャネル幅をスケーリングする標準偏差の数を指定する
5表示をカスタマイズする視覚的な明瞭さと取引判断のため、色、スタイル、ラベルを調整する
6シグナルロジックを追加する上下のバンドとの価格の相互作用を利用して、自動取引のシグナルを生成する
7定期的に更新する新しいバーごとにチャネルを再計算および更新して精度を維持する

ExpertStdDevChannel

1. ファイルヘッダとコンパイル時プロパティ

このセクションでは、EAの識別情報とコンパイル時制約を宣言します。#property行はファイルのメタデータ(著作権、リンク、バージョン)を提供し、「#property strict」はコンパイラに厳格な型チェックと意味チェックを適用するよう指示します。これにより、型の不一致やその他の問題をビルド時に検出し、開発およびテスト中の挙動をより予測可能にします。

//ExpertStdDevChannel.mq5
#property copyright "Copyright 2025, Clemence Benjamin"
#property link      "https://www.mql5.com/go?link=https://www.mql5.com/ja/users/billionaire2024/seller"
#property version   "1.00"
#property strict

2. ライブラリのインポートと目的

EAが依存する標準ライブラリモジュールをインポートします。ChartObjectsChannels.mqhはチャネルクラス(CChartObjectStdDevChannelを含む)を提供し、プラットフォームの描画およびオブジェクト操作を再利用できます。Trade.mqhはCTradeラッパーを提供し、注文実行や結果処理を簡素化します。これらのモジュールをインポートすることで低レベル機能を再実装せずに済み、コードの明瞭性と保守性が向上します。

#include <ChartObjects\ChartObjectsChannels.mqh>
#include <Trade\Trade.mqh>  // For CTrade

3. ユーザー向け入力パラメータ(設定)

このブロックは、EAのパラメータをユーザーやストラテジーテスターに公開します。各入力パラメータは調整可能な設定を提供し、チャネル計算期間、偏差倍率、ロットサイズ、マジックナンバー、SLバッファ、取引方向、可視化オプション、および取引モードを制御します。これらの値はEAの動作を定義するものであり、データ要件(たとえば、PeriodBarsは回帰計算に十分なバー数である必要がある)やブローカーの制約(たとえばSLBuffer)を考慮した上で設定する必要があります。

//--- Inputs
input double   Deviation    = 2.0;        // StdDev multiplier
input int      PeriodBars   = 50;         // Bars for channel calculation
input double   LotSize      = 0.1;        // Trade size
input int      Magic        = 12345;      // EA identifier
input double   SLBuffer     = 20.0;       // Points buffer for SL (increased for min distance)
input bool     EnableSells  = true;       // Enable sell trades (set to false for buys only)
input bool     EnableBuys   = true;       // Enable buy trades
input bool     DrawGraphical= true;       // Draw channel object for visualization (disable for faster backtest)
input bool     UseMeanReversion = true;   // True: Mean reversion (bounce buys/sells); False: Breakout (break buys/sells)

4. グローバルオブジェクトと内部状態

ここでは、EA全体で使用されるランタイム状態やオブジェクトのライフサイクルを宣言します。「CTrade trade」は注文実行用の取引エンジン、「CChartObjectStdDevChannel *channel」はチャート可視化用(割り当て/解放が必要)です。g_upper、g_lower、g_medianは手動計算によるチャネルレベル、g_levels_validは計算結果の有効性を示します。数値モデルと視覚オブジェクトを分離することで、バックテスト時に再現可能なロジックが保証されます。

//--- Global objects
CTrade         trade;
CChartObjectStdDevChannel *channel;       // Optional for drawing
double         g_upper, g_lower, g_median; // Manually computed levels
bool           g_levels_valid = false;    // Flag for valid computation

5. 初期化:リソース設定と初回計算(OnInit)

OnInit()はリソース割り当てと初期検証をおこないます。この処理では、まずCTradeにEAのマジックナンバーを設定し、必要に応じてグラフィカルなStdDevチャネルをnewで割り当てて作成を試みます。可視化の作成に失敗した場合は、ポインタを削除して処理を続行することで安全にフォールバックします。最後にUpdateChannel()を呼び出して初期の数値レベルを計算します。データが不足している場合はEAがその状況をログに記録し、有効なレベルが得られるまで取引をおこないません。これにより、EAがランタイムループに入る前に、視覚的にもプログラム的にも準備が整った状態を確立できます。

int OnInit()
{
   trade.SetExpertMagicNumber(Magic);
   
   if (DrawGraphical)
   {
      channel = new CChartObjectStdDevChannel();
      if (!channel.Create(0, "StdDevChannel", 0,
                          iTime(_Symbol, PERIOD_CURRENT, PeriodBars),
                          iTime(_Symbol, PERIOD_CURRENT, 0),
                          Deviation))
      {
         Print("Failed to create graphical StdDev channel (continuing without viz)");
         delete channel;
         channel = NULL;
      }
      else
      {
         channel.Deviations(Deviation);
         Print("Graphical channel created for visualization");
      }
   }
   
   // Initial calculation
   if (UpdateChannel())
      g_levels_valid = true;
   else
      Print("Initial channel calculation failed - need more bars");
   
   Print("Channel EA initialized successfully. Mode: ", (UseMeanReversion ? "Mean Reversion" : "Breakout"));
   return INIT_SUCCEEDED;
}

6. ランタイムループと制御(OnTick):概要

OnTick()はEAの心臓部であり、状態の更新、検証、取引判断がおこなわれる箇所です。このルーチンではまず、新しいバーが確定した際に決定論的な再計算をトリガーします。必要に応じてのみ、ティック単位での再計算にフォールバックします。その後、リアルタイムの価格情報やブローカーのパラメータを読み取り、数値の有効性を確認します。すべての条件が満たされた場合にのみ、シグナル検出と取引執行がおこなわれます。この構造により、確定バー単位での計算を優先して決定論的な結果を確保し、ティック単位の再計算はあくまで補助的手段としてのみ利用されます。

void OnTick()
{
   static int skip_count = 0;
   
   // Update on new bar
   if (IsNewBar())
   {
      if (!UpdateChannel())
      {
         Print("Channel update failed - skipping until valid");
         g_levels_valid = false;
         return;
      }
      g_levels_valid = true;
   }
   
   if (!g_levels_valid)
   {
      // Fallback: Recalc on tick if needed (rare)
      CalculateStdDevChannel();
      if (g_upper == 0.0 || g_lower == 0.0 || g_upper <= g_lower)
      {
         if (++skip_count % 100 == 0)  // Log every 100 ticks to avoid spam
            Print("Invalid manual channel values (", skip_count, " ticks skipped)");
         return;
      }
      // Reset counter on successful recalc
      skip_count = 0;
      g_levels_valid = true;
   }
   ...
}

7. 市場データの取得、検証、およびブローカー制約

EAは各ティックで、現在の市場データ(Bid、Ask)や銘柄のメタ情報(ポイント値、桁数)を取得します。そして、計算されたレベルが適切なチャネルを形成しているか(g_upper > g_lower)を検証します。さらに、ブローカーのSYMBOL_TRADE_STOPS_LEVELを取得し、SL/TPの設定がブローカー規則を満たすように価格距離minDistに変換します。これらのチェックにより、注文拒否や論理的に無効な注文を防止できます。

   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   
   // Validate levels
   if (g_upper <= g_lower)
   {
      Print("Invalid channel levels: Upper=", g_upper, " Lower=", g_lower, " - skipping tick");
      return;
   }
   
   // Get minimum stop distance
   long minStopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
   double minDist = minStopLevel * point;

8. エントリー制御と単一ポジション方針

新規注文をおこなう前に、EAは指定されたマジックナンバーでその銘柄のポジションを保有していないことを確認します。この「銘柄ごと、EAごとに単一ポジション」方針は、状態管理とリスク管理を簡素化します。保有ポジションがゼロであれば、EAはEnableBuys、EnableSellsの設定および選択された取引モード(平均回帰 vs ブレイクアウト)に基づいて、買いと売りロジックを評価します。この制御により、ポジションの重複や過剰なレバレッジの発生を防ぎます。

   // No open positions (check for this symbol and magic)
   if (PositionsTotalByMagic() == 0)
   {
      // buy and sell logic follows...
   }

9. 買いシグナルの生成と注文パラメータの構築

買いロジックはUseMeanReversionの設定によって分岐します。平均回帰モードでは、EAは下部バンドからの反発を確認します(直前の安値 ≤ 下部バンドかつ現在のBid > 下部バンド)。ブレイクアウトモードでは、Askが上部バンドを上回ることを条件とします。買いシグナルが発生した場合、EAはエントリー価格、ストップロス(SL)、テイクプロフィット(TP)を計算し、桁数に合わせて正規化します。必要に応じてSLを調整して最小距離(minDist)を確保し、TPが十分に離れていることを確認した後、trade.Buy(...)を呼び出します。注文の成功および失敗はResultRetcode()と説明メッセージでログに記録され、トラブルシューティングに役立ちます。この設計により、注文は正確さとブローカーの制約を満たす形で実行されます。

      if (EnableBuys)
      {
         bool buySignal = false;
         if (UseMeanReversion)
         {
            // Mean reversion buy: Bounce off lower (current > lower, prior low <= lower)
            double prevLow = iLow(_Symbol, PERIOD_CURRENT, 1);
            buySignal = (bid > g_lower && prevLow <= g_lower);
         }
         else
         {
            // Breakout buy: Break above upper
            buySignal = (ask > g_upper);
         }
         
         if (buySignal)
         {
            double entry = ask;
            double sl = NormalizeDouble(g_lower - (SLBuffer * point), digits);
            double tp = NormalizeDouble(g_upper, digits);
            
            // Adjust SL to meet min distance
            if (entry - sl < minDist)
               sl = NormalizeDouble(entry - minDist, digits);
            
            // Check TP min distance
            if (tp - entry < minDist)
            {
               Print("TP too close for buy (dist=", (tp - entry)/point, " < ", minDist/point, ") - skipping");
            }
            else if (trade.Buy(LotSize, _Symbol, entry, sl, tp, "Channel Buy"))
            {
               Print("Buy order placed: Entry=", entry, " SL=", sl, " TP=", tp, " (TP > SL)");
            }
            else
            {
               Print("Buy order failed: ", trade.ResultRetcode(), " - ", trade.ResultRetcodeDescription());
            }
         }
      }

10. 売りシグナルの生成と逆条件による保護

売りロジックは買いロジックを逆条件で反映します。平均回帰モードでは、直前の高値 ≥ 上部バンドかつ現在のAsk < 上部バンドを確認し、反発を検知します。ブレイクアウトモードでは、Bidが下部バンドを下回ることを条件とします。売りエントリーはBid価格でおこない、SLは上部バンドの上に、TPは下部バンドに設定します。EAはTP < SLを検証し、SLとTPに最小距離(minDist)を適用し、値を正規化した後、trade.Sell(...)を呼び出します。注文の結果やエラーは詳細にログに記録され、即時のデバッグや状況確認に役立ちます。

      if (EnableSells)
      {
         bool sellSignal = false;
         if (UseMeanReversion)
         {
            // Mean reversion sell: Bounce off upper (current < upper, prior high >= upper)
            double prevHigh = iHigh(_Symbol, PERIOD_CURRENT, 1);
            sellSignal = (ask < g_upper && prevHigh >= g_upper);
         }
         else
         {
            // Breakout sell: Break below lower
            sellSignal = (bid < g_lower);
         }
         
         if (sellSignal)
         {
            double entry = bid;
            double sl = NormalizeDouble(g_upper + (SLBuffer * point), digits);
            double tp = NormalizeDouble(g_lower, digits);
            
            // For sells: Ensure TP < SL (lower < upper + buffer, always true)
            if (tp >= sl)
            {
               Print("Invalid TP/SL for sell (TP=", tp, " >= SL=", sl, ") - logic error");
               return;
            }
            
            // Adjust SL to meet min distance
            if (sl - entry < minDist)
               sl = NormalizeDouble(entry + minDist, digits);
            
            // Check TP min distance (entry - tp >= minDist)
            double profitDist = entry - tp;
            if (profitDist < minDist)
            {
               Print("TP too close for sell (profit dist=", profitDist/point, " < ", minDist/point, ") - skipping");
            }
            else if (trade.Sell(LotSize, _Symbol, entry, sl, tp, "Channel Sell"))
            {
               Print("Sell order placed: Entry=", entry, " SL=", sl, " TP=", tp, " (TP < SL)");
            }
            else
            {
               Print("Sell order failed: ", trade.ResultRetcode(), " - ", trade.ResultRetcodeDescription());
               Print("Sell details: Entry=", entry, " SL=", sl, " TP=", tp, " MinDist=", minDist/point);
            }
         }
      }

11. 数値計算の核心:線形回帰、残差、および標準偏差(CalculateStdDevChannel)

この関数は、EAにおける数値モデルの中核となる部分です。関数は、確定バーをコピーし、インデックスを一貫してマッピングします。ここで、i=1…nは確定バーを表し、xは最も古いバー(0)から最新のバー(n−1)までをカバーします。その後、回帰に必要な総和を計算し、最小二乗法により傾きaと切片bを求めます。次に、最新の確定バーで回帰線を評価して中央値(中心線)を算出し、二乗残差を計算します。残差の標準偏差σは、回帰に適したn−2を分母として導出されます。最終的に、オフセットを「Deviation × σ」として、上限g_upperと下限g_lowerを「中央値 ± オフセット」で決定します。また、データ不足や分母がゼロになる場合などの異常に対しても、頑健なガード処理を備えています。

void CalculateStdDevChannel()
{
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   int copied = CopyRates(_Symbol, PERIOD_CURRENT, 0, PeriodBars + 1, rates);  // +1 for safety
   if (copied < PeriodBars)
   {
      Print("Insufficient bars for calculation: ", copied, " < ", PeriodBars);
      g_upper = g_lower = g_median = 0.0;
      return;
   }
   
   // Use completed bars: shift 1 (last complete) to PeriodBars (oldest)
   int n = PeriodBars;
   double sum_x = 0.0, sum_y = 0.0, sum_xy = 0.0, sum_x2 = 0.0;
   
   for (int i = 1; i <= n; i++)  // i=1: last complete bar (x=n-1), i=n: oldest (x=0)
   {
      double x = n - i;  // x=0 (oldest) to x=n-1 (newest completed)
      double y = rates[i].close;
      sum_x += x;
      sum_y += y;
      sum_xy += x * y;
      sum_x2 += x * x;
   }
   
   // Linear regression slope (a) and intercept (b)
   double denom = (n * sum_x2 - sum_x * sum_x);
   if (denom == 0.0)
   {
      Print("Division by zero in regression - flat data?");
      g_upper = g_lower = g_median = 0.0;
      return;
   }
   double a = (n * sum_xy - sum_x * sum_y) / denom;
   double b = (sum_y - a * sum_x) / n;
   
   // Median (regression) at newest completed bar (x = n-1)
   g_median = a * (n - 1) + b;
   
   // Residuals sum of squares
   double sum_e2 = 0.0;
   for (int i = 1; i <= n; i++)
   {
      double x = n - i;
      double y_reg = a * x + b;
      double e = rates[i].close - y_reg;
      sum_e2 += e * e;
   }
   
   // StdDev of residuals (population, df = n-2 for regression fit)
   double stddev = (n > 2) ? MathSqrt(sum_e2 / (n - 2)) : 0.0;
   double offset = Deviation * stddev;
   
   g_upper = g_median + offset;
   g_lower = g_median - offset;
   
   Print("Manual Channel Calc: Median=", g_median, " Upper=", g_upper, " Lower=", g_lower, " StdDev=", stddev, " Offset=", offset);
}

12. チャートオブジェクトとの同期(UpdateChannel)

UpdateChannel()は、EAの数値モデルを唯一の参照として強制するため、まず手動計算を常に呼び出して結果を検証します。グラフィカルモードが有効でチャネルポインタが存在する場合、既存のチャートオブジェクトを削除し、新しいアンカーとDeviationパラメータで再作成して、プラットフォーム上の描画とEAの計算レベルを整合させます。オブジェクトを再作成する方法は実務的で、状態管理の複雑さを軽減しますが、その代わりに視覚的なチラつきが生じることがあります。

bool UpdateChannel()
{
   CalculateStdDevChannel();  // Always manual
   
   if (g_upper == 0.0 || g_lower == 0.0 || g_upper <= g_lower)
      return false;
   
   // Optional graphical update
   if (DrawGraphical && channel != NULL)
   {
      datetime time1 = iTime(_Symbol, PERIOD_CURRENT, PeriodBars);
      datetime time2 = iTime(_Symbol, PERIOD_CURRENT, 0);
      ObjectDelete(0, "StdDevChannel");
      if (!channel.Create(0, "StdDevChannel", 0, time1, time2, Deviation))
      {
         Print("Failed to update graphical channel");
      }
      else
      {
         channel.Deviations(Deviation);
         Print("Graphical channel updated");
      }
   }
   
   return true;
}

13. ユーティリティルーチンと決定論的トリガー

ヘルパー関数PositionsTotalByMagic()は、現在開かれているポジションを反復処理し、このEA(銘柄とマジックナンバー)の所有下にあるものをカウントします。これにより、重複したエントリーを防ぐ標準的な所有権チェックが構成されます。IsNewBar()は直前のバーの時間を追跡し、新しい確定バーが出現した場合にのみtrueを返します。この決定論的トリガーにより、再計算が確定済みの市場情報に沿っておこなわれ、形成中のバーの一時的な値に基づいて動作することを避けられます。これらのユーティリティはコンパクトですが、EAの予測可能な挙動を確保するために不可欠です。

int PositionsTotalByMagic()
{
   int count = 0;
   for (int i = 0; i < PositionsTotal(); i++)
   {
      if (PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == Magic)
         count++;
   }
   return count;
}

bool IsNewBar()
{
   static datetime lastBarTime = 0;
   datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
   if (currentBarTime != lastBarTime)
   {
      lastBarTime = currentBarTime;
      return true;
   }
   return false;
}

14. クリーンアップと安全な終了(OnDeinit)

EAの初期化解除処理では、作成済みのグラフィカルオブジェクトがあれば削除し、チャネルポインタを削除してヒープメモリを解放します。これによりチャートが整理され、EAの再度のアタッチ/デタッチによるメモリリークを防ぐことができます。初期化解除イベントをログに残すことで、ライブ運用やテスト中に正しく終了されたことを確認できます。また、削除後にchannelをNULLに設定しておくことは、誤って参照されるのを防ぐ上で良い習慣です。

void OnDeinit(const int reason)
{
   if (DrawGraphical && channel != NULL)
   {
      ObjectDelete(0, "StdDevChannel");
      delete channel;
   }
   Print("Channel EA deinitialized");
}

これらすべての要素を統合することで、ExpertStdDevChannelが完成します。完全なソースコードは記事の下部で参照できます。次のステップはテストです。実際の資金を使う前に、ストラテジーテスターでの検証や制御されたライブ/デモ環境での確認をおこなってEAを検証することが推奨されます。


テスト

テストは、私たちのアイデアが正しく実装されていることを検証するために不可欠です。今回のテストでは、MetaTrader 5のナビゲーターでコンパイル済みEAを確認し、ストラテジーテスターをビジュアルモードで起動しました。EAは正常に動作し、注文は期待どおりに実行され、標準偏差チャネルもチャート上に正しく描画されました。

ExpertStdDevChannelのテスト

図4:ストラテジーテスターのビジュアルモードでExpertStdDevChannelをテストする


結論

MQL5標準ライブラリの各要素を組み合わせ、動作するEAであるExpertStdDevChannelを構築することに成功しました。この演習を通じて、標準偏差の概念とその取引への応用に関する実践的理解が深まり、ライブラリコンポーネントを組み合わせてボラティリティに基づく実用的な取引モジュールを作成できることを示しました。標準ライブラリの広範な機能は、追加のビルディングブロックを組み合わせることでさらに拡張および改良する多くの可能性を提供します。

この例は教育目的のプロトタイプであり、利益を保証するものではありません。主な目的は、ライブラリの要素を一貫したEAに接続する方法を示すことであり、実運用向けのシステムに発展させる余地は十分にあります。添付されたソースを使ってパラメータを調整したり、実験をおこなったりして、コメントで結果を共有することを推奨します。

次のステップとしては、エントリー/エグジットルールの見直し、堅牢なシグナルフィルタ(多時間軸確認、出来高やボラティリティフィルタ)の追加、ポジションサイズとリスク管理の改善、ストラテジーテスターによる体系的な最適化などが考えられます。これらの改善により、この基盤をより効率的で耐久性のあるトレーディングソリューションへと発展させることができます。次回の記事をご期待ください。

添付ファイル

ファイル名バージョン 説明
ExpertStdDevChannel.mq5
1.00線形回帰の中心線と残差の標準偏差σを用いて標準偏差チャネルを計算するEA。

主な機能:平均回帰モードとブレイクアウトモード、中央値、上限、下限バンドの手動計算(シグナルの権威となる)、CChartObjectStdDevChannelによる任意のグラフィカル描画、CTradeを用いた注文実行(SL/TP設定、SLBufferとブローカー最小距離チェック)、マジックナンバーによる単一ポジション管理。

入力パラメータ:DeviationPeriodBarsLotSizeMagicSLBufferEnableBuys/EnableSellsDrawGraphicalUseMeanReversion。再現可能なバックテスト向けに設計(描画モードをオフにして高速化可能)で、データ十分性、正規化、注文検証のための防御的ガードを含む。

目次に戻る

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

添付されたファイル |
最後のコメント | ディスカッションに移動 (1)
Fernando Cavalcanti Martini Teixeira Dos Santos
Fernando Cavalcanti Martini Teixeira Dos Santos | 9 11月 2025 において 04:17

あなたのコードでは、UseMeanReversion = falseの場合、tp値はg_upperより高くなるはずですよね?


      if (EnableBuys)
      {
         bool buySignal = false;
         if (UseMeanReversion)
         {
            // 平均回帰買い:下値に跳ね返される(現在>下値、事前安値≦下値)
            double prevLow = iLow(_Symbol, PERIOD_CURRENT, 1);
            buySignal = (bid > g_lower && prevLow <= g_lower);
         }
         else
         {
            // ブレイクアウト買い:上限を上回る
            buySignal = (ask > g_upper);
         }
         
         if (buySignal)
         {
            double entry = ask;
            double sl = NormalizeDouble(g_lower - (SLBuffer * point), digits);
            double tp = NormalizeDouble(g_upper, digits);
            
            // 最小距離を満たすようにSLを調整する
            if (entry - sl < minDist)
               sl = NormalizeDouble(entry - minDist, digits);
            
            // TPの最小距離をチェックする
            if (tp - entry < minDist)
            {
               Print("TP too close for buy (dist=", (tp - entry)/point, " < ", minDist/point, ") - skipping");
            }
            else if (trade.Buy(LotSize, _Symbol, entry, sl, tp, "Channel Buy"))
            {
               Print("Buy order placed: Entry=", entry, " SL=", sl, " TP=", tp, " (TP > SL)");
            }
            else
            {
               Print("Buy order failed: ", trade.ResultRetcode(), " - ", trade.ResultRetcodeDescription());
            }
         }
      }
MQL5における市場ポジショニング戦略の体系(第1回):NVIDIAのビットワイズ戦略研究 MQL5における市場ポジショニング戦略の体系(第1回):NVIDIAのビットワイズ戦略研究
これまでの「MQL5ウィザード」シリーズで積み上げてきた取り組みを基盤とし、それをさらに発展させる新連載を開始します。本連載は、システムトレードおよび戦略テストへのアプローチを一段引き上げることを目的としています。単一タイプのポジションのみを保有するように設計されたエキスパートアドバイザーに焦点を当てます。主にロングポジションのみを扱う設計です。市場トレンドを一方向に限定することで、分析が簡素化され、戦略の複雑さが軽減されます。また、特に為替以外の資産を扱う場合には、重要な洞察が得られる可能性があります。したがって本連載では、株式やその他の非為替資産において、このアプローチが有効かどうかを検証していきます。買い専用戦略は、スマートマネーや機関投資家の戦略と相関することが多いため、その実用性を体系的に探究します。
古典的な戦略を再構築する(第17回):テクニカル指標のモデリング 古典的な戦略を再構築する(第17回):テクニカル指標のモデリング
金融における古典的機械学習手法によって課されている「ガラスの天井」をいかに打ち破るかに焦点を当てます。統計モデルから引き出せる価値に対する最大の制約は、モデルそのもの、すなわちデータやアルゴリズムの複雑さではなく、それらを適用する方法論にあるようです。言い換えれば、真のボトルネックはモデルの内在的能力ではなく、私たちがそれをどのように運用しているかにあるのかもしれません。
MetaTrader 5機械学習の設計図(第5回):逐次ブートストラップ - ラベルのバイアス除去とリターンの向上 MetaTrader 5機械学習の設計図(第5回):逐次ブートストラップ - ラベルのバイアス除去とリターンの向上
逐次ブートストラップは、金融機械学習におけるブートストラップサンプリングを再構築する手法であり、時間的に重複するラベルを積極的に回避することで、より独立性の高い学習サンプル、より鋭い不確実性推定、そしてより堅牢な取引モデルを実現します。この実践ガイドでは、その直感的な考え方を説明し、アルゴリズムを段階的に示し、大規模データセット向けの最適化コードパターンを提供し、シミュレーションおよび実際のバックテストを通じて測定可能な性能向上を実証します。
プライスアクション分析ツールキットの開発(第48回):加重バイアスダッシュボードを備えた多時間軸ハーモニー指数 プライスアクション分析ツールキットの開発(第48回):加重バイアスダッシュボードを備えた多時間軸ハーモニー指数
本記事では、「多時間軸ハーモニー指数」を紹介します。これはMetaTrader 5向けの高度なエキスパートアドバイザー(EA)で、複数の時間軸からのトレンドの傾向を加重平均し、EMAによって平滑化したうえで、見やすいチャートパネル型ダッシュボードに表示します。さらに、カスタマイズ可能なアラート機能に加え、強いバイアスの閾値を超えた際には自動で売買シグナルをチャート上に描画します。複数時間軸分析を活用し、市場構造に沿ったエントリーを目指すトレーダーに最適なEAです。