English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
チャネルの描画 : 内見および外観

チャネルの描画 : 内見および外観

MetaTrader 5インディケータ | 7 10月 2015, 16:45
829 0
Dmitriy Skub
Dmitriy Skub

はじめに

マーケット分析および移動平均後トレードを判断するのにチャネルはもっとも人気あるツールだと言っても過言ではないでしょう。チャネルについて特筆しているシリーズの初稿では、クライアント端末画面の3つの極値によって設定されるインディケータの数学的基本と理論的実装について述べます。

一見すると、チャネルを描くことはたやすいタスクです。それは小学校で学ぶ直線の係数に基づくものだからです。ただし、それをクライアント端末で実用的に実装するには簡単に答えられない数々の問題があります。

極値を設定し、最良の方法でその変化を追跡するにはどうすればよいのでしょう?中央部分が失われたバーの上に位置していたとしたらチャネルを描くには何をどうすればよいのでしょうか?チャネルの左側の極値が金曜日、右側が月曜日で、間にバーのない週末が挟まっていた場合にはどうなるのでしょうか?チャネル境界の現在値を取得する方法は?

こういった質問、まだ他にもある質問はチャネルに関するシリーズの初稿で回答されています。ここでは、みなさんには標準クラスとオブジェクト指向の方法を使って3つの指定された極値によりチャネルを描く実装方法を確認いただけます。それではこれからインディケータ形式でチャネル描画を実装していきます。


極値の設定

事実、チャート上のチャネル位置は3つの極値によって決定されます。極値を定義すれば、これが受け入れ可能です。それは所定の範囲にある関数の最大値または最小値です。極値が到達するポイントは極値ポイントと呼ばれます。それぞれ、最小値が到達すると極値ポイントは 最小ポイント、最大値が到達すると最大ポイントと呼ばれます。

数学的分析では別の用語を定義します。ローカル極値(それぞれ最小、および最大)です。最大(最小)ポイントでは、関数値はすべての隣接したポイントよりも大きく(小さく)なっています。定義はウィキペディアから引用しています。(ロシア語からの翻訳です。)

チャネルを描くには、ローカル極値が必要です。数式は抜きでグラフィックとして表示します。下の図1では、3つローカル極値 があり、赤の価格レベルでマークされています。長方形のポイントは最大最小ポイントを示しています。

図1 ローカル極値例

図1 ローカル極値例

既存の極値が必ずチャートでマークされているとは限りません。マークしているのはもっとも顕著なものだけです。そうろく足チャートまたは棒グラフでは、極値を定義するのに『フラクタル』用語を使用すると好都合です。複数の隣接するバーが左および右に急減または急増している場合(図1参照)。

自動チャネル描画機能を作成することが目的ではないので、極値の位置は図1に示すような位置に設定します。それは、時間と価格軸にあによるものです。本目的にもっとも有用なのは、価格ラベルです。これはMetaTrader 5 クライアント端末の特別なグラフィカルオフジェクトです。価格ラベルには時刻と価格連動プロパティがあり、それによりチャート上で極値ポイントをまちがいなく特定することができます。


極値を格納するオブジェクトは TExtremumクラスです。

まず最初にすることは、極値を格納するコンテナクラスと極値グループを操作するクラスを作成することです。可能な限り端末内蔵の 標準クラス を使用していくので、TExtremum クラスは標準クラスCObjectからの継承となります。ここでのクラス記述は以下のとおりです。

class TExtremum : public CObject
{
private:
  datetime  extr_datetime;              // data/time in an extremum point
  double    extr_price;                 // price in an extremum point
        
protected:
  virtual int  Compare(const CObject* _node, int _mode = 0) const;

public:
  void      TExtremum();               // constructor
  void      ~TExtremum();              // destructor
  void      SetExtremum(datetime _time, double _price);  // change date/time and price in an extremum point
  void      SetDateTime(datetime _time);                 // change date/time in an extremum point
  void      SetPrice(double _price);  // change price in an extremum point

public:
  datetime  GetDateTime() const;      // get date/time in an extremum point
  double    GetPrice() const;         // get price in an extremum point

public:
  virtual bool  SaveExtremum(string _dt_name, string _p_name);  // save extremum
  virtual bool  LoadExtremum(string _dt_name, string _p_name);  // load extremum
  virtual bool  DeleteExtremum(string _dt_name, string _p_name);// delete extremum
};

メソッドの多くは重要ではないので、その実装には注意を払う必要はありません。注意する必要があるのは、TExtremum::Compare メソッドです。このメソッドは CObject クラスで宣言され、リスト内に格納するのに使用されます。ここでは以下のように実装しました。

//---------------------------------------------------------------------
//  Comparing two extremums by time:
//---------------------------------------------------------------------
int TExtremum::Compare(const CObject* _node, int _mode = 0) const
{
  datetime  temp = ((TExtremum* )_node).GetDateTime();
  datetime  curr = GetDateTime();
  if(curr > temp)
  {
    return(_mode > 0 ? 1 : -1);
  }
  else if(curr < temp)
  {
    return(_mode > 0 ? -1 : 1);
  }

  return(0);
}

ここでのパラメータ _mode はソート方向設定に使用します。それはゼロ以上で、ソードは正(昇順)です。それ以外は逆向き(降順)です。

また、極値を保存/ロードするメソッドが2つあります。それではわれわれの極値をグローバル変数に格納します。以下がそのメソッドです。

//---------------------------------------------------------------------
//  Save extremum (date/time):
//---------------------------------------------------------------------
bool TExtremum::SaveExtremum(string _dt_name, string _p_name)
{
  datetime  dt_result = GlobalVariableSet(_dt_name, (double)extr_datetime);
  datetime  p_result = GlobalVariableSet(_p_name, (double) extr_price);
  if(dt_result != 0 && p_result != 0)
  {
    return(true);
  }

  return(false);
}

//---------------------------------------------------------------------
//  Load extremum (date/time):
//---------------------------------------------------------------------
bool TExtremum::LoadExtremum(string _dt_name, string _p_name)
{
  double  dt_temp, p_temp;
  bool    result = GlobalVariableGet(_dt_name, dt_temp);
  result &= GlobalVariableGet(_p_name, p_temp);
  if(result != false)
  {
    extr_datetime = (datetime)dt_temp;
    extr_price = p_temp;
    return(true);
  }

  return(false);
}

グローバル変数で読み/書く2つのメソッドは TExtremum::LoadExtremum および TExtremum::SaveExtremum で、問題なく実行すると 'true' を返します。


極値リストの操作: TExtremumLis クラス

格納および時刻によるソートが必要なため、標準クラスCListよりTExtremumList クラスを継承します。この継承で、数とタイプに制限のない汎用的な極値マニピュレーターを取得します。これにより描かれたチャネル数を拡大することが可能となります。たとえば、複数極値による非線形回帰の場合は、チャネル描画を追加することが可能です。

本クラスの記述は以下のとおりです。

class TExtremumList : public CList
{
private:
  string              channel_prefix;     // channel name (prefix)
  ENUM_TIMEFRAMES      chart_timeframe;    // current timeframe
  string              chart_symbol;       // work symbols of the chart

protected:
  string    MakeDTimeName(int _nmb);     // get name for saving/reading data/time of an extremum
  string    MakePriceName(int _nmb);     // get name for saving/reading price of an extremum

public:
  void      TExtremumList();             // конструктор
  void     ~TExtremumList();             // деструктор
  void     SetChannelParams(string _pref, string _symbol = NULL, ENUM_TIMEFRAMES _curr_tf = PERIOD_CURRENT);
  void     AddExtremum(datetime _time, double  _price);
  void     DeleteAllExtremum();
  void     SaveExtremumList();
  void     LoadExtremumList();
  int      FindExtremum(datetime _dt);  // search extremum by specified time

public:
  datetime GetDateTime(int _index);
  double   GetPrice(int _index);
};

クラスの主要メソッドは TExtremumList::AddExtremumです。それはリストに新規極値を追加します。極値ポイント時刻による極値の格納は、追加後に行われます。本メソッドのコードは下記です。

void TExtremumList::AddExtremum(datetime _time, double  _price)
{
//  Create extremum:
  TExtremum*    extr = new TExtremum();
  extr.SetExtremum(_time, _price);

//  Add it in the list:
  Add(extr);

//  Sort:
  Sort(1);
}

以下の基本クラスのメソッドがここで使用されます。CList::Add : リストに新規エレメントを追加します。そして、CList::Sort :リスト上でエレメントをソートします。メソッド TExtremum::CompareCList::Sort内にて使用されます。

リストTExtremumList::FindExtremum上の所定時刻により極値を検索するメソッドを見ます。本メソッドのコードは下記です。

int TExtremumList::FindExtremum(datetime _dt)
{
  int           k = 0;
  TExtremum*    extr = (TExtremum*)(GetFirstNode());
  while(extr != NULL)
  {
    if(extr.GetDateTime() == _dt)
    {
      return(k);
    }
    extr = (TExtremum*)(GetNextNode());
  }
  return(-1);                     // extremum not found
}

以下の基本クラスのメソッドがここで使用されます。 CList::GetFirstNode : リストの最初のエレメントを取得します。(リストが空の場合、ゼロポインターを返します。) そして、CList::GetNextNode : リスト上の次のエレメントを取得します(次のエレメントがなく、リストが終わればゼロポインターを返します)。

注意:

クラスリストCListの内部データに現在エレメントのポインターがあります。リスト上で、移動メソッドが呼ばれると、このポインターは変更されます。(CList::GetFirstNode、CList::GetNextNode、 CList::GetPrevNodeなど)以前にそれらメソッドが呼ばれたことがなければ、現エレメントのポインターは最初のメソッドを指示します。

所定の時刻を伴う極値がうまく検索できたら、メソッドTExtremumList::FindExtremum は、検索したエレメントの指数を返します。そのようなエレメントがなければ、1を返します。

メソッド TExtremum::MakeDTimeName および TExtremum::MakePriceName は予備メソッドです。極値を保存、または読むとき使用されるグローバル変数名を取得します。以下のように実装されます。

string TExtremumList::MakeDTimeName(int _nmb)
{
  string    name;
  StringConcatenate(name, channel_prefix, "_", channel_symbol, "_", channel_timeframe, "_DTime_Extr", _nmb);
  return(name);
}

string TExtremumList::MakePriceName( int _nmb )
{
  string    name;
  StringConcatenate(name, channel_prefix, "_", channel_symbol, "_", channel_timeframe, "_Price_DTime_Extr", _nmb);
  return(name);
}

取得された名前の例:MainChannel_EURUSD_5_DTime_Extr1それら名前は、チャネル、MainChannel(従来の名前)の一時的極値ポイントに対応しています。シンボル=EURUSD、時間枠=5M、極値数 1です。極値数は1から始まり、時刻の降順で割り当てられます。実際には、降順のソートリストで1ずれた指数です。

端末に保存された3つの極値の値例は以下の図に示しています。

図2 グローバル変数に格納された極値

図2 グローバル変数に格納された極値

上述のクラスは ExtremumClasses.mqhファイル内の記事に添付されています。


マニュアルで極値を設定するインディケータ - ExtremumHandSet

これで、最初のインディケータを作成するのに要求されるものはすべてそろいました。それを使ってマニュアルモードで極値配置の設定を行うこととします。インディケータのコードは ExtremumHandSet.MQ5ファイル内の記事に添付されています。描画について詳しく分析していきます。

まず、画面上に何を表示したいのか視覚的にイメージします。

図3 極値設定のインディケータ

図3 極値設定のインディケータ

左側の価格ラベルを用いて、チャートの時刻軸と価格軸に極値の位置を設定しますインディケータがチャート上のこれらレベル位置を判断し、画面上に仮の極値ポイントを表示し、上述の形式でクライアント端末のグローバル変数に保存します。また、インディケータはチャート上の価格ラベルの移動を追いかけ、ロードされた仮の極値ポイントを修正する必要があります。

チャート上の価格ラベル移動の追跡は1秒ごとに行われます。これによりシステムはクオートの到来と稼働日/非稼働日から影響を受けることはありません。

ではまず、要求されるライブラリの連携を行います。

//---------------------------------------------------------------------
//  Included libraries:
//---------------------------------------------------------------------
#include  <TextDisplay.mqh>
#include  <ExtremumClasses.mqh>

最初のライブラリは画面にテキスト情報を表示するクラスを持ちます。(『標準ライブラリクラス使用による独自のMarket Watch作成』稿を参照ください。)それを使い、仮の極値ポイント値を表示していきます。

そして、インディケータの入力パラメータ を追加します。(ここでは主要なもののみ記述しています。)

input string  PrefixString = "MainChannel";
//---------------------------------------------------------------------
input color   ExtremumPointColor = Yellow;
//---------------------------------------------------------------------
input bool    ShowInfo = true;

最初のパラメータ PrefixString は、極値を書き/読む際、 グローバル変数 名を構成するのに使われる接頭辞を設定します。また、単一チャート上でこの種のインディケータを複数使用する機能も提供します。すべきことは、それらに異なる接頭辞を設定することだけです。

パラメータ ExtremumPointColor は、極値の位置を決定する左の価格ラベルの色を設定します。価格ラベルは指定の色となる必要があります。指定色と一致しているかどうかはインディケータ内で確認されます。異なるパラメータのラベルは無視されます。

パラメータ ShowInfo は画面上で指定された極値ポイントについてテキスト情報の表示を管理します。

次に、情報表示と極値操作をおこなうオブジェクトを作成します。

TableDisplay    TitlesDisplay;    // displaying information on the screen
//---------------------------------------------------------------------
TExtremumList*  PrevExtr_List;    // list of previous extremums
TExtremumList*  CurrExtr_List;    // list of current extremums
TExtremumList*  NewExtr_List;     // list of new extremums

これらオブジェクトは次のように初期化されます。

PrevExtr_List = new TExtremumList();
PrevExtr_List.SetChannelParams(PrefixString, Symbol(), Period());
PrevExtr_List.LoadExtremumList();

CurrExtr_List = PrevExtr_List;

NewExtr_List = new TExtremumList();
NewExtr_List.SetChannelParams(PrefixString, Symbol(), Period());

PrevExtr_List リストでは、TExtremumList::LoadExtremumListメソッドを用いてグローバル変数から極値をロードします。このリストは新しい極値と比較するために極値を格納します。新規極値は画面上で価格ラベルをドラッグするとき、チャートから読み取られます。

CurrExtr_List リストは現在リストとして使用され、現在極値を格納します。最初にグローバル変数から読まれる極値のみなので、それらは実際の極値として取り込まれます。

NewExtr_List リストには、チャートで確認された新規極値を書きこんでいきます。

では、インディケータで使用される主要な関数を見ていきます。最初の関数、FindExtremumPoints は極値の位置を決定する価格ラベルのパラメータを読み、確認するのに使用されます。

bool FindExtremumPoints(long _chart_id)
{
  string  name;

//  1. Search for the total number of objects with specified parameters and write them to the list:
  int total_objects = ObjectsTotal(_chart_id, -1, OBJ_ARROW_LEFT_PRICE);
  if(total_objects <= 0)
  {
    return(false);
  }

  NewExtr_List.Clear();
  for(int i = 0; i < total_objects; i++)
  {
    name = ObjectName(_chart_id, i, -1, OBJ_ARROW_LEFT_PRICE);

    if( IsGraphicObjectGood(_chart_id, name, OBJ_ARROW_LEFT_PRICE, ExtremumPointColor) == true)
    {
      NewExtr_List.AddExtremum(ObjectGetInteger( _chart_id, name, OBJPROP_TIME),
                               ObjectGetDouble(_chart_id, name, OBJPROP_PRICE));
    }
  }

//  2. If three extremums are found, we can try to draw a channel:
  if(NewExtr_List.Total() == 3)
  {

//  Save the list of new extremums:
    NewExtr_List.SaveExtremumList();
    return(true);
  }

  NewExtr_List.Clear();
  return(false);
}

まず、リストNewExtr_ListTExtremumList::Clearメソッドを呼ぶことで消去され、それからみつかった指定のパラメータを持つ極値ポイントがすべてそこに追加されます。みつかったポイント数が3であれば、リストはグローバル変数に保存され、関数 'true'を返します。

その他の関数、CheakExtremumMoving はチャート上の極値移動を追跡します。チャートの時間軸に沿って、最低でも1つポイントが移動していたら、この関数は'true'を返します。

以下がそのコードです。

//---------------------------------------------------------------------
//  Check whether extremums have been moved on the screen:
//---------------------------------------------------------------------
bool CheakExtremumMoving()
{
  if(FindExtremumLines(0) == true)
  {
    int  count = NewExtr_List.Total();
    int  index;
    for(int i = 0; i < count; i++)
    {
      index = CurrExtr_List.FindExtremum(NewExtr_List.GetDateTime(i));

//  If a new extremum is found:
      if(index == -1)
      {
        PrevExtr_List = CurrExtr_List;
        CurrExtr_List = NewExtr_List;
        return(true);
      }
    }
    CurrExtr_List = PrevExtr_List;
  }

  return(false);
}

ここまで、マニュアルモードで極値ポイントを設定する方法を考察してきました。既製のインディケータがあり、それによりこの手順を管理し、グローバル変数にポイントを書き込むことができます。インディケータのコード全体は ExtremumHandSet.mq5添付ファイル内にあります。それでは主要な部分に移ります。チャネルの描画です。


チャネル描画 : セオリー

線形チャネルは極値ポイントを正確に通貨する二本の平行な直線で構成されます。また、線の1本は2つのポイントを通過する必要があり、他方は最初の線に平行になるよう1点を通ります。シンプルな絵で示すことができます。

3極値点を用いたチャネル描画

図4 3極値点を用いたチャネル描画

幾何で習ったとおり、2点を通って1本の直線のみを引くことが可能です。この線は図4に赤で示すものです。この直線は、次の座標の2点を通っています。(T1, P1) と (T2, P2)です。2点はA および Bと印がつけられています。この直線の方程式は以下です。

(1) P(t) = P1 + (t - T1)*(P2 - P1) / (T2 - T1);ここでP(t) は時刻 't'時点で計算された価格です。

点 C(3番目の極値)を通り、最初の直線に平行な直線を引きます。 これは図3にグリーンで示される直線です。点 T1 および 点T2 が両直線に対して同じなので、点 P1' と P2' とします。(図4参照)

先に進む前に、ひとつ重要な点について述べます。端末チャートは時間の『穴』を表示しません。たとえば、週末、クオートが端末に来ないとき、価格の途切れ目が表示される必要があります。それが表示されないと困ったことになります。何も表示されないチャートをみる理由は?ただ、上記引用に絶対時刻を使うなら、誤ったチャネルを取得することになります。

さいわい、状況は絶望的ではありません。絶対時刻をバーの相対数に変えれば、その座標をチャネルラインを引くのに使用することができるでしょう。なぜなら、バーの列挙には断絶はありえないからです。(実際、それは価格配列の指数です。)

先に進んで、図4の点Aは常に時間軸のゼロ座標にある(ゼロバー)と仮定するなら、ここでの方程式はもっと簡単なものとなります。ということで、T1=0、T3=B3、Т2=В2です。ここでВ3 および В2 はТ1 (ゼロ点)に対する相対的なバーの数です。この予測がラインの傾斜につながらならないことは明白です。そして、点A と点Bを通る直線について次の方程式を得ます。

(2) P(n) = P1 + n * (P2-P1) / B2;ここで P(n) は'n'番のバーについて計算された価格です。

よって値は、 P1、P2、 P3 また B2、B3となります。では、点 P1' および点 P2'の値を求めます。2つの方程式をまとめて解きます。そうすると以下の式を得ます。これを解くと、未知の値を求めることができます。

(3) P1' = P3 - B3 * (P2 - P1) / B2

(4) P2' = P2 - P1 + P1'

得た値 P1' を (4) の式に代入します。そして値、 P2'を得ます。チャネルラインを引くにあたり、理論的基礎は抑えました。それでは実装に入っていきます。


チャネル境界線を引く : TChannelBorderObjectクラス

このクラスは標準クラス CChartObjectTrendから派生しています。それは境界に関連したパラメータをすべて格納し、境界線を描画/削除し、そういった線のグラフィカルパラメータを管理します。

本クラスの記述は以下のとおりです。

class TChannelBorderObject : public CChartObjectTrend
{
//  General properties of a border:
private:
  bool             is_created;       // whether the graphical object is created on the screen
  long             chart_id;         // identifier of the chart window
  int              window;           // identifier of the subwindow

//  Parameters of a border line:
private:
  string           border_name;      // name of the border line
  color            border_color;     // color of the border line
  int              border_width;     // thickness of the border line
  ENUM_LINE_STYLE   border_style;     // style of the border line

//  Coordinates of a border:
private:
  datetime         point_left;       // time of the left point (T1)
  datetime         point_right;      // time of the right point (T2)
  double           price_left;       // price of the left point (P1)
  double           price_right;      // price of the right point (P2)

public:
  void     TChannelBorderObject();  // constructor
  void    ~TChannelBorderObject();  // destructor
  bool     IsCreated();             // check whether the line is created

//  Creating/deleting a line:
public:
  bool     CreateBorder(long _chart_id, int _window, string _name, datetime _t_left, datetime _t_right, 
                           double _p_left, double _p_right, color _color, int _width, ENUM_LINE_STYLE _style);
  bool     CreateBorder(long _chart_id, int _window, string _name, datetime _t_left, datetime _t_right, 
                           double _p_left, double _p_right);
  bool     CreateBorder(datetime _t_left, datetime _t_right, double _p_left, double _p_right);
  bool     RemoveBorder();          // delete line from the chart

//  Setting parameters of the line:
public:
  void     SetCommonParams(long _chart_id, int _window, string _name);
  bool     SetBorderParams(color _color, int _width, ENUM_LINE_STYLE _style);
  bool     SetBorderColor(color _color);
  bool     SetBorderWidth(int _width);
  bool     SetBorderStyle(ENUM_LINE_STYLE _style);

//  Getting values on the line:
  double   GetPrice(datetime _dt); // get price value in the specified position of the border line
};

このクラスには特別なコメントは必要ありませんね。

指定されたポイントにて境界価格を取得する方法に着目します。

//---------------------------------------------------------------------
//  Get price value in the specified position of the border line:
//---------------------------------------------------------------------
double TChannelBorderObject::GetPrice(datetime _dt)
{
//  If the graphical object is created:
  if(is_created == true)
  {
    return(ObjectGetValueByTime( chart_id, border_name, _dt));
  }
  return(0.0);
}

ここではターミナル関数 ObjectGetValueByTime が使われます。これは指定時刻に対する価格値を返します。数式を使って値を計算するより、ターミナル機能を利用する方が便利です。


チャネルラインを引く : TSlideChannelObjectクラス

このクラスは標準クラス CListから派生しています。目的は以下です。

  • TChannelBorderObject クラスのオブジェクトを格納し、それを使って異なる処理を行うこと
  • チャネルを構成するのに必要な線を引くためのポイントを計算すること
  • チャネルのパラメータを格納し変更すること
  • 描かれたチャネルラインを記述する値の計算結果(高さ、境界の価格値など)を取得すること

このクラスを記述するコードは膨大なので、ここでは表示しません。参照されたい方は、本稿に添付されている SlideChannelClasses.mqh ファイルをご覧ください。それでは主要部分の抜粋を分析します。

まず、それぞれ点T2 と点T3で値 B2 と B3を取得します。(図4参照)それには以下のコードが使用されています。

//  Get relative shifts in bars relatively to the extremum points:
  total_bars = Bars(symbol, time_frame);     // total number of bars in history
  if(total_bars == 0)
  {
    return(false);                           // channel cannot be drawn
  }
  double  B2 = Bars(symbol, time_frame, point_left, point_right);
  double  B3 = Bars(symbol, time_frame, point_left, point_middle);

空のバーを呼ばないように、指定されたシンボルと期間に対して履歴内のバー数を返すターミナル関数 Bars を使用します。まだ情報が生成されていない場合は、関数はゼロを返します。これは確認用に使用されます。

関数がゼロ以外を返すと、値В2 および値В3 を取得します。同じ関数 Bars を使っても行いますが、これは別の形式で呼びます。時間制限を設け、その範囲内でバー数を取得します。左側の境界は同じなので、点Т2 および点 Т3に対するズレを取得します。点Т1 に対するズレは常にゼロです。

これで、チャネルラインの点すべてを計算することができます。ここでのチャネルは中心線と境界および中心線付近のゼロパーセンテージ線を表示する(上限、下限に加え)ので、点は最大9個になります。

計算の主要部分を分析します。計算全体 はTSlideChannelObject::CalcChannel メソッドで行われます。

//  Coefficient of the line inclination:
  koeff_A = (price_right - price_left) / B2;

//  Price value on the AB line in the point T3:
  double  P3_AB = price_left + B3 * koeff_A;

// Determine the channel type - 2MAX_1MIN или 1MAX_2MIN:
  if(P3_AB > price_middle)              // 2MAX_1MIN
  {
    channel_type = CHANNEL_2MAX_1MIN;

    left_prices[BORDER_UP_INDEX] = price_left;
    right_prices[BORDER_UP_INDEX] = price_right;
        
    left_prices[BORDER_DN_INDEX] = price_middle - B3 * koeff_A;
    right_prices[BORDER_DN_INDEX] = left_prices[BORDER_DN_INDEX] + (price_right - price_left);
  }
  else if(P3_AB < price_middle)         // 1MAX_2MIN
  {
    channel_type = CHANNEL_1MAX_2MIN;

    left_prices[BORDER_DN_INDEX] = price_left;
    right_prices[BORDER_DN_INDEX] = price_right;
        
    left_prices[BORDER_UP_INDEX] = price_middle - B3 * koeff_A;
    right_prices[BORDER_UP_INDEX] = left_prices[BORDER_UP_INDEX] + (price_right - price_left);
  }
  else
  {
    return( false );                      // channel cannot be drawn (all extremums are on the same line)
  }

ここでのleft_prices および right_prices はチャネルの9本のラインの価格座標を格納する配列です。チャネルのすべてのラインの時間座標はすでにわかっています。

まず、ライン傾斜、koeff_Aの係数を決定します。(式(2)を参照ください)それから、点T3 における線分ABの価格値を計算します。(図4参照)それは描画にどのタイプのチャネルを指定するか決めるために行います。2つの最大と1つの最小、または2つの最小と1つの最大によって。価格軸において、どの点がより高いか確認します。点C または座標(P3', T3) の点。それらの位置に応じて、チャネルは最初のタイプか2番目のタイプか決定します。

チャネルの2本の主要ライン(上下)の座標が決まれば、その他7本のラインの座標を求めるのは難しいことではありません。たとえば、次のようにして、チャネルの上下境界座標を用いて中央線の座標を計算します。

left_prices[BORDER_MD_INDEX] = (left_prices[BORDER_DN_INDEX] + left_prices[BORDER_UP_INDEX ]) / 2.0;
  right_prices[BORDER_MD_INDEX] = (right_prices[BORDER_DN_INDEX] + right_prices[BORDER_UP_INDEX]) / 2.0;

チャネルの上下境界から平均値をとります。


指定の極値によりチャネルラインを引くインディケータ : SlideChannel

ここまでで、すでにチャネルラインを引くクラスは手に入れました。ここから、グローバル変数から極値のパラメータを読みチャートにチャネルラインを引くインディケータを書いていきます。以下に示します。

図5 極値を使用したチャネルラインの描画例

図5 極値を使用したチャネルラインの描画例

描かれたチャネルラインの情報もまたここに表示されます。巾、チャネル境界および中央線までの現在価格からの距離のポイント表示です。

必要なライブラリを連携します。

#include  <TextDisplay.mqh>
#include  <SlideChannelClasses.mqh>

最初のライブラリは画面にテキスト情報を表示するクラスを持ちます。(『標準ライブラリクラス使用による独自のMarket Watch作成』稿を参照ください。)それを使い、仮の極値ポイント値を表示していきます。

そして、インディケータの入力パラメータを追加します。(ここでは主要なものを一つだけ記述しています。)

input string          PrefixString = "MainChannel";
//---------------------------------------------------------------------
input ENUM_TIMEFRAMES  ExtremumTimeFrame = PERIOD_CURRENT;
//---------------------------------------------------------------------
input bool            ShowInfo = true;

最初のパラメータ PrefixStringExtremumHandSet インディケータとちょうど同じで、極値を読むときにグローバル変数名を構成するのに使用される接頭辞の設定をします。また、単一チャート上でこの種のインディケータを複数使用する機能も提供します。すべきことは、それらに異なる接頭辞を設定することだけです。

ExtremumTimeFrame パラメータは時間枠を設定します。これはグローバル変数から極値点を読むのに使用されます。たいへん便利なパラメータで、それにより異なる時間枠で同期するチャネルラインを描くことができます。たとえば、極値を H1から設定したら M5の時間枠でも同じチャネルラインを描くことが可能です。そのためには、チャネルラインを描くインディケータを M5 チャートに追加します。それですべての変更を同期的に表示します。

パラメータ ShowInfo は画面上のチャネルパラメータに関するテキスト情報の表示を管理します。

次に、情報を表示しチャネルラインを描くオブジェクトの作成をします。

TableDisplay         ChannalDisplay;  // displaying of general information about a channel on the screen
TableDisplay         BordersDisplay;  // displaying information about the borders of a channel on the screen
//---------------------------------------------------------------------
TSlideChannelObject  Channel;         // drawing of a channel

チャネルラインを描くためのオブジェクトは以下の方法で初期化されます。

Channel.CreateChannel(PrefixString, 0, 0, Symbol(), period_current, curr_left_point, curr_middle_point, 
                        curr_right_point, curr_left_price, curr_middle_price, curr_right_price);
  Channel.SetBorderWidth(BorderWidth );
  Channel.SetmiddleWidth(middleLineWidth);
  Channel.SetUpBorderColor(UpBorderColor);
  Channel.SetDnBorderColor(DnBorderColor);
  Channel.SetmiddleColor(middleLineColor );
  Channel.ShowBorderZone(ShowBorderPercentageLines);
  Channel.BorderZonePercentage( PercentageZoneSize);
  Channel.Showmiddle(ShowmiddleLine);
  Channel.ShowmiddleZone( ShowmiddlePercentageLines);
  Channel.middleZonePercentage(PercentagemiddleZoneSize);

ここではまず、メソッド TSlideChannelObject::CreateChannel を呼んでチャネルを作成し、それからチャネルラインに必要なパラメータを設定します。設定の順序は問題ありません。パラメータを設定し、それからチャネル作成、と順番を逆にしても作成可能です。

パラメータ period_current はグローバル変数から極値を読むときに使われる期間です。それは現在チャートの期間とは異なっている可能性があります。

では、インディケータで使用される主要な関数を見ていきます。最初の関数 GetExtremums は極値の位置を読み、取得された値に応じてチャネルをリフレッシュするのに使われます。

void GetExtremums()
{
  double  temp;
  string  name;

  StringConcatenate(name, PrefixString, "_", Symbol(), "_", period_current, "_DTime_Extr1");
  if( GlobalVariableGet(name, temp) != false)
  {
    curr_left_point = (datetime)temp;
  }

  StringConcatenate(name, PrefixString, "_", Symbol(), "_", period_current, "_DTime_Extr2");
  if( GlobalVariableGet(name, temp) != false)
  {
    curr_middle_point = (datetime)temp;
  }

  StringConcatenate(name, PrefixString, "_", Symbol(), "_", period_current, "_DTime_Extr3");
  if( GlobalVariableGet(name, temp) != false)
  {
    curr_right_point = (datetime)temp;
  }

  StringConcatenate(name, PrefixString, "_", Symbol(), "_", period_current, "_Price_Extr1");
  if( GlobalVariableGet(name, temp) != false)
  {
    curr_left_price = temp;
  }

  StringConcatenate(name, PrefixString, "_", Symbol( ), "_", period_current, "_Price_Extr2");
  if( GlobalVariableGet(name, temp) != false )
  {
    curr_middle_price = temp;
  }

  StringConcatenate(name, PrefixString, "_", Symbol(), "_", period_current, "_Price_Extr3");
  if( GlobalVariableGet(name, temp) != false)
  {
    curr_right_price = temp;
  }

//  Update the position of channel:
  Channel.SetExtremums(curr_left_point, curr_middle_point, curr_right_point, 
                       curr_left_price, curr_middle_price, curr_right_price);
}

画面上でチャネルをリフレッシュするには、メソッド TSlideChannelObject::SetExtremums を使用します。このメソッドはチャネルラインの座標を再計算し、画面上にチャネルを再描画します。

異なる時間枠でチャネルラインを描く例は以下のビデオを参照ください。

インディケータを起動する順序はまったく問いませんが、最初にExtremumHandSet インディケータから始め、それから3個黄色(ラベルの色はインディケータパラメータで設定します。初期設定は黄色となっています。)の 左側の価格ラベル を追加し、指定の極値でチャネルラインを描く SlideChannel インディケータを起動する、という順序が理にかなっているでしょう。。

最初のチャートの極値を同期してチャネルラインを描くには、 SlideChannel インディケータ内のExtremumTimeFrame パラメータに、極値を設定したチャートのひとつと同じように時間枠を設定する必要があります。

これが、チャネルラインの極値点を設定する関数を端末画面上にチャネルラインを描く関数と分離した結果です。


おわりに

画面上にチャネル位置を設定するところからそれを描くまでの一部始終を見てきました。すべてそれほど複雑には見えません。特に標準クラスと OOPの使用の際に顕著だと思います。

ただ、疑問は、マーケットで作業する際、チャネルをどのように利用するのか、です。まず、金融商品の現状のテクニカル分析に必要です。また、分析後の判断に必要です。そこでチャネルラインはかなりの助けとなりえます。

ポジションのオープンならびにクローズ境界を分析する半自動Expert Advisor を開発することも可能です。それだと境界のブレークスルーや、そこからのロールバックどちらも可能です。これは次稿、『 チャネルラインでの作業- ロールバックとブレークスルー』の話題としてお届けします。

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/200

添付されたファイル |
extremumclasses.mqh (12.58 KB)
extremumhandset.mq5 (11.89 KB)
slidechannel.mq5 (13.7 KB)
textdisplay.mqh (15.21 KB)
トレーディングモデルに基づくマルチエキスパートアドバイザーの作成 トレーディングモデルに基づくマルチエキスパートアドバイザーの作成
MQL5にてオブジェクト指向のアプローチを使用すると、マルチ通貨・マルチシステム・マルチタイムフレームのエキスパートアドバイザーの作成を大幅に単純化することができます。全てのタイムフレーム、全ての金融商品において、いくつものトレーディング戦略に基づいた取引をあなたのEAが行うことを想像してみてください。さらに、EAはテスターにて簡単にテストすることができ、すべての戦略において、稼働する資産管理システムがいくつもあります。
MetaTrader 5での並列計算 MetaTrader 5での並列計算
人類の歴史において時間は高価値であり、われわれはそれを無駄に費やさないよう努力しています。本稿では、マルチ コア プロセッサのコンピュータをご使用の場合、お手元のExpert Advisor の動作スピードを上げる方法について述べていきます。また、提案のメソッド実装には MQL5以外の言語知識は必要とされません。
ミクロ、ミドル、メイントレンドのインディケータ ミクロ、ミドル、メイントレンドのインディケータ
本稿は James Hyerczyk著 "Pattern, Price &amp; Time: Using Gann Theory in Trading Systems" から得たいくらかの考え方を基にインディケータやExpert Advisor形式でトレードの自動化可能性調査と分析を目的とします。完全にとは言わず、ここではモデル、すなわちギャン理論の最初の部分だけを対象とします。
CChartObject クラスに基づく新規GUIウィジェット設計と実装 CChartObject クラスに基づく新規GUIウィジェット設計と実装
前稿『GUI を使用した半自動Expert Advisor』を書いてのち、より複雑なインディケータやExpert Advisorsに新しい関数を伴うインターフェースを強化したいと思うようになりました。MQL5 標準ライブラリクラスを知ってから、新しいウィジェットを実装しました。本稿は、インディケータやExpert Advisorsで使用可能な新しい MQL5 GUI ウィジェットの設計と実装について述べます。本稿で提供しているウィジェットは CChartObjectSpinner、 CChartObjectProgressBar、CChartObjectEditTable です。