MQL5を用いた保ち合い(もちあい)相場ブレイクアウト戦略のエキスパートアドバイザー(EA)の作成

これは、Allan Munene Mutiiria氏のDeveloping an Expert Advisor (EA) based on the Consolidation Range Breakout strategy in MQL5を巧訳したものです。著作権はリンク先に由来します。

はじめに

本記事では、MetaTrader 5(MT5)のプログラミング言語であるMetaQuotes Language 5(MQL5)を使用した、 保ち合い(もちあい)相場ブレイクアウト戦略に基づくエキスパートアドバイザー(EA)の開発について解説する。 変化の速い金融取引の世界では、市場のパターンや動きを活かした戦略が成功の鍵となるが、 保ち合い相場ブレイクアウトもその一つであり、市場の調整期間を特定し、その後のブレイクアウトを取引することに焦点を当てている。この戦略は、ボラティリティの低い期間に続く大きな価格変動を捉えるのに特に有効である。以下のトピックを通じて、保ち合い相場ブレイクアウト戦略によるエキスパートアドバイザーを作成する。

  1. 戦略の概要
  2. 戦略の青写真
  3. MetaQuotes Language 5 (MQL5) での実装
  4. バックテストの結果
  5. 結論

この記事を読み終える頃には、MQL5における「レンジブレイクアウト戦略」に基づく強固なEAの開発と実装方法について包括的な理解が得られるでしょう。私たちは、MetaQuotes Language 5 (MQL5) をベースの統合開発環境 (IDE) コーディング環境として広く使用し、MetaTrader 5 (MT5) トレーディングターミナル上でファイルを実行する。したがって、上記のバージョンをインストールしておくことが非常に重要である。それでは始めよう。

戦略の概要

レンジブレイクアウト戦略を簡単に理解するために、いくつかの項目に分けて説明しよう。

  • 保ち合いレンジの説明

保ち合いレンジとは、取引レンジとも呼ばれるが、金融商品の価格が明確なレンジ内で上下に大きく変動することなく、水平方向に推移する期間を指す。この期間はボラティリティが低く、明確なサポートレベル(下限)とレジスタンスレベル(上限)の間で価格が変動する。トレーダーは、この局面をよく利用して、保ち合いが終了した後に価格が一方に大きく動く可能性のある潜在的なブレイクアウトポイントを予測する。

  • 戦略の仕組み

保ち合いレンジブレイクアウト戦略は、保ち合い期間中の価格の予測可能な動きを利用して、ブレイクアウトを特定し取引を行う。その仕組みは以下の通りである。

レンジを特定する:最初のステップは、最近の価格変動を調査してレンジを検出することである。これは、特定のバー数(ローソク足)における最高値と最安値を特定し、レンジの上限と下限を定義することである。通常、これらはレンジの抵抗線および支持線として機能する。この場合の時間軸の選択は固定的なものではなく、特定のチャートを識別プロセスに使用できるため、自分の取引スタイルに合ったチャートの時間軸を選択する必要がある。

ブレイクアウトの監視: レンジが形成されると、この戦略では、そのレンジの上限または下限を破るような価格の動きを監視する。 抵抗線または支持線を上回る価格で取引が終了した場合に、ブレイクアウトが発生する。 他のトレーダーは、レンジを破った同じローソク足でスキャルピングを行う。つまり、価格がレンジの最高値または最安値を下回るか上回った時点で、ブレイクアウトが発生したと見なす。

ブレイクアウトの取引:ブレイクアウトを検知すると、この戦略はブレイクアウトの方向に取引を開始する。価格が抵抗レベルを上回ってブレイクアウトした場合、買い注文が発注される。逆に、価格がサポートレベルを下回った場合は、売り注文が発注される。 また、一部のトレーダーは、反発を待つことを検討する。つまり、ブレイクアウト後、さらなる確認のために、価格がレンジを再訪し、再び最初のブレイクアウトと同じ方向にブレイクアウトするのを待ってから、市場に参入する。ここでは、反発(リトレースメント)の設定は考慮しない。

  • 戦略の実行
  • レンジブレイクアウト戦略の実行には、いくつかのステップが含まれる。
  • レンジパラメータの定義: レンジを特定するために分析するバーの数を決定し、ブレイクアウトの条件を設定する。 バーの範囲と価格のターゲットレンジが決定され、設定される。 例えば、レンジが有効であるためには、700ポイントまたは70ピップの価格帯内に少なくとも10本のバーが必要である。
  • 検出ロジックの開発:過去の価格データをスキャンし、指定された範囲内で最高値と最安値を特定し、チャート上にこれらのレベルをプロットするコードを記述する。コードは、保ち合いレンジが有効であると見なされるために満たさなければならない条件を明確に記述し、想定される条件はあいまいさを避けるために明確に概説しなければならない。
  • リアルタイムの価格データのモニタリング:価格データの受信を継続的にモニタリングし、ブレイクアウトが発生した場合は直ちに検知する。ティックごとにモニタリングを行い、必要がない場合は、新しいローソク足が生成されるごとにモニタリングを行う。
  • 取引の実行:ブレイクアウトが検出された際に、適切な損切りおよび利益確定レベルを設定してリスクを管理しながら、買いまたは売り注文を出すための取引実行ロジックを実装する。
  • 最適化とテスト:実際の取引に導入する前に、パラメータを最適化し、その有効性を確認するために、過去のデータを使用して戦略のバックテストを行う。これにより、最適なパラメータを特定し、システムを強化・改善するために改善またはフィルタリングが必要な主要機能を特定することができる。

以下の手順に従うことで、保ち合い相場ブレイクアウト戦略を活用し、市場の状況を効率的に利用する強力なツールを作成することができる。要点をまとめると、この戦略を構築するために必要なパラメータと、一般的に求められる要件の概要は以下の通りである。

戦略の概要図

これまでお伝えしてきた考え方をわかりやすく理解するために、戦略の概要図として視覚化してみよう。

MetaQuotes Language 5 (MQL5) による実装
保ち合い相場ブレイクアウト戦略を構築するための基本的な手順とアプローチを学んだ後は、その理論を自動化し、MetaTrader 5 (MT5) 用に MetaQuotes Language 5 (MQL5) でエキスパートアドバイザー(EA)を作成しましょう。

エキスパートアドバイザー(EA)を作成するには、MetaTrader 5 ターミナルで「ツール」タブをクリックし、MetaQuotes Language Editor を選択するか、キーボードの F4 を押してください。別の方法として、ツールバーにある IDE(統合開発環境)アイコンをクリックしても同じエディタが開きます。これにより、MetaQuotes Language Editor 環境が立ち上がり、トレーディングロボット、テクニカルインジケーター、スクリプト、関数ライブラリの作成が可能になります。

 

名前セクションでは、作成するエキスパートアドバイザー(EA)のファイル名を指定します。指定したフォルダが存在しない場合には、名前の前にバックスラッシュを使用してフォルダを作成または指定することができます。例えば、デフォルトでは「Experts\」と設定されています。この場合、EAは「Experts」フォルダ内に作成され、そこで見つけることができます。その他のセクションはほぼ直感的に操作できますが、ウィザードの下部にあるリンクを参照することで、プロセスを正確に進める方法を確認できます。

EA NAME

希望するエキスパートアドバイザーのファイル名を入力したら、「次へ」をクリックし、さらに「次へ」をクリックし、「完了」をクリックする。 以上の作業が完了したら、ストラテジーのコーディングとプログラミングの準備が整ったことになる。

まず、ソースコードの冒頭で#includeを使用してトレードのインスタンスを組み込む。これにより、トレードオブジェクトを作成するために使用するCTradeクラスにアクセスできるようになる。これは、トレードを開始するために必要不可欠である。

#include <Trade/Trade.mqh> // 取引ライブラリをインクルードする
CTrade obj_Trade; // CTradeクラスのインスタンスを作成する

プリプロセッサによる処理について

プリプロセッサは、#include <Trade/Trade.mqh> という行をファイル Trade.mqh の内容に置き換えるものである。角括弧 < > は、Trade.mqh ファイルが標準ディレクトリ(通常は terminal_installation_directory\MQL5\Include)から取得されることを示しており、現在のディレクトリは検索対象に含まれない。

この行はプログラム内のどこにでも配置できるが、ソースコードの構造を整理し、参照を簡単にするために、すべてのインクルードをソースコードの冒頭に配置するのが一般的である。

CTrade クラスの obj_Trade オブジェクトを宣言することで、そのクラスに含まれるメソッドに容易にアクセスすることが可能となる。これは、MQL5 開発者による設計によって実現されているものである。

CTRADE CLASS

チャート上にレンジをグラフィカルに描画する必要があるため、その名前が必要である。同じ長方形レンジオブジェクトを使用するだけで十分であり、可視化には1つのオブジェクトを用いる。一度描画した後は再描画を行うことなく、その設定を更新するだけで済む。これを実現するため、容易に呼び出し、即座に再利用できる静的な名前を以下のように定義する。

#define rangeNAME 「CONSOLIDATION RANGE」 // 保ち合いレンジの名前を定義する

#define キーワードを使用し、「rangeNAME」という名前のマクロを「CONSOLIDATION RANGE」という値で定義する。これにより、保ち合い相場の名前を簡単に管理でき、レベルを作成するたびに名前を繰り返し入力する必要がなくなる。これによって、作業時間を大幅に節約できると同時に、名前を誤って入力する可能性を低減することができる。マクロとは、基本的にコンパイル時にテキストを置き換えるために使用されるものである。

次に、描画される長方形の座標を保存する必要がある。この座標は、(x, y) 形式で表される二次元(2D)の座標であり、x1, y1 と x2, y2 という形式で、それぞれの最初と2番目の位置を明確に特定するために使用される。価格チャートにおいて、x軸は日付と時間のスケールを表し、y軸は価格スケールを表している。理解と参照を容易にするために、視覚的な説明を示す。

提示された画像から、長方形オブジェクトをプロットするために座標が必要な理由が明確になった。この座標を毎回更新するたびに再宣言する必要がないように、レンジ座標を保存するために使用されるロジックを以下に示す。

datetime TIME1_X1, TIME2_Y2; // レンジの開始時刻と終了時刻を保持するための datetime 型の変数を宣言する。
double PRICE1_Y1, PRICE2_Y2; // レンジの高値と安値を保持するための double 型の変数を宣言する。

ここでは、datetime 型のデータ型の変数を2つ宣言する。この方法は「単一データ型宣言」と呼ばれ、同じデータ型の複数の変数を宣言するものである。これは、同じ型の変数を1つの文で宣言する簡略化された方法であり、コードの行数を減らし、関連する変数をグループ化することで、それらが同じ型であり、類似の目的で使用される可能性があることを理解しやすくするため、簡潔さを保ちながら一貫性を維持できる。以下のように記述することも可能である。

datetime TIME1_X1;

datetime TIME2_Y2;

「TIME1_X1」変数は、x軸に沿った第1の座標の時間値を保持し、「TIME2_Y2」はやはりx軸に沿った第2の座標の時間値を保持する。同様に、価格座標は以下のように宣言する。

double PRICE1_Y1, PRICE2_Y2; // レンジの高値と安値を保持するための double 型の変数を宣言する。

低ボラティリティによって形成される保ち合い相場の確立を評価するために、新しいバーが生成されるたびに市場を継続的にスキャンする必要がある。そのため、レンジが存在する場合と、価格がそのレンジ内にある場合を示すフラグを保存するための2つの変数が必要となる。

bool isRangeExist = false; // レンジが存在するかを確認するためのフラグ

bool isInRange = false; // 現在レンジ内にいるかを確認するためのフラグ

ここでは、isRangeExistisInRange という2つのブール型変数を定義し、初期化する。isRangeExist 変数は、保ち合い相場が特定され、チャートに描画されたかどうかを示すフラグとして機能する。この変数は、開始時点ではまだレンジが確立されていないため、false に初期化する。同様に、isInRange 変数も false に初期化され、現在の市場価格が特定された保ち合い相場内にあるかどうかを判断するために使用される。これらのフラグは、エキスパートアドバイザー(EA)のロジックにおいて重要であり、レンジの検出とブレイクアウト監視プロセスの状態を管理し、適切な条件が満たされた場合にのみアクションが実行されるようにする。

また、グローバルスコープ内で、保ち合い相場として考慮する最小のローソク足の本数と、ポイント単位でのレンジサイズを定義する必要がある。これはロジックの解説で述べたとおり、保ち合い相場の妥当性を維持し、意味のある保ち合い相場を確保するために重要なパラメータである。

int rangeBars = 10; // レンジとして考慮するローソク足の本数
int rangeSizePoints = 400; // ポイント単位での最大レンジサイズ

ここでも、2つの整数型変数「rangeBars」と「rangeSizePoints」を宣言し、初期化する。変数「rangeBars」は10に設定されており、保ち合い相場を特定するために分析するローソク足(またはバー)の本数を指定する。この設定では、直近10本のローソク足を遡って最高値と最安値を見つけ、それを基にレンジを定義することを意味する。

変数「rangeSizePoints」は400に設定されており、ポイント単位での保ち合い相場の最大許容サイズを定義する。この10本のローソク足内での最高値と最安値の間のレンジが400ポイントを超える場合、それは有効な保ち合い相場とは見なされない。これらのパラメータは、レンジの基準を設定し、価格データ内で意味のある保ち合い期間を特定するために重要である。

最後に、ポジションをオープンするために、ストップロスとテイクプロフィットのポイントを定義する。

double sl_points = 500.0; // ストップロスポイント
double tp_points = 500.0; // テイクプロフィットポイント

これがグローバルスコープで必要とされるすべてである。グローバルスコープとは何かについて疑問に思うかもしれないが、グローバルスコープとはプログラム内で変数や関数、その他の要素がコード全体でアクセス可能な領域のことである。グローバルスコープで宣言された変数や関数は、プログラムのどの部分からでもアクセスおよび変更が可能である。

すべての処理はOnTickイベントハンドラーで実行される。このハンドラーは純粋なプライスアクションに基づいており、コードの中心となる機能であるため、ここに大きく依存する。したがって、この関数が受け取るパラメーターについて確認していく必要がある。

//+------------------------------------------------------------------+ 
//| Expert tick function                                             | 
//+------------------------------------------------------------------+ 
void OnTick(){ 
//---    

}

すでにわかるように、これはシンプルでありながら重要な関数である。この関数は引数を受け取ることも戻り値を返すこともない。ただのvoid関数であり、何も返す必要がないことを意味している。この関数はエキスパートアドバイザー(Expert Advisor)で使用され、新しいティック、つまり特定の商品の価格が変動した際に実行される。

OnTick関数が価格の変動ごとに実行されることがわかった以上、1ティックごとではなく、バーごとに1回だけコードを実行するような制御ロジックを定義する必要がある。これにより、不必要なコードの実行を避け、デバイスメモリを節約できる。このようなロジックは、保ち合い相場のセットアップを探す際に必要となる。毎ティックごとにセットアップを探す必要はなく、同じローソク足内であれば常に同じ結果が得られるからである。以下にそのロジックを示す:

int currBars = iBars(_Symbol, _Period); // 現在のバーの数を取得
static int prevBars = currBars; // 前回のバーの数を保存する静的変数
static bool isNewBar = false; // 新しいバーが出現したかを確認するための静的フラグ
if (prevBars == currBars){isNewBar = false;} // バーの数が変化していないかどうかを確認する。バーの数が変化していない場合は、isNewBarをfalseに設定
else {isNewBar = true; prevBars = currBars;} // バーの数が変化していたら、isNewBar を true に設定し、prevBarsを現在のバーの数で更新

まず、整数型変数「currBars」を宣言し、指定された取引シンボルと期間、または一般的に「タイムフレーム」と呼ばれるものに基づいて、現在のチャート上のバーの数を計算して格納する。この処理はiBars関数を使用して行われる。この関数はシンボルと期間という2つの引数を取る。

次に、静的な整数型変数「prevBars」を宣言し、現在のバーの数で初期化する。staticキーワードを使用することで、この変数「prevBars」は関数の呼び出し間でその値を保持し、前回のティックからのバーの数を記憶することができる。

三つ目に、静的なブール型変数「isNewBar」を宣言し、falseで初期化する。この変数は、新しいバーが出現したかどうかを追跡するために使用される。

その後、条件文を使用して現在のバー数が前回のバー数と等しいかどうかを確認する。もし等しければ、新しいバーが形成されていないことを意味し、新しいバー生成のフラグをfalseに設定する。逆に、前回のバー数が現在のバー数と異なる場合、バーの数が増加したことを意味し、新しいバーが出現したことになる。したがって、新しいバー生成のフラグをtrueに設定し、前回のバー数を現在のバー数で更新する。

そして、各バーが生成されるたびに、もし保ち合い相場が存在しない場合、あらかじめ定義されたバーをスキャンし、低ボラティリティ期間の可能性を探す必要がある。

if (isRangeExist == false && isNewBar) { // 保ち合い相場が存在せず、新しいバーが出現した場合

...
}

「isRangeExist」がfalseである場合、つまりまだ保ち合い相場(レンジ)が確立されておらず、かつ「isNewBar」がtrueである場合、新しいバーが出現したことを意味する。この条件を確認することで、まだ保ち合い相場が特定されておらず、新しいバーが形成されたときのみ処理を進めることが保証される。

チャート上にプロットする四角形オブジェクトの第1点の座標を決定するには、極値(エクストリーム)としての抵抗帯の価格が必要である。それは、あらかじめ定義されたバーのスキャン範囲内で最後のバーの時間と、その範囲内の最も高いバーの価格である。

TIME1_X1 = iTime(_Symbol, _Period, rangeBars); // 範囲の開始時間を取得
int highestHigh_BarIndex = iHighest(_Symbol, _Period, MODE_HIGH, rangeBars, 1); // 範囲内で最も高い価格を持つバーのインデックスを取得
PRICE1_Y1 = iHigh(_Symbol, _Period, highestHigh_BarIndex); // 範囲内での最も高い価格を取得

まず、範囲の開始時間を設定するためにiTime関数を使用する。この関数は、指定されたシンボルと期間における特定のバーの開始時刻を返す。この関数は3つの入力パラメータ(引数)を取る。

  • _Symbol は取引シンボル(例:「AUDUSD」)を指定する。
  • _Period はタイムフレーム(例:PERIOD_M1は1分足)を指定する。
  • rangeBars は指定された期間前のバーのインデックスである。

結果はTIME1_X1に格納され、これが保ち合い相場の開始時刻を示す。

次に、指定された範囲内で最も高い高値を持つバーを特定するために、iHighest関数を使用する。この関数は、指定されたバー数内で最も高い高値を持つバーのインデックスを返す。この関数は5つの引数を取る。

  • 最初の2つのパラメータについてはすでに説明済みなので割愛する。
  • 3つ目のパラメータ**MODE_HIGH**は、最も高い高値を探すことを指定する。
  • 4つ目のパラメータ**rangeBars**は、スキャン対象のバーの数を指定する。
  • 最後のパラメータ**1**は、現在形成中のバー(インデックス0)の1つ前のバー(インデックス1)から検索を開始することを意味する。

結果として得られたインデックスはhighestHigh_BarIndexという整数型変数に格納される。

最後に、iHigh関数を使用してそのバーの最も高い価格を取得する。この関数は、特定のバーの高値を返す。

  • 最初の2つの引数は特に説明する必要はない。
  • 3つ目の引数**highestHigh_BarIndex**は、前のステップで特定されたバーのインデックスである。

高値はPRICE1_Y1に格納される。このようにして、保ち合い相場の開始点と最も高い点を定義するための変数が得られる。これらの変数は範囲をプロットし、後にブレイクアウトを検出するために重要となる。

2つ目の座標を取得するには、最初の点の座標を決定する際に使用したものと類似したアプローチを用いる。

TIME2_Y2 = iTime(_Symbol, _Period, 0); // 現在の時間を取得
int lowestLow_BarIndex = iLowest(_Symbol, _Period, MODE_LOW, rangeBars, 1); // 範囲内で最も低い価格を持つバーのインデックスを取得
PRICE2_Y2 = iLow(_Symbol, _Period, lowestLow_BarIndex); // 範囲内で最も低い価格を取得

このコードの違いとして、まず最初に、時間が現在のバーにリンクされており、これはインデックス0に位置している。次に、事前に定義されたバー範囲内で最も低いバーのインデックスを取得するために、iLowest関数を使用し、MODE_LOWを指定して最も低い安値を探すことを示している。最後に、iLow関数を使用して、その最も低いバーの価格を取得する。

保ち合い相場のポイントが取得できたので、それらの有効性を確認する必要がある。これは、冒頭で述べた有効なレンジの条件を満たしていることを保証するためであり、条件が満たされて初めて、それらのポイントがチャートに描画される。

isInRange = (PRICE1_Y1 - PRICE2_Y2) / _Point <= rangeSizePoints; // レンジサイズが許容範囲内であるかを確認

保ち合い範囲内での最高値(PRICE1_Y1)と最安値(PRICE2_Y2)の差を計算し、その値を1ポイントのサイズ(_Point)で割ることでポイント単位に変換する。たとえば、最高値が0.66777、最安値が0.66773と仮定する。この場合、差は0.66777 – 0.66773 = 0.00004となる。シンボルのポイント値を0.00001とすると、結果をポイント値で割ると0.00004 / 0.00001 = 4ポイントとなる。この値を、許容される最大レンジサイズを定義するrangeSizePointsと比較する。

最後に、有効なレンジが特定されたかどうかを確認し、特定された場合には、それをチャートに描画し、成功したことを通知する。

if (isInRange) { // レンジサイズが有効である場合
plotConsolidationRange(rangeNAME, TIME1_X1, PRICE1_Y1, TIME2_Y2, PRICE2_Y2); // 保ち合い相場を描画
isRangeExist = true; // レンジが存在するフラグをtrueに設定
Print("RANGE PLOTTED"); // レンジが描画されたことを示すメッセージを出力
}

ここでは、特定された保ち合い相場が有効であるかを、変数isInRangeを評価することで確認する。このフラグがtrueであれば、レンジサイズが許容範囲内であることを示しており、次のステップとしてチャートに保ち合い相場を描画する。

描画を行うには、plotConsolidationRange関数を呼び出す。この関数は、引数としてrangeNAMETIME1_X1PRICE1_Y1TIME2_Y2PRICE2_Y2を取り、レンジの視覚的な表現を作成する。

レンジの描画が正常に完了した後、isRangeExistフラグをtrueに設定し、有効なレンジが特定され、描画されたことを示す。また、ターミナルに「RANGE PLOTTED」というメッセージを出力し、保ち合い相場が正常に視覚化されたことをログとして記録する。

次に、保ち合い相場を描画または更新する責任を持つ関数のコードスニペットは以下のとおり:

//+------------------------------------------------------------------+
//| 保ち合い相場を描画する関数 |
//| rangeName - レンジオブジェクトの名前 |
//| time1_x1 - レンジの開始時間 |
//| price1_y1 - レンジの高値 |
//| time2_x2 - レンジの終了時間 |
//| price2_y2 - レンジの安値 |
//+------------------------------------------------------------------+
void plotConsolidationRange(string rangeName, datetime time1_x1, double price1_y1,
datetime time2_x2, double price2_y2) {
if (ObjectFind(0, rangeName) < 0) { // レンジオブジェクトが存在しない場合
ObjectCreate(0, rangeName, OBJ_RECTANGLE, 0, time1_x1, price1_y1, time2_x2, price2_y2); // レンジオブジェクトを作成
ObjectSetInteger(0, rangeName, OBJPROP_COLOR, clrBlue); // レンジの色を青に設定
ObjectSetInteger(0, rangeName, OBJPROP_FILL, true); // レンジに塗りつぶしを有効化
ObjectSetInteger(0, rangeName, OBJPROP_WIDTH, 5); // レンジの枠線の幅を設定
}
else { // レンジオブジェクトが存在する場合
ObjectSetInteger(0, rangeName, OBJPROP_TIME, 0, time1_x1); // レンジの開始時間を更新
ObjectSetDouble(0, rangeName, OBJPROP_PRICE, 0, price1_y1); // レンジの高値を更新
ObjectSetInteger(0, rangeName, OBJPROP_TIME, 1, time2_x2); // レンジの終了時間を更新
ObjectSetDouble(0, rangeName, OBJPROP_PRICE, 1, price2_y2); // レンジの安値を更新
}
ChartRedraw(0); // 変更を反映するためにチャートを再描画
}

まず、plotConsolidationRangeという名前のvoid関数を定義し、5つのパラメータ、すなわちレンジ名、最初の点の2つの座標、そして2番目の点の2つの座標を渡す。次に、条件文を使用してObjectFind関数を使い、オブジェクトが存在するかを確認する。この関数は、オブジェクトが見つからない場合に負の整数を返す。オブジェクトが見つからない場合は、OBJ_RECTANGLEとして識別されるオブジェクトを、現在の時間と指定された価格で最初と2番目の座標に基づいて作成する。

その後、オブジェクトの色、エリアの塗りつぶし、および幅を設定する。一方、オブジェクトが見つかった場合は、時間と価格を指定された値に更新し、変更を反映するためにチャートを再描画する。修飾子の値0は最初の座標を指し、1は2番目の座標を指す。

これで、特定された保ち合い相場をチャートに描画するために必要な処理はすべて整った。以下がそれを実現する完全なソースコードである:

//+------------------------------------------------------------------+
//| エキスパートのティック関数 |
//+------------------------------------------------------------------+
void OnTick(){
//---

int currBars = iBars(_Symbol, _Period); // 現在のバーの数を取得
static int prevBars = currBars; // 前回のバー数を保持する静的変数
static bool isNewBar = false; // 新しいバーが出現したかを確認する静的フラグ
if (prevBars == currBars) { isNewBar = false; } // バー数が変化していない場合、isNewBarをfalseに設定
else { isNewBar = true; prevBars = currBars; } // バー数が変化した場合、isNewBarをtrueに設定し、prevBarsを更新

if (isRangeExist == false && isNewBar) { // 保ち合い相場が存在せず、新しいバーが出現した場合
TIME1_X1 = iTime(_Symbol, _Period, rangeBars); // 範囲の開始時間を取得
int highestHigh_BarIndex = iHighest(_Symbol, _Period, MODE_HIGH, rangeBars, 1); // 範囲内で最も高い高値を持つバーのインデックスを取得
PRICE1_Y1 = iHigh(_Symbol, _Period, highestHigh_BarIndex); // 範囲内で最も高い高値を取得

TIME2_Y2 = iTime(_Symbol, _Period, 0); // 現在の時間を取得
int lowestLow_BarIndex = iLowest(_Symbol, _Period, MODE_LOW, rangeBars, 1); // 範囲内で最も低い安値を持つバーのインデックスを取得
PRICE2_Y2 = iLow(_Symbol, _Period, lowestLow_BarIndex); // 範囲内で最も低い安値を取得

isInRange = (PRICE1_Y1 - PRICE2_Y2) / _Point <= rangeSizePoints; // レンジサイズが許容範囲内か確認

if (isInRange) { // レンジサイズが有効である場合
plotConsolidationRange(rangeNAME, TIME1_X1, PRICE1_Y1, TIME2_Y2, PRICE2_Y2); // 保ち合い相場を描画
isRangeExist = true; // レンジが存在するフラグをtrueに設定
Print("RANGE PLOTTED"); // レンジが描画されたことを示すメッセージを出力
}
}

}

コンパイルした結果は次のとおりである。

このコードにより、事前に定義されたポイント内でレンジがプロットされ、ジャーナルにプロットが行われた旨の通知が記録されるのが確認できる。もしレンジを塗りつぶす必要がない場合は、塗りつぶしプロパティのフラグをfalseに設定すればよい。その場合、四角形の枠線だけが描画され、設定された幅が有効となり適用される。

以下にそのロジックを示す:

ObjectSetInteger(0, rangeName, OBJPROP_FILL, false); // レンジの塗りつぶしを無効化

これにより、以下のようなレンジが描画される:

この記事では、塗りつぶしされたレンジを使用することにします。レンジを確立できることが確認できたので、次に、レンジのブレイクアウトを監視してポジションを開くロジック、またはレンジの拡張を検出して新しい値に基づいてレンジの座標を更新するロジックを開発する必要があります。

次に、ロジック部分で説明したようにブレイクアウトのインスタンスを特定します。もしそのインスタンスが存在する場合は、それに応じてマーケットのポジションを取ります。この処理は各ティックごとに実行する必要があるため、新しいバーの制限なしで行います。まず、ポジションを開く際の条件が満たされたときに使用するAsk価格とBid価格を宣言します。この価格は最新の価格情報を取得するため、各ティックごとに更新する必要がある点に注意してください。

  double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); // 現在のAsk価格を取得し、正規化する
  double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); // 現在のBid価格を取得し、正規化する

ここでは、最新の価格を格納するためのdouble型変数を宣言し、シンボル通貨の桁数に合わせて浮動小数点数を丸めることで正確性を維持するよう正規化します。

ブレイクアウトを各ティックごとにスキャンするため、レンジが存在し、現在の価格がそのレンジ内にある場合のみプログラムを実行するようなロジックが必要です。ほとんどの場合、市場は中程度から高いボラティリティの状態にあるため、保ち合い相場(レンジ)が出現する頻度は限られています。また、レンジが特定され、チャート内に表示されている場合、価格は最終的にレンジをブレイクアウトすることになります。

これらの条件のいずれかが満たされている場合は、ブレイクアウトのチェックやレンジの更新をさらに行う必要はありません。ただ、新しいレンジが特定されるのを待つだけでよいのです。

   if (isRangeExist && isInRange){ // レンジが存在し、その範囲内にある場合
      ...
   }

ここでは、保ち合い相場が存在するかどうかをisRangeExistで確認し、現在の価格がそのレンジ内にあるかどうかをisInRangeで確認します。この両方の条件がtrueである場合、次に潜在的なブレイクアウト価格を計算する処理に進みます。

    double R_HighBreak_Prc = (PRICE2_Y2+rangeSizePoints*_Point); // 高値ブレイクアウト価格を計算する
    double R_LowBreak_Prc = (PRICE1_Y1-rangeSizePoints*_Point); // 安値ブレイクアウト価格を計算する

高値のブレイクアウト価格を求めるには、許容される最大レンジサイズ(ポイント単位)を最安値に加算します。この計算は、レンジサイズポイントにポイント値を掛け、その結果を変数PRICE2_Y2に加えることで行われます。計算結果は、R_HighBreak_Prcというdouble型の変数に格納されます。

たとえば、最安値が0.66773、レンジサイズポイントが400、ポイント値が0.00001であると仮定します。
400 × 0.00001 を計算すると0.00400となり、これを価格に加えると、0.66773 + 0.00400 = 0.67173となります。この計算結果が高値ブレイクアウト価格として格納され、アスク価格がこの値を上回った場合にブレイクアウトと判断されます。

同様に、安値のブレイクアウト価格を求めるには、許容される最大レンジサイズ(ポイント単位)を最高値から引きます。レンジサイズポイントにポイント値を掛けた結果を最高値から差し引き、その値をR_LowBreak_Prc変数に格納します。

その後、ブレイクアウトが発生しているかを確認し、該当する場合には対応するポジションを開きます。まず、アスク価格が定義された高値ブレイクアウト価格を上回り、買いの機会を示唆する場合の処理を考えます。

if (Ask > R_HighBreak_Prc){ // アスク価格が高値ブレイクアウト価格を突破した場合
   Print(「BUY NOW, ASK = 」,Ask,「, L = 」,PRICE2_Y2,「, H BREAK = 」,R_HighBreak_Prc); // 購入メッセージを表示
   isInRange = false; isRangeExist = false; // レンジフラグをリセット
   if (PositionsTotal() > 0){return;} // 関数を終了する
   obj_Trade.Buy(0.01,_Symbol,Ask,Bid-sl_points*_Point,Bid+tp_points*_Point);
   return; // 関数を終了する
}

まず、現在のAsk価格が高値ブレイクアウト価格を上回っているかどうかを確認します。この条件がtrueの場合、市場価格が保ち合い相場の上限を突破したことを示します。この場合、ターミナルにメッセージを出力してイベントを記録し、買いシグナルの文脈を提供します。メッセージには、現在のAsk価格、レンジ内の最安値、そして高値ブレイクアウト価格を含めます。

その後、isInRangeおよびisRangeExistフラグをfalseにリセットし、現在の保ち合い相場が無効であることを示し、このレンジに基づくさらなる意思決定を防ぎます。

次に、PositionsTotal関数を使用して既存のポジションがあるかを確認し、ポジションがある場合は関数を早期終了します。これにより、同時に複数のポジションを開くことを防ぎます。

もし既存のポジションがない場合、CTradeオブジェクトobj_TradeBuyメソッドを使用して買い注文を発注します。注文には、取引量、シンボル、Ask価格を指定し、ストップロス価格とテイクプロフィット価格も設定します。最後に、関数を終了してこのティック内でのコード実行を完了し、取引開始プロセスを確定させます。

市場価格が定義された安値ブレイクアウト価格を下回り、売り機会を示唆する場合には、同様の制御ロジックが採用されます。以下に、そのコードスニペットを示します。

      else if (Bid < R_LowBreak_Prc){ // ビッド価格が安値ブレイクアウト価格を更新した場合
         Print(「SELL NOW」); // 売りメッセージを表示
         isInRange = false; isRangeExist = false; // レンジフラグをリセット
         if (PositionsTotal() > 0){return;} // 関数を終了
         obj_Trade.Sell(0.01,_Symbol,Bid,Ask+sl_points*_Point,Ask-tp_points*_Point);
         return; // 関数を終了する
      }

レンジをブレイクアウトした場合(この例では高値を突破した場合)、ストップロスとテイクプロフィットを設定して買いポジションを取ります。エントリー条件や取引レベルは完全に動的であり、トレーダーのトレードスタイルに合ったものや適切と考えるものを使用できます。たとえば、エクストリーム(極値)の範囲内やリスクリワード比率に基づいたレベルを使用することも可能です。

確認のために、現在のアスク価格が0.68313、安値が0.67911であるとします。この場合、高値ブレイク価格は0.67911 + 0.00400 = 0.68311となります。数学的に計算すると、現在のアスク価格0.68313は計算された高値0.68311を上回っており、高値レンジブレイクアウト条件を満たしているため、現在のアスク価格で買いポジションが開始されます。

この時、レンジは静的で動くことはありません。つまり、四角形(レンジオブジェクト)は固定されています。このため、レンジを正しく確立しても、レンジオブジェクトが更新されることはありません。したがって、定義されたオブジェクトのレンジ価格を価格が上回った場合にレンジを更新する必要があります。

四角形に動きを与えるために、生成されたバーに基づいてレンジを常に更新するロジックを考えてみましょう。まず、現在のアスク価格が保ち合い相場内で以前に記録された最高価格を上回った場合のシナリオを検討します。この条件が満たされた場合、保ち合い相場の上限を新しい最高価格に反映するよう更新する必要があります。以下のコードスニペットでこれを実現します:

      if (Ask > PRICE1_Y1){ // アスク価格が現在の高値よりも高い場合
        PRICE1_Y1 = Ask; // 高値をアスク価格に更新する
        TIME2_Y2 = iTime(_Symbol,_Period,0); // 終了時刻を現在時刻に更新する

         Print(「UPDATED RANGE PRICE1_Y1 TO ASK, NEEDS REPLOT」); // レンジの再プロットが必要であることを示すメッセージを表示する
         plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // 保ち合いレンジを再プロットする
      }

アスク価格が以前に記録された最高価格を上回った場合、PRICE1_Y1を現在のアスク価格に設定します。同時に、レンジの終了時間TIME2_Y2を現在の時間に更新します。これは、iTime関数を使用し、ターゲットバーのインデックスとして現在のバー(インデックス0)を渡すことで取得します。

これらの調整を記録し、明確にするために、レンジが更新され、再描画が必要であることを示すメッセージをターミナルに出力します。その後、更新されたパラメータを使用してplotConsolidationRange関数を呼び出し、新しい最高価格と現在の時間を含めて、チャート上に変更を視覚的に反映させます。

現在のビッド価格が保ち合い相場内で以前に記録された最安値を下回った場合、保ち合い相場の下限を新しい最安値に更新する必要があることを示します。この場合も、同様のアプローチが採用されます。

以下にそのロジックの例を示します:

else if (Bid < PRICE2_Y2) { // 現在のビッド価格が以前の最安値を下回った場合
PRICE2_Y2 = Bid; // 最安値を現在のビッド価格に更新
TIME2_Y2 = iTime(_Symbol, _Period, 0); // 現在の時間を終了時間に更新
Print("Range updated: New Low Price: ", PRICE2_Y2); // 更新をターミナルに出力
plotConsolidationRange(rangeName, TIME1_X1, PRICE1_Y1, TIME2_Y2, PRICE2_Y2); // 更新されたレンジを描画
}

最後に、アスク価格が高値を上回ることも、ビッド価格が安値を下回ることもない場合のシナリオが考えられます。その場合でも、最新の完了したバーを含むように保ち合い相場を拡張する必要があります。以下に、その処理を行うコードスニペットを示します:

      else{
         if (isNewBar){ // 新しいバーが出現した場合
            TIME2_Y2 = iTime(_Symbol,_Period,1); // 前回のバーの時間まで終了時間を更新する
            Print(「EXTEND THE RANGE TO PREV BAR TIME」); // レンジが拡張されたことを示すメッセージを表示する

            plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // 保ち合いレンジを再プロットする
         }
      }

このコードスニペットは、価格のブレイクアウトが発生していない場合でも、保ち合い相場が最新の価格データを反映するようにするものです。これにより、レンジが最新の市場状況を常に正確に表すようになります。

「isNewBar」フラグを使用して、新しいバーが出現したかを確認します。もし新しいバーが出現していた場合、保ち合い相場の終了時間「TIME2_Y2」を、現在のバーの1つ前のバーの時間に更新します。これは、iTime関数を使用し、ターゲットバーのインデックスとして1(現在のバーの前のバー)を渡すことで取得します。

この調整を明確にし、記録するために、レンジの終了時間が前のバーの時間に拡張されたことを示すメッセージをターミナルに出力します。その後、更新されたパラメータを使用してplotConsolidationRange関数を呼び出し、新しい終了時間を含めてチャート上に変更を視覚的に反映させます。

MQL5におけるブレイクアウト戦略に基づくエキスパートアドバイザー(EA)であるConsolidation Rangeの作成に責任のある完全なソースコードは以下の通りである。


//+------------------------------------------------------------------+
//| 保ち合いレンジブレイクアウト.mq5 |
//| Copyright 2024, MetaQuotes Ltd.
//| https://www.mql5.com
//+------------------------------------------------------------------+
#property copyright 「Copyright 2024, MetaQuotes Ltd.」

#property link 「https://www.mql5.com」
#property version 「1.00」




#include <Trade/Trade.mqh> // 取引ライブラリをインクルードする
CTrade obj_Trade; // CTradeクラスのインスタンスを作成する


#define rangeNAME 「CONSOLIDATION RANGE」 // 保ち合いレンジの名前を定義する


datetime TIME1_X1, TIME2_Y2; // レンジの開始時間と終了時間を保持するdatetime変数を宣言する
double PRICE1_Y1, PRICE2_Y2; // レンジの高値と安値を保持するdouble変数を宣言する


bool isRangeExist = false; // レンジが存在するかどうかを確認するフラグ
bool isInRange = false; // 現在、レンジ内にあるかどうかを確認するフラグ


int rangeBars = 10; // 範囲を考慮するバーの数
int rangeSizePoints = 400; // ポイント単位での最大範囲サイズ


double sl_points = 500.0; // ストップロスポイント
double tp_points = 500.0; // テイクプロフィットポイント


//+------------------------------------------------------------------+
//| エキスパートアドバイザーの初期化関数                                   |
//+------------------------------------------------------------------+

int OnInit(){
//---
   // 初期化コードはここに(何も初期化しない)
//---
   return(INIT_SUCCEEDED); // 初期化成功を返す
}
//+------------------------------------------------------------------+
//| エキスパートの非初期化関数                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){
//---

   // 初期化解除コードをここに記述する(ここでは何も初期化解除しない)
}
//+------------------------------------------------------------------+
//| エキスパートアドバイザーのティック関数                                                |
//+------------------------------------------------------------------+
void OnTick(){
//---
   
   int currBars = iBars(_Symbol,_Period); // 現在のバーの数を取得する
   static int prevBars = currBars; // 以前のバーの数を保存する静的変数

   static bool isNewBar = false; // 新しいバーが出現したかどうかを確認するための静的フラグ
   if (prevBars == currBars){isNewBar = false;} // バーの数が変化していないかどうかを確認する
   else {isNewBar = true; prevBars = currBars;} // バーの数が変化していた場合、isNewBar を true に設定し、prevBars を更新する
   

   if (isRangeExist == false && isNewBar){ // レンジが存在せず、新しいバーが出現した場合
      TIME1_X1 = iTime(_Symbol,_Period,rangeBars); // レンジの開始時刻を取得
      int highestHigh_BarIndex = iHighest(_Symbol,_Period,MODE_HIGH,rangeBars,1); // レンジ内で最も高い高値のバーインデックスを取得

      PRICE1_Y1 = iHigh(_Symbol,_Period,highestHigh_BarIndex); // レンジ内の最高値を取得する
 
      TIME2_Y2 = iTime(_Symbol,_Period,0); // 現在の時刻を取得する
      int lowestLow_BarIndex = iLowest(_Symbol,_Period,MODE_LOW,rangeBars,1); // 範囲内の最安値のバーインデックスを取得する

      PRICE2_Y2 = iLow(_Symbol,_Period,lowestLow_BarIndex); // レンジ内の最安値を取得する
      
      isInRange = (PRICE1_Y1 - PRICE2_Y2)/_Point <= rangeSizePoints; // レンジのサイズが許容ポイントの範囲内にあるか確認する
      

      if (isInRange){ // レンジサイズが有効であれば
 plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // 保ち合いレンジをプロットする
 isRangeExist = true; // レンジの存在フラグを true に設定する

         Print(「RANGE PLOTTED」); // レンジがプロットされたことを示すメッセージを表示する
      }
   }
   
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); // 現在のAsk価格を取得し、正規化する
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); // 現在のBid価格を取得し、正規化する
   

   if (isRangeExist && isInRange){ // レンジが存在し、その範囲内にある場合
      double R_HighBreak_Prc = (PRICE2_Y2+rangeSizePoints*_Point); // 高値ブレイクアウト価格を計算する
      double R_LowBreak_Prc = (PRICE1_Y1-rangeSizePoints*_Point); // 安値ブレイクアウト価格を計算する

      if (Ask > R_HighBreak_Prc){ // 売値が上限ブレイクアウト価格を突破した場合
         Print(「BUY NOW, ASK = 」,Ask,「, L = 」,PRICE2_Y2,「, H BREAK = 」,R_HighBreak_Prc); // 購入メッセージを表示
         isInRange = false; isRangeExist = false; // レンジフラグをリセット

         if (PositionsTotal() > 0){return;} // 関数を終了する
         obj_Trade.Buy(0.01,_Symbol,Ask,Bid-sl_points*_Point,Bid+tp_points*_Point);
         return; // 関数を終了する
      }
      else if (Bid < R_LowBreak_Prc){ // ビッド価格が安値ブレイクアウト価格を下回った場合

         Print(「SELL NOW」); // 売りメッセージを表示
         isInRange = false; isRangeExist = false; // レンジフラグをリセット
         if (PositionsTotal() > 0){return;} // 関数を終了
         obj_Trade.Sell(0.01,_Symbol,Bid,Ask+sl_points*_Point,Ask-tp_points*_Point);
         return; // 関数を終了
      }
      

      if (Ask > PRICE1_Y1){ // 売値が現在の高値よりも高い場合
        PRICE1_Y1 = Ask; // 高値を売値に更新する
        TIME2_Y2 = iTime(_Symbol,_Period,0); // 終了時刻を現在時刻に更新する

         Print(「UPDATED RANGE PRICE1_Y1 TO ASK, NEEDS REPLOT」); // レンジの再プロットが必要であることを示すメッセージを表示する
         plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); //保ち合いレンジを再プロットする

      }
      else if (Bid < PRICE2_Y2){ // ビッド価格が現在の安値よりも低い場合
         PRICE2_Y2 = Bid; // 安値をビッド価格に更新する
         TIME2_Y2 = iTime(_Symbol,_Period,0); // 終了時刻を現在時刻に更新する

         Print(「UPDATED RANGE PRICE2_Y2 TO BID, NEEDS REPLOT」); // レンジの再プロットが必要であることを示すメッセージを表示する
         plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // 保ち合いレンジを再プロットする
      }
      else{
         if (isNewBar){ // 新しいバーが出現した場合

            TIME2_Y2 = iTime(_Symbol,_Period,1); // 終了時刻を前のバーの時刻に更新する
            Print(「EXTEND THE RANGE TO PREV BAR TIME」); // 範囲が拡張されたことを示すメッセージを表示する
            plotConsolidationRange(rangeNAME,TIME1_X1,PRICE1_Y1,TIME2_Y2,PRICE2_Y2); // 保ち合いレンジを再プロットする
         }
      }
      

   }
   
}
//+------------------------------------------------------------------+


//+------------------------------------------------------------------+
//| 保ち合いレンジをプロットする関数                                                |
//| rangeName - 範囲オブジェクトの名前                                          |
//| time1_x1 - 範囲の開始時間                                                  |
//| price1_y1 - 範囲の高値                                                    |

//| time2_x2 - レンジの終了時間                                 |
//| price2_y2 - レンジの安値                               |
//+------------------------------------------------------------------+
void plotConsolidationRange(string rangeName,datetime time1_x1,double price1_y1,
 datetime time2_x2,double price2_y2){

   if (ObjectFind(0,rangeName) < 0){ // 範囲オブジェクトが存在しない場合
      ObjectCreate(0,rangeName,OBJ_RECTANGLE,0,time1_x1,price1_y1,time2_x2,price2_y2); // 範囲オブジェクトを作成する

      ObjectSetInteger(0,rangeName,OBJPROP_COLOR,clrBlue); // 範囲の色を設定する
      ObjectSetInteger(0,rangeName,OBJPROP_FILL,true); // 範囲の塗りつぶしを有効にする
      ObjectSetInteger(0,rangeName,OBJPROP_WIDTH,5); // 範囲の幅を設定する
   }
   else { // 範囲オブジェクトが存在する場合

      ObjectSetInteger(0,rangeName,OBJPROP_TIME,0,time1_x1); // レンジの開始時刻を更新する
      ObjectSetDouble(0,rangeName,OBJPROP_PRICE,0,price1_y1); // レンジの高値を更新する

      ObjectSetInteger(0,rangeName,OBJPROP_TIME,1,time2_x2); // 範囲の終了時刻を更新する
      ObjectSetDouble(0,rangeName,OBJPROP_PRICE,1,price2_y2); // 範囲の最低価格を更新する
   }
   ChartRedraw(0); // 変更を反映させるためにチャートを再描画する
}

バックテスト結果

ストラテジーテスターでのテストに基づいた結果は以下の通りです。

バランス/エクイティグラフ:

  • このグラフは、EA(エキスパートアドバイザー)がトレードを実行する際の口座残高(Balance)および純資産(Equity)の変動を視覚的に示します。
  • グラフはEAのパフォーマンスを一目で確認できる指標であり、安定した増加、ドローダウン(下落幅)、および回復能力を評価するために使用されます。

結論として、保ち合い相場ブレイクアウト戦略の自動化は、必要な考察を行えば、それほど複雑ではないことが自信を持って言えます。技術的には、この戦略の構築には、戦略の明確な理解と、具体的な要件や達成すべき目的をしっかりと把握することが必要だったことがわかります。

この記事では、保ち合い相場ブレイクアウト戦略を作成するために考慮すべき理論的な部分を強調しています。この理論的部分には、定義、概要、および戦略設計図が含まれます。また、戦略のコーディング側面では、ローソク足を分析し、低ボラティリティ期間を特定し、その期間のサポートとレジスタンスレベルを特定し、それらのブレイクアウトを追跡し、結果を視覚化し、生成されたシグナルに基づいてトレードポジションを開くための手順が解説されています。

長期的には、これにより保ち合い相場ブレイクアウト戦略の自動化が可能となり、戦略の迅速な実行とスケーラビリティが実現されます。

これは、Allan Munene Mutiiria氏のDeveloping an Expert Advisor (EA) based on the Consolidation Range Breakout strategy in MQL5を巧訳したものです。著作権はリンク先に由来します。


免責事項

この記事で説明されている情報は、教育目的のみに提供されています。本記事の内容は、価格アクションアプローチに基づいた**保ち合い相場ブレイクアウトエキスパートアドバイザー(EA)**を作成する方法についての洞察を示すことを目的としており、より優れたEAを作成するための基礎として使用されるべきです。最適化やデータ抽出をさらに考慮することで、改善が可能です。本記事の情報は、プラスの取引結果を保証するものではありません。

ABOUTこの記事をかいた人

当サイトは、システムトレードに関する専門的な知識、技術、経験を皆様と共有することで、真剣なトレーダーの方々にインスピレーション(アイデアの源泉)の供給をいたします。トレーダーの競争力強化に焦点をあて、トレーディングシステムの構築を目的としてその普及に貢献します。 詳細は「概要とサービス内容」をご覧下さい。 ご要望はこちらで承ります。