CGraphic ライブラリを使用したスキャルピング相場深度の実装

Vasiliy Sokolov | 3 11月, 2017


目次

イントロダクション

相場深度に関連するライブラリの説明は、 2 年前に公開されています。 以来、ティックヒストリーへのアクセスは MQL5 で実装されています。 MetaQuotes はまた、複雑な統計グラフィックスとしてカスタムデータを視覚化することができるCGraphicライブラリをリリースしました。 CGraphic の目的は、 Rプログラミング言語のプロット関数に似ています。 このライブラリの使用メソッドについては、別の記事で詳しく説明します。 

この導入で、以前提供した相場深度を近代化することができます。 オーダーブックに加えて、最後のトレードとティックチャートは、新しいバージョンでも表示されます:

図1. ティックチャートを使用した相場深度。

以前のライブラリのバージョンは、相場深度をレンダリングしたグラフィカルパネルを操作するためのCMarketBookクラスの2つの主要モジュールで構成されています。 このコードは変更され、大幅に向上しました。 バグが修正され, 相場深度のグラフィック部分は、シンプルで軽量なCPanelグラフィックスライブラリとなっています。

では、CGraphic と、図表と線形チャートを別のウィンドウに描画する関数について、もう一度確認してみましょう。 このような特定の関数は、統計的な問題にのみ有用であると思われます。 しかし、そんなことはありません! この記事では、CGraphic の関数が統計からかけ離れたプロジェクト (たとえば、スキャルピング相場の深度を作成する場合など) でどのように使用できるかを説明します。

以前のバージョンのリリース以降に行われた変更

MQL5 クックブック: 独自の相場深度の実装」を公開した後、実際に多くの CMarketBook を使用し、コード内のエラーの数を見つけました。 徐々にインターフェイスを変更しました。 ここでは、すべて変更されます:

  1. 相場深度のウィンドウの元のグラフィックは非常にミニマルでした。 オーダーブックのセルは、初等クラスを使用して表示されました。 しばらくすると、クラスに追加関数が実装され、そのシンプルさと軽さが、他のタイプのパネルを設計する際に非常に便利であることがわかりました。 結果として、このクラスのセットは別の独立したプロジェクト-CPanel のライブラリに浮上しました。 これはインクルードフォルダーにあります。
  2. 相場深度の外観も改善されています。 たとえば、小さな3角形は、相場深度のウィンドウを開いたり閉じたりする大きな正方形のボタンに置き換えられています。 要素の重なりが修正されました (テーブルを再オープンした場合、相場深度の要素は既存のもの上に再び描画されました)。
  3. 新しい設定を使用すると、チャートの X および Y 軸に沿って相場深度ウィンドウを開いたり閉じたりするボタンのポジションを変更できます。 多くの場合、非標準のシンボル名と追加のトレーディングパネルに、相場深度の開く/閉じるボタンは、チャートの他のアクティブな要素をカ足することができます。 ボタンのポジションを裁量で設定する可能性は、このようなオーバーラップを回避することができます。
  4. CMarketBook クラスが大きく変わりました。 このクラスでは次のエラーが修正されました: 配列範囲エラーがありません。空または部分的に埋められたオーダーブックのエラー。シンボルの変更時にゼロ除算エラーが発生しました。 CMarketBook クラスは現在、MQL5\Include\Trade の独立したモジュールです。
  5. インジケーターの全体的な安定性を向上させるために、小さな変更が実装されています。

徐々にスキャルピング相場深度のツールにを回して改善し、改訂版で動作するようにされています。

CPanel のグラフィックライブラリの概要

MQL5 では、ユーザーインターフェイスの作成に関する記事が多数あります。 Anatoly Kazharsky の「グラフィカルインターフェース」シリーズは、その中でも際立っています。 この記事は本当に良くできています。 したがって、詳細にグラフィカルインターフェイスの作成を分析することはありません。 既に述べたように、相場深度のグラフィカルな部分は、完全に関数 CPanel のライブラリとして利用可能になりました。 このライブラリに基づいて特別なグラフィック要素 "ティックチャート" を作成するので、その基本的なアーキテクチャについて説明する必要があります。 要素を持つフル関数のパネルを作成するためにオーダーブックと組み合わせることになります。

さらに動作原理を理解するために、詳細にCPanelを検討してみましょう。 MQL5 のグラフィカルな要素は、グラフィックプリミティブとして表されます。 次のようになります。

  • テキストラベル
  • ボタン
  • インプットボックス
  • 長方形のラベル
  • ビットマップラベル

すべて同じプロパティ数があります。 たとえば、長方形のラベル、ボタン、インプットボックスは区別するのが難しいように構成することができます。 したがって、要素はほぼすべてのグラフィックプリミティブに基づいてすることができます。 たとえば、ボタンの代わりにインプットボックスを表示したり、長方形のラベルの代わりにボタンを示すことができます。 視覚的に、このような置換がされ、ユーザーがインプットボックスをクリックすると、ボタンを押していると思うでしょう。

このような置換は、奇妙なことがあり、UI 作成の原則の一般的な理解を複雑にする可能性があります。 しかし、共通の特性に加えて、それぞれの基本的な要素は、独自のプロパティを持って理解する必要があります。 たとえば、インプットボックスを透明にすることはできませんが、長方形のラベルは可能です。 したがって、同じクラスから一意のビューを持つ要素を作成することができます。

例を示しましょう。 従来のテキストラベルを持つグラフィカルパネルを作成する必要があるとします。

図2. 枠のないテキストを含むフォームの例。

ただし、フレームを追加したい場合、テキストラベルに "frame" プロパティがないため、問題が発生します。 解決策は簡単です: テキストラベルの代わりにボタンを使用します! ここでは、ボタンを持つフォームです:

図3. フレーム内のテキストを含むフォームの例。

グラフィカルインターフェイスを作成する場合、同様のケースが多く発生する可能性があります。 ユーザーが必要とすることを事前に予測することは不可能です。 したがって、特定のプリミティブに対してグラフィック要素をベースにするのではなく、ユーザーに決定する機会を与えることをお勧めします。

要素の CPanel ライブラリが配置されている方法です。 CPanel は、それぞれの高レベルのグラフィカルインターフェイスの特定の要素を表すクラスのセットです。 このような要素を初期化するには、ベースとなるグラフィックプリミティブの型を指定する必要があります。 このようなクラスには共通の親があり、1つの関数だけを実行する CNode クラスは基本的なプリミティブの型を格納します。 プロテクトコンストラクターは、要素の作成時に型指定を必要とします。 

高レベルのグラフィック要素はほとんどありません。 いわゆる一意性は、基本要素に割り当てる必要があるプロパティのセットによって異なります。 CPanel では、このようなユニークな要素は、CElChart クラスです。 他のすべてのクラスと同様に、CNode から派生し、次のプロパティを構成するメソッドがあります。

  • 要素の長さと高さ、
  • チャートを基準とした要素の X および Y 座標。
  • 要素の幅と高さ、
  • 要素の背景とフレームの色 (そのようなプロパティがサポートされている場合)、
  • 要素フレームの型 (このプロパティがサポートされている場合)、
  • 要素内のテキスト、フォント、サイズ、および配置 (そのようなプロパティがサポートされている場合)。

CElChart はプロパティを設定するためのメソッドを提供しますが、プロパティが実際に配置されることは保証されません。 CElChart が特定のプロパティをサポートするかどうかは、基本要素だけが決定します。 CNode と同様に、CElChart は、グラフィックプリミティブの型の仕様を必要とします。 したがって、CElChart を使用すると、通常のフォームと、たとえば、ボタンやテキストフィールドの両方を作成することができます。 実際には非常に便利な関数です。

例: 図3のように、パネルを描画してみましょう。 フレームを持つ背景とフレームを持つテキストの2つの要素が必要です。 どちらも同じ CElChart クラスのインスタンスです。 しかし、2つの異なるグラフィカルなプリミティブがで使用されます: OBJ_RECTANGLE_LABEL と BJ_BUTTON。 次に、結果のコードを示します。

//+------------------------------------------------------------------+
//|                                       MarketBookArticlePanel.mq5 |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include<Panel\ElChart.mqh>

CElChart Fon(OBJ_RECTANGLE_LABEL);
CElChart Label(OBJ_BUTTON);
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int()
{
//---
   Fon.Height(100);
   Fon.Width(250);
   Fon.YCoord(200);
   Fon.XCoord(200);
   Fon.BackgroundColor(clrWhite);
   Fon.BorderType(BORDER_FLAT);
   Fon.BorderColor(clrBlack);
   Fon.Show();
   
   Label.Text("Meta Quotes Language");
   Label.BorderType(BORDER_FLAT);
   Label.BorderColor(clrBlack);
   Label.Width(150);
   Label.Show();
   Label.YCoord(240);
   Label.XCoord(250);
   ChartRedraw();
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
+------------------------------------------------------------------+
  {
//---
   
  }
//+------------------------------------------------------------------+


すべての要素が作成されたら、プロパティを [関数] に設定する必要があります。 Show メソッドを呼び出すことによって、チャートに要素を表示できます。

基本的なプリミティブと CElChart クラスを組み合わせることにより、シンプルグラフィカルインターフェイスを作成できます。 相場深度のグラフィカルな表示も同様に配置されます。 オーダーブックは、CElChart に基づいている複数の CBookCell 要素で構成されています。

CPanel のグラフィックスエンジンは、ネストをサポートしています。 つまり、追加の要素を1つの要素内に配置できます。 ネストすると、ユニバーサルコントロールが有効になります。 たとえば、グローバルフォームに対して指定されたコマンドは、そのサブ要素のすべてに送信できます。 上記の例を変更してみましょう:

//+------------------------------------------------------------------+
//|                                       MarketBookArticlePanel.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include<Panel\ElChart.mqh>

CElChart Fon(OBJ_RECTANGLE_LABEL);
CElChart *Label;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int()
{
//---
   Fon.Height(100);
   Fon.Width(250);
   Fon.YCoord(200);
   Fon.XCoord(200);
   Fon.BackgroundColor(clrWhite);
   Fon.BorderType(BORDER_FLAT);
   Fon.BorderColor(clrBlack);
   
   Label = new CElChart(OBJ_BUTTON);
   Label.Text("Meta Quotes Language");
   Label.BorderType(BORDER_FLAT);
   Label.BorderColor(clrBlack);
   Label.Width(150);
   Label.YCoord(240);
   Label.XCoord(250);
   //Label.Show();
   Fon.AddElement(Label);
   
   Fon.Show();
   ChartRedraw();
//---
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
+------------------------------------------------------------------+
  {
//---
   
  }


プログラムの実行中に、CElCahrt 要素へのポインタである CLabel が動的に作成されます。 作成後、適切な確認作業を行うと、フォームに追加されます。 別の Show コマンドを使用して表示する必要はありません。 代わりに、アプリのメインフォームである Fon 要素の Show コマンドを実行するので十分です。 コマンドはラベルを含むすべてのサブ要素に対して実行されます。 

要素のプロパティを設定するだけでなく、CPanel は、高度なイベントモデルをサポートしています。 イベントがチャートから受信するだけでなく、何でもCPanel のイベントになることができます。 この関数は、CEvent クラスとイベントメソッドによって提供されます。 CEvent は抽象クラスです。 より多くの特定のクラスは基づいています。例えば CEventChartObjClickです。

ユーザーフォームにネストになったサブ要素がいくつかあるとします。 ユーザーは、1つのイベントを作成することができます。 例として、要素のいずれかをクリックすることができます。 どのようにしてクラスのインスタンスがイベントを処理すべきかをご存じでしょうか。 この目的のために、CEventChartObjClick イベントを使用します。つまり、クラスのインスタンスを作成し、中央のフォームに送信してみましょう:

CElChart Fon(OBJ_RECTANGLE_LABEL);
...
...
void OnChartEvent(const int id,         //イベント識別子
                  const long& lparam,   //long 型のイベントパラメータ
                  const double& dparam, //double 型のイベントパラメータ
                  const string& sparam  //文字列型のイベントパラメータ
  )
{ 
   CEvent* event = NULL;
   switch(id)
   {
      case CHARTEVENT_OBJECT_CLICK:
         event = new CEventChartObjClick(sparam);
         break;
   }
   if(event != NULL)
   {
      Fon.Event(event);
      delete event;
   }
}

このメソッドを使用して、Fon インスタンス内のすべての要素が受信する CEventChartObjClick ブロードキャストイベントを送信しました。 イベントが処理されるかどうかは、フォームの内部ロジックによって異なります。 

MetaQuotes 言語ラベルで、このクリックを処理し、テキストを"Enjoy" に変更してみましょう。 これを行うには、CEnjoy クラスを作成し、必要なロジックを入れます。 適切なイベントのハンドラである OnClick メソッドをオーバーライドします。

//+------------------------------------------------------------------+
//|                                       MarketBookArticlePanel.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include<Panel\ElChart.mqh>
+------------------------------------------------------------------+

CElChart Fon(OBJ_RECTANGLE_LABEL);

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
class CEnjoy : public CElChart
{
protected:
   virtual void OnClick(void);
public:
                CEnjoy(void);
   
};

CEnjoy::CEnjoy(void) : CElChart(OBJ_BUTTON)
{
}
void CEnjoy::OnClick(void)
{
   if(Text() != "Enjoy!")
      Text("Enjoy!");
   else
      Text("MetaQuotes Language");
}
CEnjoy Label;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int()
{
//---
   Fon.Height(100);
   Fon.Width(250);
   Fon.YCoord(200);
   Fon.XCoord(200);
   Fon.BackgroundColor(clrWhite);
   Fon.BorderType(BORDER_FLAT);
   Fon.BorderColor(clrBlack);
   
   Label.Text("Meta Quotes Language");
   Label.BorderType(BORDER_FLAT);
   Label.BorderColor(clrBlack);
   Label.Width(150);
   Label.YCoord(240);
   Label.XCoord(250);
   //Label.Show();
   Fon.AddElement(&Label);
   
   Fon.Show();
   ChartRedraw();
//---
   return(INIT_SUCCEEDED);
}
void OnChartEvent(const int id,         //イベント識別子
                  const long& lparam,   //long 型のイベントパラメータ
                  const double& dparam, //double 型のイベントパラメータ
                  const string& sparam  //文字列型のイベントパラメータ
  )
{ 
   CEvent* event = NULL;
   switch(id)
   {
      case CHARTEVENT_OBJECT_CLICK:
         event = new CEventChartObjClick(sparam);
         break;
   }
   if(event != NULL)
   {
      Fon.Event(event);
      delete event;
   }
   ChartRedraw();
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
+------------------------------------------------------------------+
  {
//---
   
  }
//+------------------------------------------------------------------+


イベントメソッドを使用して CEventObjClick イベントをフォームに送信し、OnClick で処理することは、奇妙に思えるかもしれません。 多くの標準イベント (マウスクリックなど) には、独自の特別なイベントメソッドがあります。 オーバーライドすると、対応するイベントが表示されます。 オーバーライドしない場合、イベントメソッドでは、すべてのイベントが1レベル高く処理されます。 また、OnClick と同様にオーバーライドできます。 このレベルでは、受信した CEvent インスタンスの分析によってイベントが処理されます。 

今は詳しくはしません。 CPanel の主なプロパティを指定してみましょう。

  • GUI 要素を実装するすべての CPanel クラスは、選択したグラフィックプリミティブに基づいて行うことができます。 クラスインスタンスの作成時に選択および指定されます。
  • 任意の CPanel の要素は、他の CPanel の要素の無制限の数を含めることができます。 ネストとコントロールの普遍性がどのように実装されるかです。 すべてのイベントはネストツリーに沿って配布されるため、すべての要素がすべてのイベントにアクセスできます。
  • CPanel のイベントモデルは、2つのレベルがあります。 低いレベルモデルは、イベントメソッドと CEvent 型クラスに基づいています。 MQL でサポートされていないものでも、必ずすべてのイベントを処理することができます。 CEvent を介して送信されるイベントは常にブロードキャストします。 高いレベルでは、標準イベントは適切なメソッドの呼び出しに変換されます。 たとえば、CEventChartObjClick イベントは OnClick 呼び出しに変換され、Show メソッド呼び出しは、すべての子要素に対して OnShow メソッドの再帰呼び出しを生成します。 このレベルでは、直接イベント呼び出しが可能です。 たとえば、Show () を呼び出すと、パネルが表示されます。 リフレッシュの呼び出しは、パネルの表示を更新します。

レビューはかなり簡潔であることが判明, しかし、一般的なアイデアは、スキャルピング相場深度のツールを作成しながら取るつもりであることを理解するために十分なはずです.

オーダーブックでのティックストリームの同期

オーダーブックは動的な構造で、その値はボラティリティが高い相場では1秒間に数十回変更されることがあります。 オーダーブックの現在の状態にアクセスするには、対応するイベントハンドラ OnBookEvent 関数で特別な BookEvent を処理する必要があります。 オーダーブックが変更されると、ターミナルは OnBookEvent を呼び出し、変更に対応するシンボルを示します。 前回の記事では、現在のオーダーブックの状態に便利なアクセスを提供する CMarketBook クラスを開発しました。 このクラスでは、OnBookEvent 関数の Refresh () メソッドを呼び出すことによって、オーダーブックの現在の状態を取得できます。 これがどのように見えるかです。:

//+------------------------------------------------------------------+
//                                                  MarketBook mq5 |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#propertyindicator_chart_window
#propertyindicator_buffers0
#propertyindicator_plots0
#include<MarketBook.mqh>

CMarketBook MarketBook mqh
double fake_buffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int()
  {
   MarketBook.SetMarketBookSymbol(Symbol());
//---インジケーターバッファマッピング
   SetIndexBuffer(0,fake_buffer,INDICATOR_CALCULATIONS);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| MarketBook change event                                          |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
   if(symbol != MarketBook.GetMarketBookSymbol())
      return;
   MarketBook.Refresh();
   ChartRedraw();
}

新しいバージョンでは、オーダーブックに加えて、相場深度は、リアルタイムのティックチャートを表示する必要があります。 したがって、新たに受信したティックを操作するための追加関数を実装する必要があります。 MQL5 では、ティックを解析するための基本的なメカニズムが3つあります。 次のようになります。

  • SymbolInfoTick 関数を使用して、最後の既知のティックを取得する
  • 新しいティックの到着を処理し、インジケーターの OnCalculate で処理する。
  • CopyTicks および CopyTicksRange 関数を使用したティックヒストリーの取得

最初の2つのメソッドを組み合わせることができます。 たとえば、最後のティックパラメータにアクセスするために、SymbolInfoTick 関数を OnTick または OnCalculate で呼び出すことができます。 しかし、2つのメソッドは、ティックの流れの出現に動作しません。

ティックがどのように形成されているかを理解するためには、モスクワ為替デリバティブ相場の例を通じた為替価格設定の記事の原則を確認し、金の相場深度を考えてみましょう: 

価格は、金のオンス当たり $ オンス数 (コントラクト)
1280.8 17
1280.3 3
1280.1 5
1280.0 1
1279.8 2
1279.7 15
1279.3 3
1278.8 13

図4. 相場深度の例。

相場深度更新時に、ティックヒストリーをリクエストし、以前の更新以降に到着したティック数を特定するための特別なアルゴリズムを使用するとします。 理論的には、すべてのティックがオーダーブックの少なくとも1つの変更に対応している必要があり、オーダーブックの変更ごとに1つ以上のティックが到着できないことを意味します。 しかし、現実には異なり、適切な同期のティックヒストリーを使用する必要があります。

9ゴールドのコントラクトの買い手がいると仮定します。 買い手は、少なくとも3つのトレードを実行します。 より多くの買い手が1280.1 または1280.3 で買う意思がある場合は、さらにお得になります。 1 つのアクション (買い) を実行することにより、買い手は同時に複数のトレードを作成します。 よって、ティックの "パック" は、MetaTrader5ターミナルに到着します。 そして、 OnCalculate で SymbolInfoTick 関数を使用する場合、前のティックが失われますが、このシリーズから最後のティックを返します。

そのため、CopyTicks 関数を使用してティックを取得するための信頼性の高いメカニズムが必要になります。 SymbolInfoTick とは異なり、この関数は CopyTicksRange と同様に、一連のティックを受け取ることができます。 ティックヒストリーを正確に表示することができます。

しかし、CopyTiks 関数は、N の最後のティックをリクエストすることはできません。 代わりに、指定された時間の後に到着したすべてのティックを提供します。 タスクが複雑になります。 リクエストを実行し、ティックの配列を受け取り、前回の更新時に受信したティックの配列と比較する必要があります。 したがって、ティックが前のパックに含まれていなかったものを見つけることができます。 しかし、間には目に見える違いがないため、ティックは互いに直接比較することはできません。 たとえば、次のオーダーブックの例を参照してください。

図5. 同一のトレードのオーダーブック。

同一のティックの2つのグループを見ることができます。 赤いフレームと印が付いている: 同じ時間、ボリューム、方向および価格を有します。 よって、お互いにティックを比較することはできないことは明確です。

しかし、ティックのグループを比較することができます。 ティックの2つのグループが等しい場合、ティックはすでに前の価格更新時に分析されています。

CMarketBook クラスをティック・ストリームと同期させます。つまり、以前の更新以降に受信した新しいティックを含む、MqlTiks 配列を追加します。 新しいティックは、内部 CompareTiks メソッドによって計算されます。

//+------------------------------------------------------------------+
//| Compare two ticks collections and find new ticks                 |
//+------------------------------------------------------------------+
void CMarketBook::CompareTiks(void)
{
   MqlTick n_tiks[];
   ulong t_begin = (TimeCurrent()-(1*20))*1000; //18秒前
   int total = CopyTicks(m_symbol, n_tiks, COPY_TICKS_ALL, t_begin, 1000);
   if(total<1)
   {
      printf("Failed to receive ticks");
      return;
   }
   if(ArraySize(m_ticks) == 0)
   {
      ArrayCopy(m_ticks, n_tiks, 0, 0, WHOLE_ARRAY);
      return;
   }
   int k = ArraySize(m_ticks)-1;
   int n_t = 0;
   int limit_comp = 20;
   int comp_sucess = 0;
   //Go through all received deals starting with the last one
   for(int i = ArraySize(n_tiks)-1; i >= 0 && k >= 0; i--)
   {
      if(!CompareTiks(n_tiks[i], m_ticks[k]))
      {
         n_t = ArraySize(n_tiks) - i;
         k = ArraySize(m_ticks)-1;
         comp_sucess = 0;
      }
      else
      {
         comp_sucess += 1;
         if(comp_sucess >= limit_comp)
            break;
         k--;
      }
   }
   //受信したティックを記録する
   ArrayResize(m_ticks, total);
   ArrayCopy(m_ticks, n_tiks, 0, 0, WHOLE_ARRAY);
   //新しいティックの先頭のインデックスを計算し、アクセス用のバッファにコピー。
   ArrayResize(LastTicks, n_t);
   if(n_t > 0)
   {
      int index = ArraySize(n_tiks)-n_t;
      ArrayCopy(LastTicks, m_ticks, 0, index, n_t);
   }
}

このアルゴリズムは非自明です。 CompareTicks は、最後の20秒間、すべてのティックをリクエストし、最後の1から始まるティックの以前の記録された配列と比較します。 現在の配列の20ティックが前の配列の20ティックに連続して等しい場合は、次のすべてのティックが新しいものと見なされます。

ここでは、このアルゴリズムを説明する簡単なスキームを示します。 呼び出しの間に短い間隔で2回 CopyTiks を呼び出すと仮定します。 ティックの代わりに、関数はゼロの配列を返します。 この2つの配列を取得したので、最初の配列の要素と一致しない2番目の配列に存在する一意の最後の要素の数を確認します。 これは前回の更新以降に受信した新しいティックの数になります。 スキームの動作です。

図6. 繰り返し同期スキーム。

比較では、最初の配列の 6 ~ 14 の数値が、2番目の配列の 1 ~ 8 の数値と等しくなることを示しています。 従って配列に5つの新しい価値があります。 アルゴリズムは、異なる組み合わせで動作します。つまり、配列は、共通の要素を持っていないか、絶対に同一である可能性があります。 これらの場合、新しい値の数が正しく決定されます。

比較では、最初の配列の 6 ~ 14 の数値が、2番目の配列の 1 ~ 8 の数値と等しくなることを示しています。 この配列は、CMarketBook クラス内のパブリックフィールドとして決定されます。

これはCMarketBook クラスの新しいバージョンであり、オーダーブックに加えて、前と現在の更新の間に受信したティックの配列があります。 たとえば、次のコードを使用して、新しいティックについて調べることができます。

//+------------------------------------------------------------------+
//| MarketBook change event                                          |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
   
   if(symbol != MarketBook.GetMarketBookSymbol())
      return;
   MarketBook.Refresh();
   string new_tiks = (string)ArraySize(MarketBook.LastTicks);
   printf("" + new_tiks + " new ticks have been received");
}

相場深度クラスのティックの可用性によって、ティックストリームとの現在のオーダーの適切な同期が可能になります。 オーダーブックの更新の各瞬間に、更新の前にティックのリストがあります。 そのため、ティックストリームとオーダーブックは完全に同期されます。 この重要性を後で使用します。 CGraphic のグラフィックライブラリに移ります。

CGraphic の基本

CGraphic ライブラリには、線分、ヒストグラム、点、および複雑な幾何学図形が含まれます。 その関数のごく一部を使用します。 Ask とBidレベルを表示するために2つの行が必要になります。 CGraphic は、 CCurveオブジェクトのコンテナです。 名前が示すように、各オブジェクトは、X と Y 座標を持つ点で構成される曲線です。 表示タイプによって、ラインによって接続することができ、ヒストグラム棒のトップとして示すことができます。 

最後のディールの表示関数に、2次元のチャートで動作します。 2次元のリニアチャートを作成しましょう:

//+------------------------------------------------------------------+
//                                                  TestCanvas mq5 |
//|                                 Copyright 2017, Vasiliy Sokolov. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Vasiliy Sokolov."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include<Graphics\Graphic.mqh>

CGraphic Graph;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int()
{
   double x[] = {1,2,3,4,5,6,7,8,9,10};
   double y[] = {1,2,3,2,4,3,5,6,4,3};
   CCurve* cur = Graph.CurveAdd(x, y, CURVE_LINES, "Line");   
   Graph.CurvePlotAll();
   Graph.Create(ChartID(), "Ticks", 0, (int)50, (int)60, 510, 300); 
   Graph.Redraw(true);
   Graph.Update();
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
+------------------------------------------------------------------+
  {
//---
   
  }
//+------------------------------------------------------------------+


EAとして実行すると、次の図がチャートに表示されます。

図7. CGraphic を使用して作成された2次元線形チャートの例

次に、2次元 CCurve の表示タイプをポイントに変更する表現を変更してみます。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int()
{
   double x[] = {1,2,3,4,5,6,7,8,9,10};
   double y[] = {1,2,3,2,4,3,5,6,4,3};
   CCurve* cur = Graph.CurveAdd(x, y, CURVE_POINTS, "Points");   
   cur.PointsType(POINT_CIRCLE);
   Graph.CurvePlotAll();
   Graph.Create(ChartID(), "Ticks", 0, (int)50, (int)60, 510, 300); 
   Graph.Redraw(true);
   Graph.Update();
   return(INIT_SUCCEEDED);
}

ポイントの形でチャートは次のようになります。

図8. CGraphic を使用して作成された2次元ポイントチャートの例

ご覧のように、ここでは曲線オブジェクトを作成し、その値を x および y 配列に含める必要があります。 

double x[] = {1,2,3,4,5,6,7,8,9,10};
double y[] = {1,2,3,2,4,3,5,6,4,3};
CCurve* cur = Graph.CurveAdd(x, y, CURVE_POINTS, "Points"); 

作成されたオブジェクトは CGraphic 内にあり、CurveAdd メソッドの参照を返します。 カーブタイプを CURVE_POINTS に設定し、ポイントタイプを円として指定することによって、2番目の例で行った曲線の必要なプロパティを設定できます。

cur.PointsType(POINT_CIRCLE);

すべての行を追加した後、[作成] および [再描画] コマンドを使用してチャートにプロットを表示する必要があります。

相場深度プロジェクトのアクションの同じシーケンスを繰り返しますが、曲線のデータは特別なメソッドで準備され、すべてのコマンドは、CElChart から派生した特別なCElTickGraphクラスの内部に配置されます。

CPanel のライブラリと CGraphic の統合

CGraphic でのタスクの主なポイントを議論します。 このクラスに CPanel のライブラリを追加しましょう。 すでに述べたように、CPanel のイベントへアクセスし、適切にグラフィカルな要素を配置し、そのプロパティを管理します。 すべて相場深度のパネルの有機的な部分がティックチャートを作成するために必要です。 したがって、最初のパネルに CGraphic を統合 CPanel の一部である特別な CElTickGraph 要素を、書き込みます。 さらに、CElTickGraph は、更新されたティック・プライス・ストリームを受け取り、ティック・チャートを再描画します。 最後のタスクは、最も困難なものです。 CElTickGraph ができるものを列挙します。

  • CElTickGraphは、相場深度パネル内の長方形の領域をマークします。 エリアには黒い枠が付いています。 
  • CElTickGraph領域内で、ティック価格ストリームを示す CGraphic チャートが表示されます。
  • ティックストリームは、最後の N ティックを示します。 N 個の数値は設定で変更できます。
  • CElTickGraphでは、CGraphic に含まれている CCurve 曲線の値が更新されるため、古いティックはチャートから削除され、新しいものが追加されます。 CElTickGraphは、スムーズに変更されるティックチャートの効果を作成できます。

CElTickGraph のタスクを簡略化するために、補助的な解決策を使用します。操作原理を詳細に説明したリングバッファを別の記事に示します。

次の値を表示する4つのリングバッファを作成してみましょう。

  • Ask レベル (赤線);
  • Bid レベル (青線);
  • 買い側から最後のトレード (下を指す青色の3角形として表示);
  • 売り側からの最後のトレード (赤い三角形を指すように表示)。

これが2つのリングバッファを必要とする理由です。

2番目の難しさは、Ask/Bidと最後の価格の間のポイントの数が等しくないという事実に基づきます。 連続線を描画する場合、X 軸に沿った各点の Y 値があります。 ラインポイントの代りに使用されれば、X の時にポイントは図表でない場合もあります。 このプロパティを考慮し、2次元のチャートを使用する必要があります。 次の x-y 座標を持つ点があるとします: 1000-57034。 新しいティック到着の時点で、同じポイントは座標999-57034 になります。 5つ以上のティックの後、位置994-57034 に移動します。 その最後の位置は0-57034 になります。 その後、チャートから消えます。 次の点は、異なる数のステップから遠くすることができます。 ポイント1の座標が994-57034 の場合、ポイント2は995:57035 または998:57035 になります。 2次元チャートに線を組み合わせることで、ティック・ストリームを連続キューにダンプすることなく、ギャップを正しくマッピングすることができます。

アカウントのインデックスに取ってオーダーブックを表示し、ティックの架空のテーブルを想像してみましょう:

Index Ask Bid Buy Sell
999 57034 57032 57034
998 57034 57032
57032
997 57034 57031 57034
996 57035 57033 57035
995 57036 57035

994 57036 57035

993 57036 57035
57035
992 57036 57034
57034
991 57036 57035 57035
...



表 1. 2次元テーブルにおけるティック・ストリーム同期方式 (チャート)

Ask と Bid の値はすべてテーブルに記入され、売買は空白になります。 インデックスに基づいて値を配置すると、異なる長さの系列が正しく同期されます。 最後のトレードに関係なく、常に必要な Ask とBidレベルを相関します。

CElTickGraph の一般的な原則を説明します。 下記はその完全なコードです。 その後、最も困難な部分を分析します。

//+------------------------------------------------------------------+
//|                                                         Graf.mqh |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include<Panel\ElChart.mqh>
#include<RingBuffer\RiBuffDbl.mqh>
#include<RingBuffer\RiBuffInt.mqh>
#include<RingBuffer\RiMaxMin.mqh>
#include"GlobalMarketBook mqh"
#include"GraphicMain mqh"
#include"EventNewTick mqh"

input int TicksHistoryTotal = 200;
//+------------------------------------------------------------------+
//| Determines the number of a curve in the CGraphic object          |
//+------------------------------------------------------------------+
+------------------------------------------------------------------+
{
   ASK_LINE,
   BID_LINE,
   LAST_BUY,
   LAST_SELL,
   LAST_LINE,
   VOL_LINE
};
//+------------------------------------------------------------------+
このソリューションの欠点は、目盛を大きくしたティックを表示できないことです。
//+------------------------------------------------------------------+
CElTickGraph:: CElTickGraph (ボイド): CElChart (OBJ_RECTANGLE_LABEL)
{
private:
   
   CGraphicMain m_graf;
   /* Key buffers for fast operation with the tick stream*/
   CRiMaxMin    m_ask;
   CRiMaxMin    m_bid;
   CRiMaxMin    m_last;
   CRiBuffDbl   m_last_buy;
   CRiMaxMin    m_last_sell;
   CRiBuffInt   m_vol;
   CRiBuffInt   m_flags;
   
   double       m_xpoints[];  //インデックスの配列
   void         RefreshCurves();
   void         SetMaxMin(void);
public:
                CElTickGraph(void);
   virtual void Event(CEvent* event);
   void         SetTiksTotal(int tiks);
   int          GetTiksTotal(void);
   void         Redraw(void);
   virtual void Show(void);
   virtual void OnHide(void);
   virtual void OnRefresh(CEventRefresh* refresh);
   void         AddLastTick();
};
//+------------------------------------------------------------------+
//チャートの初期化                                                     |
//+------------------------------------------------------------------+
CElTickGraph:: CElTickGraph (ボイド): CElChart (OBJ_RECTANGLE_LABEL)
{
   double y[] = {0};
   y[0] = MarketBook.InfoGetDouble(MBOOK_BEST_ASK_PRICE);
   double x[] = {0};
   
   CCurve* cur = m_graf.CurveAdd(x, y, CURVE_LINES, "Ask");   
   cur.Color(ColorToARGB(clrLightCoral, 255));
   cur.LinesEndStyle(LINE_END_ROUND);
   
   cur = m_graf.CurveAdd(x, y, CURVE_LINES, "Bid");
   cur.Color(ColorToARGB(clrCornflowerBlue, 255));
   
   cur = m_graf.CurveAdd(x, y, CURVE_POINTS, "Buy");
   cur.PointsType(POINT_TRIANGLE_DOWN);
   cur.PointsColor(ColorToARGB(clrCornflowerBlue, 255));
   cur.Color(ColorToARGB(clrCornflowerBlue, 255));
   cur.PointsFill(true);
   cur.PointsSize(5);
   
   
   cur = m_graf.CurveAdd(x, y, CURVE_POINTS, "Sell");
   cur.PointsType(POINT_TRIANGLE);
   cur.PointsColor(ColorToARGB(clrLightCoral, 255));
   cur.Color(ColorToARGB(clrLightCoral, 255));
   cur.PointsFill(true);
   cur.PointsSize(5);
   
   m_graf.CurvePlotAll();
   m_graf.IndentRight(1);
   m_graf.GapSize(1);
   SetTiksTotal(TicksHistoryTotal);
}
//+------------------------------------------------------------------+
//| Sets the number of ticks in t he chart window                    |
//+------------------------------------------------------------------+
void CElTickGraph::SetTiksTotal(int tiks)
{
   m_last.SetMaxTotal(tiks);
   m_last_buy.SetMaxTotal(tiks);
   m_last_sell.SetMaxTotal(tiks);
   m_ask.SetMaxTotal(tiks);
   m_bid.SetMaxTotal(tiks);
   m_vol.SetMaxTotal(tiks);
   ArrayResize(m_xpoints, tiks);
   for(int i = 0; i < ArraySize(m_xpoints); i++)
      m_xpoints[i] = i;
}

//+------------------------------------------------------------------+
//| Updates tick lines                                               |
//+------------------------------------------------------------------+
void CElTickGraph::RefreshCurves(void) 
{
   int total_last = m_last.GetTotal();
   int total_ask = m_ask.GetTotal();
   int total_bid = m_bid.GetTotal();
   int total = 10;
   for(int i = 0; i < m_graf.CurvesTotal(); i++)
   {
      CCurve* curve = m_graf.CurveGetByIndex(i);
      double y_points[];
      double x_points[];
      switch(i)
      {
         case LAST_LINE:
         {
            m_last.ToArray(y_points);
            if(ArraySize(x_points) < ArraySize(y_points))
               ArrayCopy(x_points, m_xpoints, 0, 0, ArraySize(y_points));
            curve.Update(x_points, y_points);
            break;
         }
         case ASK_LINE:
            m_ask.ToArray(y_points);
            if(ArraySize(x_points) < ArraySize(y_points))
               ArrayCopy(x_points, m_xpoints, 0, 0, ArraySize(y_points));
            curve.Update(x_points, y_points);
            break;
         case BID_LINE:
            m_bid.ToArray(y_points);
            if(ArraySize(x_points) < ArraySize(y_points))
               ArrayCopy(x_points, m_xpoints, 0, 0, ArraySize(y_points));
            curve.Update(x_points, y_points);
            break;
         case LAST_BUY:
         {
            m_last_buy.ToArray(y_points);
            CPoint2D points[];
            ArrayResize(points, ArraySize(y_points));
            int k = 0;
            for(int p = 0; p < ArraySize(y_points);p++)
            {
               if(y_points[p] == -1)
                  continue;
               points[k].x = p;
               points[k].y = y_points[p];
               k++;
            }
            if(k > 0)
            {
               ArrayResize(points, k);
               curve.Update(points);
            }
            break;
         }
         case LAST_SELL:
         {
            m_last_sell.ToArray(y_points);
            CPoint2D points[];
            ArrayResize(points, ArraySize(y_points));
            int k = 0;
            for(int p = 0; p < ArraySize(y_points);p++)
            {
               if(y_points[p] == -1)
                  continue;
               points[k].x = p;
               points[k].y = y_points[p];
               k++;
            }
            if(k > 0)
            {
               ArrayResize(points, k);
               curve.Update(points);
            }
            break;
         }
      }
   }
   
}
//+------------------------------------------------------------------+
//| Returns the number of ticks in the chart window                  |
//+------------------------------------------------------------------+
intCElTickGraph:: GetTiksTotal (void)
{
   return m_ask.GetMaxTotal();
}
//+------------------------------------------------------------------+
たとえば、次のオーダーブックの例を参照してください。
//+------------------------------------------------------------------+
void CElTickGraph::OnRefresh(CEventRefresh* refresh)
{
   //チャート上で最後に受信したティックを描画する
   int dbg = 5;
   int total = ArraySize(MarketBook.LastTicks);
   for(int i = 0; i < ArraySize(MarketBook.LastTicks); i++)
   {
      MqlTick tick = MarketBook.LastTicks[i];
      if((tick.flags & TICK_FLAG_BUY)==TICK_FLAG_BUY)
      {
         m_last_buy.AddValue(tick.last);
         m_last_sell.AddValue(-1);
         m_ask.AddValue(tick.last);
         m_bid.AddValue(tick.bid);
      }
      if((tick.flags & TICK_FLAG_SELL)==TICK_FLAG_SELL)
      {
         m_last_sell.AddValue(tick.last);
         m_last_buy.AddValue(-1);
         m_bid.AddValue(tick.last);
         m_ask.AddValue(tick.ask);
      }
      if((tick.flags & TICK_FLAG_ASK)==TICK_FLAG_ASK ||
         (tick.flags & TICK_FLAG_BID)==TICK_FLAG_BID)
      {
         m_last_sell.AddValue(-1);
         m_last_buy.AddValue(-1);
         m_bid.AddValue(tick.bid);
         m_ask.AddValue(tick.ask);
      }
   }
   MqlTick tick;
   if(!SymbolInfoTick(Symbol(), tick))
       return;
   if(ArraySize(MarketBook.LastTicks)>0)
   {
      RefreshCurves();
      m_graf.Redraw(true);
      m_graf.Update();
   }
}
void CElTickGraph::Event(CEvent *event)
{
   CElChart::Event(event);
   if(event.EventType() != EVENT_CHART_CUSTOM)
      return;
   CEventNewTick* ent = dynamic_cast<CEventNewTick*>(event);
   if(ent == NULL)
      return;
   MqlTick tick;
   ent.GetNewTick(tick);
   if((tick.flags & TICK_FLAG_BUY) == TICK_FLAG_BUY)
   {
      int last = m_last_buy.GetTotal()-1;
      if(last >= 0)
         m_last_buy.ChangeValue(last, tick.last);
   }
}
//+------------------------------------------------------------------+
//| Calculates the scale along axes so that the current price is     |
//| always in the middle of the price chart                          |
//+------------------------------------------------------------------+
void CElTickGraph::SetMaxMin(void)
{
   double max = m_last.MaxValue();
   double min = m_last.MinValue();
   double curr = m_last.GetValue(m_last.GetTotal()-1);
   double max_delta = max - curr;
   double min_delta = curr - min;
   if(max_delta > min_delta)
      m_graf.SetMaxMinValues(0, m_last.GetTotal(), (max-max_delta*2.0), max);
   else
      m_graf.SetMaxMinValues(0, m_last.GetTotal(), min, (min+min_delta*2.0));
}
//+------------------------------------------------------------------+
//| Refreshes the chart                                              |
//+------------------------------------------------------------------+
void CElTickGraph::Redraw(void)
{
   m_graf.Redraw(true);
   m_graf.Update();
}
//+------------------------------------------------------------------+
//| Intercepts chart display and changes display priority            |
//+------------------------------------------------------------------+
void CElTickGraph::Show(void)
{
   BackgroundColor(clrNONE);
   BorderColor(clrBlack);
   Text("Ticks:");
   //m_graf.BackgroundColor(clrWhiteSmoke);
   m_graf.Create(ChartID(), "Ticks", 0, (int)XCoord()+20, (int)YCoord()+30, 610, 600); 
   m_graf.Redraw(true);
   m_graf.Update();
   CElChart::Show();
}

//+------------------------------------------------------------------+
//| At the time of display we show the chart                         |
//+------------------------------------------------------------------+
void CElTickGraph::OnHide(void)
{
   m_graf.Destroy();
   CNode::OnHide();
}

このコードを詳しく分析してみましょう。 まず、CElTickGraph:: CElTickGraph クラスコンストラクタを使用します。 これはクラスが OBJ_RECTANGLE_LABEL グラフィックプリミティブ、つまり長方形のラベルに基づいているというコンストラクタ宣言から明らかです。 コンストラクターでは、 CCurve 型曲線が作成され、それぞれが特定のデータ型を担当します。 次のプロパティは、各曲線に対して設定されます。: 行名、種類、および色。 曲線の作成時には、どのような値が表示されるかはまだ不明なので、最初の点の座標を含む偽の配列をdouble x と y として使用します。 カーブを作成して CGraphic オブジェクトに配置すると、リングバッファは SetTiksTotal メソッドで構成されます。 ここでの設定は、TicksHistoryTotal 外部パラメータによって設定されたティックの最大数を設定することを意味します。

必要なカーブが CGraphic に追加され、リングバッファが適切に構成されると、相場深度が使用できる状態になります。 相場深度の操作時には、 CElTickGraph:: OnRefresh とCElTickGraph:: RefreshCurves の2つのメソッドが呼び出されます。 見てみましょう。

OnRefresh メソッドは、オーダーブックの変更後に呼び出されます。 このような変更は、OnBookEvent 関数を使用して追跡されます。

//+------------------------------------------------------------------+
//| MarketBook change event                                          |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
   
   if(symbol != MarketBook.GetMarketBookSymbol())
      return;
   MarketBook.Refresh();
   MButton.Refresh();
   ChartRedraw();
}

最初にオーダーブックが更新されます (MarketBook.Refresh())、表示するパネル: MButton.Refresh()をします。 パネルはボタンとして表示され、最小化/拡張することができるため、ボタンはパネル全体の親です。 このボタンを使用して、更新リクエストを含むすべてのイベントが受信されます。 更新リクエストは、ボタン内のすべての要素を通過し、最後にチャートを更新するアルゴリズムを含むCElTickGraphに到達します。 このアルゴリズムは、OnRefresh メソッドに実装されています。

アルゴリズムは、前回の更新以降に出現したティック数を受け取ります。 次に、各ティックの値が適切なリングバッファに追加されます。 m_ask リングバッファにティックの ask 価格が追加され、m_bid にBidが追加されます。最後のティックのタイプが最後のトレードである場合、ask と bid は最後の価格で同期さます。 この手順は、ターミナルが値を同期せず、前のティックの Ask と Bid を提供するために必要です。 したがって、最後のトレードは、常にいずれかの Ask またはBidレベルにあります。 標準のオーダーブックではこの同期が生成されず、最後の価格がこの2行の間で視覚的に位置することがあります。

ティックの最後のキューがリングバッファに配置されると、チャート上のティック描画を担当する OnRefreshCurves メソッドが呼び出されます。 このメソッドには、使用可能なすべての CCurve 曲線をチェックするループがあります。 カーブを使用して、各カーブに対してポイントの完全なリフレッシュが実行されます。 Y軸のポイントは、リングバッファのすべての要素をダブルバッファにコピーすることによって取得されます。 X軸の点は、より高度な方法で得られます。 各 y 点について、x 座標は x-1 に変更されます。 つまり、x 要素が1000に等しい場合、その値は999に変更されます。 チャートが新しい値を描画するときの移動で、古いものはその中から消えます。

すべての値が目的のインデックスに配置され、CCurve 曲線が更新されると、相場深度を更新する必要があります。 チャートの更新メソッドは、OnRefresh メソッド: m_graf.Redraw で呼び出されます。

ティックチャート表示アルゴリズムでは、次の2つのモードから選択できます。

  • 最終価格をオーダーブックの中央にバインドせずに、ティックチャートを表示できます。 チャートの高値と安値は、CGraphic 内で自動的に計算されます。
  • 最終価格がオーダーブックの中央にバインドされている場合、ティックチャートを表示できます。 ハイローがどこでも、現在の (最後の) 価格は図表の真中に常にあります。

最初のケースでは、CGraphic によって生成された自動スケーリングが呼び出されます。 2番目のケースでは、スケーリングは SetMaxMin メソッドによって実行されます。

インストール ダイナミクスのパフォーマンス。 相場深度の特徴の比較特性

このアプリケーションに必要なすべてのファイルは、次の4つのグループに分けることができます。

  • CPanel のグラフィックライブラリのファイル。 MQL5\Include\Panelで利用可能;
  • MarketBook クラスのファイル。 MQL5\Include\Tradeで利用可能;
  • リングバッファクラスのファイル。 MQL5\Include\RingBufferで利用可能;
  • スキャルピング相場深度のファイル。 MQL5\Indicators\MarketBookArticle. で利用可能

添付された zipファイル は、適切なフォルダ内の上記のファイルがあります。 プログラムをインストールするには、アーカイブを MQL5 フォルダに解凍します。 追加のサブフォルダを作成する必要はありません。 解凍後、ファイル MQL5\Indicators\MarketBookArticle\MarketBook.mq5. をコンパイルします。 コンパイル後、MetaTrader5ナビゲータウィンドウに適切なインジケーターが追加されます。 

結果として得られるアルゴリズムを評価する最良の方法は、ダイナミクスのティックチャートの変更を表示することです。 以下のビデオでは、時間の経過とともにティックチャートがどのように変化するか、およびチャートウィンドウがスムーズに右に移動する様子を示しています。


相場深度の結果のティックチャートは MetaTrader5 の標準相場深度のチャートとは異なります。 違いは、以下の比較表に記載されています:

標準的なMetaTrader5の相場深度 この記事で開発された相場深度
最後に、Ask とBidは相互に関連していません。 最後の価格は、AskとBidとは異なるレベルにすることができます。 最後に、Ask、Bidが同期されます。 最後の価格は、常に Ask またはBidのレベルにあります。
最後の価格は、トレード量に直接比例するさまざまな直径の円として表示されます。 最大直径の円は、最後の N ティックに対して実行された最大ボリュームとのトレードに対応し、N は移動ティック・チャート・ウィンドウの期間です。 買い取引は青い下向きの3角形として示され、売り取引は赤い上向きの3角形となっています。 トレードは、ボリュームに基づいて強調表示されません。
ティックチャートの尺度は、予約オーダーのテーブルの高さと同期されます。 したがって、オーダーブックの任意のレベルは、ティックチャート上の同じレベルに対応しています。 このソリューションの欠点は、目盛を大きくしたティックを表示できないことです。 利点は、価格の視認性とティックチャートとオーダーブックのレベルの完全な対応です。 チャートスケールとオーダー表の縮尺が一致しません。 ティックチャートの現在の価格は、オーダーブックの途中でしか近似できません。 このソリューションの欠点は、ティックチャートでオーダーブックレベルの視覚的な対応の欠如です。 利点は、ティックのチャートの任意のスケールを設定できることです。
ティックチャートには、ボリュームの追加のヒストグラムが装備されています。  ティックチャートには追加がありません。

表 2. 標準と新たに開発された相場深度の比較特性。

結論

スキャルピング相場深度の開発のプロセスを議論しています。

  • オーダーブックの外観を改良しました。
  • パネルに CGraphic に基づいてティックチャートを追加し、グラフィカルエンジンをアップグレードしました。
  • 相場深度のクラスを改善し、現在のオーダーブックとティックを同期させるためのアルゴリズムを実装しています。

しかし、相場深度の現在のバージョンでも本格的なスキャルピングバージョンからかけ離れています。 もちろん、多くのユーザーは、このれまでの記事を読んで、失望するかもしれません。 しかし、複雑なソフトウェアプロダクトは、徐々に開発する必要があります。 次のバージョンで相場深度に追加することができるものです。:

  • リミットオーダーを相場深度からまっすぐに送る関数
  • ティックチャートで大量のオーダーを追跡する関数
  • ボリューム別の最後のトレードの差別化とチャート上のさまざまな方法で表示する
  • ティックチャートで追加のインジケーターを表示。 例として、買いリミットオーダーと売りリミットオーダーの比率のヒストグラムを表示するティックチャートがあります。
  • そして、最後に、最も重要な部分は、相場深度のヒストリーをダウンロードし、オフラインテストモードでトレード戦略を作成できるようにすることです。

すべてのアイデアを実装することができます。 おそらくそのようなオプションは、いつか達成できます。 もし読者がこの記事を面白いと思えば、この記事は続くことでしょう。