MQL5入門(第23回):オープニングレンジブレイクアウト戦略の自動化
はじめに
連載「MQL5入門」の第23回へようこそ。本記事では、MQL5を使ってオープニングレンジブレイクアウト(ORB)手法の自動化を実践的に解説します。目的は、利益を保証したり特定のトレード戦略を推奨することではなく、プロジェクトベースで初心者にもわかりやすくMQL5を学ぶことです。このような実例に取り組むことで、ブレイクアウトレンジの認識や自動注文の実行、プログラムによる取引管理など、実践的なスキルを身につけることができます。
この記事では、ORB戦略の仕組みを解説し、MQL5での自動化方法を紹介します。取引条件の設定、オープニングレンジの記録、そしてブレイクアウトに応じて自動的にポジションを建てるEAの設定方法を学びます。さらに、取引が指定された市場時間内でのみおこなわれるよう、時間ベースのロジックを組み込む方法も解説します。記事を読み終える頃には、この古典的取引手法をMetaTrader 5上で完全自動化する方法がわかるようになります。
オープニングレンジブレイクアウト(ORB)
オープニングレンジブレイクアウト戦略は、市場が開いた直後の短い時間帯における高値と安値を追跡する手法です。この期間の高値と安値をまとめて「オープニングレンジ」と呼びます。その後、価格がレンジの上限を超えるか、下限を下回るかを確認します。高値を上抜けた場合は強気の勢いが示唆され、安値を下抜けた場合は弱気の勢いが示唆されます。この考え方は、当日の価格方向のバイアスが早い時間帯のボラティリティによって形成されやすいというものです。
一般的なオープニングレンジの期間は、最初の5分、15分、30分、または60分です。短いレンジはシグナルの発生頻度が高くなりますが、同時にノイズも多く、極端に早いボラティリティを捉えてしまいます。長いレンジはシグナルが少なく滑らかになりますが、より精度の高いシグナルを提供する傾向があります。使用する資産や取引期間に応じて適切なレンジを選択しましょう。 たとえば、デイトレード向けの株式戦略では、最初の15分または30分のレンジを使用することが多いです。
ロングエントリーの場合はオープニングレンジの高値を上抜けたとき、ショートエントリーの場合はレンジの安値を下抜けたときに注文する、というのが基本的なブレイクアウトルールです。より慎重なトレーダーは、ノイズによる誤ブレイクアウトを防ぐために、ローソク足のクローズ+数ピップスのバッファを確認条件にすることがあります。また、ブレイク後に価格がレンジの境界に戻って再度ブレイクする「リテスト」を確認するトレーダーもいます。自分のリスク許容度に合った確認ルールを選び、バックテストで検証することが重要です。ブレイクアウトは、レンジの境界にストップ注文を置いて市場がそれを発動するのを待つ方法や、最初に確認されたブレイクアウトで即座に参入する方法で実装できます。
比喩的な説明
たとえば、市場が午前9時30分に開いたとします。この時に形成され始めるM15(15分足)が注目対象です。ローソク足が確定した後、その高値と安値を記録し、これがオープニングレンジとなります。
その後、より短い時間足(例えば5分足)に切り替え、辛抱強く価格の動きを観察します。価格がこの15分足の高値を上抜けた場合は強気ブレイクアウト(価格上昇の可能性)が示され、逆に安値を下抜けた場合は弱気ブレイクアウト(価格下落の可能性)が示されます。
簡単に言えば、市場が開いてから最初の15分間でレンジの境界を設定し、その後は価格がその範囲を突破するのを注意深く見守るというのがオープニングレンジブレイクアウト(ORB)の基本的な考え方です。

EAの動作概要
プログラムによる実装を始める前に、EAの動作手順を理解しておく必要があります。最初のステップは、市場が午前9時30分(サーバー時間)に開くことを前提とします。その時点で形成される最初の15分足ローソク足をEAは注意深く監視します。ローソク足が確定したら、その高値と安値に線を引き、オープニングレンジを特定します。
次にEAは、関連する価格データをコピーしてブレイクアウトの兆候を探し始めます。新規買いポジションを建てる場合は、最初のレンジの高値を上抜けてローソク足が確定するのを確認します。ストップロス(SL)はレンジの安値に設定し、テイクプロフィット(TP)はユーザーが指定したリスクリワードレシオ(RRR)に従って決定します。

同様に、新規売りポジションを建てる場合は、15分足ローソク足の安値を下抜けてローソク足が確定するのを確認します。TPは再びユーザー指定のRRRに従い、SLはレンジの高値に設定されます。EAが同時に建てるポジションの数もユーザーが設定可能です。EAは、当日の最初のブレイクアウトを確認した後、取引を停止するように設計されています。

市場の開始時刻の特定
次のステップは、EAの動作を十分に理解したうえで、市場の開始時刻を特定することです。たとえば、市場がサーバー時間で午前9時30分に開くとします。EAには、常にこの時刻を自動で検出させる必要があります。もしEAをVPS上で稼働させており、毎日手動で日付を更新したくない場合は、時刻部分だけを抽出する方法が必要です。MQL5の時刻は非常に精密で、年、月、日、時、分、秒まで含まれているためです。この方法により、プログラムは毎回の取引セッションの年、月、日を自動で特定できます。
問題となるのは、日付は変化する一方で、同じ時刻(時と分)は毎日繰り返されることです。もし単純に日時全体と特定の数値を比較するだけだと、完全に一致するのは1回だけで、翌日以降は失敗してしまいます。そのため、EAは時刻部分と日付部分を区別する必要があります。こうすることで、現在のバーやティックが、日付に関係なく市場の開始時刻に一致するかどうかを判定できるようになります。
例:
string open_time_string = "9:30"; datetime open_time; ulong chart_id = ChartID(); //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- open_time = StringToTime(open_time_string); Comment("OPEN TIME: ",open_time); ObjectCreate(chart_id,"OPEN TIME",OBJ_VLINE,0,open_time,0); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_STYLE,STYLE_DASH); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_WIDTH,2); }
出力

説明
市場の開始時刻は、まず、たとえば「string open_time_string = "9:30";」という行で単純な文字列として保存されます。これは単に、人間が読みやすい形式で市場の開始時刻を表したもので、EAが認識できるようにするためのものです。この時刻はその後、MQL5で計算やチャート描画に利用できる形式で、変数(datetime open_time;)に格納されます。
プログラムは、この文字列を有効なdatetime値に変換します。MQL5のdatetime型には日付と時刻が含まれていますが、このような文字列を使用すると、時刻は自動的に当日の日付で9:30に設定されます。これにより、日付を明示的に更新しなくても、EAは毎日開始時刻を計算できるようになります。
次に、チャート上に市場開始時刻の位置に縦線を描画します。この縦線は、オープニングレンジの開始を視覚的に示す目印として機能します。
最初の15分足の高値と安値の特定
次のステップは、サーバー時間で正確に9時30分に形成される最初の15分足ローソク足のデータをコピーすることです。この2つの価格は、その日のオープニングレンジを決定するため非常に重要です。このローソク足の安値は弱気ブレイクアウトの目安となり、高値は強気ブレイクアウトが確認される水準を示します。これら2つの重要なポイントを特定することで、EAが午前中の取引でポジション参入を判断するための基準レンジを設定できます。
オープニングレンジを取得することは、価格の変動範囲を決定するうえで欠かせません。初期の小幅な値動きを経た後、このレンジの高値と安値を基準に市場の上昇または下落方向を判断できます。これらの値はEAの基準値として利用され、価格が高値を上抜けた場合は買いのシグナル、安値を下抜けた場合は売りのシグナルが生成されます。これにより、市場がオープニングレンジから明確にブレイクしたときのみ取引がおこなわれるようになります。
例:string open_time_string = "9:30"; datetime open_time; string open_time_bar_close_string = "9:45"; datetime open_time_bar_close; ulong chart_id = ChartID(); double m15_high[]; double m15_low[]; double m15_close[]; double m15_open[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- open_time = StringToTime(open_time_string); Comment("OPEN TIME: ",open_time); ObjectCreate(chart_id,"OPEN TIME",OBJ_VLINE,0,open_time,0); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_STYLE,STYLE_DASH); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_WIDTH,2); open_time_bar_close = StringToTime(open_time_bar_close_string); if(TimeCurrent() >= open_time_bar_close) { CopyHigh(_Symbol,PERIOD_M15,open_time,1,m15_high); CopyLow(_Symbol,PERIOD_M15,open_time,1,m15_low); CopyClose(_Symbol,PERIOD_M15,open_time,1,m15_close); CopyOpen(_Symbol,PERIOD_M15,open_time,1,m15_open); ObjectCreate(chart_id,"High",OBJ_TREND,0,open_time,m15_high[0],TimeCurrent(),m15_high[0]); ObjectSetInteger(chart_id,"High",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"High",OBJPROP_WIDTH,2); ObjectCreate(chart_id,"Low",OBJ_TREND,0,open_time,m15_low[0],TimeCurrent(),m15_low[0]); ObjectSetInteger(chart_id,"Low",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"Low",OBJPROP_WIDTH,2); } }
出力

説明
この部分では、最初の15分足ローソク足の形成時間全体を特定し、その重要な価格情報を抽出するとともに、クローズ時刻を文字列から変換して計算に利用できる形式にします。プログラムはローソク足が確定するまで待機し、サーバー時間がこの確定時刻に達するか超えたタイミングで、高値、安値、始値、終値を確実に取得します。
オープニングレンジブレイクアウト(ORB)手法は、この抽出された値に依存します。プログラムは、オープニングレンジを示す高値と安値を利用して、より短い時間足でブレイクアウトの状況を監視します。視覚的な確認のために、チャート上にはこの高値と安値に線も描画されます。取引を開始する前に、EAが正しいレベルを追跡していることを確認でき、オープニングレンジを明確に把握することができます。
また、開始時刻は単一の変数として扱われるため、プログラムは毎日正確な市場開始時刻に縦線を描画します。その後、M15ローソク足が完全に確定するのを待ってから高値と安値を使用することで、データの正確性を保証し、配列外参照などのエラーを防ぎます。
5分足チャートを用いたオープニングレンジブレイクアウトの検出
最初の高値と安値が特定された後、プログラムは次にM5(5分足)で、レンジの上下を突破する動きを確認します。M15の高値を上抜けて確定した場合は買いの可能性が示され、安値を下抜けて確定した場合は売りの可能性が示されます。下位時間足での分析により、タイムリーかつ正確なポジション参入が可能になります。
例:double m5_high[]; double m5_low[]; double m5_close[]; double m5_open[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- ArraySetAsSeries(m5_high,true); ArraySetAsSeries(m5_low,true); ArraySetAsSeries(m5_close,true); ArraySetAsSeries(m5_open,true); //--- return(INIT_SUCCEEDED); }
if(TimeCurrent() >= open_time_bar_close) { CopyHigh(_Symbol,PERIOD_M15,open_time,1,m15_high); CopyLow(_Symbol,PERIOD_M15,open_time,1,m15_low); CopyClose(_Symbol,PERIOD_M15,open_time,1,m15_close); CopyOpen(_Symbol,PERIOD_M15,open_time,1,m15_open); ObjectCreate(chart_id,"High",OBJ_TREND,0,open_time,m15_high[0],TimeCurrent(),m15_high[0]); ObjectSetInteger(chart_id,"High",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"High",OBJPROP_WIDTH,2); ObjectCreate(chart_id,"Low",OBJ_TREND,0,open_time,m15_low[0],TimeCurrent(),m15_low[0]); ObjectSetInteger(chart_id,"Low",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"Low",OBJPROP_WIDTH,2); } CopyHigh(_Symbol,PERIOD_M5,1,5,m5_high); CopyLow(_Symbol,PERIOD_M5,1,5,m5_low); CopyClose(_Symbol,PERIOD_M5,1,5,m5_close); CopyOpen(_Symbol,PERIOD_M5,1,5,m5_open); if(TimeCurrent() >= open_time_bar_close && m5_close[0] > m15_high[0] && m5_close[1] < m15_high[0]) { //BUY } if(TimeCurrent() >= open_time_bar_close && m5_close[0] < m15_low[0] && m5_close[1] > m15_low[0]) { //SELL }
説明
プログラムのこの部分では、4つの配列が宣言されます。M5ローソク足の高値、安値、終値、始値がそれぞれの配列に格納されます。最新のローソク足はインデックス0で表され、過去のローソク足はインデックスが大きくなるにつれて古いデータを示します。各配列には、インデックスを指定することで取得できる価格データの系列が含まれます。
これらすべての配列は、初期化コード内でArraySetAsSeries()関数を用いて時系列として設定されます。こうすることで、最新のデータが常にインデックス0に表示され、古いデータはインデックスが大きくなる順に並ぶようになります。これは、MetaTrader 5がチャートデータを時系列として配置する方法(最新のバーが先頭に来る)と整合するため、非常に重要です。
このプログラムは、チャートから最新の5分足ローソク足の高値・安値・始値・終値を抽出します。条件に従い、現在形成中のローソク足の前に形成されたローソク足から、過去に確定した直近5本の5分足データを取得します。このデータを配列に格納することで、EAは価格の動きを追跡し、最初の15分足レンジの上下を突破するブレイクアウトが発生したかどうかを判定できます。
最初の15分足が確定した後、このロジックはブレイクアウトの条件を確認します。最新の短期足が15分足オープニングレンジの高値を上抜けて確定し、直前の5分足ローソク足がまだ高値を下回っていた場合、これは強気ブレイクアウトの成立を示します。価格が上方向に突破したことが確認されます。同様に、最新の5分足が15分足レンジの安値を下回って確定し、前のローソク足がまだ安値を上回っていた場合、弱気ブレイクアウトと判定されます。これは下方向のブレイクが確認され、売りの機会が示唆されることになります。
さらに、ローソク足の向き(陽線か陰線か)を確認することで、判定の信頼性が高まります。買いシグナルを発生させる際は、最新のローソク足が陽線であり、始値より終値が高く、かつレンジの高値を突破して確定していることを確認します。これにより、一時的な価格上昇による偽ブレイクアウトを排除できます。同様に、売りシグナルの場合は、最新のローソク足が陰線であり、始値より終値が低く、レンジの安値を下回って確定していることを確認します。これにより、ブレイクアウトが実際に勢いを伴っていることが保証され、ポジション参入の根拠となります。
この判定方法は、より一般的な始値ベースの方法とは異なり、現在のローソク足の始値ではなく前のローソク足の終値を参照する点が特徴です。終値ベースのチェックは、価格が2本の確定ローソク足の間で実際にオープニングレンジを突破したかを確認できる利点があります。前のローソク足がレンジ内にあり、最新のローソク足がレンジを上抜けて確定した場合、それが真のブレイクアウトであることを示しています。
一方、ギャップや急激な価格上昇が発生した場合、ローソク足の始値だけを参照すると誤解を招く可能性があります。ローソク足の始値がレンジを上回っていたとしても、価格がまだレンジ内で推移している場合や、異常なティックによって始値が変化した場合には誤ったシグナルが生成されることがあります。こうした問題は前のローソク足の終値を用いることで回避でき、特に値動きが不規則な市場でも、真のブレイクアウトを正確に特定することが可能になります。

取引の実行
次のステップは、作成したブレイクアウトのロジックに基づいて、プログラムに取引を実行させることです。EAが15分足レンジの上抜けまたは下抜けによる有効なブレイクアウトを検知した場合、それに応じて自動的に買いまたは売りのポジションを建てます。
例:#include <Trade/Trade.mqh> CTrade trade; int MagicNumber = 533930; // Unique Number input double RRR= 2; // RRR input double lot_size = 0.2;
double ask_price; double take_profit; datetime lastTradeBarTime = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- ArraySetAsSeries(m5_high,true); ArraySetAsSeries(m5_low,true); ArraySetAsSeries(m5_close,true); ArraySetAsSeries(m5_open,true); trade.SetExpertMagicNumber(MagicNumber); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- open_time = StringToTime(open_time_string); Comment("OPEN TIME: ",open_time); ObjectCreate(chart_id,"OPEN TIME",OBJ_VLINE,0,open_time,0); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_STYLE,STYLE_DASH); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_WIDTH,2); open_time_bar_close = StringToTime(open_time_bar_close_string); if(TimeCurrent() >= open_time_bar_close) { CopyHigh(_Symbol,PERIOD_M15,open_time,1,m15_high); CopyLow(_Symbol,PERIOD_M15,open_time,1,m15_low); CopyClose(_Symbol,PERIOD_M15,open_time,1,m15_close); CopyOpen(_Symbol,PERIOD_M15,open_time,1,m15_open); ObjectCreate(chart_id,"High",OBJ_TREND,0,open_time,m15_high[0],TimeCurrent(),m15_high[0]); ObjectSetInteger(chart_id,"High",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"High",OBJPROP_WIDTH,2); ObjectCreate(chart_id,"Low",OBJ_TREND,0,open_time,m15_low[0],TimeCurrent(),m15_low[0]); ObjectSetInteger(chart_id,"Low",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"Low",OBJPROP_WIDTH,2); } CopyHigh(_Symbol,PERIOD_M5,1,5,m5_high); CopyLow(_Symbol,PERIOD_M5,1,5,m5_low); CopyClose(_Symbol,PERIOD_M5,1,5,m5_close); CopyOpen(_Symbol,PERIOD_M5,1,5,m5_open); datetime currentBarTime = iTime(_Symbol, PERIOD_M5, 0); ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK); if(TimeCurrent() >= open_time_bar_close && m5_close[0] > m15_high[0] && m5_close[1] < m15_high[0] && m5_close[0] > m5_open[0] && currentBarTime != lastTradeBarTime) { //BUY take_profit = MathAbs(ask_price + ((ask_price - m15_low[0]) * RRR)); trade.Buy(lot_size,_Symbol,ask_price,m15_low[0],take_profit); lastTradeBarTime = currentBarTime; } if(TimeCurrent() >= open_time_bar_close && m5_close[0] < m15_low[0] && m5_close[1] > m15_low[0] && m5_close[0] < m5_open[0] && currentBarTime != lastTradeBarTime) { //SELL take_profit = MathAbs(ask_price - ((m15_high[0] - ask_price) * RRR)); trade.Sell(lot_size,_Symbol,ask_price,m15_high[0],take_profit); lastTradeBarTime = currentBarTime; } }

説明
ソフトウェアは、取引ライブラリをインポートすることで、ポジションを建てる、変更する、決済するなどの組み込み取引ルーチンを利用できます。取引に関するすべての操作は、取引オブジェクトによって管理されます。MagicNumberは各EAが自分の取引を識別し、管理するための固有の番号であり、複数の自動売買システムが同じ口座で稼働していても、自身の取引を区別することができます。
ユーザーが設定するパラメータとしては、lot_sizeとRRR(リスクリワードレシオ)があります。lot_sizeは取引量を調整し、RRRはTPがSLの何倍になるかを設定します。現在のAsk価格、計算されたTP、直近取引のローソク足時刻を記録することで、同一ローソク足で複数回取引をおこなわないように、変数ask_price、take_profit、lastTradeBarTimeが取引管理に役立ちます。
EAが建てる各取引に固有の識別番号(MagicNumber)を確実に付与するための命令も使用されます。一方の変数は直近の市場買値を記録し、もう一方の変数は現在ローソク足の開始時刻を取得します。しかし、1セッション内でEAが複数のブレイクアウト取引をおこなう場合、不規則な市場ではリスクが増大します。複数のブレイクアウト取引をおこなうと、通常ストップロスがエントリーポイントから離れているため、リスクが大きくなります。そのため、EAは1日の最初の有効なブレイクアウト取引のみを実行するように設定するのが理想的です。これにより、リスクを抑え、過剰取引を防ぐことができます。
そのためには、プログラムを修正して、最初のブレイクアウトを検知した後は、そのセッション中は追加の取引を受け付けないようにする必要があります。こうすることで、EAは規律を維持し、1日の最初のブレイクアウト機会にのみ集中し、後で発生する可能性のある偽の動きに反応しないようにできます。
例:int open_to_current; bool isBreakout = false;
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- open_time = StringToTime(open_time_string); ObjectCreate(chart_id,"OPEN TIME",OBJ_VLINE,0,open_time,0); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_STYLE,STYLE_DASH); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_WIDTH,2); open_time_bar_close = StringToTime(open_time_bar_close_string); if(TimeCurrent() >= open_time_bar_close) { CopyHigh(_Symbol,PERIOD_M15,open_time,1,m15_high); CopyLow(_Symbol,PERIOD_M15,open_time,1,m15_low); CopyClose(_Symbol,PERIOD_M15,open_time,1,m15_close); CopyOpen(_Symbol,PERIOD_M15,open_time,1,m15_open); ObjectCreate(chart_id,"High",OBJ_TREND,0,open_time,m15_high[0],TimeCurrent(),m15_high[0]); ObjectSetInteger(chart_id,"High",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"High",OBJPROP_WIDTH,2); ObjectCreate(chart_id,"Low",OBJ_TREND,0,open_time,m15_low[0],TimeCurrent(),m15_low[0]); ObjectSetInteger(chart_id,"Low",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"Low",OBJPROP_WIDTH,2); open_to_current = Bars(_Symbol,PERIOD_M5,open_time_bar_close,TimeCurrent()); CopyHigh(_Symbol,PERIOD_M5,1,open_to_current,m5_high); CopyLow(_Symbol,PERIOD_M5,1,open_to_current,m5_low); CopyClose(_Symbol,PERIOD_M5,1,open_to_current,m5_close); CopyOpen(_Symbol,PERIOD_M5,1,open_to_current,m5_open); } datetime currentBarTime = iTime(_Symbol, PERIOD_M5, 0); ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK); if(TimeCurrent() >= open_time_bar_close && m5_close[0] > m15_high[0] && m5_close[1] < m15_high[0] && m5_close[0] > m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false) { //BUY take_profit = MathAbs(ask_price + ((ask_price - m15_low[0]) * RRR)); trade.Buy(lot_size,_Symbol,ask_price,m15_low[0],take_profit); lastTradeBarTime = currentBarTime; } if(TimeCurrent() >= open_time_bar_close && m5_close[0] < m15_low[0] && m5_close[1] > m15_low[0] && m5_close[0] < m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false) { //SELL take_profit = MathAbs(ask_price - ((m15_high[0] - ask_price) * RRR)); trade.Sell(lot_size,_Symbol,ask_price,m15_high[0],take_profit); lastTradeBarTime = currentBarTime; } if(TimeCurrent() >= open_time_bar_close) { for(int i = 0; i < open_to_current; i++) { if(i + 1 < open_to_current) { if((m5_close[i] > m15_high[0] && m5_close[i + 1] < m15_high[0]) || (m5_close[i] < m15_low[0] && m5_close[i + 1] > m15_low[0])) { isBreakout = true; break; } } } } if(TimeCurrent() < open_time) { isBreakout = false; } Comment(isBreakout); }
出力

説明
2つの新しい変数が追加されました。1つは、15分足ローソク足の確定以降に形成されたローソク足の本数を管理するための変数で、もう1つはブレイクアウトの発生を監視するための変数です。プログラムは、動的な変数を用いて現在時刻までのローソク足データを常にチェックし、ブレイクアウトを検出します。
EAが1セッションにつき1回だけ取引を実行するようにするため、isBreakout変数はフラグとして機能します。最初にfalseに設定されている場合、EAはブレイクアウト取引を実行できます。強気か弱気のいずれの正当なブレイクアウトが検出されると、ループ内のコードでisBreakoutをtrueに設定し、それ以降の取引を停止します。これにより、EAが1日に1回のみブレイクアウト取引を行うというルールを遵守できます。
オープニングレンジ確定以降から現在までのすべてのローソク足データは、動的変数を用いてプログラムによって収集されます。この改良により、新しいローソク足が形成されるたびに連続的に監視できるため、ブレイクアウト検出の精度と効率が向上します。
さらに、「if(TimeCurrent() < open_time)」の部分では、新しい取引日が始まる前にisBreakoutをfalseにリセットし、EAが翌日のブレイクアウトを検出して取引できる準備を整えます。
なお、1日の最初のブレイクアウト取引のみを実行する設定ではありますが、ソフトウェアに1回のブレイクアウト時に複数のポジションを建てる機能を持たせることも可能です。この考え方は「複数エントリー」と呼ばれます。ブレイクアウトが発生した際に、EAは1回だけでなく、ユーザーが指定した数の取引を同時に建てることができます。
例:
#include <Trade/Trade.mqh> CTrade trade; int MagicNumber = 533930; // Unique Number input double RRR= 2; // RRR input double lot_size = 0.2; input int pos_num = 2; // Number of Positions to Open
if(TimeCurrent() >= open_time_bar_close && TimeCurrent() <= close_time && m5_close[0] > m15_high[0] && m5_close[1] < m15_high[0] && m5_close[0] > m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false) { //BUY take_profit = MathAbs(ask_price + ((ask_price - m15_low[0]) * RRR)); for(int i = 0; i < pos_num; i++) // open 3 trades { trade.Buy(lot_size,_Symbol,ask_price,m15_low[0],take_profit); } lastTradeBarTime = currentBarTime; } if(TimeCurrent() >= open_time_bar_close && m5_close[0] < m15_low[0] && m5_close[1] > m15_low[0] && m5_close[0] < m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false) { //SELL take_profit = MathAbs(ask_price - ((m15_high[0] - ask_price) * RRR)); for(int i = 0; i < pos_num; i++) // open 3 trades { trade.Sell(lot_size,_Symbol,ask_price,m15_high[0],take_profit); } lastTradeBarTime = currentBarTime; } if(TimeCurrent() >= open_time_bar_close) { for(int i = 0; i < open_to_current; i++) { if(i + 1 < open_to_current) { if((m5_close[i] > m15_high[0] && m5_close[i + 1] < m15_high[0]) || (m5_close[i] < m15_low[0] && m5_close[i + 1] > m15_low[0])) { isBreakout = true; break; } } } }
説明
有効なブレイクアウト時にプログラムが建てるポジション数をトレーダーが選択できるよう、ユーザー定義の入力項目が追加されました。EAを起動する前に、この値は入力設定で変更可能です。たとえば、ブレイクアウト条件が成立した場合に、この値が2に設定されていれば、EAは自動的に2つの別々のポジションを建てます。
プログラムはループを用いて、ユーザーが指定した数だけ取引を建てます。入力値に応じてループが繰り返され、毎回同じエントリー条件、SL、TPのパラメータで新しい取引が実行されます。たとえば、正当なブレイクアウトが確認された後、入力値が2に設定されていれば、ソフトウェアは連続して2つの取引をおこないます。
さらに、EAはサーバー時間で15:00以前にのみ取引をおこなうように設定する必要があります。これにより、取引規律が保たれ、ボラティリティや流動性が低下している時間帯に不必要な取引をおこなわないようにできます。時計が15時を指した時点で、それ以降の取引は停止されるべきです。また、同時にEAはその時点でまだ保有中のポジションをすべて決済する必要があります。これにより、取引戦略の一貫性が維持され、すべての取引がアクティブなセッション内で完了するため、不要なリスクが軽減されます。
例:
open_time = StringToTime(open_time_string); close_time = StringToTime(close_time_string);
ObjectCreate(chart_id,"OPEN TIME",OBJ_VLINE,0,open_time,0); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_COLOR,clrBlue); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_STYLE,STYLE_DASH); ObjectSetInteger(chart_id,"OPEN TIME",OBJPROP_WIDTH,2); ObjectCreate(chart_id,"CLOSE TIME",OBJ_VLINE,0,close_time,0); ObjectSetInteger(chart_id,"CLOSE TIME",OBJPROP_COLOR,clrRed); ObjectSetInteger(chart_id,"CLOSE TIME",OBJPROP_STYLE,STYLE_DASH); ObjectSetInteger(chart_id,"CLOSE TIME",OBJPROP_WIDTH,2);
if(TimeCurrent() >= open_time_bar_close && TimeCurrent() <= close_time && m5_close[0] > m15_high[0] && m5_close[1] < m15_high[0] && m5_close[0] > m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false) { //BUY take_profit = MathAbs(ask_price + ((ask_price - m15_low[0]) * RRR)); for(int i = 0; i < pos_num; i++) // open 3 trades { trade.Buy(lot_size,_Symbol,ask_price,m15_low[0],take_profit); } lastTradeBarTime = currentBarTime; } if(TimeCurrent() >= open_time_bar_close && TimeCurrent() <= close_time && m5_close[0] < m15_low[0] && m5_close[1] > m15_low[0] && m5_close[0] < m5_open[0] && currentBarTime != lastTradeBarTime && isBreakout == false) { //SELL take_profit = MathAbs(ask_price - ((m15_high[0] - ask_price) * RRR)); for(int i = 0; i < pos_num; i++) // open 3 trades { trade.Sell(lot_size,_Symbol,ask_price,m15_high[0],take_profit); } lastTradeBarTime = currentBarTime; } if(TimeCurrent() >= open_time_bar_close) { for(int i = 0; i < open_to_current; i++) { if(i + 1 < open_to_current) { if((m5_close[i] > m15_high[0] && m5_close[i + 1] < m15_high[0]) || (m5_close[i] < m15_low[0] && m5_close[i + 1] > m15_low[0])) { isBreakout = true; break; } } } } if(TimeCurrent() < open_time) { isBreakout = false; } // Comment(isBreakout); for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { if(TimeCurrent() >= close_time) { // Close the position trade.PositionClose(ticket); } } }
出力

説明
EAが新規取引を受け付けるのを停止する目標の取引終了時刻は、まずプログラム内で文字列として定義されます。その後、この時刻を保持できる変数が生成され、システムが認識し、実行時の時間比較に利用できる形式に変換されます。 プログラムは指定された終了時刻の文字列を実際の時間値に変換し、現在の市場時間と照合できるようにします。さらに、チャート上には赤の破線で縦線が描画され、取引を停止すべき正確な時刻を視覚的に示します。これにより、トレーダーはチャート上で取引終了のポイントを簡単に確認できます。
この設定により、EAは指定された終了時刻以前にのみ新規取引をおこなうようになります。現在の時間が指定時刻を超えた場合、プログラムは自動的に新規ポジションの建てる動作を停止し、許可された取引時間外での取引を防ぎます。 また、すべてのポジションも確認されます。EAはそれぞれのポジションの識別番号と通貨ペアを比較し、このEAが管理する取引であることを確認したうえで、現在時刻が指定の終了時刻に達した場合、すべての保有ポジションを自動的に決済します。
注意:
本記事で紹介している戦略は、あくまでプロジェクトベースでMQL5を学ぶことを目的としたものであり、実際の取引で利益を保証する手法ではありません。
結論
本記事では、MQL5を用いたオープニングレンジブレイクアウト(ORB)手法の自動化について解説しました。複数の時間足にまたがるブレイクアウトを認識し、あらかじめ設定したルールに従って自動的に取引をおこなうEAを作成しました。さらに、サーバー時間15:00以降は取引を停止する専用の取引ウィンドウを設け、ブレイクアウトごとのエントリー回数を制限する機能も実装しました。取引の規律を維持し、リスク管理を効率的に行うため、EAは取引期間終了後にすべてのポジションを自動的に決済します。このような手順的アプローチにより、より高度で信頼性の高い自動売買システムの構築が可能になります。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/19886
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5における二変量コピュラ(第1回):依存関係モデリングのための正規コピュラおよびtコピュラの実装
プライスアクション分析ツールキットの開発(第45回):MQL5で動的水準分析パネルを作成する
MQL5入門(第24回):チャートオブジェクトで取引するEAの構築
MQL5入門(第22回):5-0ハーモニックパターンを用いたエキスパートアドバイザーの構築
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
あなたのORB取引のアイデアを共有していただきありがとうございます、しかし、あなたの売り/買いの条件があなたの定義と逆になっているか、または反しているようであることを確認してください(私ももう一度確認します)。
ORBトレードのアイデアをシェアしていただきありがとうございます。