English Русский 中文 Español Deutsch Português
preview
MQL5を使用してトレンドとチャートパターンを検出する方法

MQL5を使用してトレンドとチャートパターンを検出する方法

MetaTrader 5トレーディング | 15 6月 2023, 09:48
2 175 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

はじめに

トレーダーは皆チャートを使います。値動きで起こり得るさまざまなシナリオを理解する上で優位に立って正しい決定を下すために、チャートを正しく読むよう努めます。なぜなら、チャートには、認識すれば潜在的な価格変動をを予測するのに役立つような、多くのパターンが表示される可能性があるからです。これを簡単かつ正確におこなうのに役立つ便利なツールがあれば、それは良いことだと思います。この記事では、この文脈で役立つツールをいくつか提供しようとします。チャートに表示されるものを検出する方法と、すべてのトレーダーが読み取る必要がある価格パターンを説明します。これらのパターンは、値動きによって形成することができるトレンドやチャートのパターンと同じです。

これについて、次のトピックで説明します。 

この記事を読むと、高値と安値を検出し、それに応じてトレンドの種類、ダブルトップ、ボトムを識別できるようになります。実際の口座で使用する前に、前述のコードを自分で書いてみて、より良い洞察と結果を得るために必要なものをテストして開発する必要があります。この記事の主な目的は、高値と安値およびチャートパターンを検出する主な概念を理解してコードを開発し、既知または未知の重要なパターンから必要なものを検出することです。このチャートから利益を得る方法を理解していれば、取引に大きな変化をもたらす可能性があります。

この記事では、MetaTrader 5取引ターミナルに組み込まれているMQL5(MetaQuotes言語)IDEを使用します。MetaTrader 5をインストールする方法やMetaEditorを使用する方法を知りたい方は、前回の記事の「MetaEditorでMQL5コードを書く」トピックを読んでみてください。

免責条項:すべての情報は「現状有姿」で提供され、情報提供のみを目的としており、取引目的やアドバイスを目的としたものではありません。いかなる結果も保証するものではありません。読者がこれらの資料を自分の取引口座で使用する場合、自己責任でおこなってください。

高値と安値の検出

この部分では、MQL5によってチャート上の高値と安値を検出することから始め、次にこれをベースとして使用して、各チャートパターンごとに条件を宣言します。まず、誰もが知っているように、高値と安値の定義は次のとおりです。

高値:

高値とは、買い手の強さにより特定のレベルまで上昇した後、売り手が現れ、この高レベルから価格を押し下げたことを意味します。次の図は一例です。   

高値

安値:

安値は、売り手の強さにより特定のレベルまで下落し、その後買い手が現れ、この低いレベルから価格を押し上げたことを意味します。次の図は一例です。

安値

これら2つの重要な価格レベルを特定した後、これらの種類の動きを検出できるMQL5プログラムまたはエキスパートアドバイザー(EA)を作成する必要があります。これをおこなうために使用できる方法は多数ありますが、次にこれらの方法の1つを提供します。

特定の価格レベル(高値と安値)を決定する必要があり、その後、他の特定の価格レベル(高値と安値)に移動し、高値と高値、低値と安値を比較して、別の高値か安値があるかを判断します。これをおこなうには、次のような特定の手順を実行する必要があります。

OnTick()スコープの外に関数を作成して、高値または安値を返します。整数変数としてgetNextMoveという名前を付けます。この関数に設定する必要があるパラメータは次のとおりです。

  • Intmove:移動が高いか低いか
  • intcount:startPos変数に関連するカウント
  • intstartPos:開始する必要がある開始位置
int getNextMove(int move, int count, int startPos)

この関数内では、if文を使用して関数パラメータの値を特定することで次のチェックをおこないます。startPosがゼロより小さいかどうかをチェックし、startPosの値をカウント値に追加して更新する必要があります。startPosにゼロ値を指定すると、現在のバーから開始されます。

   if(startPos<0)
     {
      count +=startPos;
      startPos =0;
     }

ここで、関数内のcount変数とstartPos変数を特定しました。move変数は、関数の実行を終了して移動値を返すreturn演算子を使用し、3つの式で構成される三項演算子(?:)を使用することにより、戻り値で識別されます。最初の式はbool型のデータを返します。trueの場合は2番目の式が実行され、falseの場合は3番目の式が実行されます。

最初の演算子で移動変数がhighに等しいかどうかを指定します。trueの場合は最高値Highが返され、falseの場合は最低値Lowが返されます。

値動きが高いかどうかを確認するために、高値を返すためにiHighest()関数とiLowest()関数で使用される時系列識別子の1つであるMODE_HIGH関数を使用します。iHighest関数とiLowest関数のパラメータが最高値と最低値のインデックスを返すには、次のようになります。

  • symbol:Symbol()を使用して、現在の銘柄名をconst文字列として返す
  • timeframe:Period()を使用して、現在の時間枠をENUM_TIMEFRAMESとして返す
  • type:(ENUM_SERIESMODE)moveを使用して、移動タイプを時系列識別子として返す(iHighestの場合は高値、iLowestの場合は安値)
  • count:整数のcount変数を使用して要素の数を返す
  • start:整数のstartPos変数を使用してインデックスを返す
   return((move==MODE_HIGH)?
          iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
          iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));

次の移動を返すこの関数を作成した後、現在の移動の高値または低値を取得するためのメインとなる別の整数関数を作成します。その名前はgetmoveとなり、パラメータとして3つの整数変数(move、count、startPos)が含まれます。

int getmove(int move, int count, int startPos)

この関数内で、moveがMODE_HIGHにもMODE_LOWにも等しくない場合、戻り値を-1にします。

if(move!=MODE_HIGH && move!=MODE_LOW)
      return (-1);

新しいcurrentBar整数変数を作成し、startPosを割り当てます。

int currentBar=startPos;

新しいmoveReturned整数変数を作成し、パラメータ(move, (count*2+1), currentBar-count))を使用して、作成したgetNextMove関数を割り当てます。

int moveReturned=getNextMove(move,count*2+1,currentBar-count);

式をチェックするためにwhileを使用するループを作成します。式がtrueの場合、演算子が実行されます。ここでチェックする必要がある式は、moveReturnedがcurrentBarと等しくないかどうかです。それがtrueの場合、次を実行します。

  • getNextMoveを(move, count,currentBar+1)のパラメータで使用して、currentBar変数を更新
  • getNextMoveを(move,count*2+1,currentBar-count)のパラメータで使用して、moveReturned変数を更新
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+1);
      moveReturned=getNextMove(move,count*2+1,currentBar-count);
     }

次に、returnを使用してcurrentBar値を返して関数を終了します。

return(currentBar);

次に、OnTick()の内部に移動し、高値と安値の検出に役立つものを呼び出します。まず、3つの整数変数を作成します。

   int checkBars= 5; 
   int move1;
   int move2;

事前に作成したgetmove関数でmove1とmove2を、更新し、2つの高値を検出します(move1の場合パラメータ(MODE_HIGH,checkBars,0)、とmove2の場合パラメータ(MODE_HIGH,checkBars,move1+1))。

   move1=getmove(MODE_HIGH,checkBars,0);
   move2=getmove(MODE_HIGH,checkBars,move1+1);

次の手順で、これら2つのhighの上にラインオブジェクトを作成します。

名前付きのオブジェクトを削除するObjectDeleteを使用して、既存の行があれば削除します。この関数にはパラメータがあります。最初のパラメータはチャート識別子を示すchart_idで、現在のチャートには0を使用します。2番目のパラメータはオブジェクト名を示す名前で、文字列「topLine」を使用します。

ObjectDelete(0,"topLine");

topLineオブジェクトを新規作成するには、オブジェクトを新規作成するObjectCreate関数を使用します。パラメータは次の通りです。

  • chart_id:long型のチャート識別子を返す(0を使用)
  • name:文字列型のオブジェクト名を返す(topLineを使用)
  • type:ENUM_OBJECT型またはオブジェクト型を返す(OBJ_TRENDを使用)
  • nwin:現在のチャートのウィンドウインデックス(0を使用)
  • time1:move2アンカーの時間を決定してdatetype型を返す(iTime(Symbol(),Period(),move2)を使用)
  • price1:move2アンカーの価格を決定してdouble型を返す(iHigh(Symbol(),Period(),move2)を使用)
  • timeN=0:move1アンカーの時間を決定してdatetype型を返す(iTime(Symbol(),Period(),move1)を使用)
  • PriceN=0:move1アンカーの価格を決定してdouble型を返す(iHigh(Symbol(),Period(),move1)を使用)

ご覧のとおり、iHigh関数はバーの高値を返し、そのパラメータは銘柄、時間枠、シフトです。iTime関数はバーの開始時間を返し、そのパラメータはiHigh関数と同じです。

ObjectCreate(0,"topLine",OBJ_TREND,0,iTime(Symbol(),Period(),move2),iHigh(Symbol(),Period(),move2),iTime(Symbol(),Period(),move1),iHigh(Symbol(),Period(),move1));

ObjectSetInteger関数を使用して、この作成されたオブジェクトの色、特定の幅、および線の種類を設定します。パラメータは次の通りです。

  • chart_id:チャートの識別子(0)
  • name:オブジェクト名(高値の場合は「TopLine」)
  • prop_id:オブジェクトのプロパティ(色はOBJPROP_COLOR、幅はOBJPROP_WIDTH、線の種類はOBJPROP_RAY_RIGHT)
  • prop_value:目的の値(色はclrRed、幅は3、線の種類はtrue)
   ObjectSetInteger(0,"topLine",OBJPROP_COLOR,clrRed);
   ObjectSetInteger(0,"topLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"topLine",OBJPROP_RAY_RIGHT,true);

高値の場合と同じようにmove1変数とmove2変数を更新して2つの安値を取得しますが、時系列識別子としてモードはMODE_LOWになります。

   move1=getmove(MODE_LOW,checkBars,0);
   move2=getmove(MODE_LOW,checkBars,move1+1);

これら2つの安値の下のラインオブジェクトの削除と作成は高値と同じですが、オブジェクトの名前が「bottomLine」になることと緑色であることが若干異なります。

   ObjectDelete(0,"bottomLine");
   ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),move2),iLow(Symbol(),Period(),move2),iTime(Symbol(),Period(),move1),iLow(Symbol(),Period(),move1));
   ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true);

以下は、1つのコードブロック内の完全なコードです。

//+------------------------------------------------------------------+
//|                                                   moveFinder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnTick()
  {
   int checkBars= 5; 
   int move1;
   int move2;
   move1=getmove(MODE_HIGH,checkBars,0);
   move2=getmove(MODE_HIGH,checkBars,move1+1);
   ObjectDelete(0,"topLine");
   ObjectCreate(0,"topLine",OBJ_TREND,0,iTime(Symbol(),Period(),move2),iHigh(Symbol(),Period(),move2),iTime(Symbol(),Period(),move1),iHigh(Symbol(),Period(),move1));
   ObjectSetInteger(0,"topLine",OBJPROP_COLOR,clrRed);
   ObjectSetInteger(0,"topLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"topLine",OBJPROP_RAY_RIGHT,true);
   move1=getmove(MODE_LOW,checkBars,0);
   move2=getmove(MODE_LOW,checkBars,move1+1);
   ObjectDelete(0,"bottomLine");
   ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),move2),iLow(Symbol(),Period(),move2),iTime(Symbol(),Period(),move1),iLow(Symbol(),Period(),move1));
   ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true);
  }
int getmove(int move, int count, int startPos)
  {
   if(move!=MODE_HIGH && move!=MODE_LOW)
      return (-1);
   int currentBar=startPos;
   int moveReturned=getNextMove(move,count*2+1,currentBar-count);
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+1);
      moveReturned=getNextMove(move,count*2+1,currentBar-count);
     }
   return(currentBar);
  }
int getNextMove(int move, int count, int startPos)
  {
   if(startPos<0)
     {
      count +=startPos;
      startPos =0;
     }
   return((move==MODE_HIGH)?
          iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
          iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));
  }

このコードをエラーなしでコンパイルして実行すると、チャート上に2つの線が表示され、その上に赤い線、2つの安値とその下にある緑の線で2つの高値を検出できます。以下はテストの例です。

moveFinderシグナル1

前の例でわかるように、これら2本の線はチャート上で価格の動きを示すパターンを示すことができます。前の例では、2本の上向きの線と、上のものは下のものより角度が広いです。したがって、値動きを解釈するための非常に便利なツールとなります。

以下は、異なる値動きに応じた異なるパターンの別の例です。

moveFinderシグナル2

前のチャートで確認できるのと同じように、2本の線は平行に移動していますが、下の線が上向きに移動し、上の線が下向きに移動しているため、異なる動きを示す異なる値動きがあります。これは、間のバランスがあることを示しています。買い手と売り手は、買い手が価格を高く押し、売り手は同時に価格を下げます。

以下は、値動きの別のパターンの例です。

moveFinderシグナル3

前のチャートでわかるように、売り手が価格を押し下げる強さを示すことができる2本の平行な下降線があるため、異なるチャートパターンになっています。

前の部分でチャート上の高値と安値を検出する方法を学習した後、2つの高値と2つの安値を検出したため、チャート上のトレンドを検出するコードを開発できます。これがトレンドを特定するために必要なものです。この記事のこの部分の以下の内容は、いくつかの違いはあるものの、前のコードによって3つのタイプのチャート上のトレンドを可能な限り検出するための以前のコードの開発についてです。

簡単に言うと、トレンドは値動きであり、この動きは上向き、下向き、または明確な方向性がない場合があります。これらは次の3種類のトレンドと同じです。

上昇トレンド:

このタイプの価格変動は、市場において買い手が強い当事者であるため、価格が上昇し続け、より高い価格を達成することになります。したがって、チャートでは、価格がより高い安値とより高い高値を明確に形成していることがわかります。次の図は、このタイプのグラフです。

上昇トレンド

下降トレンド:

このタイプのトレンドは、上昇トレンドタイプの逆のシナリオです。この下降トレンドタイプでは売り手が買い手よりも強く、価格を押し下げてより低い価格を実現します。したがって、チャートでは、価格がより低い高値とより低い安値を形成していることがわかります。

以下は、それを視覚的な観点から説明したグラフです。

下降トレンド

横ばい:

このタイプでは、上昇トレンドまたは下降トレンドと言える値動きは見つかりません。したがって、このタイプは上昇トレンドまたは下降トレンドを除くあらゆる形式であり、多くの形式があり、次の図はこれらの形式の一部です。

トレンドなし  トレンド2なし  トレンド3なし

ここで、トレンド(上昇または下降)があるかどうか、またはトレンドがない(横向き)かを検出できるMQL5 EAを作成する必要があります。次のコードは、このタイプのEAを作成するものです。

//+------------------------------------------------------------------+
//|                                                  trendFinder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnTick()
  {
   int checkBars= 5;
   int high1, high2, low1, low2;
   double highVal1, highVal2, lowVal1, lowVal2;
   high1=getmove(MODE_HIGH,checkBars,0);
   high2=getmove(MODE_HIGH,checkBars,high1+1);
   highVal1=NormalizeDouble(iHigh(_Symbol,_Period,high1),5);
   highVal2=NormalizeDouble(iHigh(_Symbol,_Period,high2),5);
   ObjectDelete(0,"topLine");
   ObjectCreate(0,"topLine",OBJ_TREND,0,iTime(Symbol(),Period(),high2),iHigh(Symbol(),Period(),high2),iTime(Symbol(),Period(),high1),iHigh(Symbol(),Period(),high1));
   ObjectSetInteger(0,"topLine",OBJPROP_COLOR,clrRed);
   ObjectSetInteger(0,"topLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"topLine",OBJPROP_RAY_RIGHT,true);
   low1=getmove(MODE_LOW,checkBars,0);
   low2=getmove(MODE_LOW,checkBars,low1+1);
   lowVal1=NormalizeDouble(iLow(_Symbol,_Period,low1),5);
   lowVal2=NormalizeDouble(iLow(_Symbol,_Period,low2),5);
   ObjectDelete(0,"bottomLine");
   ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1));
   ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true);
   if(lowVal1>lowVal2&&highVal1>highVal2)
     {
      Comment("Uptrend",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }
   else
      if(highVal1<highVal2&&lowVal1<lowVal2)
        {
         Comment("Downtrend",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }
      else
        {
         Comment("Sideways",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }
  }
int getmove(int move, int count, int startPos)
  {
   if(move!=MODE_HIGH && move!=MODE_LOW)
      return (-1);
   int currentBar=startPos;
   int moveReturned=getNextMove(move,count*2+1,currentBar-count);
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+1);
      moveReturned=getNextMove(move,count*2+1,currentBar-count);
     }
   return(currentBar);
  }
int getNextMove(int move, int count, int startPos)
  {
   if(startPos<0)
     {
      count +=startPos;
      startPos =0;
     }
   return((move==MODE_HIGH)?
          iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
          iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));
  }

トレンドを検出するためのこのコードの違いは次のとおりです。

OnTick関数のスコープ内に、2つの高値と2つの安値を表す4つの整数変数と、2つの高値と2つの安値を表す別の4つのdouble変数を作成します。

   int high1, high2, low1, low2;
   double highVal1, highVal2, lowVal1, lowVal2;

NormalizeDouble関数を使用して2つの高値(highVal1、highVal2)を更新し、高値の結果を丸めます。そのパラメータは次のとおりです。

  • value:正規化する必要がある数値。iHigh関数を使用して高値を返します。パラメータは、銘柄(現在の銘柄_Symbol)、時間枠(現在の時間枠_Period)、シフト(high1とhigh2)です。
  • digits:小数点以下の桁数(5)
   highVal1=NormalizeDouble(iHigh(_Symbol,_Period,high1),5);
   highVal2=NormalizeDouble(iHigh(_Symbol,_Period,high2),5);

NormalizeDouble関数を使用して2つの安値(lowVal1、lowVal2)を更新します。前に説明したものと同じパラメータを使用しますが、次の点が異なります。

  • value:iLow関数を使用して安値を返します。そのパラメータは、low1とlowになるインデックスのシフトを除いて同じです。
   lowVal1=NormalizeDouble(iLow(_Symbol,_Period,low1),5);
   lowVal2=NormalizeDouble(iLow(_Symbol,_Period,low2),5);

トレンドを特定するために設定する必要がある条件についてif文を使用します。EAに高値と安値の4つの値を継続的にチェックさせ、相互に関連する位置を決定し、トレンドがあるか(上昇または下降)トレンドがないか(横向き)を判断する必要があります。

上昇トレンドの条件

lowVal1がlowVal2を上回り、同時にhighVal1がhighVal2を上回る場合は上昇トレンドがあります。EAがチャートに次のコメントを返します。

  • Uptrend
  • Current High
  • Previous High
  • Current Low
  • Previous Low
   if(lowVal1>lowVal2&&highVal1>highVal2)
     {
      Comment("Uptrend",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }

下降トレンドの条件

highVal1がhighVal2より低く、同時にlowVal1がlowVal2よりも低い場合、下降トレンドがあり、EAがチャートに次のコメントを返す必要があります。

  • 下降トレンド
  • Current High
  • Previous High
  • Current Low
  • Previous Low
   else
      if(highVal1<highVal2&&lowVal1<lowVal2)
        {
         Comment("Downtrend",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }

横向きの条件

4つの値のポジションが上昇トレンドと下降トレンドの条件以外の場合、横向きになり、EAがチャート上のコメントとして以下を返す必要があります。

      else
        {
         Comment("Sideways",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }

このコードをエラーを含めてコンパイルし、EAを実行すると、必要に応じてトレンドのシグナルを受け取ることができます。以下は、トレンドの種類とその条件に応じたテストの例です。

上昇トレンド:

トレンドファインダー-上昇トレンドシグナル

前の図では、このチャートの値動きで安値と高値が高くなっているため、上昇トレンドの例であることがわかります。したがって、チャートの左上隅にコメントとして上昇トレンドのシグナルを受け取りました。

下降トレンド:

トレンドファインダー-下降トレンドシグナル

前のチャートではっきりとわかるように、値動きに従って高値が低く、安値が低くなっているため、下降トレンドになっています。したがって、チャート上のコメントとして下降トレンドのシグナルを受け取りました。

横ばい:

トレンドファインダー-横向きのシグナル

前の例でわかるように、上昇トレンドと下降トレンドとは異なるフォームがあり、高値が低く、安値が横ばいになっています。したがって、チャート上のコメントとして横ばいのシグナルを受け取りました。

チャートのダブルトップ検出

高値と安値を検出する方法を学習した後、高値と安値を検出する基本コードを開発して、それに基づいてトレンドを検出します。コードをさらに開発して、潜在的な動きを示す可能性のある特定のチャートまたは値動きパターンの検出を試みることができることが分かったと思います。

この部分では、主なアイデアを理解するために、コードはほとんど開発せずに、これらのチャートパターンの例を示します。特にいくつかの便利な技術ツールをコードにマージする場合は、より重要なパターンを検出するためにさらに開発を実行します。記事のこの部分では、チャートで見られる人気のチャートパターンの1つであるダブルトップについて説明します。

ダブルトップは、チャート上で確認できるチャートパターンであり、ほぼ同じ高値で構成されており、購買力が弱まっており、価格が下落する可能性があることを示しています。重要な詳細はたくさんありますが、その形式についてのみ言及しますが、それは私たちが述べたものと同じであると考えます。次のグラフは、潜在的なダブルトップパターンの視覚的な例です。

潜在的DT

お気づきかもしれませんが、前の例で、これは潜在的なパターンであり、次のグラフと同じように、価格が2つの高値の間の安値を下回って終了すると、適合パターンとなります。

DT

次に、MetaTrader 5でこれら2つの数値を検出するために使用できるMQL5EAを作成する必要があります。EAは2つの高値と2つの安値を継続的にチェックし、相互に関連する位置を判断し、ダブルトップパターンの条件である特定の条件に基づいて特定の結果を返す必要があります。ここでは、同じ高値だけでなく、わずかに低いまたは高い高値が来る可能性があるため、実際のパターンを簡単に説明します。つまり、現在の高値が前の高値以下であり、同時に、現在の最低値が以前の最低値よりも大きい場合、これは潜在的なダブルトップのシグナルになります。現在の高値が前の高値を下回り、同時に現在の安値が以前の高値を下回る場合、これはダブルトップのシグナルになります。

これをおこなう完全なコードは次のとおりです。

//+------------------------------------------------------------------+
//|                                             DT patternFinder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnTick()
  {
   int checkBars= 5;
   int high1, high2, low1, low2;
   double highVal1, highVal2, lowVal1, lowVal2;
   high1=getmove(MODE_HIGH,checkBars,0);
   high2=getmove(MODE_HIGH,checkBars,high1+1);
   highVal1=NormalizeDouble(iHigh(_Symbol,_Period,high1),5);
   highVal2=NormalizeDouble(iHigh(_Symbol,_Period,high2),5);
   ObjectDelete(0,"topLine");
   ObjectCreate(0,"topLine",OBJ_TREND,0,iTime(Symbol(),Period(),high2),iHigh(Symbol(),Period(),high2),iTime(Symbol(),Period(),high1),iHigh(Symbol(),Period(),high1));
   ObjectSetInteger(0,"topLine",OBJPROP_COLOR,clrRed);
   ObjectSetInteger(0,"topLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"topLine",OBJPROP_RAY_RIGHT,true);
   low1=getmove(MODE_LOW,checkBars,0);
   low2=getmove(MODE_LOW,checkBars,low1+1);
   lowVal1=NormalizeDouble(iLow(_Symbol,_Period,low1),5);
   lowVal2=NormalizeDouble(iLow(_Symbol,_Period,low2),5);
   ObjectDelete(0,"bottomLine");
   ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1));
   ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true);
   if(highVal1<=highVal2&&lowVal1>lowVal2)
     {
      Comment("Potential Double Top",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }

   else
      if(highVal1<=highVal2&&lowVal1<lowVal2)
        {
         Comment("Double Top",
                 "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
                 "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }
      else
         Comment(" ");
  }
int getmove(int move, int count, int startPos)
  {
   if(move!=MODE_HIGH && move!=MODE_LOW)
      return (-1);
   int currentBar=startPos;
   int moveReturned=getNextMove(move,count*2+1,currentBar-count);
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+1);
      moveReturned=getNextMove(move,count*2+1,currentBar-count);
     }
   return(currentBar);
  }
int getNextMove(int move, int count, int startPos)
  {
   if(startPos<0)
     {
      count +=startPos;
      startPos =0;
     }
   return((move==MODE_HIGH)?
          iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
          iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));
  }

このコードの違いはパターンの条件です。

潜在的ダブルトップの場合、highVal1がhighVal2を下回り、lowVal1がlowVal2を上回る場合、次の値を含むチャート上のコメントとしてシグナルを取得する必要があります。

  • Potential Double Top
  • Current High
  • Previous High
  • Current Low
  • Previous Low
   if(highVal1<=highVal2&&lowVal1>lowVal2)
     {
      Comment("Potential Double Top",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }

ダブルトップの場合、highVal1がhighVal2を下回り、lowVal1がlowVal2を下回る場合、次の値を含むチャート上のコメントとしてシグナルを取得します。

  • Double Top
  • Current High
  • Previous High
  • Current Low
  • Previous Low
   else
      if(highVal1<=highVal2&&lowVal1<lowVal2)
        {
         Comment("Double Top",
                 "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
                 "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }

ダブルトップパターンやその可能性がない場合は、コメントとして何も返しません

      else
         Comment(" ");

このコードをエラーなしでコンパイルし、そのEAを実行した後、シグナルとしてのテストから次の例を見つけることができます。

潜在的なダブルトップの場合:

DTパターンファインダー存在的シグナル

前のチャートでわかるように、プリセット条件(より高い安値と同等の高値)との一致があるため、ダブルトップシグナルの可能性があります。

ダブルトップの場合:

DTパターンファインダーDTシグナル

前のチャートでわかるように、プリセット条件との一致があるため、ダブルトップシグナルが表示されます。つまり、より低い、または同等の高値とより低い安値です。

チャートのダブルボトムの検出

この部分では、ダブルトップの逆のパターン、つまりダブルボトムのパターンを検出する方法を学びます。ダブルボトムはチャート上で確認できるチャートパターンであり、ほぼ同じ安値で構成されています。これは、販売力が弱くなり、価格が上昇する可能性があることを示します。また、重要な詳細も数多くありますが、もしその形式についてのみ言及しますが、それが私たちが述べたものと同じであることがわかります。次のグラフは、潜在的なダブルボトムパターンの視覚的な例です。

滞在的DB

次のグラフと同じように、価格が2つの安値の間の高値を上抜けて終了すると、以前の潜在的なダブルボトムパターンが確認されます。

DB

MetaTrader 5で前の2つの数字を検出するために使用できる別のMQL5 EAを作成する必要があります。EAが2つの安値と2つの高値を継続的にチェックし、相互に関連する位置を判断し、ダブルボトムパターンの条件に基づいて特定の結果を返す必要があります。コード内の同じ単純な開発を逆のケースに適用して、同じ高値だけでなく、わずかに高いまたは低い安値を持つ実用的なパターンにアプローチします。つまり、現在の安値が前の安値以上であると同時に現在の高値が以前の高値を下回る場合は、潜在的なダブルボトムのシグナルとします。現在の安値が前の値以上であり、同時に現在の高値が以前の値を上回る場合は、ダブルボトムのシグナルとします。

これをおこなう完全なコードは次のとおりです。

//+------------------------------------------------------------------+
//|                                             DB patternFinder.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnTick()
  {
   int checkBars= 5;
   int high1, high2, low1, low2;
   double highVal1, highVal2, lowVal1, lowVal2;
   high1=getmove(MODE_HIGH,checkBars,0);
   high2=getmove(MODE_HIGH,checkBars,high1+1);
   highVal1=NormalizeDouble(iHigh(_Symbol,_Period,high1),5);
   highVal2=NormalizeDouble(iHigh(_Symbol,_Period,high2),5);
   ObjectDelete(0,"topLine");
   ObjectCreate(0,"topLine",OBJ_TREND,0,iTime(Symbol(),Period(),high2),iHigh(Symbol(),Period(),high2),iTime(Symbol(),Period(),high1),iHigh(Symbol(),Period(),high1));
   ObjectSetInteger(0,"topLine",OBJPROP_COLOR,clrRed);
   ObjectSetInteger(0,"topLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"topLine",OBJPROP_RAY_RIGHT,true);
   low1=getmove(MODE_LOW,checkBars,0);
   low2=getmove(MODE_LOW,checkBars,low1+1);
   lowVal1=NormalizeDouble(iLow(_Symbol,_Period,low1),5);
   lowVal2=NormalizeDouble(iLow(_Symbol,_Period,low2),5);
   ObjectDelete(0,"bottomLine");
   ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1));
   ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3);
   ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true);
   if(lowVal1>=lowVal2&&highVal1<highVal2)
     {
      Comment("Potential Double Bottom",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }
   else
      if(lowVal1>=lowVal2&&highVal1>highVal2)
        {
         Comment("Double Bottom",
                 "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
                 "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }
      else
         Comment(" ");
  }
int getmove(int move, int count, int startPos)
  {
   if(move!=MODE_HIGH && move!=MODE_LOW)
      return (-1);
   int currentBar=startPos;
   int moveReturned=getNextMove(move,count*2+1,currentBar-count);
   while(moveReturned!=currentBar)
     {
      currentBar=getNextMove(move,count,currentBar+1);
      moveReturned=getNextMove(move,count*2+1,currentBar-count);
     }
   return(currentBar);
  }
int getNextMove(int move, int count, int startPos)
  {
   if(startPos<0)
     {
      count +=startPos;
      startPos =0;
     }
   return((move==MODE_HIGH)?
          iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos):
          iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos));
  }

このコードの違いはパターンの条件です。

潜在的なダブルボトムの場合、lowVal1がlowVal2以上で、highVal1がhighVal2を下回る場合、次の値を含むチャート上のコメントとしてシグナルを取得する必要があります。

  • Potential Double Bottom
  • Current High
  • Previous High
  • Current Low
  • Previous Low
   if(lowVal1>=lowVal2&&highVal1<highVal2)
     {
      Comment("Potential Double Bottom",
              "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
              "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
     }

ダブルトップの場合、lowVal1がlowVal2以上で、highVal1がhighVal2を上回る場合、次の値を含むチャート上のコメントとしてシグナルを取得します。

  • Double Bottom
  • Current High
  • Previous High
  • Current Low
  • Previous Low
   else
      if(lowVal1>=lowVal2&&highVal1>highVal2)
        {
         Comment("Double Bottom",
                 "\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
                 "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
        }

このコードをエラーなしでコンパイルし、そのEAを実行すると、テストの例として次のシグナルを取得できます。

潜在的なダブルボトムの場合:

DBパターンファインダー潜在的シグナル

前のチャートでわかるように、より低い高値と同等またはより高い安値があるという事前設定された条件との一致があるため、潜在的なダブルボトムシグナルが存在します。

ダブルボトムの場合:

DB

前のチャートでわかるように、より高い高値と同等以上の安値があるという事前設定された条件と一致しているため、ダブルボトムシグナルが存在します。

結論

トレーダーにとって値動きは最も重要なものです。トレーダーはこの値動きの理解に基づいて取引を行い、それをよく理解していれば、より良い投資や取引の意思決定をおこなうことができます。値動きは多くのパターンを形成しており、それを読んで理解する必要があります。この記事では、MetaTrader 5取引ターミナルで使用されるMQL5によるシステムを作成することで、このタスクを容易にするものを提供しようとしました。

高値と安値を検出する方法を学んだ後、トレンド(上昇トレンド、下降トレンド、横ばい)を検出する方法と、ダブルトップとその逆のダブルボトムという人気のチャートパターンの1つを学びました。また、適切な条件に基づいて上記のプログラムやシステムを開発できるように、トレンドのあらゆる概念とこのチャートタイプの優れたベースも提供しました。また、高値と安値を検出できるシステムを作成するための主要な概念を学んだ後、このシステムをもっと開発して、ヘッドアンドショルダー、トライアングル、レクタングルなどのより多くのチャートパターンを検出できるようにすることができます。この記事が、読者が取引ビジネスでより良い結果を得るために、それに応じて取引および取引システムを開発するのに役立つことを願っています。

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

添付されたファイル |
moveFinder.mq5 (2.24 KB)
trendFinder.mq5 (3.18 KB)
MQL5でのARIMAトレーニングアルゴリズムの実装 MQL5でのARIMAトレーニングアルゴリズムの実装
この記事では、関数最小化のPowell法を使用して、ボックス・ジェンキンス法の自己回帰和分移動平均モデルを適用するアルゴリズムを実装します。ボックスとジェンキンスは、ほとんどの時系列は2つのフレームワークの一方または両方でモデル化できると述べました。
MQL5を使用したカスタムインディケータ(平均足)の作成方法 MQL5を使用したカスタムインディケータ(平均足)の作成方法
この記事では、MQL5を使用して好みに基づいてカスタムインディケータを作成し、MetaTrader 5でチャートの読み取りに使用したり、自動エキスパートアドバイザー(EA)で使用したりする方法を学びます。
MQL5の圏論(第7回):多重集合、相対集合、添字集合 MQL5の圏論(第7回):多重集合、相対集合、添字集合
圏論は、数学の多様かつ拡大を続ける分野であり、最近になってMQL5コミュニティである程度取り上げられるようになりました。この連載では、その概念と原理のいくつかを探索して考察することで、トレーダーの戦略開発におけるこの注目すべき分野の利用を促進することを目的としたオープンなライブラリを確立することを目指しています。
プロップファームから少し教訓を得よう(第1回)-導入編 プロップファームから少し教訓を得よう(第1回)-導入編
今回は、プロップファーム(自己勘定取引会社)が実施するチャレンジルールから得られる教訓のいくつかを取り上げます。これは特に、初心者の方や、この取引の世界で足元を固めるのに苦労している方には重要です。次の記事では、コードの実装について説明します。