English Русский Español Deutsch Português
preview
注文板に基づいた取引システムの開発(第1回):インジケーター

注文板に基づいた取引システムの開発(第1回):インジケーター

MetaTrader 5 | 5 5月 2025, 13:48
54 0
Daniel Santos
Daniel Santos

はじめに

まず、「市場の厚み」(DOM)とは何かを改めて確認しましょう。それは、未決済の指値注文の一覧のことを指します。これらの注文は、市場参加者の売買意図を表しているものの、必ずしも実際の取引に至るわけではありません。なぜなら、トレーダーはさまざまな理由により、過去に発注した注文をキャンセルできるからです。たとえば、市場状況の変化や、当初指定した価格・数量での取引に対する関心の低下などが挙げられます。

関数SymbolInfoInteger(_Symbol, SYMBOL_TICKS_BOOKDEPTH)によって返される値は、注文板の深さを表すものであり、分析対象の価格レベルを格納する配列の半分を示します。この配列のうち、半分は指値売り注文に、残りの半分は指値買い注文に割り当てられます。ドキュメントによれば、注文キューを持たない銘柄の場合、このプロパティの値は0になります。以下の図はその一例として、深さ10の注文板を表示しており、すべての利用可能な価格レベルが視覚化されています。

例:深さ10の注文板

厚みは必ずしも市場の注文板から取得する必要はなく、銘柄情報から直接取得できることに注意が必要ですSymbolInfoInteger関数を使用するだけで該当プロパティの値を取得でき、OnBookEventハンドラやMarketBookAddなどの関連関数に依存する必要はありません。もちろん、後述するように、OnBookEventハンドラが更新するMqlBookInfo配列の要素数をカウントすることでも同じ結果を得ることが可能です。

MetaTrader 5の標準の注文板ではなく、なぜこのインジケーターを使用すべきなのかと疑問に思われるかもしれません。以下に、その主な理由を挙げます。

  • チャートスペースの効率的な活用が可能で、ヒストグラムのサイズや画面上の表示位置を自由にカスタマイズできます。
  • 注文板イベントの可視化がよりシンプルかつ明確になり、情報の把握がしやすくなります。
  • 現時点ではストラテジーテスターによるネイティブサポートが存在しないため、将来的にBookEventイベントをディスクに保存してテストに利用するメカニズムを実装することを見据えています。


カスタム銘柄の生成

このプロセスを通じて、市場が閉じている場合や、ブローカーが特定の銘柄に対してイベントを送信していない場合でも、インジケーターのテストが可能になります。こうしたケースでは、ライブの注文キューが存在せず、イベントもローカルコンピューターにキャッシュされません。この段階では、実際の銘柄に基づく過去のイベントは使用せず、代わりに架空の資産に対してシミュレートされたBookEventデータの生成に焦点を当てます。これは、CustomBookAdd関数を使用するために必要不可欠です。この関数はカスタム銘柄専用に設計されており、そのような銘柄を作成し、イベントをシミュレーションする環境が前提となっています。

以下は、カスタム銘柄を生成するためのCloneSymbolTicksAndRatesスクリプトです。このスクリプトは、ドキュメントをベースにしつつ目的に合わせて調整されており、まずいくつかの定数を定義し、日付操作用の標準ライブラリDateTime.mqhをインクルードするところから始まります。カスタム銘柄の名前は、Symbol()関数を通じて渡された実際の銘柄名に基づいて命名される点に注意してください。そのため、このスクリプトは複製元となる実際の資産銘柄で実行する必要があります。もちろん、既存のカスタム銘柄をさらに複製することも可能ですが、実用的なメリットはあまりないようです。

#define   CUSTOM_SYMBOL_NAME     Symbol()+".C"     
#define   CUSTOM_SYMBOL_PATH     "Forex"           
#define   CUSTOM_SYMBOL_ORIGIN   Symbol()          

#define   DATATICKS_TO_COPY      UINT_MAX 
#define   DAYS_TO_COPY           5
#include <Tools\DateTime.mqh>

同じスクリプトのOnStart()関数内に挿入された以下のコードフラグメントでは、timemaster日付オブジェクトを作成しています。このオブジェクトは、ティックおよびバーのデータをクローンする期間を計算するために使用されます。定義済みのDAYS_TO_COPY定数に従い、Bars関数はソース銘柄の直近5日間のデータをコピーします。この期間の開始時刻はミリ秒単位に変換されてCopyTicks関数に渡され、これにより銘柄の「複製」処理が完了します。

   CDateTime timemaster;
   datetime now = TimeTradeServer();
   timemaster.Date(now);
   timemaster.DayDec(DAYS_TO_COPY);
   long DaysAgoMsc = 1000 * timemaster.DateTime();
   int bars_origin = Bars(CUSTOM_SYMBOL_ORIGIN, PERIOD_M1, timemaster.DateTime(), now);
   int create = CreateCustomSymbol(CUSTOM_SYMBOL_NAME, CUSTOM_SYMBOL_PATH, CUSTOM_SYMBOL_ORIGIN);
   if(create != 0 && create != 5304)
      return;
   MqlTick array[] = {};
   MqlRates rates[] = {};
   int attempts = 0;
   while(attempts < 3)
     {
      int received = CopyTicks(CUSTOM_SYMBOL_ORIGIN, array, COPY_TICKS_ALL, DaysAgoMsc, DATATICKS_TO_COPY);
      if(received != -1)
        {
         if(GetLastError() == 0)
            break;
        }
      attempts++;
      Sleep(1000);
     }

プロセスが完了すると、新しい銘柄が<AtivodeOrigem>.Cという名前で「気配値表示」リストに表示されます。この時点で、この合成銘柄で新しいチャートを開き、次のステップに進む必要があります。

すでに他の合成銘柄が存在する場合は、それを再利用することで、このセクションで説明した新規作成プロセスを省略できます。最終的には、このカスタム銘柄のチャートを開き、ここで開発する2つのMQL5アプリケーション(インジケーターとイベント生成スクリプト)を実行するだけです。詳細は次のセクションで順を追って解説していきます。


BookEvent型イベント生成スクリプト(テスト用)

カスタム銘柄を用意するだけでは、注文板イベントに依存するインジケーターをバックテストする際に必要な、オンライン注文板のティックシーケンスを補うことはできません。そのため、シミュレートされたデータを自ら生成する必要があります。この目的のために、以下のスクリプトが開発されました。

//+------------------------------------------------------------------+
//|                                            GenerateBookEvent.mq5 |
//|                                               Daniel Santos      |
//+------------------------------------------------------------------+
#property copyright "Daniel Santos"
#property version   "1.00"
#define SYNTH_SYMBOL_MARKET_DEPTH      32
#define SYNTH_SYMBOL_BOOK_ITERATIONS   20
#include <Random.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double BidValue, tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
MqlBookInfo    books[];
int marketDepth = SYNTH_SYMBOL_MARKET_DEPTH;
CRandom rdn;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   if(!SymbolInfoInteger(_Symbol, SYMBOL_CUSTOM)) // if the symbol exists
     {
      Print("Custom symbol ", _Symbol, " does not exist");
      return;
     }
   else
      BookGenerationLoop();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void BookGenerationLoop()
  {
   MqlRates BarRates_D1[];
   CopyRates(_Symbol, PERIOD_D1, 0, 1, BarRates_D1);
   if(ArraySize(BarRates_D1) == 0)
      return;
   BidValue = BarRates_D1[0].close;
   ArrayResize(books, 2 * marketDepth);
   for(int j = 0; j < SYNTH_SYMBOL_BOOK_ITERATIONS; j++)
     {
      for(int i = 0, j = 0; i < marketDepth; i++)
        {
         books[i].type = BOOK_TYPE_SELL;
         books[i].price = BidValue + ((marketDepth - i) * tickSize);
         books[i].volume_real = rdn.RandomInteger(10, 500);
         books[i].volume_real = round((books[i].volume_real + books[j].volume_real) / 2);
         books[i].volume = (int)books[i].volume_real;
         //----
         books[marketDepth + i].type = BOOK_TYPE_BUY;
         books[marketDepth + i].price = BidValue - (i * tickSize);
         books[marketDepth + i].volume_real = rdn.RandomInteger(10, 500);
         books[marketDepth + i].volume_real = round((books[marketDepth + i].volume_real
                                              + books[marketDepth + j].volume_real) / 2);
         books[marketDepth + i].volume = (int)books[marketDepth + i].volume_real;
         if(j != i)
            j++;
        }
      CustomBookAdd(_Symbol, books);
      Sleep(rdn.RandomInteger(400, 1000));
     }
  }
//+------------------------------------------------------------------+

標準のMathRand()関数の代わりに、32ビットの乱数を生成する代替実装を使用しています。この選択は、指定された範囲内で整数値を簡単に生成できるという利点があるためで、本スクリプトではその利点を活かしてRandomInteger(min, max)関数を使用しています。

注文板の深さには、比較的大きめの値である32を選択しました。これにより、1回の反復で64レベルの価格情報(32の買い・32の売り注文)が生成されます。必要に応じて、この値はより小さなものに変更可能です。

アルゴリズムはまず、対象の銘柄がカスタム銘柄かどうかをチェックします。カスタム銘柄である場合は、注文板の各レベルを生成し、指定された反復回数に従ってこのプロセスをループで繰り返します。この実装では、ランダムに選ばれた400〜1000ミリ秒(最大1秒)の間隔を空けながら、20回の反復が実行されます。このような動的なアプローチにより、ティックの動きがよりリアルに再現され、視覚的にも魅力的な表示となります。

価格レベルは、ソース銘柄の日足チャートにおける直近の終値を基準点として垂直方向に配置されます。この基準点より上に32の売り注文レベルが、下には32の買い注文レベルが存在します。インジケーターの標準カラースキームでは、売り注文に対応するヒストグラムバーは赤系色、買い注文は水色で表示されます。

連続する価格レベル間の価格差は、SYMBOL_TRADE_TICK_SIZEプロパティから取得される銘柄のティックサイズに基づいて計算されます。


市場の厚みの変化を表示するインジケーター

ライブラリソースコード

このインジケーターはオブジェクト指向プログラミングを用いて開発されました。BookEventHistogramクラスは、注文板ヒストグラムの作成・更新・削除といった管理処理を担うために設計されています。クラスのインスタンスが破棄される際には、関連するバーの削除も自動的におこなわれます。

以下に、BookEventHistogramクラスで宣言されている変数および関数を示します。

class BookEventHistogram
  {
protected:
   color                histogramColors[]; //Extreme / Mid-high / Mid-low
   int                  bookSize;
   int                  currElements;
   int                  elementMaxPixelsWidth;
   bool                 showMessages;
   ENUM_ALIGN_MODE      corner;
   string               bookEventElementPrefix;
public:
   MqlBookInfo          lastBook[];
   datetime             lastDate;
   void                 SetAlignLeft(void);
   void                 SetCustomHistogramColors(color &colors[]);
   void                 SetBookSize(int value) {bookSize = value;}
   void                 SetElementMaxPixelsWidth(int m);
   int                  GetBookSize(void) {return bookSize;}
   void                 DrawBookElements(MqlBookInfo& book[], datetime now);
   void                 CleanBookElements(void);
   void                 CreateBookElements(MqlBookInfo& book[], datetime now);
   void                 CreateOrRefreshElement(int buttonHeigh, int buttonWidth, int i, color clr, int ydistance);
   //--- Default constructor
                     BookEventHistogram(void);
                    ~BookEventHistogram(void);
  };

このセグメントでは、すべての関数が定義されているわけではありません。残りはBookEventHistogram.mqhファイルの後半で定義されています。

中でも重要な関数であるCreateBookElementsとCreateOrRefreshElementは連携して動作し、既存の要素を更新しつつ、必要に応じて新しい要素を作成します。その他の関数は、プロパティの更新や、特定のオブジェクト変数の値を取得する目的で使用されます。


インジケーターのソースコード

コードの冒頭では、プロット数とバッファ数が3として定義されています。詳細に分析すると、実際にはMQL5インジケーターのルート構造バッファは使用されていません。しかし、この定義により、インジケーターの初期化時にユーザーが特定のプロパティを操作できるようなコード構造が実現されます。今回は、色に関するプロパティに焦点を当てており、直感的でユーザーフレンドリーな入力スキームを提供することを目的としています。

各プロットには、買い注文用と売り注文用に2色ずつが割り当てられています。これにより、合計6色のカラーパターンが定義されており、あらかじめ定義された基準に従って、ヒストグラム内の各セグメントに色が割り当てられます。大まかに分類すると、最大のセグメントは「極値」、平均サイズを超えるものは「中高値」、それ以外は「中低値」に分類されます。

各色は、PlotIndexGetInteger関数を使って取得されます。この関数では、対象プロットとその中のインデックス位置を指定して、対応する情報を取得します。

#define NUMBER_OF_PLOTS 3
#property indicator_chart_window
#property indicator_buffers NUMBER_OF_PLOTS
#property indicator_plots   NUMBER_OF_PLOTS
//--- Invisible plots
#property indicator_label1  "Extreme volume elements colors"
#property indicator_type1   DRAW_NONE
#property indicator_color1  C'212,135,114', C'155,208,226'
//---
#property indicator_label2  "Mid-high volume elements colors"
#property indicator_type2   DRAW_NONE
#property indicator_color2  C'217,111,86', C'124,195,216'
//---
#property indicator_label3  "Mid-low volume elements color"
#property indicator_type3   DRAW_NONE
#property indicator_color3  C'208,101,74', C'114,190,214'
#include "BookEventHistogram.mqh"
enum HistogramPosition
  {
   LEFT,      //<<<< Histogram on the left
   RIGHT,     //Histogram on the right >>>>
  };
enum HistogramProportion
  {
   A_QUARTER,   // A quarter of the chart
   A_THIRD,     // A third of the chart
   HALF,        // Half of the chart
  };
input  HistogramPosition position = RIGHT; // Indicator position
input  HistogramProportion proportion = A_QUARTER; // Histogram ratio (compared to chart width)
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
double volumes[];
color histogramColors[];
BookEventHistogram bookEvent;

次に、インジケーターの読み込み時にユーザーに明確な選択肢を提供するために設計された、2つの列挙型を紹介します。1つ目は、ヒストグラムをチャートのどちら側に描画するか(右側または左側)を決定するものです。2つ目は、ヒストグラムがチャート全体の幅のうち、どれくらいの割合を占めるかを指定するもので、選択肢として「4分の1」「3分の1」「半分」が用意されています。たとえば、チャートの幅が500ピクセルで「半分」が選択された場合、ヒストグラムバーの最大幅は250ピクセルとなります。

最後に、BookEvents.mq5のソースコードでは、ヒストグラムの更新リクエストの大半がOnBookEvent関数とOnChartEvent関数によってトリガーされます。一方で、OnCalculate関数は本アルゴリズムには関与しておらず、MQL5の構文要件を満たすためだけに保持されています。OnCalculate関数はアルゴリズムでは役割を果たさず、MQL構文準拠のためにのみ保持されます。


スクリプトとインジケーターの使用

これまでに開発されたリソースと一貫性を保ちながら、スクリプトとインジケーターを実行する正しい順序は次の通りです。

  • 実際の銘柄のチャートでCloneSymbolTicksAndRatesスクリプトを実行します。
  • ->生成されたカスタム銘柄のチャートでBookEventsインジケーターを実行します。
  • ->同じくカスタム銘柄のチャートでGenerateBookEventスクリプトを実行します。

BookEventは、対象となるカスタム資産のすべてのグラフィカルインスタンスにブロードキャストされます。したがって、インジケーターとイベント生成スクリプトは、同じカスタム銘柄を参照している限り、別々のチャートで実行することも、同じチャート内で実行することもできます。

以下のアニメーションは、このシーケンスとインジケーターの機能を示しています。楽しんでいただければ幸いです。

デモ:BookEventsインジケーター


結論

市場の厚みは、特に高頻度取引(HFT)アルゴリズムにおいて、高速な取引を実行するために不可欠な要素です。これは多くの取引銘柄に対してブローカーが提供する市場イベントの一種であり、時間が経つにつれて、ブローカーは他の資産に対してもその範囲と利用可能性を拡大する可能性があります。

ただし、注文板のみを基に取引システムを構築することはお勧めしません。DOMは流動性ゾーンを特定するのに有効で、価格の動きとの相関関係を示すことがあります。そのため、注文板の分析を他のツールや指標と組み合わせることが、一貫した取引結果を得るためには賢明なアプローチです。

また、将来的には、BookEventデータを保存し、手動取引や自動戦略のバックテストで利用するメカニズムを実装するなど、インジケーターのさらなる改善の余地があります。


MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/15748

添付されたファイル |
細菌走化性最適化(BCO) 細菌走化性最適化(BCO)
この記事では、細菌走化性最適化(BCO)アルゴリズムのオリジナルバージョンとその改良版を紹介します。新バージョン「BCOm」では、細菌の移動メカニズムを簡素化し、位置履歴への依78ytf存を軽減するとともに、計算負荷の大きかった元のバージョンに比べて、より単純な数学的手法を採用しています。この記事では両者の違いを詳しく検討し、とくにBCOmの特徴に焦点を当てます。また、テストを実施し、その結果をまとめます。
取引におけるニューラルネットワーク:点群解析(PointNet) 取引におけるニューラルネットワーク:点群解析(PointNet)
直接的な点群解析は、不要なデータの増加を避け、分類やセグメンテーションタスクにおけるモデルの性能を向上させます。このような手法は、元データの摂動に対して高い性能と堅牢性を示します。
リプレイシステムの開発(第63回):サービスの再生(IV) リプレイシステムの開発(第63回):サービスの再生(IV)
この記事では、1分足のティックシミュレーションに関する問題を最終的に解決し、実際のティックと共存できるようにします。これにより、将来的なトラブルを回避することが可能になります。ここで提示される資料は教育目的のみに使用されます。いかなる状況においても、提示された概念を学習し習得する以外の目的でアプリケーションを閲覧することは避けてください。
初級から中級へ:Includeディレクティブ 初級から中級へ:Includeディレクティブ
本日の記事では、MQL5のさまざまなコードで広く使用されているコンパイルディレクティブについて解説します。本稿ではこのディレクティブについて表面的な説明に留めますが、今後プログラミングレベルが上がるにつれて不可欠なものとなるため、使い方を理解し始めることが重要です。ここで提示されるコンテンツは、教育目的のみを目的としています。いかなる状況においても、提示された概念を学習し習得する以外の目的でアプリケーションを閲覧することは避けてください。