English Deutsch
preview
ボリンジャーバンドを活用したピラニア戦略に基づくMQL5エキスパートアドバイザーの作成

ボリンジャーバンドを活用したピラニア戦略に基づくMQL5エキスパートアドバイザーの作成

MetaTrader 5トレーディングシステム | 16 12月 2024, 16:13
478 0
Allan Munene Mutiiria
Allan Munene Mutiiria

はじめに

この記事では、ピラニア戦略に基づくMetaQuotes Language 5 (MQL5) EAの作成方法を、Bollinger Bandsの統合に焦点を当てながら探ります。トレーダーが効果的な自動売買ソリューションを求める中、ピラニア戦略は市場の変動を利用する体系的なアプローチとして登場し、多くのFX愛好家にとって魅力的な選択肢となっています。

まず、ピラニア戦略の基本原則を概説し、自動売買に導入するための強固な基礎を提供します。次に、市場のボラティリティを測定することで、潜在的なエントリーポイントやエグジットポイントを特定するのに役立つ人気のテクニカルインジケーターであるボリンジャーバンドについて掘り下げます。

続いて、MQL5でのコーディングプロセスをガイドし、戦略を推進する重要な機能とロジックに焦点を当てます。さらに、EAのパフォーマンスのテスト、パラメータの最適化、ライブ取引環境に導入するためのベストプラクティスについても説明します。この記事で取り上げるトピックは以下の通りです。

  1. ピラニア戦略の概要
  2. ボリンジャーバンドについて
  3. 戦略の設計図
  4. MQL5での実装
  5. テスト
  6. 結論

この記事を読み終わる頃には、ピラニア戦略とボリンジャーバンドを効果的に活用するMQL5 EAを開発するための知識が身につき、取引手法を強化することができるでしょう。始めましょう。


ピラニア戦略の概要

ピラニア戦略は、外国為替市場の値動きを利用できるダイナミック取引システムです。この戦略は、迅速かつ日和見的な取引によって定義され、そのスピードと正確さから、このタイプの漁業が敏捷な捕食魚に似ていることから名づけられました。スキルセット:ピラニア戦略はボラティリティをベースとした戦略で、高度に相対的なアプローチにより、トレーダーが市場のエントリポイントとエグジットポイントを正確に判断できるように開発されました。

ピラニア戦略の有効な構成要素の1つは、ボリンジャーバンドの適用です。ボリンジャーバンドは、トレーダーが市場のボラティリティを確認するのに役立つ一般的なインジケーターです。私たちの方法では、12期間のボリンジャーバンドを使用します。優れたツールであり、長期的に見れば、価格行動に関する優れた洞察を与えてくれるトポロジーを持っているからです。また、標準偏差を2に設定します。これは基本的に、小さな変動からのノイズをフィルタリングしながら、大きな値動きを捉えることに等しいです。これらのチャネルは天井と床を形成し、市場の潜在的な買われすぎ、売られすぎの状態を表します。価格が下部バンドを下回れば絶好の買いチャンスとみなされ、上部バンドを上回れば売りのチャンスとなります。図解を以下に示します。

戦略概要

リスク管理はピラニア戦略のもう1つの重要な要素で、損切りと利食いの水準を明確にすることで資本を保護することの重要性が強調されています。この戦略では、買い取引の場合、損切りはエントリ価格より100ポイント下に置き、利食いはエントリ価格より50ポイント上に設定します。この規律あるアプローチにより、利益を確保しながら潜在的な損失を確実に軽減し、より持続可能な取引手法を育成しています。

要約すると、ピラニア戦略はボラティリティとリスク管理に重点を置いたテクニカル分析を組み合わせたものです。これらの原則と設定を理解することで、トレーダーは外国為替市場をより効果的にナビゲートし、取引目標に沿った情報に基づいた意思決定をおこなうことができます。次に、この戦略をMQL5ピラニア戦略を自動売買システムで実現する方法を探っていきます。


ボリンジャーバンドについて

トレーダーは、Bollinger Bandsという強力なテクニカル分析ツールを使って、不安定な市場における潜在的な値動きを判断することができます。ジョン・ボリンジャーは1980年代にこのインディケーターを開発しました。このインディケーターは、移動平均線、上部バンド、下部バンドの3つで構成されます。トレーダーがおこなう計算によって、価格がその平均からどちらかの方向に離れているかどうかを測ることができます。

まず、ミドルバンド(移動平均線)を計算します。ミドルバンドは通常、20期間の単純移動平均線(SMA)です。SMAの計算式は以下の通りです。

SMA式

ここで、𝑃𝑖は各期間の終値を表し、𝑛は期間数(ここでは20)です。例えば、過去20期間の終値が以下の通りだとします。

期間終値 (𝑃𝑖)
11.1050
21.1070
31.1030
41.1080
51.1040
61.1100
71.1120
81.1150
91.1090
101.1060
111.1085
121.1105
131.1130
14 1.1110
15 1.1075
16 1.1055
17 1.1080
18 1.1095
19 1.1115
20 1.1120

これらの価格を合計し、20で割ります。

SMA計算

次に、SMAからの終値の分散を測定する標準偏差を計算します。標準偏差(𝜎)の式は以下の通りです。

標準偏差の式

計算したSMA1.1080を使い、各終値の二乗差を計算し、その平均をとり、最後に平方根をとります。例えば、最初のいくつかの2乗の差は以下のようになります。

一乗差

20の二乗差をすべて計算すると、以下のようになります。

すべての違い

ミドルバンド(SMA)と標準偏差が計算されたので、今度は上部バンドと下部バンドを決定することができます。数式は次のとおりです。

  • 上部バンド = SMA + (k × σ)
  • 下部バンド = SMA - (k × σ)

ここでは通常、k=2(2つの標準偏差を表す)とします。以下の値を挿入します。

  1. 上部バンド = 1.1080 + (2 × 0.0030) = 1.1140
  2. 下部バンド = 1.1080 - (2 × 0.0030) = 1.1020

その結果、ボリンジャーバンドは以下のようになります。

  • ミドルバンド(SMA):1.1080
  • 上部バンド:1.1140
  • 下部バンド:1.1020

これら3つの帯域は下図の通りです。

3バンド

これらのバンドの間は、市場の状況によって変化します。その幅が広がるということは、ボラティリティが高まるということであり、動く市場はボラティリティが高くなる傾向があります。バンドが狭まるときは、市場が固まりつつあることを示唆します。トレーダーは取引シグナルを生成するために、価格とバンドの間の相互作用を探すことを好みます。上部バンドと価格が連動すれば買われすぎ、下部バンドと価格が連動すれば売られすぎと解釈する傾向があるかもしれません。

要約すると、ボリンジャーバンドの計算には、SMA、標準偏差、上部バンドと下部バンドの反転が含まれます。これらの計算を理解することは、単なる定量的な読解の練習ではなく、トレーダーが十分な情報に基づいた取引判断を下すために必要な弾薬を身につけることであり、特にピラニア戦略を取引に適用する際に必要なことです。


戦略の設計図

上部バンドの設計図:売りの条件

価格が上部ボリンジャーバンドを上回り、終値をつけた場合、市場が買われ過ぎであることを示します。この状態は、価格が過度に上昇し、下方修正される可能性が高いことを示唆しています。その結果、このシナリオは売りシグナルと判断します。したがって、現在のバーの終値が上部バンドより上にあるときに売りポジションを建てます。その狙いは、反転や反落の可能性を利用することです。

上部バンド設計図

下部バンドの設計図:買いの条件

逆に、価格が下部ボリンジャーバンドの下を横切り、終値が下回る場合は、市場が売られ過ぎであることを示しています。このシナリオは、価格が大幅に下落し、反発する可能性があることを示唆しています。したがって、これは買いシグナルと考えられます。したがって、現在のバーの終値が下部バンドを下回ったら買いポジションを建てます。

下部バンドの設計図

このような戦略設計図の視覚的表現は、で取引条件を実装する際に役立ちtitle、title正確なエントリールールとエグジットルールをコーディングする際の参考となります。


MQL5での実装

ピラニア取引戦略に関するすべての理論を学んだ後、その理論を自動化し、MetaTrader 5用のMetaQuotes Language 5 (MQL5)でEAを作成しましょう。

EAを作成するには、MetaTrader 5端末で[ツール]タブをクリックし、[MetaQuotes言語エディタ]を選択するか、キーボードのF4を押します。または、ツールバーのIDE(統合開発環境)アイコンをクリックすることもできます。これにより、自動売買ロボット、テクニカルインジケーター、スクリプト、関数のライブラリを作成できるMetaQuotes言語エディタ環境が開きます。

METAEDITORを開く

MetaEditorを開いたら、ツールバーの[ファイル]タブで[新しいファイル]を選択するか、CTRL + Nキーを押して新規ドキュメントを開きます。または、[ツール]タブの新規アイコンをクリックすることもできます。MQLウィザードのポップアップが表示されます。

新しいEAを作成する

ウィザードが表示されたら、[EA(テンプレート)]を選択し、[次へ]をクリックします。

MQL WIZARD

EAの一般プロパティで、[名前]フィールドにEAのファイル名を入力します。フォルダが存在しない場合にフォルダを指定または作成するには、EA名の前にバックスラッシュを使用することに注意してください。例えば、ここではデフォルトで「Experts\」となっています。つまり、私たちのEAはExpertsフォルダに作成され、そこで見つけることができます。他のフィールドはごく簡単ですが、ウィザードの一番下にあるリンクから、正確な手順を知ることができます。

新しいEA名

希望するEAファイル名を入力した後、[次へ]をクリックし、[完了]をクリックします。ここまでくれば、あとはコードを書いて戦略をプログラムするだけです。

まず、EAに関するメタデータを定義することから始めます。これには、EA名、著作権情報、MetaQuotes Webサイトへのリンクが含まれます。EAのバージョンも指定し、1.00とします。

//+------------------------------------------------------------------+
//|                                                      PIRANHA.mq5 |
//|                        Allan Munene Mutiiria, Forex Algo-Trader. |
//|                                     https://forexalgo-trader.com |
//+------------------------------------------------------------------+

//--- Properties to define metadata about the Expert Advisor (EA)
#property copyright "Allan Munene Mutiiria, Forex Algo-Trader."   //--- Copyright information
#property link      "https://forexalgo-trader.com"               //--- Link to the creator's website
#property version   "1.00"                                       //--- Version number of the EA

プログラムをロードすると、下図のような情報が表示されます。

メタデータ

まず、ソースコードの先頭に#includeを使用して取引インスタンスをインクルードします。これでCTradeクラスにアクセスできるようになります。これを使用して取引オブジェクトを作成します。これは取引を開始するために必要なので、非常に重要です。

//--- Including the MQL5 trading library
#include <Trade/Trade.mqh>      //--- Import trading functionalities
CTrade obj_Trade;               //--- Creating an object of the CTrade class to handle trading operations

プリプロセッサは#include<Trade/Trade.mqh>という行をTrade.mqhというファイルの内容に置き換えます。角括弧は、Trade.mqhファイルが標準ディレクトリ(通常、terminal_installation_directory\MQL5\Include)から取得されることを示します。カレントディレクトリは検索に含まれません。この行はプログラム中のどこにでも配置できますが、通常は、より良いコード構造と参照を容易にするために、すべてのインクルージョンはソースコードの先頭に置かれます。CTradeクラスのobj_Tradeオブジェクトを宣言すると、MQL5開発者のおかげで、そのクラスに含まれるメソッドに簡単にアクセスできるようになります。

CTRADEクラス

戦略に必要なインジケーターを含めるために、インジケーターハンドルを作成する必要があります。

//--- Defining variables for Bollinger Bands indicator and price arrays
int handleBB = INVALID_HANDLE;  //--- Store Bollinger Bands handle; initialized as invalid
double bb_upper[], bb_lower[];  //--- Arrays to store upper and lower Bollinger Bands values

ここでは、EAのボリンジャーバンドインジケーターのハンドルとして機能する単一の整数変数「handleBB」を宣言して初期化します。MQL5では、ハンドルはインジケーターに割り当てられた一意の識別子であり、コード全体でそのインジケーターを簡単に参照できるようにします。最初に「handleBB」をINVALID_HANDLEに設定することで、正しく作成される前にプログラムが無効なインジケーターハンドルを参照しないようにし、予期せぬエラーを防ぎます。ハンドルとともに、2つの動的配列bb_upperとbb_lowerも定義し、それぞれにボリンジャーバンドの上部値と下部値を格納します。これらの配列は、インジケーターの現在の状態をキャプチャして分析するのに役立ち、ボリンジャーバンドの条件に基づいて取引戦略を実行するための信頼できる基盤を提供します。ここでも、一方向のポジションを1つだけ建てるようにする必要があります。

//--- Flags to track if the last trade was a buy or sell
bool isPrevTradeBuy = false, isPrevTradeSell = false;  //--- Prevent consecutive trades in the same direction

ここでは、2つのブール型フラグ「isPrevTradeBuy」と「isPrevTradeSell」を宣言して初期化し、最後に実行された取引の方向を追跡します。どちらも最初はfalseに設定されており、まだ取引がおこなわれていないことを示しています。これらのフラグは、EAが同じ方向に連続して取引を開始しないようにすることで、取引ロジックを管理する上で重要な役割を果たします。例えば、前の取引が買いであった場合、「isPrevTradeBuy」はtrueに設定され、売り取引が発生するまで次の買い取引を防ぎます。この仕組みは、冗長な取引を避け、バランスの取れた取引戦略を維持するのに役立ちます。

次に、OnInitイベントハンドラが必要です。このハンドラは、チャート上でEAが初期化されるときに自動的に呼び出されるため、必要不可欠です。この関数は、必要なインジケーターハンドルの作成、変数の初期化、リソースの準備など、EAのセットアップをおこないます。言い換えれば、OnInitは、EAがマーケットデータの処理を開始する前に、すべてが適切に設定されていることを確認する内蔵関数です。それは次のようなものです。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   // OnInit is called when the EA is initialized on the chart
//...
}

OnInitイベントハンドラで、インジケーターハンドルを初期化し、データ値が割り当てられるようにする必要があります。

   //--- Create Bollinger Bands indicator handle with a period of 12, no shift, and a deviation of 2
   handleBB = iBands(_Symbol, _Period, 12, 0, 2, PRICE_CLOSE);  

ここでは、指定したパラメータに基づいてインジケーターを生成するiBands関数を呼び出すことで、ボリンジャーバンドインジケーターのハンドルを作成します。この関数にはいくつかの引数を渡します:_Symbolは分析対象の通貨ペアを指し_Periodはインジケーターの時間枠を示します。これは数分から数時間、または数日までの範囲になります。ボリンジャーバンドのパラメータには、インジケーターの計算に使用されるバーの数を示す期間12、バンドに調整が適用されないことを意味するシフト0、およびバンドが移動平均からどれだけ離れるかを決定する標準偏差2が含まれます。PRICE_CLOSEの使用は、バーの終値に基づいて計算することを示しています。これが正常に実行されると、ハンドル変数handleBBにボリンジャーバンドインディケーターの有効な識別子が格納され、データの取得と分析のために参照できるようになります。したがって、先に進む前にハンドルが正常に作成されたかどうかをチェックする必要があります。

   //--- Check if the Bollinger Bands handle was created successfully
   if (handleBB == INVALID_HANDLE){
      Print("ERROR: UNABLE TO CREATE THE BB HANDLE. REVERTING");  //--- Print error if handle creation fails
      return (INIT_FAILED);  //--- Return initialization failed
   }

ここでは、ボリンジャーバンドインディケーターのハンドルが正常に作成されたかどうかを、そのハンドルがINVALID_HANDLEと等しいかどうかをチェックすることで検証しています。ハンドルが無効な場合は、「ERROR:UNABLE TO CREATE THE BB HANDLE.REVERTING」というエラーメッセージを出力します。これは、初期化プロセス中の問題を特定するのに役立ちます。そして、EAが正しく初期化できなかったことを示すINIT_FAILEDを戻します。これがパスしたら、データ配列を時系列として設定し続けます。

   //--- Set the arrays for the Bollinger Bands to be time-series based (most recent data at index 0)
   ArraySetAsSeries(bb_upper, true);  //--- Set upper band array as series
   ArraySetAsSeries(bb_lower, true);  //--- Set lower band array as series
   
   return(INIT_SUCCEEDED);  //--- Initialization successful

ここでは、ArraySetAsSeries関数を呼び出し、2番目のパラメータをtrueに設定することで、ボリンジャーバンドの配列bb_upperとbb_lowerを時系列データとして扱うように設定します。これにより、最新のデータがインデックス0に保存され、市場状況を分析する際に最新の値に簡単にアクセスできるようになります。このように配列を整理することで、最新の情報が最も適切であることが多い取引アルゴリズムにおける典型的な使用法にデータ構造を合わせることができます。最後にINIT_SUCCEEDEDを戻し、初期化処理が正常に完了したことを示し、EAの操作を続行できるようにします。

ここまでは、初期化セクションのすべてが正しく機能していました。プログラムの初期化を担当するソースコードの全文は以下の通りです。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Create Bollinger Bands indicator handle with a period of 12, no shift, and a deviation of 2
   handleBB = iBands(_Symbol, _Period, 12, 0, 2, PRICE_CLOSE);  
   
   //--- Check if the Bollinger Bands handle was created successfully
   if (handleBB == INVALID_HANDLE){
      Print("ERROR: UNABLE TO CREATE THE BB HANDLE. REVERTING");  //--- Print error if handle creation fails
      return (INIT_FAILED);  //--- Return initialization failed
   }
   
   //--- Set the arrays for the Bollinger Bands to be time-series based (most recent data at index 0)
   ArraySetAsSeries(bb_upper, true);  //--- Set upper band array as series
   ArraySetAsSeries(bb_lower, true);  //--- Set lower band array as series
   
   return(INIT_SUCCEEDED);  //--- Initialization successful
  }

次に、OnDeinitイベントハンドラに移ります。これは、プログラムが初期化されたときに呼び出される関数です。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
   // OnDeinit is called when the EA is removed from the chart or terminated
//...
}

OnDeinit関数は、EAがチャートから削除されたとき、または端末がシャットダウンしたときに呼び出されます。このイベントハンドラを使用して、正しい維持管理とリソース管理をおこなわなければなりません。EAが終了したら、初期化フェーズで作成したインジケーターのハンドルをすべて解放しなければなりません。これをおこなわないと、使用したメモリの場所が残ってしまう可能性があり、非効率的になります。不要なリソースを残すリスクは絶対に避けたいと考えました。これが、OnDeinitが重要な理由であり、どのようなプログラミング環境においてもクリーンアップステップが重要な理由です。

IndicatorRelease(handleBB); //--- Release the indicator handle

ここでは、IndicatorRelease関数を引数handleBBで呼び出し、以前に作成したボリンジャーバンドのインジケーターハンドルを解放しています。特に複数のエキスパートアドバイザーを使用している場合や、長時間プラットフォームを稼動させている場合は、プラットフォームのパフォーマンスを維持するためにクリーンアップは非常に重要です。従って、リソース解放のための完全なソースコードは以下の通りです。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- Function to handle cleanup when the EA is removed from the chart
   IndicatorRelease(handleBB); //--- Release the indicator handle
  }

次に、価格が更新されるたびに取引機会をチェックする必要があります。これはOnTickイベントハンドラで実現されます。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){
   // OnTick is called whenever there is a new market tick (price update)

//...

}

イベントハンドラ関数のOnTickは、新しいティックや市場状況の変化があるたびに実行され、最近の価格情報を処理します。これは、EAの動作に不可欠な部分です。これは、取引ロジックを実行する場所であり、取引条件は、利益を生む取引を生み出すように構成されているためです。市場データが変化したとき、市場の現状を判断し、ポジションを建てるか決済するかを決定します。この関数は、市場環境の変化に応じて頻繁に実行されるため、戦略がリアルタイムで作動し、現在の価格や市場インジケーターの値の変化に対応できます。

現在の市況を常に更新するためには、現在の価格相場の値を入手する必要があります。

   //--- Get current Ask and Bid prices
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);  //--- Normalize Ask price to correct digits
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);  //--- Normalize Bid price to correct digits

ここでは、取引されている銘柄の最新の売呼値と買呼値を取得します。これらの価格を得るには、SymbolInfoDouble関数を使います。Ask価格にはSYMBOL_ASK、Bid価格にはSYMBOL_BIDを指定します。価格を取得したら、NormalizeDouble関数を使って_Digitsで定義された小数点以下の桁数に丸めます。このステップは、標準化された正確な価格を使用して取引業務を確実に実行するために非常に重要です。もし四捨五入しなければ、浮動小数点の不正確さが運行価格の計算に誤解を招く結果をもたらす可能性があります。次に、そのインジケーター値をコピーして分析や取引操作に使用します。

   //--- Retrieve the most recent Bollinger Bands values (3 data points)
   if (CopyBuffer(handleBB, UPPER_BAND, 0, 3, bb_upper) < 3){
      Print("UNABLE TO GET UPPER BAND REQUESTED DATA. REVERTING NOW!");  //--- Error if data fetch fails
      return;
   }
   if (CopyBuffer(handleBB, LOWER_BAND, 0, 3, bb_lower) < 3){
      Print("UNABLE TO GET LOWER BAND REQUESTED DATA. REVERTING NOW!");  //--- Error if data fetch fails
      return;
   }

ここでは、CopyBuffer関数を使って、直近のボリンジャーバンドの値、具体的には上下両方のバンドについて3つのデータポイントを取得します。CopyBufferの最初の呼び出しは、インデックス0から始まる上位バンドのデータを要求し、bb_upper配列に格納します。この関数が3未満の値を返した場合、データ検索に失敗したことを示し、エラーメッセージ「UNABLE TO GET UPPER BAND REQUESTED DATA.REVERTING NOW!」を表示するよう促します。そして、それ以上実行されないように関数を終了します。下位の帯域についても同様のプロセスを踏んで、データを取得する際のエラーも確実に処理します。バッファインデックスを参照する際には、バッファ番号の代わりに、ボリンジャーバンドインジケーターの値をコピーする際に許容されるインジケーターラインの識別子を使用することに注意してください。混乱を避けるための最も簡単な方法ですが、理屈は変わらない。以下は、バッファの数を視覚的に表したものです。

バッファ可視化

インジケーターの値と価格を比較する必要があるため、関連するバー価格(この場合は高値と安値)を取得する必要があります。

   //--- Get the low and high prices of the current bar
   double low0 = iLow(_Symbol, _Period, 0);    //--- Lowest price of the current bar
   double high0 = iHigh(_Symbol, _Period, 0);  //--- Highest price of the current bar

ここでは、iLow関数とiHigh関数を呼び出して、現在のバーの安値と高値を取得します。関数iLowは、指定した銘柄(_Symbol)と時間枠(_Period)の現在のバー(インデックス0)の最安値を取得し、この値を変数「low0」に格納します。同様に、iHighは現在のバーの最高値を取得し、変数「high0」に代入します。1つのバーで1つのシグナルを実行することを確認する必要があります。これが採用されたロジックです。

   //--- Get the timestamp of the current bar
   datetime currTimeBar0 = iTime(_Symbol, _Period, 0);  //--- Time of the current bar
   static datetime signalTime = currTimeBar0;           //--- Static variable to store the signal time

iTime関数は、指定した銘柄(_Symbol)と時間枠(_Period)の指定したバー(インデックス0)の時刻を返します。このタイムスタンプは変数currTimeBar0に格納されます。さらに、signalTimeという静的変数を宣言し、currTimeBar0という値で初期化します。signalTimeをstaticにすることで、その値が関数呼び出しの間持続することを保証し、取引シグナルが最後に生成された時刻を追跡できるようにします。これは、同じバーで複数のシグナルがトリガーされるのを防ぎ、1つの期間につき1つのシグナルにしか反応しないようにするためで、当戦略にとって極めて重要です。ここまでできたら、次はシグナルのチェックです。最初にすることは、買いシグナルをチェックすることです。

   //--- Check for a buy signal when price crosses below the lower Bollinger Band
   if (low0 < bb_lower[0]){
      Print("BUY SIGNAL @ ", TimeCurrent());  //--- Log the buy signal with the current time

   }

ここでは、変数low0に格納されている現在のバーの最安値が、配列bb_lowerのインデックス0に格納されている直近のボリンジャーバンドの下部バンドの値よりも低いかどうかを評価することで、買いシグナルの可能性をチェックします。もしlow0がbb_lower[0]よりも小さければ、価格が下部バンドを下回ったことを示し、売られ過ぎの可能性を示唆し、買いのチャンスとなります。この条件が満たされると、プログラムはPrint関数を使ってメッセージを記録し、TimeCurrent関数を使って取得した現在時刻とともに「BUY SIGNAL @」と表示します。このアラートは、買いシグナルがいつ検出されたかを追跡するのに役立ち、EAの意思決定プロセスに透明性とトレーサビリティを提供します。これを実行すると、次の出力が得られます。

買いシグナル1

提供された出力から、強気条件が満たされたティックごとにシグナルを表示していることがわかります。条件が満たされるたびに、1バーにつき1回シグナルを表示するために、次のようなロジックを使います。

   //--- Check for a buy signal when price crosses below the lower Bollinger Band
   if (low0 < bb_lower[0] && signalTime != currTimeBar0){
      Print("BUY SIGNAL @ ", TimeCurrent());  //--- Log the buy signal with the current time
      signalTime = currTimeBar0;              //--- Update signal time to avoid duplicate trades
   }

ここでは、同じバー内で重複取引を発生させないようにするための追加チェックを追加することで、買いシグナル条件を洗練させます。当初は、変数low0に格納されている現在のバーの最安値が、直近のボリンジャーバンドの下部値(bb_lower[0])を下回っているかどうかだけを検証していました。ここで、2番目の条件「signalTime != currTimeBar0」を組み込みます。これにより、現在のバーのタイムスタンプ(currTimeBar0)が最後に記録されたシグナル時刻(signalTime)と異なることを保証します。次に、signalTimeをcurrTimeBar0と一致するように更新し、価格がバンドの下を複数回横切ったとしても、1つのバーにつき1つの買いシグナルしか考慮しないことを確認します。アップデートを実行すると、次のような出力が得られます。

シングルバー買いシグナル

うまくいきました。これで、1バーにつき1回シグナルを表示していることがわかります。その後、買いポジションを建てることで、生成されたシグナルに対して行動を起こし続けることができます。

      if (PositionsTotal() == 0 && !isPrevTradeBuy){
         obj_Trade.Buy(0.01, _Symbol, Ask, Ask - 100 * _Point, Ask + 50 * _Point);  //--- Open a buy position with predefined parameters
         isPrevTradeBuy = true; isPrevTradeSell = false;  //--- Update trade flags
      }

ここでは、特定の状況下でのみ買い取引が実行されるように条件を追加します。まず、PositionsTotal関数を使って、ポジションの合計がゼロかどうかをチェックします。これにより、現在他の取引がアクティブになっていないことが保証されます。次に、「!isPrevTradeBuy」を評価することで、最後に約定した取引が買いでないことを確認します。これは連続した買い注文を防ぎ、前回の取引がすでに買いであった場合、EAが新たな買いポジションを建てることがないようにするためです。

両方の条件が満たされたら、「obj_Trade.Buy」を使って買いポジションを建てます。注文数量は0.01ロット、現在の取引銘柄(_Symbol)とAsk価格を指定します。ストップロスとテイクプロフィットのレベルは、それぞれ提示価格より100ポイント下と50ポイント上に設定し、リスク管理ルールを定義します。買い取引を正常に開始した後、取引フラグを更新します。isPrevTradeBuyはtrueに設定され、isPrevTradeSellはfalseに設定され、最後の取引が買いであったことが示され、売りシグナルがトリガーされるまで別の買いが防止されます。売りロジックについては、同様のアプローチを以下のように用いる。

   //--- Check for a sell signal when price crosses above the upper Bollinger Band
   else if (high0 > bb_upper[0] && signalTime != currTimeBar0){
      Print("SELL SIGNAL @ ", TimeCurrent());  //--- Log the sell signal with the current time
      signalTime = currTimeBar0;               //--- Update signal time to avoid duplicate trades
      if (PositionsTotal() == 0 && !isPrevTradeSell){
         obj_Trade.Sell(0.01, _Symbol, Bid, Bid + 100 * _Point, Bid - 50 * _Point);  //--- Open a sell position with predefined parameters
         isPrevTradeBuy = false; isPrevTradeSell = true;  //--- Update trade flags
      }
   }

プログラムをコンパイルして実行すると、次のような出力が得られます。

買いポジション

買いポジションがうまく約定したことがわかります。実装が完了したので、ボリンジャーバンドを使ってピラニア戦略を統合し、定義された条件に基づいて売買シグナルに反応するようにプログラムを設定しました。次のセクションでは、プログラムのテストに焦点を当て、そのパフォーマンスを評価し、最適な結果を得るためにパラメータを微調整します。


テスト

実装が完了したら、次の重要なステップは、EAを徹底的にテストし、そのパフォーマンスを評価し、パラメータを最適化することです。効果的なテストにより、様々な市場環境において戦略が期待通りに動作することが保証され、取引中に予期せぬ問題が発生するリスクを最小限に抑えることができます。ここでは、MetaTrader 5ストラテジーテスターを使ってバックテストと最適化をおこない、戦略に最適な入力値を見つけます。

まず、戦略のリスク管理に大きな影響を与えるストップロス(SL)とテイクプロフィット(TP)の値の初期入力パラメータを設定します。当初の実装では、SLとTPは固定ピップ値で定義されていました。しかし、戦略に十分な余裕を持たせ、市場の動きをよりよく捉えるために、テスト中に入力パラメータをより柔軟で最適なものに変更します。コードを次のように更新しましょう。

//--- INPUTS
input int sl_points = 500;
input int tp_points = 250;

//---

   //--- Check for a buy signal when price crosses below the lower Bollinger Band
   if (low0 < bb_lower[0] && signalTime != currTimeBar0){
      Print("BUY SIGNAL @ ", TimeCurrent());  //--- Log the buy signal with the current time
      signalTime = currTimeBar0;              //--- Update signal time to avoid duplicate trades
      if (PositionsTotal() == 0 && !isPrevTradeBuy){
         obj_Trade.Buy(0.01, _Symbol, Ask, Ask - sl_points * _Point, Ask + tp_points * _Point);  //--- Open a buy position with predefined parameters
         isPrevTradeBuy = true; isPrevTradeSell = false;  //--- Update trade flags
      }
   }
   
   //--- Check for a sell signal when price crosses above the upper Bollinger Band
   else if (high0 > bb_upper[0] && signalTime != currTimeBar0){
      Print("SELL SIGNAL @ ", TimeCurrent());  //--- Log the sell signal with the current time
      signalTime = currTimeBar0;               //--- Update signal time to avoid duplicate trades
      if (PositionsTotal() == 0 && !isPrevTradeSell){
         obj_Trade.Sell(0.01, _Symbol, Bid, Bid + sl_points * _Point, Bid - tp_points * _Point);  //--- Open a sell position with predefined parameters
         isPrevTradeBuy = false; isPrevTradeSell = true;  //--- Update trade flags
      }
   }

この入力により、さまざまな銘柄や取引商品について動的最適化をおこなうことができます。これを実行すると、次の出力が得られます。

最終テスト

成功でした。このプログラムは期待通りに機能したと結論づけることができます。ピラニア戦略の作成と実装を担当する最終的なソースコードの断片は以下の通りです。

//+------------------------------------------------------------------+
//|                                                      PIRANHA.mq5 |
//|                        Allan Munene Mutiiria, Forex Algo-Trader. |
//|                                     https://forexalgo-trader.com |
//+------------------------------------------------------------------+

//--- Properties to define metadata about the Expert Advisor (EA)
#property copyright "Allan Munene Mutiiria, Forex Algo-Trader."   //--- Copyright information
#property link      "https://forexalgo-trader.com"               //--- Link to the creator's website
#property version   "1.00"                                       //--- Version number of the EA

//--- Including the MQL5 trading library
#include <Trade/Trade.mqh>      //--- Import trading functionalities
CTrade obj_Trade;               //--- Creating an object of the CTrade class to handle trading operations

input int sl_points = 500;
input int tp_points = 250;

//--- Defining variables for Bollinger Bands indicator and price arrays
int handleBB = INVALID_HANDLE;  //--- Store Bollinger Bands handle; initialized as invalid
double bb_upper[], bb_lower[];  //--- Arrays to store upper and lower Bollinger Bands values

//--- Flags to track if the last trade was a buy or sell
bool isPrevTradeBuy = false, isPrevTradeSell = false;  //--- Prevent consecutive trades in the same direction

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Create Bollinger Bands indicator handle with a period of 12, no shift, and a deviation of 2
   handleBB = iBands(_Symbol, _Period, 12, 0, 2, PRICE_CLOSE);  
   
   //--- Check if the Bollinger Bands handle was created successfully
   if (handleBB == INVALID_HANDLE){
      Print("ERROR: UNABLE TO CREATE THE BB HANDLE. REVERTING");  //--- Print error if handle creation fails
      return (INIT_FAILED);  //--- Return initialization failed
   }
   
   //--- Set the arrays for the Bollinger Bands to be time-series based (most recent data at index 0)
   ArraySetAsSeries(bb_upper, true);  //--- Set upper band array as series
   ArraySetAsSeries(bb_lower, true);  //--- Set lower band array as series
   
   return(INIT_SUCCEEDED);  //--- Initialization successful
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- Function to handle cleanup when the EA is removed from the chart
   IndicatorRelease(handleBB); //--- Release the indicator handle
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- Retrieve the most recent Bollinger Bands values (3 data points)
   if (CopyBuffer(handleBB, UPPER_BAND, 0, 3, bb_upper) < 3){
      Print("UNABLE TO GET UPPER BAND REQUESTED DATA. REVERTING NOW!");  //--- Error if data fetch fails
      return;
   }
   if (CopyBuffer(handleBB, LOWER_BAND, 0, 3, bb_lower) < 3){
      Print("UNABLE TO GET LOWER BAND REQUESTED DATA. REVERTING NOW!");  //--- Error if data fetch fails
      return;
   }
   
   //--- Get current Ask and Bid prices
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);  //--- Normalize Ask price to correct digits
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);  //--- Normalize Bid price to correct digits

   //--- Get the low and high prices of the current bar
   double low0 = iLow(_Symbol, _Period, 0);    //--- Lowest price of the current bar
   double high0 = iHigh(_Symbol, _Period, 0);  //--- Highest price of the current bar
   
   //--- Get the timestamp of the current bar
   datetime currTimeBar0 = iTime(_Symbol, _Period, 0);  //--- Time of the current bar
   static datetime signalTime = currTimeBar0;           //--- Static variable to store the signal time

   //--- Check for a buy signal when price crosses below the lower Bollinger Band
   if (low0 < bb_lower[0] && signalTime != currTimeBar0){
      Print("BUY SIGNAL @ ", TimeCurrent());  //--- Log the buy signal with the current time
      signalTime = currTimeBar0;              //--- Update signal time to avoid duplicate trades
      if (PositionsTotal() == 0 && !isPrevTradeBuy){
         obj_Trade.Buy(0.01, _Symbol, Ask, Ask - sl_points * _Point, Ask + tp_points * _Point);  //--- Open a buy position with predefined parameters
         isPrevTradeBuy = true; isPrevTradeSell = false;  //--- Update trade flags
      }
   }
   
   //--- Check for a sell signal when price crosses above the upper Bollinger Band
   else if (high0 > bb_upper[0] && signalTime != currTimeBar0){
      Print("SELL SIGNAL @ ", TimeCurrent());  //--- Log the sell signal with the current time
      signalTime = currTimeBar0;               //--- Update signal time to avoid duplicate trades
      if (PositionsTotal() == 0 && !isPrevTradeSell){
         obj_Trade.Sell(0.01, _Symbol, Bid, Bid + sl_points * _Point, Bid - tp_points * _Point);  //--- Open a sell position with predefined parameters
         isPrevTradeBuy = false; isPrevTradeSell = true;  //--- Update trade flags
      }
   }
  }
//+------------------------------------------------------------------+

バックテスト結果

バックテスト結果

バックテストのグラフ

バックテストグラフ

このテスト段階で、入力パラメータを最適化し、ストラテジーテスターで戦略のパフォーマンスを検証しました。ストップロスとテイクプロフィットの値に加えた調整により、ピラニア戦略はより柔軟になりました。市場の変動にも対応できるようになりました。バックテストと最適化をおこなった結果、この戦略が意図したとおりに機能し、良好な結果が得られることが確認されました。


結論

この記事では、ピラニア戦略に基づき、ボリンジャーバンドを利用して潜在的な売買シグナルを識別するMetaQuotes Language 5 (MQL5) EAの開発について検討しました。まず、ピラニア戦略の基本を理解し、次にボリンジャーバンドの詳細な概要を説明し、市場のボラティリティを検出し、取引のエントリとエグジットを設定する際のボリンジャーバンドの役割に焦点を当てました。

実装を通して、ステップバイステップのコーディングプロセス、インジケーターハンドルの設定、取引ロジックの実装を説明しました。最適なパフォーマンスを確保するため、重要な入力を調整し、MetaTrader 5のストラテジーテスターを使用してプログラムをテストし、さまざまな市場条件で戦略の有効性を検証しました。

免責条項:この記事に掲載されている情報は、教育目的のみのものです。これは、ピラニア戦略に基づくエキスパートアドバイザー(EA)を作成するための洞察を提供することを意図しており、さらなる最適化とテストにより、より高度なシステムを開発するための基礎となるはずです。また、本コンテンツの利用は自己責任でお願いします。自動売買ソリューションを適用する前に、常に徹底的なテストをおこない、潜在的な市場環境を考慮してください。

全体として、この記事はピラニア戦略を自動化し、自分の取引スタイルに合わせてカスタマイズするためのガイドとなります。MQL5で洗練された取引システムを構築するための貴重な洞察を提供し、さらなる探求を促すことを願っています。コーディングと取引の成功を祈ります。

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

添付されたファイル |
PIRANHA.mq5 (5.57 KB)
Connexusにおけるヘッダ(第3部):リクエスト用HTTPヘッダの使い方をマスターする Connexusにおけるヘッダ(第3部):リクエスト用HTTPヘッダの使い方をマスターする
Connexusライブラリの開発を続けます。この章では、HTTPプロトコルにおけるヘッダの概念を探求し、ヘッダとは何か、何のためにあるのか、リクエストでどのように使うのかを説明します。APIとの通信で使用される主なヘッダを取り上げ、ライブラリでの設定方法の実践例を紹介します。
MQL5とPythonで自己最適化エキスパートアドバイザーを構築する(第5回):深層マルコフモデル MQL5とPythonで自己最適化エキスパートアドバイザーを構築する(第5回):深層マルコフモデル
この記事では、RSIインジケーターに単純なマルコフ連鎖を適用し、インジケーターが主要なレベルを通過した後の価格の挙動を観察します。NZDJPYペアで最も強い買いシグナルと売りシグナルは、RSIがそれぞれ11~20の範囲と71~80の範囲にあるときに生成されるという結論に達しました。データを操作して、保有するデータから直接学習した最適な取引戦略を作成する方法を説明します。さらに、遷移行列を最適に使用することを学習するためにディープニューラルネットワークを訓練する方法を説明します。
MQL5で取引管理者パネルを作成する(第3回):テーマ管理のための組み込みクラスの拡張(II) MQL5で取引管理者パネルを作成する(第3回):テーマ管理のための組み込みクラスの拡張(II)
このディスカッションでは、既存のダイアログライブラリを慎重に拡張して、テーマ管理ロジックを組み込みます。さらに、管理パネルプロジェクトで使用されるCDialog、CEdit、およびCButtonクラスにテーマ切り替えのメソッドを統合します。さらに洞察力のある視点については、引き続きお読みください。
知っておくべきMQL5ウィザードのテクニック(第41回):DQN (Deep-Q-Network) 知っておくべきMQL5ウィザードのテクニック(第41回):DQN (Deep-Q-Network)
DQN (Deep-Q-Network)は強化学習アルゴリズムであり、機械学習モジュールの学習プロセスにおいて、次のQ値と理想的な行動を予測する際にニューラルネットワークを関与させます。別の強化学習アルゴリズムであるQ学習についてはすでに検討しました。そこでこの記事では、強化学習で訓練されたMLPが、カスタムシグナルクラス内でどのように使用できるかを示すもう1つの例を紹介します。