English Русский Deutsch
preview
MQL5入門(第16回):テクニカルチャートパターンを使用したエキスパートアドバイザーの構築

MQL5入門(第16回):テクニカルチャートパターンを使用したエキスパートアドバイザーの構築

MetaTrader 5 |
140 6
Israel Pelumi Abioye
Israel Pelumi Abioye

はじめに

連載「MQL5入門」の第16回へようこそ。今回は特に興味深い内容となっており、これまで学んできたことを基盤に、いつも通りプロジェクトベースのアプローチで進めていきます。テクニカル分析のパターンとMQL5での実装を組み合わせた実践的なプロジェクトに取り組み、ハンズオンでの経験を通してスキルをさらに深めていただきます。

今回の焦点は、トレンド反転の可能性を特定するために広く利用されている人気のテクニカルパターン「三尊」です。本プロジェクトでは、このパターンを自動的に認識し、取引を実行するエキスパートアドバイザー(EA)を構築します。さらに、このパターンをチャート上で視覚的に強調するインジケーターとしての役割も持たせ、実際の取引環境でパターンを容易に発見・理解できるようにします。

この記事では、次の内容を学びます。

  • チャートパターン取引の自動化
  • 三尊の見分け方
  • スイングポイントのプログラムによる描画
  • MQL5におけるチャートオブジェクトの活用
  • エントリー、ストップロス、テイクプロフィットの定義
  • シグナルの重複回避

1. チャートパターンを理解する

将来の相場の動きを予測するために利用できる価格チャート上の視覚的な形状は、チャートパターンと呼ばれます。これらの傾向は、多くの場合、トレンドが継続するのか、それとも反転するのかを示すサインであり、買い手と売り手の継続的な攻防の産物です。例えば、三尊は通常、上昇トレンドから下降トレンドへの反転の可能性を示唆します。一方、上昇トレンド中のフラッグパターンは、そのトレンドが継続する可能性が高いことを示すことが多いです。トライアングル、レンジ(レクタングル)、ダブルトップといったチャートパターンを識別できれば、過去の価格挙動に基づいて取引チャンスを見つける能力が向上します。

比喩的な説明

砂浜の足跡に似て、チャートパターンは買い手と売り手の絶え間ない戦いが残した明確な痕跡です。動物の足跡が熟練した追跡者に、その動物が何で、どこへ向かったかを判断させるように、チャートパターンは市場で何が起こったのか、そして次に何が起こる可能性があるのかを理解する手助けをします。価格チャート上のこれらの視覚的パターンは、市場参加者の集団的な行動を示し、現在のトレンドが継続するのか、それとも反転するのかを示すことが多いのです。

1.1. パターンの分類

チャートパターンは一般的に、以下の3つの主要なカテゴリに分類されます。

1.1.1.  反転パターン

反転パターンと呼ばれるチャートパターンは、市場の転換点の可能性を見極めるために利用されます。これらは、既存のトレンドが終了し、逆方向に新しいトレンドが始まる可能性を示します。たとえば、市場が下落中であれば、反転パターンは価格が上昇トレンドへ移行する準備をしている可能性を示唆します。同様に、上昇トレンド中にこれらのパターンが現れた場合、市場が反転する兆しを示すことがあります。  代表的な反転パターンには、三尊やダブルボトムがあります。

比喩的な説明

反転パターンは、市場の方向転換の可能性を示す指標のようなものです。上昇トレンドでも下降トレンドでも、その勢いが弱まり反転の準備が整っていることを示唆します。それはまるで、砂浜に残された足跡が進行方向を変えているのを見て、人が方向転換したことが分かるのと同じです。反転パターンは、買い手や売り手が力を発揮し始めるタイミングを示し、トレーダーが価格変動の転換点を把握するのに役立ちます。

図1:反転パターン

1.1.2.  継続パターン

継続パターンと呼ばれるチャートパターンは、市場が現在の方向性を維持する可能性が高いことを示します。これらは通常、トレンドが再開する前の調整局面や一時的な価格の停滞中に現れます。たとえば、上昇トレンド中の継続パターンは、価格がわずかな横ばいを経て、再び上昇を続ける可能性を示します。下降トレンドでは、休止後に下落が続くことを意味します。レクタングルやフラッグは、代表的な継続パターンです。

比喩的な説明

継続パターンは、同じ方向に進み続ける足跡に似ています。あなたが浜辺を歩く人を追っていると想像してください。足跡が連続して伸びていますが、途中で少し立ち止まった跡があります。靴紐を結んだり、周囲を見渡したりしたのかもしれません。しかし、その後の足跡は同じ方向に続いています。これは、一時的に止まっただけで、進む方向を変えていないことを意味します。 同様に、継続パターンは、市場が一時的に停滞しているだけで、現在の方向性を維持する可能性を示します。市場が上昇していようと下降していようと、勢いが反転したわけではなく、ただ一時的に休んでいるだけであることを示しているのです。

図2:継続パターン

1.1.3. 中立パターン

中立パターンと呼ばれるチャートパターンは、市場がどちらの方向にもブレイクアウトする可能性がある調整局面を示します。買い手と売り手のどちらにも明確な優位性がないため、価格は狭いレンジ内で推移し、トレーダーは確定したブレイクアウトを待たざるを得ません。ブレイクアウトの方向性を予測することはできませんが、中立パターンはトレーダーに対し、大きな動きへの備えを促します。シンメトリカルトライアングルは、典型的な中立パターンの一例です。

比喩的な説明

取引における中立パターンは、砂浜で次にどちらへ進むか迷って立ち止まっている人を観察しているようなものです。買い手と売り手の力が拮抗している状況を反映しており、その人が決断するまでは次の行動を予測できないのと同じです。明確な上昇や下降のバイアスはありませんが、中立パターンはどちらかへのブレイクスルーが発生する可能性を示しています。

図3:ニュートラルパターン



2. プロジェクトの準備

2.1. EAの仕組み

このプロジェクトのEAは、市場において三尊を自動的に識別し、その構造に基づいて取引を実行するように構築されています。三尊であっても逆三尊であっても、EAは有効なパターンを検出し、最適なエントリーポイントを選び、想定されるブレイクアウト方向に沿って取引を実行します。

しかし、EAの役割はそれだけではありません。チャート上でパターンをより見やすく、理解を容易にするために、グラフィカルオブジェクトを用いて左肩、頭、右肩をマーキングします。これらの視覚的なマーカーは、トレーダーがパターンを確認する際に役立ち、チャートの見直しやバックテストをおこなう際の明確性を高めます。 この方法は、自動化とパターン認識に加え、目に見える確認レイヤーを提供し、デバッグ、学習、さらにはリアルタイムのモニタリングを改善することができます。

2.1.1. 買いロジック

買い取引を開始するために、EAは逆三尊を検出し、以下の6つのスイングポイントを順に特定します。

  • スイングハイ(X):最初の高値。Xとラベル付けします。
  • スイングロー(A):Xの後に現れる安値。Aとラベル付けします。
  • スイングハイ(B):Xより低いがAより高い戻り高値。Bとラベル付けします。
  • スイングロー(C):Aより深い安値。Cとラベル付けします。これが「頭」です。
  • スイングハイ(D):Bとほぼ同じ水準の高値。Dとラベル付けします。これがネックラインの一部を形成します。
  • スイングロー(E):Cより高い安値で、Aとほぼ同じ水準。Eとラベル付けします。これが2つ目の「肩」です。

この構造が成立した後、EAは次の動作をおこないます。

  • ローソク足がポイントD(ネックライン)の上でクローズするのを待ちます。
  • そのとき、買い取引を実行します。
  • ストップロス(SL)はポイントEの安値に設定されます。
  • テイクプロフィット(TP)は、当初ポイントX(最初のスイングハイ)に設定されます。

ただし、エントリーポイントからTP (X)までの距離が、ストップロス距離の1倍未満である場合、EAはXを目標として採用せず、代わりにストップロス距離に基づく固定の1:3リスクリワード比を設定します。これにより、戦略は最低限のリスクリワード比を維持し、リスクに見合わない低リターンの取引を回避します。

図4:ロングポジション

2.1.1. 売りのロジック

売り取引を開始するために、EAは三尊を検出し、以下の6つのスイングポイントを順に特定します。

  • スイングロー(X):最初の安値。Xとラベル付けします。
  • スイングハイ(A):Xの後に現れる高値。Aとラベル付けします。
  • スイングロー(B):Xより高く、Aより低い戻り安値。Bとラベル付けします。
  • スイングハイ(C):Aより高い高値。Cとラベル付けします。これが「頭」です。
  • スイングロー(D):Bとほぼ同じ水準の安値。Dとラベル付けします。これがネックラインの一部を形成します。
  • スイングハイ(E):Cより低く、Aとほぼ同じ水準の高値。Eとラベル付けします。これが2つ目の「肩」です。

この構造が確認された後、EAは次の動作をおこないます。

  • ローソク足がポイントD(ネックライン)の下でクローズするのを待ちます。
  • そのとき、売り取引を実行します。
  • ストップロス(SL)はポイントEの高値に設定されます。
  • テイクプロフィット(TP)は、当初ポイントX(最初のスイングロー)に設定されます。

エントリーポイントからTP (X)までの距離がストップロス距離の1倍未満である場合、EAはXを目標として採用せず、代わりにストップロス幅に基づく固定の1:3リスクリワード比を適用します。

図5:ショートポジション

注記: 本プロジェクトで検討する取引戦略の主な目的は、MQL5プログラミングの概念、特にチャートパターンを扱う方法や実用的なEAの作成方法に関する知識を深めることです。実際の資金を用いた取引やライブ取引に使用することを意図したものではありません。ライブ市場で手法を実装する前に、必ず徹底的なバックテストをおこない、金融の専門家に助言を求めてください。

 

3. チャート上での三尊の識別

ここまでで、チャートパターンの概念を十分に理解し、私たちがEAに求めている動作を明確に把握できていると思います。EAが実際に取引をおこなう前に、チャート上で三尊を視覚的に確認できることは非常に重要です。これは、EAのロジックが実際のプライスアクションと一致していることを確認できるだけでなく、テストプロセスの初期段階でランタイムエラーや論理的な問題を特定するのにも役立ちます。

本章では、トレンドライン、テキストラベル、シェイプなど、さまざまなチャートコンポーネントを使用してチャート上に三尊の構造を手動でハイライトし、検証する方法について説明します。これにより、後にEAでパターン検出を自動化するための強固な基盤を築くことができます。

3.1. ローソク足データの取得

最初のステップは、チャート上で三尊を特定するためにローソク足データを取得することです。これには、各バーの始値、高値、安値、時間、終値に関する情報が含まれます。これらの値は、複数のローソク足にわたる価格のスイングによってパターンの構造が定義されるため、非常に重要です。 ただし、これらのデータを収集するのは単にトレンドを識別するためだけではありません。EAがこれらのパターンを認識して自律的に取引をおこなうことを目的としているため、この情報をインジケーターのように機能させたいのです。つまり、過去のチャート上で三尊をハイライトし、再確認し、検証できるようにする必要があります。

要するに、今回開発するEAには2つの機能があります。三尊を認識して自動的に取引を実行する自動売買ボットであると同時に、過去データの中で同様の構造を強調表示するパターンインジケーターとしても機能します。この二重の機能により、自動取引を可能にするだけでなく、トレーダーはシグナルを視覚的に検証し、パターンが過去にどのように形成され、どのような結果をもたらしたのかを確認できるようになります。

input ENUM_TIMEFRAMES    timeframe = PERIOD_CURRENT; // MA Time Frame
input int bars_check  = 1000; // Number of bars to check for swing points

// Variable to store how many bars are available on the chart for the selected timeframe
int rates_total;

double open[];   // Array for opening prices
double close[];  // Array for closing prices
double low[];    // Array for lowest prices
double high[];   // Array for highest prices
datetime time[]; // Array for time (timestamps) of each bar


//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

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

// Get the total number of bars available on the selected symbol and timeframe
   rates_total = Bars(_Symbol, timeframe);

// Copy the open prices of the last 'rates_total' bars into the 'open' array
   CopyOpen(_Symbol, timeframe, 0, rates_total, open);

// Copy the close prices of the last 'rates_total' bars into the 'close' array
   CopyClose(_Symbol, timeframe, 0, rates_total, close);

// Copy the low prices of the last 'rates_total' bars into the 'low' array
   CopyLow(_Symbol, timeframe, 0, rates_total, low);

// Copy the high prices of the last 'rates_total' bars into the 'high' array
   CopyHigh(_Symbol, timeframe, 0, rates_total, high);

// Copy the time (timestamps) of the last 'rates_total' bars into the 'time' array
   CopyTime(_Symbol, timeframe, 0, rates_total, time);

  }

説明

関数の冒頭には、ユーザー定義の入力変数が2つあります。最初の変数であるtimeframeは、EAが利用する正確な時間足をユーザーが選択できるようにします。デフォルト設定はPERIOD_CURRENTです。したがって、EAはリンクされているチャートと同じ時間足を利用します。この柔軟性により、コードを変更することなく複数の時間足を検証することが可能になります。2つ目のパラメータであるbars_checkは、価格の挙動を検証する際に、EAが過去何本のローソク足(バー)を参照するかを指示します。この例では1000に設定されているため、EAは直近1000本のローソク足からパターン構造を探索することになります。

入力定義の後、コードはいくつかの配列と市場データを保持する変数を宣言します。選択された銘柄と時間足で利用可能なバー(ローソク足)の総数は、rates_total変数に格納されます。各ローソク足に関連する価格データは、open[]、close[]、low[]、high[]といった配列に保存されます。また、各バーの正確な時刻はtime[]配列に格納されており、これを用いてローソク足のタイムスタンプを取得できます。これらの配列は、EAがチャートを分析し、三尊のようなパターンを検出するために必要不可欠な情報を提供します。

本プロジェクトでは、カスタムインディケーターではなくEAとして設計されているため、CopyOpen()、CopyHigh()などの関数を用いてローソク足データを手動でコピーする必要があります。もしOnCalculate()メソッドを利用するインジケーターであれば、この情報は関数パラメータを通じて自動的に提供されるため、コピー処理を行う手間は不要です。

さらに、このEAの構造には、MQL5で一般的に使用される3つの関数(OnInit()、OnDeinit()、OnTick())が含まれています。OnInit()関数はEAが読み込まれた際に1度だけ呼び出され、INIT_SUCCEEDEDを返すことでEAが実行準備完了であることを示します。OnDeinit()関数は、ターミナルが終了する際やEAが削除される際に呼び出されますが、現時点ではクリーンアップ処理のロジックは含まれていません。

OnTick()関数は新しい価格更新(ティック)が発生するたびに実行され、実際の処理がおこなわれます。この関数内では、まずBars()関数を用いて現在チャート上で利用可能なバー数を取得します。その後、5つのコピー関数を使用して価格と時間データを対応する配列に読み込みます。具体的には、CopyOpen()で始値をopen[]配列に、CopyClose()で終値をclose[]配列に、CopyLow()とCopyHigh()で安値と高値をそれぞれlow[]とhigh[]配列に、そしてCopyTime()で各ローソク足のタイムスタンプをtime[]配列に格納します。この準備は、EAがチャートパターンを検出し、市場の動きを分析するために利用する履歴データを準備するものであり、非常に重要な処理です。

3.2. スイングの識別

ローソク足の履歴データを正しく取得した後は、三尊または逆三尊のパターン構造を構成する重要なスイングポイントX、A、B、C、D、Eを特定する作業に移ります。EAは、これらのスイングを正確に識別することで、価格変動における転換点としての有効なチャートパターンを判断します。EAは、選択したバー数の高値と安値を分析し、主要な反転ポイントを特定することで、パターンを段階的にマッピングする手助けをします。

//+------------------------------------------------------------------+
//| FUNCTION FOR SWING LOW                                           |
//+------------------------------------------------------------------+
bool IsSwingLow(const double &low_price[], int index, int lookback)
  {
   for(int i = 1; i <= lookback; i++)
     {
      if(low_price[index] > low_price[index - i] || low_price[index] > low_price[index + i])
         return false;
     }
   return true;
  }


//+------------------------------------------------------------------+
//| FUNCTION FOR SWING HIGH                                          |
//+------------------------------------------------------------------+
bool IsSwingHigh(const double &high_price[], int index, int lookback)
  {
   for(int i = 1; i <= lookback; i++)
     {
      if(high_price[index] < high_price[index - i] || high_price[index] < high_price[index + i])
         return false; // If the current high is not the highest, return false.
     }
   return true;
  }

説明

価格のスイングポイントを判定するために、IsSwingLow()とIsSwingHigh()の2つの関数を使用します。これらの関数は、対象となるローソク足が周囲のローソク足と比較してスイングハイ(高値の転換点)またはスイングロー(安値の転換点)を形成しているかどうかを判断します。スイングローの場合、指定されたlookback値に基づき、現在のローソク足の安値が、その前後の一定本数のローソク足の安値よりも低いことを確認します。スイングハイの場合も同様で、現在のローソク足の高値が周囲のローソク足の高値より高いことを検証します。この概念については、第14回で詳細に解説しているため、ここでは詳しく説明しません。

3.2.1. XABCDEの識別

本記事では、買いおよび売りの両方の設定で三尊を認識するために、主要なスイングポイントX、A、B、C、Dを正確に特定する重要性が強調されています。これらのポイントは、パターンに影響を与える重要な高値および安値を表しており、EAの取引判断を導く役割を果たします。

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

// Get the total number of bars available on the selected symbol and timeframe
   rates_total = Bars(_Symbol, timeframe);

// Copy the open prices of the last 'rates_total' bars into the 'open' array
   CopyOpen(_Symbol, timeframe, 0, rates_total, open);

// Copy the close prices of the last 'rates_total' bars into the 'close' array
   CopyClose(_Symbol, timeframe, 0, rates_total, close);

// Copy the low prices of the last 'rates_total' bars into the 'low' array
   CopyLow(_Symbol, timeframe, 0, rates_total, low);

// Copy the high prices of the last 'rates_total' bars into the 'high' array
   CopyHigh(_Symbol, timeframe, 0, rates_total, high);

// Copy the time (timestamps) of the last 'rates_total' bars into the 'time' array
   CopyTime(_Symbol, timeframe, 0, rates_total, time);



//FOR SELL
   if(show_sell)
     {
      if(rates_total >= bars_check)
        {

         for(int z = 7; z <= 10; z++)
           {

            for(int i = rates_total - bars_check; i < rates_total - z; i++)
              {

               if(IsSwingLow(low, i, z))
                 {

                  // If a swing low is found, store its price, time, and create a name for the objects to mark the X.
                  X = low[i]; // Price of the swing low (X).
                  X_time = time[i]; // Time of the swing low (X).
                  X_letter = StringFormat("X%d", i); // Unique name for the text label object.


                  for(int j = i; j < rates_total - z; j++)
                    {
                     if(IsSwingHigh(high, j, z) && time[j] > X_time)
                       {

                        A = high[j]; // Price of the swing high (A).
                        A_time = time[j]; // Time of the swing high (A)
                        A_letter = StringFormat("A%d", j); // Unique name for the text label object


                        for(int k = j; k < rates_total - z; k++)
                          {
                           if(IsSwingLow(low, k, z) && time[k] > A_time)
                             {

                              B = low[k]; // Price of the swing low (B).
                              B_time = time[k]; // Time of the swing low (B).
                              B_letter = StringFormat("B%d", k); // Unique name for the text label object.


                              for(int l = k ; l < rates_total - z; l++)
                                {

                                 if(IsSwingHigh(high, l, z) && time[l] > B_time)
                                   {

                                    C = high[l]; // Price of the swing high (C).
                                    C_time = time[l]; // Time of the swing high (C).
                                    C_letter = StringFormat("C%d", l); // Unique name for the text label object.

                                    for(int m = l; m < rates_total - z; m++)
                                      {

                                       if(IsSwingLow(low, m, z) && time[m] > C_time)
                                         {

                                          D = low[m]; // Price of the swing low (D).
                                          D_time = time[m]; // Time of the swing low (D).
                                          D_letter = StringFormat("D%d", m); // Unique name for the text label object.

                                          for(int n = m ; n < rates_total - (z/2) - 1; n++)
                                            {

                                             if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time)
                                               {

                                                E = high[n]; // Price of the swing low (B).
                                                E_time = time[n]; // Time of the swing low (B).
                                                E_letter = StringFormat("E%d", n); // Unique name for the text label object.


                                                break;
                                               }
                                            }

                                          break;
                                         }
                                      }

                                    break;
                                   }
                                }

                              break;
                             }
                          }

                        break;
                       }
                    }
                 }
              }
           }

        }
     }

  }
説明
//X
double X; // Price of the swing low (X).
datetime X_time; // Time of the swing low (X).
string X_letter; // Unique name for the text label object.

//A
double A; // Price of the swing high (A).
datetime A_time; // Time of the swing high (A).
string A_letter; // Unique name for the text label object.


//B
double B; // Price of the swing low (B).
datetime B_time; // Time of the swing low (B).
string B_letter; // Unique name for the text label object.

//C
double C; // Price of the swing low (B).
datetime C_time; // Time of the swing low (B).
string C_letter; // Unique name for the text label object.

//D
double D; // Price of the swing low (B).
datetime D_time; // Time of the swing low (B).
string D_letter; // Unique name for the text label object.

//E
double E; // Price of the swing low (B).
datetime E_time; // Time of the swing low (B).
string E_letter; // Unique name for the text label object.

このコードでは、パターンの主要なスイングポイントであるX、A、B、C、D、Eを表すために、3種類の異なる変数が使用されています。まず、スイングポイントの正確な価格レベルはdouble型の変数に格納されます。たとえば、double AはラベルAに対応するスイングハイの価格を保持し、double XはラベルXに対応するスイングローの価格を保持します。各スイングポイントは、価格に加えて発生時刻を記録するためのdatetime型変数も使用します。これにより、EAはスイングポイントをチャート上で正確に時系列順に配置できます。たとえば、datetime X_time;はスイングローXの時刻を記録し、datetime A_time;はスイングハイAの時刻を記録します。

最後に、文字列型の変数を使用して、各スイングポイントに対して固有のラベル名を生成します。これらのラベル(例:X_letter、A_letter)は、チャート上にテキストオブジェクトを作成するために使用され、各スイングポイントの位置を視覚的に示します。このラベル付けシステムにより、EAはスイングポイントを整理して表示し、トレーダーがパターンの形成を容易に確認できるようになります。 EAは、価格、時刻、ラベルという3つの情報を用いて各スイングポイントを配置し、チャート上で三尊を正確に識別し、視覚的に描画します。この体系的な手法は、パターンの認識と、トレーダーが理解しやすい視覚的手掛かりの配置の両方に不可欠です。

if(show_sell)
  {
   if(rates_total >= bars_check)
     {

      for(int z = 7; z <= 10; z++)
        {

         for(int i = rates_total - bars_check; i < rates_total - z; i++)
           {

            if(IsSwingLow(low, i, z))
              {

               // If a swing low is found, store its price, time, and create a name for the objects to mark the X.
               X = low[i]; // Price of the swing low (X).
               X_time = time[i]; // Time of the swing low (X).
               X_letter = StringFormat("X%d", i); // Unique name for the text label object.


               for(int j = i; j < rates_total - z; j++)
                 {
                  if(IsSwingHigh(high, j, z) && time[j] > X_time)
                    {

                     A = high[j]; // Price of the swing high (A).
                     A_time = time[j]; // Time of the swing high (A)
                     A_letter = StringFormat("A%d", j); // Unique name for the text label object


                     for(int k = j; k < rates_total - z; k++)
                       {
                        if(IsSwingLow(low, k, z) && time[k] > A_time)
                          {

                           B = low[k]; // Price of the swing low (B).
                           B_time = time[k]; // Time of the swing low (B).
                           B_letter = StringFormat("B%d", k); // Unique name for the text label object.


                           for(int l = k ; l < rates_total - z; l++)
                             {

                              if(IsSwingHigh(high, l, z) && time[l] > B_time)
                                {

                                 C = high[l]; // Price of the swing high (C).
                                 C_time = time[l]; // Time of the swing high (C).
                                 C_letter = StringFormat("C%d", l); // Unique name for the text label object.

                                 for(int m = l; m < rates_total - z; m++)
                                   {

                                    if(IsSwingLow(low, m, z) && time[m] > C_time)
                                      {

                                       D = low[m]; // Price of the swing low (D).
                                       D_time = time[m]; // Time of the swing low (D).
                                       D_letter = StringFormat("D%d", m); // Unique name for the text label object.

                                       for(int n = m ; n < rates_total - (z/2) - 1; n++)
                                         {

                                          if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time)
                                            {

                                             E = high[n]; // Price of the swing low (B).
                                             E_time = time[n]; // Time of the swing low (B).
                                             E_letter = StringFormat("E%d", n); // Unique name for the text label object.

                                             ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X);
                                             ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X");
                                             ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr);

                                             ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A);
                                             ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A");
                                             ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr);

                                             ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B);
                                             ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B");
                                             ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr);

                                             ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C);
                                             ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C");
                                             ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr);

                                             ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D);
                                             ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D");
                                             ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr);

                                             ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E);
                                             ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E");
                                             ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr);

                                             break;
                                            }
                                         }

                                       break;
                                      }
                                   }

                                 break;
                                }
                             }

                           break;
                          }
                       }

                     break;
                    }
                 }
              }
           }
        }

     }
  }

出力

図6:スイングの識別

説明

プログラムが売りパターンを検出する設定になっているかどうかを判定するため、まず変数show_sellがtrueかどうかを確認します。売りパターンの検出が不要な場合、この簡単な条件判定により無駄な計算を防ぐことができます。次に、チャート上の総バー数(rates_total)が、最低限必要なバー数(bars_check)以上であるかどうかを確認します。これにより、正確なパターン認識に十分な過去データが存在することが保証されます。

通常、スイングポイント検出のためのウィンドウサイズやルックバック期間として使用される変数zは、最外側のループで限られた範囲の値を反復します。この手法により、z = 7からz = 10の範囲で複数のルックバック期間を試すことで、スイング検出の柔軟性と精度を向上させることができます。このループにより、アルゴリズムはやや異なる解像度や感度でパターンを検出することが可能になります。

次のforループは、rates_total - bars_checkからrates_total - zまでの範囲で、チャート上の直近バーを順に処理します。この範囲により、現在の価格動向により関連性の高い直近バーに焦点を絞ってパターンを探索できます。このループ内では、IsSwingLow(low, i, z)関数を用いてスイングローを検出し、位置iのバーがルックバック期間zにおける局所的な安値であるかを判定します。

位置iでスイングローが検出されると、StringFormat("X%d", i)を用いて一意のラベル文字列X_letterを生成し、その価格を変数Xに、タイムスタンプをX_timeに格納します。このラベル付けにより、EAはチャート上でこのポイントを視覚的に強調し、後の処理で一意に識別できるようになります。三尊は、このポイントXを基準として開始されます。

次に、Xの時刻以降に発生する次のスイングハイAを特定するため、位置iから別のネストされたループを開始します。パターンに必要な時系列順を維持するため、IsSwingHigh(high, j, z)を用いて位置jのバーがスイングハイかどうかを判定し、time[j]がX_timeより後であることを確認します。有効なスイングハイが検出された場合、価格、時刻、ラベルはXと同様に記録されます。

B、C、D、Eの各ポイントも、このネストされたループ構造によって順番に特定されます。バーを順に走査することで、各スイングポイントが有効なスイングローまたはスイングハイであること、かつ前のポイントの時刻より後であることを確認します。各スイングが確認されると、一意のラベル文字列を生成し、価格と時刻を保存します。この厳密な順序確認により、パターンポイントは正しい順序で保持されます。

最も内側のループでは、やや狭いルックバック期間(z/2)を用いて、パターンの最後のスイングハイであるEを検出します。この差異により、最後のポイントに対する検出感度を調整できます。Eが特定されると、無駄な探索を避けるためにループを即座に終了し、その価格、時刻、ラベルを格納します。

このように、ネストされたループと時刻のチェックにより、三尊はスイングポイントの正しい順序で正確に検出されます。「X%d」や「A%d」といった一意のラベルを使用することで、EAはチャート上でテキストやグラフィカル要素を生成・管理でき、トレーダーにパターンを視覚的に示すことが可能です。この体系的な手法により、コンピュータは複雑な価格変動パターンを信頼性高く識別し、取引判断に活用できます。

このコードサンプルの目的は、ヘッドアンドショルダーパターンを認識するために必要な6つの重要なスイングポイントX、A、B、C、D、Eを特定することです。ただし、現時点の実装では、三尊構造を検証するための正確な論理的条件はまだ適用されておらず、価格のスイングと時系列順序に基づいてポイントを決定するのみです。アルゴリズムはポイントの価格、時刻、ラベルを収集しますが、それらが本当に特徴的なパターンを形成するかは確認していません。

三尊売りパターンの有効性を確認するためには、いくつかの重要な価格構造に基づく条件が必要です。AはXより高く、BはXより高くAより低い位置にあり、C(頭)はBより高くなければなりません。右肩のピークはAと一致するEで形成され、右肩の谷はDがB付近で表されます。さらに、取引シグナルを出す前に、これらのポイント間の特定の高値・安値の系列を観察して、パターン構造を確認する必要があります。

input color txt_clr = clrBlue; // Texts color
//X
double X; // Price of the swing low (X).
datetime X_time; // Time of the swing low (X).
string X_letter; // Unique name for the text label object.

int x_a_bars;
int x_lowest_index;
double x_a_ll;
datetime x_a_ll_t;


//A
double A; // Price of the swing high (A).
datetime A_time; // Time of the swing high (A).
string A_letter; // Unique name for the text label object.


int a_b_bars;
int a_highest_index;
double a_b_hh;
datetime a_b_hh_t;

string A_zone;
double A_low;


//B
double B; // Price of the swing low (B).
datetime B_time; // Time of the swing low (B).
string B_letter; // Unique name for the text label object.

int b_c_bars;
int b_lowest_index;
double b_c_ll;
datetime b_c_ll_t;

string B_zone;
double B_high;


//C
double C; // Price of the swing low (B).
datetime C_time; // Time of the swing low (B).
string C_letter; // Unique name for the text label object.

int c_d_bars;
int c_highest_index;
double c_d_hh;
datetime c_d_hh_t;

//D
double D; // Price of the swing low (B).
datetime D_time; // Time of the swing low (B).
string D_letter; // Unique name for the text label object.

int d_e_bars;
int d_lowest_index;
double d_e_ll;
datetime d_e_ll_t;

double D_3bar_high;

//E
double E; // Price of the swing low (B).
datetime E_time; // Time of the swing low (B).
string E_letter; // Unique name for the text label object.
double E_3bar_low;

string xa; // Unique name for the trendline for XA.
string ab; // Unique name for the trendline for AB.
string bc; // Unique name for the trendline for BC.
string cd; // Unique name for the trendline for CD.
string de; // Unique name for the trendline for DE.
string ex; // Unique name for the trendline for EX.



//FOR SELL
if(show_sell)
  {
   if(rates_total >= bars_check)
     {

      for(int z = 7; z <= 10; z++)
        {

         for(int i = rates_total - bars_check; i < rates_total - z; i++)
           {

            if(IsSwingLow(low, i, z))
              {

               // If a swing low is found, store its price, time, and create a name for the objects to mark the X.
               X = low[i]; // Price of the swing low (X).
               X_time = time[i]; // Time of the swing low (X).
               X_letter = StringFormat("X%d", i); // Unique name for the text label object.


               for(int j = i; j < rates_total - z; j++)
                 {
                  if(IsSwingHigh(high, j, z) && time[j] > X_time)
                    {

                     A = high[j]; // Price of the swing high (A).
                     A_time = time[j]; // Time of the swing high (A)
                     A_letter = StringFormat("A%d", j); // Unique name for the text label object


                     for(int k = j; k < rates_total - z; k++)
                       {
                        if(IsSwingLow(low, k, z) && time[k] > A_time)
                          {

                           B = low[k]; // Price of the swing low (B).
                           B_time = time[k]; // Time of the swing low (B).
                           B_letter = StringFormat("B%d", k); // Unique name for the text label object.


                           for(int l = k ; l < rates_total - z; l++)
                             {

                              if(IsSwingHigh(high, l, z) && time[l] > B_time)
                                {

                                 C = high[l]; // Price of the swing high (C).
                                 C_time = time[l]; // Time of the swing high (C).
                                 C_letter = StringFormat("C%d", l); // Unique name for the text label object.

                                 for(int m = l; m < rates_total - z; m++)
                                   {

                                    if(IsSwingLow(low, m, z) && time[m] > C_time)
                                      {

                                       D = low[m]; // Price of the swing low (D).
                                       D_time = time[m]; // Time of the swing low (D).
                                       D_letter = StringFormat("D%d", m); // Unique name for the text label object.

                                       for(int n = m ; n < rates_total - (z/2) - 1; n++)
                                         {

                                          if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time)
                                            {

                                             E = high[n]; // Price of the swing high (E).
                                             E_time = time[n]; // Time of the swing high (E).
                                             E_letter = StringFormat("E%d", n); // Unique name for the text label object.


                                             d_e_bars = Bars(_Symbol, PERIOD_CURRENT, D_time, E_time); // Count the number of bars between D and E
                                             d_lowest_index = ArrayMinimum(low, m, d_e_bars); // Find the index of the lowest low in the range
                                             d_e_ll = low[d_lowest_index]; // Store the lowest low (D - E lowest point)
                                             d_e_ll_t = time[d_lowest_index]; // Store the corresponding time
                                             D_3bar_high = high[d_lowest_index - 3]; // The high price of the third bar before the bar that formed D

                                             c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time,d_e_ll_t); // Count the number of bars between C and V
                                             c_highest_index = ArrayMaximum(high,l,c_d_bars); // Find the index of the highest high in the range
                                             c_d_hh = high[c_highest_index];  // Store the lowest high (C - D lowest point)
                                             c_d_hh_t = time[c_highest_index]; // Store the corresponding time

                                             b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_hh_t); // Count the number of bars between B and C
                                             b_lowest_index = ArrayMinimum(low, k, b_c_bars); // Find the index of the lowest low in the range
                                             b_c_ll = low[b_lowest_index]; // Store the lowest low B - C lowest point)
                                             b_c_ll_t = time[b_lowest_index]; // Store the corresponding time
                                             B_high = high[b_lowest_index];  // The high price of the bar that formed swing low D


                                             a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time,b_c_ll_t); // Count the number of bars between A and B
                                             a_highest_index = ArrayMaximum(high,j,a_b_bars); // Find the index of the highest high in the range
                                             a_b_hh = high[a_highest_index]; // Store the lowest low A - B lowest point)
                                             a_b_hh_t = time[a_highest_index];  // Store the corresponding time
                                             A_low = low[a_highest_index];

                                             x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_hh_t); // Count the number of bars between C and D
                                             x_lowest_index = ArrayMinimum(low, i, x_a_bars); // Find the index of the lowest low in the range
                                             x_a_ll = low[x_lowest_index]; // Store the lowest low (C - D lowest point)
                                             x_a_ll_t = time[x_lowest_index]; // Store the corresponding time for C - D

                                             E_3bar_low = low[n - 3]; // The LOW price of the third bar before the bar that formed E


                                             if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll
                                                && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh)
                                               {

                                                ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X);
                                                ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X");
                                                ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A);
                                                ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A");
                                                ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B);
                                                ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B");
                                                ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C);
                                                ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C");
                                                ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D);
                                                ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D");
                                                ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E);
                                                ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E");
                                                ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr);

                                                xa = StringFormat("XA line%d", i);
                                                ab = StringFormat("AB line%d", i);
                                                bc = StringFormat("BC line%d", i);
                                                cd = StringFormat("CD line%d", i);
                                                de = StringFormat("DE line%d", i);
                                                ex = StringFormat("EX line%d", i);


                                                ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll);
                                                ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X");
                                                ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh);
                                                ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A");
                                                ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll);
                                                ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B");
                                                ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh);
                                                ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C");
                                                ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll);
                                                ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D");
                                                ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E);
                                                ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E");
                                                ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr);


                                                ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh);
                                                ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true);

                                                ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll);
                                                ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true);

                                                ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh);
                                                ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true);

                                                ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll);
                                                ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true);

                                                ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E);
                                                ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, de, OBJPROP_BACK, true);

                                                ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll);
                                                ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true);

                                                A_zone = StringFormat("A ZONEe%d", i);
                                                B_zone = StringFormat("B ZONEe%d", i);

                                                ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low);
                                                ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high);




                                               }

                                             break;
                                            }
                                         }

                                       break;
                                      }
                                   }

                                 break;
                                }
                             }

                           break;
                          }
                       }

                     break;
                    }
                 }
              }
           }
        }

     }
  }

出力

図7:三尊

説明

このコードには、プライスアクションにおけるカスタムXABCDEパターンの構造と構成要素に関する重要な情報を保持するための変数宣言が含まれています。各変数は、パターンを構成するスイングハイ・スイングローの特定および視覚化、さらにチャート上でのラベル付けや描画において特定の役割を果たします。 最初の変数群は、ポイントXからAまでのセグメントに関連しています。x_a_barsはこの2点間のバー数を保持し、x_lowest_indexはこのセグメント内で最も低い価格バーのインデックスを保持します。x_a_llは実際の最安値(安値更新)を、x_a_ll_tはその安値が発生した時刻を保存します。

AからBの範囲は次の変数群で扱います。セグメントのバー数はa_b_barsで、最高値バーのインデックスはa_highest_indexで特定されます。実際の最高値(高値更新)はa_b_hhに格納され、発生時刻はa_b_hh_tに記録されます。これにより、Bがより高い安値であり、Aがスイングハイであることをサポートします。 続くBからCのセグメントについては、b_c_bars、b_lowest_index、b_c_ll、b_c_ll_tがバー数、最安値インデックス、最安値、時刻を記録し、構造上の次の安値更新を検証します。

文字列型の変数A_zoneおよびB_zoneは、おそらくAおよびB付近に長方形ゾーンを作成する際のオブジェクト名を保持するために使用されます。チャート上で視覚的な手掛かりや意思決定領域として用いられる価格レベルA_lowとB_highは、これらのゾーンの下限および上限を示します。DからEのセグメントについても、d_e_bars、d_lowest_index、d_e_ll、d_e_ll_tがバー数、インデックス、価格、時刻データを保持し、パターンの最後の足を特定するのに役立ちます。

D_3bar_highとE_3bar_lowは、通常3本バーの構造から算出されるD付近の最高値とE付近の最安値を保持します。これによりスイングポイントの正当性を確認し、誤検出を減らすことができます。 最後に、XからA、AからBといったパターン内の各転換点間に作成されるトレンドラインの文字列IDは、xa、ab、bc、cd、de、exの変数で管理されます。これらの文字列により、各トレンドラインオブジェクトに一意の名前が与えられ、チャート上でパターン全体を正確かつ整合的に描画できます。

6つの重要ポイント(X、A、B、C、D、E)間の価格変動を確認し、これらが特定の構造的関係を作るスイングハイ・スイングローに対応すると仮定します。スクリプトではまず、DとE間の最安値(d_e_ll)を特定します。次に、その最安値の3本前のバーの参照高値(D_3bar_high)を取得します。同様に、Cとこのd_e_llの間の最高値(c_d_hh)を取得します。このように、関連するスイングハイ・スイングローとそのタイムスタンプをすべて収集し、時間を逆方向にさかのぼってXまで繰り返します。これにより、コードはバー解析を通じてX-A-B-C-D-Eのスイング構造全体を構築します。

2つのタイムスタンプ間のローソク足本数はBars()関数でカウントされます。ArrayMaximum()やArrayMinimum()を用いることで、スイングポイント間の特定範囲を分離し、その範囲内の高値・安値を調べられます。最大高値や最小安値を見つけるために、これらの関数は指定したオフセット(i、j、kなど)から一定本数のバーをスキャンします。これはスイングポイントを見つけるのに役立ちます。たとえばArrayMaximum(high, l, c_d_bars)はCとD間の最高値を見つけ、ポイントCを特定します。ポイントXに至るまで、同様の手順で構造全体を解析します。

ポイント間の構造的関係は一連の条件式で確認されます。これらの基準はセグメントの高値・安値を比較し、たとえばAがXより高いか、BがAより低いがXより高いか、CがAより高いか、といった判定をおこないます。チャートに描画する前に、プライスアクションが予想されるパターンを形成しているか確認します。例として、E > A_lowおよびE_3bar_low < a_b_hhは、Eの足が構造上妥当な位置にあることを保証します。

パターンがすべての条件を満たす場合、ObjectCreate()を用いてチャート上に認識されたポイントをテキストラベル(「X」「A」「B」など)で描画し、トレンドラインで接続します。トレンドラインはOBJ_TRENDオブジェクトでパターンのXA、AB、BC、CD、DE、EXの各脚を描き、OBJ_TEXTでポイントを表示します。色、線幅、表示順序(OBJPROP_BACK)を調整することで可読性が向上します。

最後に、A-zoneとB-zoneの2つの重要価格ゾーンを長方形でハイライトします。A-zoneはAの高値からEの安値まで、B-zoneはBの安値からDの高値まで広がります。エントリー、ストップ、ターゲットなどの取引判断時に、これらの長方形が特定領域での価格反応や合致点を視覚的に示す目印として使用される可能性があります。この表示により、トレーダーはプログラムが自動的に検出した複雑なパターンをより理解しやすくなります。

3.2.2. 三角形でパターン構造を強調する

すべての重要なスイングポイント(X、A、B、C、D、E)を特定してラベルを付けた後におこなう次のステップは、パターンの構造を強調するために三角形の形を描くことです。特に三尊のようなフォーメーションが現れる場合に有効です。これらの三角形は、全ての価格の足をたどるのではなく、それぞれのパターンの「頭」と「肩」を特徴づける高値と安値を示します。ネックラインを下回らない線を持つ三尊は、X、A、Bを結ぶXAB三角形によって視覚的に表されます。

次に進むのは、B、C、Dを結ぶBCD三角形であり、構造の第2波を強調します。形を視覚的に完成させるのは、D、E、Xを結ぶDEX三角形です。補助ツールとして機能するこれらの三角形は、トレーダーがチャートを線で埋め尽くすことなく、重要な転換点とパターンの形状をより迅速に特定できるようにします。

input ENUM_TIMEFRAMES    timeframe = PERIOD_CURRENT; // MA Time Frame
input int bars_check  = 1000; // Number of bars to check for swing points
input bool show_sell = true; // Display sell signals
input bool show_buy = true; // Display buy signals
input color txt_clr = clrBlue; // Texts color
input color head_clr = clrCornflowerBlue; // Head color
input color shoulder_clr = clrLightSeaGreen; // Shoulder color
if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll
   && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh)
  {

   ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X);
   ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X");
   ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr);

   ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A);
   ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A");
   ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr);

   ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B);
   ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B");
   ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr);

   ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C);
   ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C");
   ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr);

   ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D);
   ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D");
   ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr);

   ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E);
   ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E");
   ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr);

   xa = StringFormat("XA line%d", i);
   ab = StringFormat("AB line%d", i);
   bc = StringFormat("BC line%d", i);
   cd = StringFormat("CD line%d", i);
   de = StringFormat("DE line%d", i);
   ex = StringFormat("EX line%d", i);


   ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll);
   ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X");
   ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr);

   ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh);
   ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A");
   ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr);

   ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll);
   ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B");
   ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr);

   ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh);
   ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C");
   ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr);

   ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll);
   ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D");
   ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr);

   ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E);
   ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E");
   ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr);


   ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh);
   ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3);
   ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown);
   ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true);

   ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll);
   ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3);
   ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown);
   ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true);

   ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh);
   ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3);
   ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown);
   ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true);

   ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll);
   ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3);
   ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown);
   ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true);

   ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E);
   ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3);
   ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown);
   ObjectSetInteger(chart_id, de, OBJPROP_BACK, true);

   ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll);
   ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3);
   ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown);
   ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true);

   A_zone = StringFormat("A ZONEe%d", i);
   B_zone = StringFormat("B ZONEe%d", i);

   ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low);
   ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high);

   xa_line_t = ObjectGetTimeByValue(chart_id,xa,b_c_ll,0);
   ex_line_t = ObjectGetTimeByValue(chart_id,ex,d_e_ll,0);

   X_A_B = StringFormat("XAB %d", i);
   ObjectCreate(chart_id,X_A_B,OBJ_TRIANGLE,0,xa_line_t,b_c_ll,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll);
   ObjectSetInteger(chart_id, X_A_B, OBJPROP_FILL, true);
   ObjectSetInteger(chart_id, X_A_B, OBJPROP_BACK, true);
   ObjectSetInteger(chart_id, X_A_B, OBJPROP_COLOR, shoulder_clr);

   B_C_D = StringFormat("BCD %d", i);
   ObjectCreate(chart_id, B_C_D, OBJ_TRIANGLE, 0, b_c_ll_t, b_c_ll, c_d_hh_t, c_d_hh, d_e_ll_t, d_e_ll);
   ObjectSetInteger(chart_id, B_C_D, OBJPROP_COLOR, head_clr);
   ObjectSetInteger(chart_id, B_C_D, OBJPROP_FILL, true);
   ObjectSetInteger(chart_id, B_C_D, OBJPROP_BACK, true);

   D_E_X = StringFormat("DEX %d", i);
   ObjectCreate(chart_id, D_E_X, OBJ_TRIANGLE, 0, d_e_ll_t, d_e_ll, E_time, E, ex_line_t, d_e_ll);
   ObjectSetInteger(chart_id, D_E_X, OBJPROP_COLOR, shoulder_clr);
   ObjectSetInteger(chart_id, D_E_X, OBJPROP_FILL, true);
   ObjectSetInteger(chart_id, D_E_X, OBJPROP_BACK, true);




  }

出力

図8:三角形でパターン構造を強調表示

説明

このコードのセクションは、特にXAB、BCD、DEXをMetaTrader 5チャート上でグラフィカルにパターン構造として描写するために三角形と長方形を使用することに焦点を当てています。作成される三角形オブジェクトの動的な名前を保持するために、3つの文字列変数(X_A_B、B_C_D、D_E_X)が宣言されます。チャート上の適切な位置に三角形の一部を固定するために、2つのdatetime変数(xa_line_tとex_line_t)を使用して特定の時間座標が取得されます。

トレーダーの理解を高めるために、コードはまず「A ZONE」と「B ZONE」と呼ばれる2つの長方形エリアを確立します。価格構造の重要な領域を強調するこれらの長方形は、ObjectCreate()を使用して作成されます。A ZONEは、ポイントAとBの間の高値(a_b_hh)からポイントAの安値(A_low)までEポイントにまで広がります。同様に、B ZONEはポイントBとCの間の安値位置(b_c_ll)から最高値(B_high)まで広がり、DからEの脚の時間座標で終了します。

次のセクションでは、ObjectGetTimeByValue()を呼び出してXABとDEX三角形の一部を固定するために必要な時間座標を決定します。この関数は、オブジェクトの進行に沿った特定の価格値を検索し、対応する時間を返すことで、視覚的マーカーが強調すべき価格レベルに正しく一致することを保証します。

その後、それぞれの三角形は個別のパターンの脚として描写されます。完全な脚を結ぶわけではありませんが、XAB三角形は最初のスイング構造を示します。視覚的な混雑を避け、構造に集中するために、肩に似た重要な転換点だけを強調します。同様に、安値(B)、高値(C)、リトレースメント(D)を結ぶBCD三角形は、パターンの主要なヘッドを強調します。最後に、元の肩を反映する戻りスイングを描写するDEX三角形がフレームワークを完成させます。

プロパティであるOBJPROP_FILLおよびOBJPROP_BACKは、3つの三角形すべてを色で塗りつぶし、他のチャート要素の背後に配置するために使用されます。トレーダーは、shoulder_clrやhead_clrといった変数に保持されている色のおかげで、パターンを一目で認識しやすくなります。同一チャート上でこれらのパターンが複数検出されるケースを処理・識別するために、各オブジェクトの名前にはインデックスiが含まれます。

3.2.3. エントリーポイント、ストップロス、テイクプロフィットの表示

次の段階は、エントリーポイント、ストップロス(SL)、テイクプロフィット(TP)といった取引パラメータを指定することです。これは、XAB、BCD、DEXの構造を三角形でマークし、三尊の形状を強調した後におこなわれます。これらの要素は、検出したパターンを包括的な取引戦略へと発展させるために不可欠です。 ローソク足がポイントDを下抜けてクローズした際にエントリーをおこないます。これは、価格がネックライン領域を拒否し、パターンが予測する方向へ動き始めた可能性を示しています。この時点で取引は有効と見なされ、実行が可能となります。

ストップロスはポイントEのすぐ上に設定します。ここにSLを置くことで、パターンが失敗し価格が予期せず反転した場合に取引を保護できます。Eはパターン完成前の直近のスイングハイであるため、ここを基準にすることで、パターンが否定された場合のみ取引が無効化される合理的な設定となります。 テイクプロフィットは、パターンの起点であり価格が過去に反転した信頼性の高い参照レベルであるポイントXに最初に設定します。ただし、エントリーからXまでの距離でリスクリワード比(RRR)が少なくとも1:1に達しない場合、TPはさらに拡張され、最低でも1:2を目指すようにします。リスクに対して有利なリターンを確保することは、持続可能な取引戦略の重要な要素です。

int n_bars;
int n_bars_2;

string sl_t;
string tp_t;

double sl_price;
double tp_price;
for(int o = n; o < rates_total - 1; o++)
  {

   if(close[o] < d_e_ll && time[o] >= time[n+(z/2)])
     {

      n_bars = Bars(_Symbol,PERIOD_CURRENT,x_a_ll_t, E_time);
      n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[n+(z/2)], time[o]);
      if(n_bars_2 <= n_bars)
        {


         double sl_zone = MathAbs(E - close[o]);
         double tp_zone = MathAbs(close[o] - x_a_ll);



         bool no_cross = false;

         for(int p = n + (z/2); p < o; p++)
           {

            if(close[p] < d_e_ll)
              {

               no_cross = true;

               break;

              }

           }

         if(no_cross == false)
           {
            if(tp_zone >= sl_zone)
              {

               string loss_zone = StringFormat("Loss %d", i);
               ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]);
               ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true);
               ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true);
               ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr);



               string   sell_object = StringFormat("Sell Object%d", i);
               ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]);

               string win_zone = StringFormat("Win %d", i);
               ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],x_a_ll);
               ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true);
               ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true);
               ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr);


               sl_price =  E;
               tp_price =  x_a_ll;

               string sl_d_s = DoubleToString(sl_price,_Digits);
               string tp_d_s = DoubleToString(tp_price,_Digits);

               sl_t = StringFormat("sl %d", i);
               tp_t = StringFormat("tp %d", i);

               ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price);
               ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s);
               ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8);
               ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr);

               ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],x_a_ll);
               ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s);
               ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8);
               ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr);

              }

            if(tp_zone < sl_zone)
              {
               string loss_zone = StringFormat("Loss %d", i);
               ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]);
               ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true);
               ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true);
               ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr);

               string   sell_object = StringFormat("Sell Object%d", i);
               ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]);

               double n_tp = MathAbs(close[o] - (sl_zone * 2));

               string win_zone = StringFormat("Win %d", i);
               ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],n_tp);
               ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true);
               ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true);
               ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr);

               sl_price =  E;
               tp_price =  n_tp;

               string sl_d_s = DoubleToString(sl_price,_Digits);
               string tp_d_s = DoubleToString(tp_price,_Digits);

               sl_t = StringFormat("sl %d", i);
               tp_t = StringFormat("tp %d", i);

               ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price);;
               ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s);
               ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8);
               ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr);

               ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],tp_price);
               ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s);
               ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8);
               ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr);


              }

            
           }



        }

      break;

     }
  }

出力

図9:エントリーポイント、ストップロス、テイクプロフィットの表示

説明

このコード部分のロジックは、先に強調された三尊構造を用いて正当な取引設定を判定することを中心としています。その後、グラフィカルオブジェクトを利用して、チャート上にエントリーポジション、ストップロス(SL)、テイクプロフィット(TP)を視覚的に表示します。 重要なポイント間のローソク足本数は、整数変数n_barsおよびn_bars_2を使用して計算・比較されます。これにより、現在の価格推移が依然としてセットアップの許容範囲内にあるかどうかを判断できます。一方、sl_priceとtp_priceにはそれぞれの価格レベルが格納され、sl_tとtp_tの文字列はSLおよびTPのテキストオブジェクトを動的に命名するために使用されます。

パターン完成点(n)から開始し、メインのロジックはローソク足を走査するループ内で動作します。条件「if (close[o] < d_e_ll && time[o] >= time[n+(z/2)])」は、ネックラインレベル(ポイントD)を下抜けてクローズしたローソク足を探し、これがエントリーの可能性を示します。この条件が満たされると、ポイントXとE間のバー数(n_bars)、およびパターンの中点と現在のローソク足間のバー数(n_bars_2)が計算されます。これにより、取引が妥当な時間枠内にあることが保証されます。

次に、エントリープライスをポイントEおよびXと比較して、ストップロス(sl_zone)およびテイクプロフィット(tp_zone)の値を算出します。さらに、このエントリーが初回のブレイクアウトであることを確認するため、現時点より前にポイントDを下抜けてクローズしたローソク足が存在しないかどうかをチェックするループが実行されます。このチェックに合格すると、スクリプトは処理を継続します。

チャート上でストップロスとテイクプロフィットのゾーンを視覚的に表現するために、コードはOBJ_RECTANGLEを用いて長方形を生成します。ただしこれは、潜在的なリワード(TP)がリスク(SL)以上である場合に限られます。加えて、エントリーポイント付近には売り矢印(OBJ_ARROW_SELL)が配置されます。sl_priceおよびtp_price変数には、実際のSLとTPの価格値が保持されます。ラベルはOBJ_TEXTで作成され、色やフォントを用いてチャート上にこれらのレベルを明確に表示します。 リスクリワード比が1:1未満(すなわちTPがSLより小さい)である場合、スクリプトは調整を行い、SL距離の3倍に相当する新しいTPゾーンを計算して、最低でも1:2のより有利なRRRを確保します。その後、この代替構成に対しても同様のグラフィカルオブジェクトが生成され、修正されます。


4. パターンに基づく取引の実行

このパートの目的は、単にトレードシナリオを認識して表示すること以上のものです。本セクションは、検出されたパターンに基づいて実際にトレードを実行することに焦点を当てています。一方、前のセクションでは、チャート上にエントリー、ストップロス(SL)、テイクプロフィット(TP)のレベルをマークすることに重点が置かれていました。EAは、例えば価格がポイントDを下抜けてクローズした場合の売りシナリオのように、要件が満たされた時点で自動的に取引を開始する必要があります。テイクプロフィットはポイントXに設定するか、リスクリワード比が少なくとも1:2になるように調整し、ストップロスはポイントEに設定します。このステップを踏むことで、EAは単なる視覚的ツールから、人間の介入を必要とせずにエントリーし、管理できる完全自動化システムへと変換されます。

#include <Trade/Trade.mqh>
CTrade trade;
int MagicNumber = 5122025;
datetime lastTradeBarTime = 0;
double ask_price;


//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   trade.SetExpertMagicNumber(MagicNumber);


//---
   return(INIT_SUCCEEDED);
  }
ask_price = ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
datetime currentBarTime = iTime(_Symbol, timeframe, 0);


//FOR SELL
if(show_sell)
  {
   if(rates_total >= bars_check)
     {

      for(int z = 7; z <= 10; z++)
        {

         for(int i = rates_total - bars_check; i < rates_total - z; i++)
           {

            if(IsSwingLow(low, i, z))
              {

               // If a swing low is found, store its price, time, and create a name for the objects to mark the X.
               X = low[i]; // Price of the swing low (X).
               X_time = time[i]; // Time of the swing low (X).
               X_letter = StringFormat("X%d", i); // Unique name for the text label object.


               for(int j = i; j < rates_total - z; j++)
                 {
                  if(IsSwingHigh(high, j, z) && time[j] > X_time)
                    {

                     A = high[j]; // Price of the swing high (A).
                     A_time = time[j]; // Time of the swing high (A)
                     A_letter = StringFormat("A%d", j); // Unique name for the text label object


                     for(int k = j; k < rates_total - z; k++)
                       {
                        if(IsSwingLow(low, k, z) && time[k] > A_time)
                          {

                           B = low[k]; // Price of the swing low (B).
                           B_time = time[k]; // Time of the swing low (B).
                           B_letter = StringFormat("B%d", k); // Unique name for the text label object.


                           for(int l = k ; l < rates_total - z; l++)
                             {

                              if(IsSwingHigh(high, l, z) && time[l] > B_time)
                                {

                                 C = high[l]; // Price of the swing high (C).
                                 C_time = time[l]; // Time of the swing high (C).
                                 C_letter = StringFormat("C%d", l); // Unique name for the text label object.

                                 for(int m = l; m < rates_total - z; m++)
                                   {

                                    if(IsSwingLow(low, m, z) && time[m] > C_time)
                                      {

                                       D = low[m]; // Price of the swing low (D).
                                       D_time = time[m]; // Time of the swing low (D).
                                       D_letter = StringFormat("D%d", m); // Unique name for the text label object.

                                       for(int n = m ; n < rates_total - (z/2) - 1; n++)
                                         {

                                          if(IsSwingHigh(high, n, (z/2)) && time[n] > D_time)
                                            {

                                             E = high[n]; // Price of the swing high (E).
                                             E_time = time[n]; // Time of the swing high (E).
                                             E_letter = StringFormat("E%d", n); // Unique name for the text label object.


                                             d_e_bars = Bars(_Symbol, PERIOD_CURRENT, D_time, E_time); // Count the number of bars between D and E
                                             d_lowest_index = ArrayMinimum(low, m, d_e_bars); // Find the index of the lowest low in the range
                                             d_e_ll = low[d_lowest_index]; // Store the lowest low (D - E lowest point)
                                             d_e_ll_t = time[d_lowest_index]; // Store the corresponding time
                                             D_3bar_high = high[d_lowest_index - 3]; // The high price of the third bar before the bar that formed D

                                             c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time,d_e_ll_t); // Count the number of bars between C and V
                                             c_highest_index = ArrayMaximum(high,l,c_d_bars); // Find the index of the highest high in the range
                                             c_d_hh = high[c_highest_index];  // Store the lowest high (C - D lowest point)
                                             c_d_hh_t = time[c_highest_index]; // Store the corresponding time

                                             b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_hh_t); // Count the number of bars between B and C
                                             b_lowest_index = ArrayMinimum(low, k, b_c_bars); // Find the index of the lowest low in the range
                                             b_c_ll = low[b_lowest_index]; // Store the lowest low B - C lowest point)
                                             b_c_ll_t = time[b_lowest_index]; // Store the corresponding time
                                             B_high = high[b_lowest_index];  // The high price of the bar that formed swing low D


                                             a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time,b_c_ll_t); // Count the number of bars between A and B
                                             a_highest_index = ArrayMaximum(high,j,a_b_bars); // Find the index of the highest high in the range
                                             a_b_hh = high[a_highest_index]; // Store the lowest low A - B lowest point)
                                             a_b_hh_t = time[a_highest_index];  // Store the corresponding time
                                             A_low = low[a_highest_index];

                                             x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_hh_t); // Count the number of bars between C and D
                                             x_lowest_index = ArrayMinimum(low, i, x_a_bars); // Find the index of the lowest low in the range
                                             x_a_ll = low[x_lowest_index]; // Store the lowest low (C - D lowest point)
                                             x_a_ll_t = time[x_lowest_index]; // Store the corresponding time for C - D

                                             E_3bar_low = low[n - 3]; // The LOW price of the third bar before the bar that formed E


                                             if(a_b_hh > x_a_ll && b_c_ll < a_b_hh && b_c_ll > x_a_ll && c_d_hh > a_b_hh && E < c_d_hh && d_e_ll > x_a_ll
                                                && d_e_ll <= B_high && D_3bar_high >= B_high && E > A_low && E_3bar_low < a_b_hh)
                                               {

                                                ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X);
                                                ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X");
                                                ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A);
                                                ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A");
                                                ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B);
                                                ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B");
                                                ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C);
                                                ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C");
                                                ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D);
                                                ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D");
                                                ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E);
                                                ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E");
                                                ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr);

                                                xa = StringFormat("XA line%d", i);
                                                ab = StringFormat("AB line%d", i);
                                                bc = StringFormat("BC line%d", i);
                                                cd = StringFormat("CD line%d", i);
                                                de = StringFormat("DE line%d", i);
                                                ex = StringFormat("EX line%d", i);


                                                ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_ll_t,x_a_ll);
                                                ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X");
                                                ObjectSetInteger(chart_id,X_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_hh_t,a_b_hh);
                                                ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A");
                                                ObjectSetInteger(chart_id,A_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_ll_t,b_c_ll);
                                                ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B");
                                                ObjectSetInteger(chart_id,B_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_hh_t,c_d_hh);
                                                ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C");
                                                ObjectSetInteger(chart_id,C_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,d_e_ll_t,d_e_ll);
                                                ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D");
                                                ObjectSetInteger(chart_id,D_letter,OBJPROP_COLOR,txt_clr);

                                                ObjectCreate(chart_id,E_letter,OBJ_TEXT,0,E_time,E);
                                                ObjectSetString(chart_id,E_letter,OBJPROP_TEXT,"E");
                                                ObjectSetInteger(chart_id,E_letter,OBJPROP_COLOR,txt_clr);


                                                ObjectCreate(chart_id, xa,OBJ_TREND,0,x_a_ll_t,x_a_ll,a_b_hh_t,a_b_hh);
                                                ObjectSetInteger(chart_id,xa,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,xa,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, xa, OBJPROP_BACK, true);

                                                ObjectCreate(chart_id, ab,OBJ_TREND,0,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll);
                                                ObjectSetInteger(chart_id,ab,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,ab,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, ab, OBJPROP_BACK, true);

                                                ObjectCreate(chart_id, bc,OBJ_TREND,0,b_c_ll_t,b_c_ll,c_d_hh_t,c_d_hh);
                                                ObjectSetInteger(chart_id,bc,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,bc,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, bc, OBJPROP_BACK, true);

                                                ObjectCreate(chart_id, cd,OBJ_TREND,0,c_d_hh_t,c_d_hh,d_e_ll_t,d_e_ll);
                                                ObjectSetInteger(chart_id,cd,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,cd,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, cd, OBJPROP_BACK, true);

                                                ObjectCreate(chart_id, de,OBJ_TREND,0,d_e_ll_t,d_e_ll,E_time,E);
                                                ObjectSetInteger(chart_id,de,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,de,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, de, OBJPROP_BACK, true);

                                                ObjectCreate(chart_id, ex,OBJ_TREND,0,E_time,E,time[n+(z/2)],x_a_ll);
                                                ObjectSetInteger(chart_id,ex,OBJPROP_WIDTH,3);
                                                ObjectSetInteger(chart_id,ex,OBJPROP_COLOR,clrSaddleBrown);
                                                ObjectSetInteger(chart_id, ex, OBJPROP_BACK, true);

                                                A_zone = StringFormat("A ZONEe%d", i);
                                                B_zone = StringFormat("B ZONEe%d", i);

                                                ObjectCreate(chart_id,A_zone,OBJ_RECTANGLE,0,a_b_hh_t,a_b_hh,E_time,A_low);
                                                ObjectCreate(chart_id,B_zone,OBJ_RECTANGLE,0,b_c_ll_t,b_c_ll,d_e_ll_t,B_high);

                                                xa_line_t = ObjectGetTimeByValue(chart_id,xa,b_c_ll,0);
                                                ex_line_t = ObjectGetTimeByValue(chart_id,ex,d_e_ll,0);

                                                X_A_B = StringFormat("XAB %d", i);
                                                ObjectCreate(chart_id,X_A_B,OBJ_TRIANGLE,0,xa_line_t,b_c_ll,a_b_hh_t,a_b_hh,b_c_ll_t,b_c_ll);
                                                ObjectSetInteger(chart_id, X_A_B, OBJPROP_FILL, true);
                                                ObjectSetInteger(chart_id, X_A_B, OBJPROP_BACK, true);
                                                ObjectSetInteger(chart_id, X_A_B, OBJPROP_COLOR, shoulder_clr);

                                                B_C_D = StringFormat("BCD %d", i);
                                                ObjectCreate(chart_id, B_C_D, OBJ_TRIANGLE, 0, b_c_ll_t, b_c_ll, c_d_hh_t, c_d_hh, d_e_ll_t, d_e_ll);
                                                ObjectSetInteger(chart_id, B_C_D, OBJPROP_COLOR, head_clr);
                                                ObjectSetInteger(chart_id, B_C_D, OBJPROP_FILL, true);
                                                ObjectSetInteger(chart_id, B_C_D, OBJPROP_BACK, true);

                                                D_E_X = StringFormat("DEX %d", i);
                                                ObjectCreate(chart_id, D_E_X, OBJ_TRIANGLE, 0, d_e_ll_t, d_e_ll, E_time, E, ex_line_t, d_e_ll);
                                                ObjectSetInteger(chart_id, D_E_X, OBJPROP_COLOR, shoulder_clr);
                                                ObjectSetInteger(chart_id, D_E_X, OBJPROP_FILL, true);
                                                ObjectSetInteger(chart_id, D_E_X, OBJPROP_BACK, true);

                                                for(int o = n; o < rates_total - 1; o++)
                                                  {

                                                   if(close[o] < d_e_ll && time[o] >= time[n+(z/2)])
                                                     {

                                                      n_bars = Bars(_Symbol,PERIOD_CURRENT,x_a_ll_t, E_time);
                                                      n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[n+(z/2)], time[o]);
                                                      if(n_bars_2 <= n_bars)
                                                        {


                                                         double sl_zone = MathAbs(E - close[o]);
                                                         double tp_zone = MathAbs(close[o] - x_a_ll);



                                                         bool no_cross = false;

                                                         for(int p = n + (z/2); p < o; p++)
                                                           {

                                                            if(close[p] < d_e_ll)
                                                              {

                                                               no_cross = true;

                                                               break;

                                                              }

                                                           }

                                                         if(no_cross == false)
                                                           {
                                                            if(tp_zone >= sl_zone)
                                                              {

                                                               string loss_zone = StringFormat("Loss %d", i);
                                                               ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]);
                                                               ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true);
                                                               ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true);
                                                               ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr);



                                                               string   sell_object = StringFormat("Sell Object%d", i);
                                                               ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]);

                                                               string win_zone = StringFormat("Win %d", i);
                                                               ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],x_a_ll);
                                                               ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true);
                                                               ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true);
                                                               ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr);


                                                               sl_price =  E;
                                                               tp_price =  x_a_ll;

                                                               string sl_d_s = DoubleToString(sl_price,_Digits);
                                                               string tp_d_s = DoubleToString(tp_price,_Digits);

                                                               sl_t = StringFormat("sl %d", i);
                                                               tp_t = StringFormat("tp %d", i);

                                                               ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price);
                                                               ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s);
                                                               ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8);
                                                               ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr);

                                                               ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],x_a_ll);
                                                               ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s);
                                                               ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8);
                                                               ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr);

                                                              }

                                                            if(tp_zone < sl_zone)
                                                              {
                                                               string loss_zone = StringFormat("Loss %d", i);
                                                               ObjectCreate(chart_id,loss_zone,OBJ_RECTANGLE,0,E_time,E,time[o],close[o]);
                                                               ObjectSetInteger(chart_id, loss_zone, OBJPROP_FILL, true);
                                                               ObjectSetInteger(chart_id, loss_zone, OBJPROP_BACK, true);
                                                               ObjectSetInteger(chart_id, loss_zone, OBJPROP_COLOR, lz_clr);

                                                               string   sell_object = StringFormat("Sell Object%d", i);
                                                               ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[o],close[o]);

                                                               double n_tp = MathAbs(close[o] - (sl_zone * 3));

                                                               string win_zone = StringFormat("Win %d", i);
                                                               ObjectCreate(chart_id,win_zone,OBJ_RECTANGLE,0,E_time,close[o],time[o],n_tp);
                                                               ObjectSetInteger(chart_id, win_zone, OBJPROP_FILL, true);
                                                               ObjectSetInteger(chart_id, win_zone, OBJPROP_BACK, true);
                                                               ObjectSetInteger(chart_id, win_zone, OBJPROP_COLOR, wz_clr);

                                                               sl_price =  E;
                                                               tp_price =  n_tp;

                                                               string sl_d_s = DoubleToString(sl_price,_Digits);
                                                               string tp_d_s = DoubleToString(tp_price,_Digits);

                                                               sl_t = StringFormat("sl %d", i);
                                                               tp_t = StringFormat("tp %d", i);

                                                               ObjectCreate(chart_id,sl_t,OBJ_TEXT,0,time[o],sl_price);;
                                                               ObjectSetString(chart_id,sl_t,OBJPROP_TEXT,"SL - " + sl_d_s);
                                                               ObjectSetInteger(chart_id,sl_t,OBJPROP_FONTSIZE,8);
                                                               ObjectSetInteger(chart_id,sl_t,OBJPROP_COLOR,txt_clr);

                                                               ObjectCreate(chart_id,tp_t,OBJ_TEXT,0,time[o],tp_price);
                                                               ObjectSetString(chart_id,tp_t,OBJPROP_TEXT,"TP - " + tp_d_s);
                                                               ObjectSetInteger(chart_id,tp_t,OBJPROP_FONTSIZE,8);
                                                               ObjectSetInteger(chart_id,tp_t,OBJPROP_COLOR,txt_clr);


                                                              }

                                                            if(time[o] == time[rates_total-2] && currentBarTime != lastTradeBarTime)
                                                              {

                                                               trade.Sell(lot_size,_Symbol,ask_price, sl_price,tp_price);
                                                               lastTradeBarTime = currentBarTime;

                                                              }

                                                           }



                                                        }

                                                      break;

                                                     }
                                                  }




                                               }

                                             break;
                                            }
                                         }

                                       break;
                                      }
                                   }

                                 break;
                                }
                             }

                           break;
                          }
                       }

                     break;
                    }
                 }
              }
           }
        }
     }
  }

出力

図10:パターンに基づいた取引の実行

説明

このコードブロックは、MQL5の組み込みクラスであるCTradeを用いて取引実行を管理しています。CTradeクラスは、注文の発注、編集、決済といった取引作業を簡略化します。この取引ライブラリは、冒頭で#include <Trade/Trade.mqh>を記述することで読み込まれます。次に、CTrade trade;でこのクラスのインスタンスを作成し、int MagicNumber = 5122025;で、このEAがおこなう取引を一意に識別するためのIDを定義します。MagicNumberは、このEAによる取引を手動や他のEAによる取引と区別するのに役立つため、非常に重要です。取引にはSetExpertMagicNumberを用いてMagicNumberを設定します。

SymbolInfoDouble(_Symbol, SYMBOL_ASK);は、銘柄の現在の売値(Ask)で変数ask_priceを初期化するために使用されます。売り注文はこの価格で実行されます。その後、コードはtime[o] == time[rates_total-2]を使用して、現在のバーが直近のものであるかどうかを確認します。加えて、currentBarTime != lastTradeBarTimeという条件で、現在のバーで既に取引がおこなわれていないことを確認します。このチェックにより、EAが同じバーで複数回注文を発行することを防ぎます。

両方の条件が満たされると、EAはSell()関数を使用して売り注文を発行します。ask_priceはエントリープライス、sl_priceはストップロスの水準、tp_priceはテイクプロフィットの水準、lot_sizeは取引ロット数、_Symbolは現在の取引銘柄(例:EURUSD)です。取引が正常に実行された後、EAはcurrentBarTimeの値をlastTradeBarTime変数に追加します。この更新は、EAが同じバーを基に複数の取引を行わず、正当なパターンシグナルごとに1回だけ取引を行うことを保証する上で不可欠です。

なお、売りシナリオを検出・表示するために用いた同じロジックは、反対の処理をおこなうことで買いシナリオにも同様に利用できます。買い戦略では、ローソク足がネックライン(ポイントD)を上抜けてクローズするのを待ちます。その後、テイクプロフィットは最高点(X)に、ストップロスは最安点(E)に設定されます。リスクリワードの考え方、視覚的表示、条件はすべて同一であり、強気の構造に対して反転させて適用されるだけです。本記事に付属するソースコードには、このロジックが完全に実装されており、詳細を検証したり修正を加えたりしたい方は利用可能です。


結論

本記事では、MQL5でテクニカルチャートパターン、特に三尊を識別し、それに基づいて取引をおこなうEAの構築方法を解説しました。まず重要なスイングポイントを検出してマークし、その後、三角形や長方形といったグラフィカルツールを用いてXAB、BCD、DEXの構造を視覚的に表現しました。次に、エントリーポイント、ストップロス、テイクプロフィットといった明確な取引パラメータを定義し、リスクリワード比が合理的な基準を満たすことも確認しました。最後に、確認済みのシグナルに基づいて実際の注文を発注するトレード実行ロジックを実装し、EAを完全自動化しました。



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

添付されたファイル |
最後のコメント | ディスカッションに移動 (6)
Israel Pelumi Abioye
Israel Pelumi Abioye | 22 5月 2025 において 17:46
Oluwatosin Mary Babalola #:
ありがとうございます。こういうわけで、いつもあなたの記事を楽しみにしているんです。とても分かりやすい!
どういたしまして
Miguel Angel Vico Alba
Miguel Angel Vico Alba | 22 5月 2025 において 22:31
Israel Pelumi Abioye # どういたしまして

君は素晴らしいよ、イスラエル!

これからも素晴らしい仕事を続けてください!❤️

Israel Pelumi Abioye
Israel Pelumi Abioye | 22 5月 2025 において 22:35
Miguel Angel Vico Alba #:

イスラエル、あなたは素晴らしい!

これからも素晴らしい仕事を続けてください!❤️

こんにちは、ミゲル。

嬉しいお言葉をありがとうございます。❤️

daniels goodness
daniels goodness | 26 5月 2025 において 14:00
私はプログラマーではないので、これを理解するのに時間がかかりましたが、分解された方法とステップバイステップのプロセスは私にやる気を与えてくれました。💯
Israel Pelumi Abioye
Israel Pelumi Abioye | 26 5月 2025 において 17:24
daniels goodness #:
私はプログラマーではないので、これを理解するのに時間がかかりましたが、分解された方法とステップバイステップのプロセスは私にやる気を与えてくれました。💯

ダニエルズさん、こんにちは。

そう言っていただけて嬉しいです!❤️

プライスアクション分析ツールキットの開発(第24回):プライスアクション定量分析ツール プライスアクション分析ツールキットの開発(第24回):プライスアクション定量分析ツール
ローソク足のパターンは、潜在的な市場の動きに関する貴重な洞察を提供します。単一のローソク足でも、現在のトレンドの継続を示すものもあれば、価格の動きの中での位置によって反転を示唆するものもあります。本記事では、4つの主要なローソク足形成を自動で識別するエキスパートアドバイザー(EA)を紹介します。次のセクションを参照して、このツールがどのようにプライスアクション分析を強化できるかを学んでください。
知っておくべきMQL5ウィザードのテクニック(第66回):FrAMAのパターンとForce Indexを内積カーネルで使用する 知っておくべきMQL5ウィザードのテクニック(第66回):FrAMAのパターンとForce Indexを内積カーネルで使用する
FrAMAインジケーターとForce Indexオシレーターは、トレンドと出来高のツールであり、エキスパートアドバイザー(EA)を開発する際に組み合わせることができます。前回の記事では、このペアを紹介し、機械学習の適用可能性を検討しました。畳み込みニューラルネットワークを使用しており、内積カーネルを利用して、これらのインジケーターの入力に基づいた予測をおこないます。これは、MQL5ウィザードと連携してEAを組み立てるカスタムシグナルクラスファイルで実行されます。
初心者からエキスパートへ:自動幾何解析システム 初心者からエキスパートへ:自動幾何解析システム
幾何学的パターンは、トレーダーに価格動向を簡潔に解釈する手段を提供します。多くのアナリストは手作業でトレンドラインや長方形、その他の形状を描き、形成されたパターンに基づいて取引判断をおこないます。本記事では、自動化による代替手段、すなわちMQL5を活用して最も一般的な幾何学パターンを検出・分析する方法を探ります。方法論を分解して説明し、実装の詳細を論じ、自動パターン認識がトレーダーの市場洞察をどのように鋭くできるかを強調します。
MQL5 Algo Forgeへの移行(第2回):複数のリポジトリの操作 MQL5 Algo Forgeへの移行(第2回):複数のリポジトリの操作
本稿では、プロジェクトのソースコードを公開リポジトリに保存する際の1つのアプローチについて検討します。コードを複数のブランチに分散させることで、プロジェクト開発における明確で便利なルールを確立していきます。