DoEasyライブラリの時系列(第38部): 時系列コレクション-リアルタイムの更新とプログラムからのデータへのアクセス

27 7月 2020, 13:18
Artyom Trishkin
0
584

内容


概念

以前の記事では、チャート期間と銘柄の時系列を作成することに焦点を当て、プログラムで使用されるすべての銘柄の本格的な時系列コレクションクラスを作成し、高速な検索と並べ替えを実行できるように時系列の履歴データを入力する方法を学びました 。
このようなツールを使用すると、履歴内の価格データのさまざまな組み合わせを検索して比較できます。ただし、使用されている各銘柄の新しいティックごとに実行する必要がある現在のデータの更新についても考慮する必要があります。

最も単純なバージョンでも、プログラムのOnTimer()ミリ秒ハンドラですべての時系列を更新できます。ただし、これにより、時系列データを常にタイマーカウンターに従って正確に更新する必要があるかどうかという疑問が生じます。結局のところ、データは新しいティックの到着時にプログラムで変更されます。新しいティックの到着に関係なく単純にデータを更新するのは誤りです。これはパフォーマンスの点で非合理的です。

EAのOnTick()ハンドラまたは指標の現在の銘柄のOnCalculate()ハンドラに新しいティックが到着することは常にわかっていますが、これは 別の銘柄で起動されたプログラムによって追跡される他の銘柄には当てはまりません。このタスクでは、EA内または指標で必要なイベントを追跡する必要があります。

ここで、現在のライブラリのニーズを満たす最も簡単なオプションは、以前のティック時間と現在のティック時間を比較することです。以前のティック時間が現在のティックと異なる場合、新しいティックはプログラムによって追跡された銘柄に到達したが、そのネイティブではない(プログラムは別の銘柄のチャートで起動される)と見なされます。

「新しいティック」クラスとプログラムで使用されるすべての時系列のリアルタイム更新を開発する前に、既存のクラスを少し改善しましょう。


時系列クラスの改善

まず、Datas.mqhファイルはライブラリの新しいメッセージインデックスを受け取ります。

//--- CTimeSeries
   MSG_LIB_TEXT_TS_TEXT_FIRST_SET_SYMBOL,             // First, set a symbol using SetSymbol()
   MSG_LIB_TEXT_TS_TEXT_IS_NOT_USE,                   // Timeseries is not used. Set the flag using SetAvailable()
   MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME,            // Unknown timeframe

また、新しく追加したインデックスに対応するメッセージテキストも受け取ります。

   {"Сначала нужно установить символ при помощи SetSymbol()","First you need to set Symbol using SetSymbol()"},
   {"Таймсерия не используется. Нужно установить флаг использования при помощи SetAvailable()","Timeseries not used. Need to set usage flag using SetAvailable()"},
   {"Неизвестный таймфрейм","Unknown timeframe"},

すべてのライブラリオブジェクトの基本オブジェクトのCBaseObjクラスには2つの変数があります。

//+------------------------------------------------------------------+
//| Base object class for all library objects                        |
//+------------------------------------------------------------------+
class CBaseObj : public CObject
  {
protected:
   ENUM_LOG_LEVEL    m_log_level;                              // Logging level
   ENUM_PROGRAM_TYPE m_program;                                // Program type
   bool              m_first_start;                            // First launch flag
   bool              m_use_sound;                              // Flag of playing the sound set for an object
   bool              m_available;                              // Flag of using a descendant object in the program
   int               m_global_error;                           // Global error code
   long              m_chart_id_main;                          // Control program chart ID
   long              m_chart_id;                               // Chart ID
   string            m_name;                                   // Object name
   string            m_folder_name;                            // Name of the folder storing CBaseObj descendant objects 
   string            m_sound_name;                             // Object sound file name
   int               m_type;                                   // Object type (corresponds to the collection IDs)

public:

m_chart_id_main変数は、制御プログラムのチャートIDを格納します。これは、プログラムが起動された銘柄のチャートです。グラフは、ライブラリのコレクションとオブジェクトに登録されているすべてのイベントを取得するものです。
m_chart_idはCBaseObjクラスから派生したオブジェクトが何らかの形で関連しているチャートのIDを格納します。このプロパティはまだどこにも使用されていません。その時は後で来るでしょう。

m_chart_idより後にm_chart_id_main変数を追加したため、すべてのメッセージはm_chart_id変数に設定されたチャートIDに送信されます。これを修正しました。現在、すべての現在のチャートIDはm_chart_id_main変数に設定されています。ライブラリから制御プログラムチャートにメッセージを送信するすべてのクラスが変更されました。「m_chart_id」のすべてのインスタンスが「m_chart_id_main」に置き換えられました。
このような変更は、\MQL5\Include\DoEasy\Objects\Events\フォルダのすべてのイベントクラス、およびAccountsCollection.mqh、EventsCollection.mqh、SymbolsCollection.mqhの各コレクションクラスファイルに加えられました。 添付ファイルですべての変更を確認できます。

時系列コレクションから指定したバーのデータを表示するには、CBarクラスのバーパラメータの説明テキストを\MQL5\Include\DoEasy\Objects\Series\Bar.mqhに追加します。

オブジェクトプロパティの説明を含むコードブロックで、バーパラメータのテキスト説明を作成するメソッドを宣言します

//+------------------------------------------------------------------+
//| Descriptions of bar object properties                            |
//+------------------------------------------------------------------+
//--- Get description of a bar's (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_BAR_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_BAR_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_BAR_PROP_STRING property);

//--- Return the bar type description
   string            BodyTypeDescription(void)  const;
//--- Send description of bar properties to the journal (full_prop=true - all properties, false - only supported ones)
   void              Print(const bool full_prop=false);
//--- Display a short bar description in the journal
   virtual void      PrintShort(void);
//--- Return the (1) short name and (2) description of bar object parameters
   virtual string    Header(void);
   string            ParameterDescription(void);
//---
  };
//+------------------------------------------------------------------+

クラス本体以外に、バーパラメータのテキスト説明を作成するメソッドを実装し、銘柄に短いバーの説明を表示するメソッドの実装を変更します。

//+------------------------------------------------------------------+
//| Return the description of the bar object parameters              |
//+------------------------------------------------------------------+
string CBar::ParameterDescription(void)
  {
   int dg=(this.m_digits>0 ? this.m_digits : 1);
   return
     (
      ::TimeToString(this.Time(),TIME_DATE|TIME_MINUTES|TIME_SECONDS)+", "+
      "O: "+::DoubleToString(this.Open(),dg)+", "+
      "H: "+::DoubleToString(this.High(),dg)+", "+
      "L: "+::DoubleToString(this.Low(),dg)+", "+
      "C: "+::DoubleToString(this.Close(),dg)+", "+
      "V: "+(string)this.VolumeTick()+", "+
      (this.VolumeReal()>0 ? "R: "+(string)this.VolumeReal()+", " : "")+
      this.BodyTypeDescription()
     );
  }
//+------------------------------------------------------------------+
//| Display a short bar description in the journal                   |
//+------------------------------------------------------------------+
void CBar::PrintShort(void)
  {
   ::Print(this.Header(),": ",this.ParameterDescription());
  }
//+------------------------------------------------------------------+

ここでは、操作ログにバーパラメータを表示するメソッドからパラメータ記述コードを削除し、テキストメッセージを返す新しいメソッドに配置しました。操作ログにバーパラメータを表示する場合短いバーオブジェクト名そのパラメータで構成される複合メッセージを表示します。そのテキストの説明は、新しいメソッドParameterDescription()で生成されます。

「非ネイティブ」時系列(プログラムが起動されたものではない)を更新するために、「新しいティック」クラスを作成し、プログラムで使用される各銘柄に対して「新しいティック」イベントの到着時にのみそのような銘柄のデータを更新することにしました 。

「新しいティック」クラスとデータの更新

\MQL5\Include\DoEasy\Objects\で、すべてのCBaseObjライブラリオブジェクトの基本オブジェクトから派生したCNewTickObjクラスのNewTickObj.mqhファイルを備えたTicks\フォルダを作成し(ファイルはクラスファイルに含まれています)、 必要なデータを入力します。

//+------------------------------------------------------------------+
//|                                                   NewTickObj.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| "New tick" class                                                 |
//+------------------------------------------------------------------+
class CNewTickObj : public CBaseObj
  {
private:
   MqlTick           m_tick;                          // Structure of the current prices
   MqlTick           m_tick_prev;                     // Structure of the current prices during the previous check
   string            m_symbol;                        // Object symbol
   bool              m_new_tick;                      // New tick flag
public:
//--- Set a symbol
   void              SetSymbol(const string symbol)   { this.m_symbol=symbol;             }
//--- Return the new tick flag
   bool              IsNewTick(void);
//--- Update price data in the tick structure and set the "New tick" event flag if necessary
   void              Refresh(void)                    { this.m_new_tick=this.IsNewTick(); }
//--- Constructors
                     CNewTickObj(void){;}
                     CNewTickObj(const string symbol);
  };
//+------------------------------------------------------------------+

m_tick変数には、最後に到着したティックの価格データが格納されます。

m_tick_prev変数は前のティックの価格データを格納します。

m_symbol変数には、新しいティックが追跡される銘柄が格納されます。

m_new_tick変数の新しい目盛りフラグは後で使用されます。
現在のライブラリのニーズに対して、銘柄の「新しいティック」イベントは、IsNewTick()メソッドによって定義されます。

//+------------------------------------------------------------------+
//| Return the new tick flag                                         |
//+------------------------------------------------------------------+
bool CNewTickObj::IsNewTick(void)
  {
//--- If failed to get the current prices to the tick structure, return 'false'
   if(!::SymbolInfoTick(this.m_symbol,this.m_tick))
      return false;
//--- If this is the first launch, copy data of the obtained tick to the previous tick data
//--- reset the first launch flag and return 'false'
   if(this.m_first_start)
     {
      this.m_tick_prev=this.m_tick;
      this.m_first_start=false;
      return false;
     }
//--- If the time of a new tick is not equal to the time of a tick during the previous check -
//--- copy data of the obtained tick to the previous tick data and return 'true'
   if(this.m_tick.time_msc!=this.m_tick_prev.time_msc)
     {
      this.m_tick_prev=this.m_tick;
      return true;
     }
//--- In all other cases, return 'false'
   return false;
  }
//+------------------------------------------------------------------+

クラスには2つの定義済みコンストラクタがあります。

  • パラメータのないデフォルトのコンストラクタは、別のクラス内の「新しいティック」オブジェクトを定義するために使用されます。この場合、SetSymbol()クラスメソッドを使用して、「新しいティック」イベントが定義されているCNewTickObjクラスオブジェクトの銘柄を設定します。
  • パラメータコンストラクタを使用して、new演算子を介してクラスオブジェクトを作成します。この場合、オブジェクトが作成される銘柄は、オブジェクトの作成時にすぐに指定できます。
//+------------------------------------------------------------------+
//| Parametric constructor CNewTickObj                               |
//+------------------------------------------------------------------+
CNewTickObj::CNewTickObj(const string symbol) : m_symbol(symbol)
  {
//--- Reset the structures of the new and previous ticks
   ::ZeroMemory(this.m_tick);
   ::ZeroMemory(this.m_tick_prev);
//--- If managed to get the current prices to the tick structure,
//--- copy data of the obtained tick to the previous tick data and reset the first launch flag
  if(::SymbolInfoTick(this.m_symbol,this.m_tick))
     { 
      this.m_tick_prev=this.m_tick;
      this.m_first_start=false;
     }
  }
//+------------------------------------------------------------------+

これは、新しいティックオブジェクトのクラス全体です。考え方は単純です。ティック構造に価格を取得し、到着したティックの時間を前のティックの時間と比較します。
これらの時間が等しくない場合は、新しいティックが到着しています。

EAではティックがスキップされる場合がありますが、ここでは重要ではありません。タイマーで常に確認するのではなく、新しいティックが到着したときにのみデータを更新するために、タイマーで「非ネイティブ」銘柄の新しいティックを追跡することができます。
指標がバッチで到着できるすべてのティックを追跡する場合、指標が起動された銘柄の現在の時系列データの更新は、OnCalculate()ハンドラで行う必要があります。「非ネイティブ」銘柄の新しいティックはタイマーで追跡されます(「非ネイティブ」銘柄の新しいティックイベントはOnOnCalculate()で受信できません)。したがって、 「非ネイティブ」銘柄の新しいティックと以前のティックにより、時系列の時系列データが更新されます。

CSeriesオブジェクトは、「新しいバー」イベントを制御プログラムに送信します。これにより、プログラムの任意の時系列からそのようなイベントを取得し、それらに応答できるようになります。

Defines.mqhファイルの最後に、考えられる時系列オブジェクトイベントのリストを含む新しい列挙を追加します。

//+------------------------------------------------------------------+
//| List of possible timeseries events                               |
//+------------------------------------------------------------------+
enum ENUM_SERIES_EVENT
  {
   SERIES_EVENTS_NO_EVENT = SYMBOL_EVENTS_NEXT_CODE,        // no event
   SERIES_EVENTS_NEW_BAR,                                   // "New bar" event
  };
#define SERIES_EVENTS_NEXT_CODE  (SERIES_EVENTS_NEW_BAR+1)  // Code of the next event after the "New bar" event
//+------------------------------------------------------------------+

ここでは、時系列イベントの状態はまだ2つしかありません。「イベントなし」と「新しいバー」イベントです。これらの列挙定数は、(CSeries 時系列の)バーコレクションリストで指定されたプロパティによってバーオブジェクトを検索するために必要です。

時系列オブジェクトはライブラリタイマーで更新されるため、時系列オブジェクトコレクション更新タイマーのパラメータを時系列コレクションリストIDとともにDefines.mqhリストファイルに追加します

//--- Trading class timer parameters
#define COLLECTION_REQ_PAUSE           (300)                      // Trading class timer pause in milliseconds
#define COLLECTION_REQ_COUNTER_STEP    (16)                       // Trading class timer counter increment
#define COLLECTION_REQ_COUNTER_ID      (5)                        // Trading class timer counter ID
//--- Parameters of the timeseries collection timer
#define COLLECTION_TS_PAUSE            (32)                       // Timeseries collection timer pause in milliseconds
#define COLLECTION_TS_COUNTER_STEP     (16)                       // Account timer counter increment
#define COLLECTION_TS_COUNTER_ID       (6)                        // Timeseries timer counter ID
//--- Collection list IDs
#define COLLECTION_HISTORY_ID          (0x777A)                   // Historical collection list ID
#define COLLECTION_MARKET_ID           (0x777B)                   // Market collection list ID
#define COLLECTION_EVENTS_ID           (0x777C)                   // Event collection list ID
#define COLLECTION_ACCOUNT_ID          (0x777D)                   // Account collection list ID
#define COLLECTION_SYMBOLS_ID          (0x777E)                   // Symbol collection list ID
#define COLLECTION_SERIES_ID           (0x777F)                   // Timeseries collection list ID
//--- Data parameters for file operations

CEngineライブラリベースオブジェクトを作成するときにコレクションタイマーパラメータを考慮しましたが、コレクションIDの目的は、ライブラリ構造を再配置するときに説明しました。

時系列オブジェクトはリストに属するバーオブジェクトへのポインタを含むリストであるため、時系列コレクションIDをすぐにバーオブジェクトに割り当てます。
\MQL5\Include\DoEasy\Objects\Series\Bar.mqhを再び開いてオブジェクトタイプを両方のコンストラクタに追加します。

//+------------------------------------------------------------------+
//| Constructor 1                                                    |
//+------------------------------------------------------------------+
CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   this.m_type=COLLECTION_SERIES_ID;
   MqlRates rates_array[1];
   this.SetSymbolPeriod(symbol,timeframe,index);
   ::ResetLastError();
//--- If failed to write bar data to the MqlRates array by index or set the time to the time structure,
//--- display an error message, create and fill the structure with zeros, and write it to the rates_array array
   if(::CopyRates(symbol,timeframe,index,1,rates_array)<1 || !::TimeToStruct(rates_array[0].time,this.m_dt_struct))
     {
      int err_code=::GetLastError();
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA),". ",CMessage::Text(MSG_LIB_SYS_ERROR)," ",CMessage::Text(err_code)," ",CMessage::Retcode(err_code));
      MqlRates err={0};
      rates_array[0]=err;
     }
//--- Set the bar properties
   this.SetProperties(rates_array[0]);
  }
//+------------------------------------------------------------------+
//| Constructor 2                                                    |
//+------------------------------------------------------------------+
CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const MqlRates &rates)
  {
   this.m_type=COLLECTION_SERIES_ID;
   this.SetSymbolPeriod(symbol,timeframe,index);
   ::ResetLastError();
//--- If failed to set time to the time structure, display the error message,
//--- create and fill the structure with zeros, set the bar properties from this structure and exit
   if(!::TimeToStruct(rates.time,this.m_dt_struct))
     {
      int err_code=::GetLastError();
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA),". ",CMessage::Text(MSG_LIB_SYS_ERROR)," ",CMessage::Text(err_code)," ",CMessage::Retcode(err_code));
      MqlRates err={0};
      this.SetProperties(err);
      return;
     }
//--- Set the bar properties
   this.SetProperties(rates);
  }
//+------------------------------------------------------------------+


次に、\MQL5\Include\DoEasy\Objects\Series\Series.mqhにあるCSeries時系列オブジェクトクラスを改善します。

クラスのpublicセクションで、コントロールプログラムチャートにイベントを送信するための新しいメソッドを宣言します。

//--- (1) Create and (2) update the timeseries list
   int               Create(const uint required=0);
   void              Refresh(const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0);
                             
//--- Create and send the "New bar" event to the control program chart
   void              SendEvent(void);

//--- Return the timeseries name
   string            Header(void);
//--- Display (1) the timeseries description and (2) the brief timeseries description in the journal
   void              Print(void);
   void              PrintShort(void);


//--- Constructors
                     CSeries(void);
                     CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0);
  };
//+------------------------------------------------------------------+

クラスリストの最後に、宣言したメソッドを実装します。

//+------------------------------------------------------------------+
//| Create and send the "New bar" event                              |
//| to the control program chart                                     |
//+------------------------------------------------------------------+
void CSeries::SendEvent(void)
  {
   ::EventChartCustom(this.m_chart_id_main,SERIES_EVENTS_NEW_BAR,this.Time(0),this.Timeframe(),this.Symbol());
  }
//+------------------------------------------------------------------+

ここでは、イベントを作成して制御プログラムチャートに送信します。イベントは以下で構成されています。
イベントレシーバーチャートID
イベントID(新規バー)
新しいバーが開いた時間をlongイベントパラメータとして送信
イベントが発生したチャートの時間枠をdoubleイベントパラメータとして送信

銘柄の名前(時間枠でイベントが発生したもの)をstringパラメータとして送信

プログラムでの時系列の使用を示すフラグのチェックを時系列データ同期メソッドに追加します。

//+------------------------------------------------------------------+
//|Synchronize symbol and timeframe data with server data            |
//+------------------------------------------------------------------+
bool CSeries::SyncData(const uint required,const uint rates_total)
  {
//--- If the timeseries is not used, notify of that and exit
   if(!this.m_available)
     {
      ::Print(DFUN,this.m_symbol," ",TimeframeDescription(this.m_timeframe),": ",CMessage::Text(MSG_LIB_TEXT_TS_TEXT_IS_NOT_USE));
      return false;
     }
//--- If managed to obtain the available number of bars in the timeseries

つまり、プログラムで時系列使用のフラグが設定されていない場合は、同期する必要はありません。使用状況フラグが設定されていないときに時系列が必要になる場合もあります。したがって、適切なメッセージが操作ログに送信されます。

同じチェックを 時系列作成メソッドに実装します。

//+------------------------------------------------------------------+
//| Create the timeseries list                                       |
//+------------------------------------------------------------------+
int CSeries::Create(const uint required=0)
  {
//--- If the timeseries is not used, notify of that and return zero
   if(!this.m_available)
     {
      ::Print(DFUN,this.m_symbol," ",TimeframeDescription(this.m_timeframe),": ",CMessage::Text(MSG_LIB_TEXT_TS_TEXT_IS_NOT_USE));
      return 0;
     }
//--- If the required history depth is not set for the list yet,

時系列インデックスによってバーオブジェクトを返すメソッドがクラスで修正されました。以前は、メソッドは次のように表示されていました。

//+------------------------------------------------------------------+
//| Return the bar object by index in the timeseries                 |
//+------------------------------------------------------------------+
CBar *CSeries::GetBarBySeriesIndex(const uint index)
  {
   CArrayObj *list=this.GetList(BAR_PROP_INDEX,index);
   return(list==NULL || list.Total()==0 ? NULL : list.At(0));
  }
//+------------------------------------------------------------------+

つまり、必要なバーのコピーを含む新しいリストが作成され、そのコピーが返されました。リクエストされたバーのデータを単に受信したいだけならこれで十分ですが、バーのプロパティを変更する必要がある場合、このメソッドは、元のオブジェクトのバーのプロパティではなくバーのコピーのプロパティに変更が加えられるため機能しません。

新しいティックの到着時に現在のバーをリアルタイムで更新したいので、リストのコピーからバーではなく、バーコレクションリストにある元のバーオブジェクトにポインタを返すようにメソッドを変更しました。

//+------------------------------------------------------------------+
//| Return the bar object by index in the timeseries                 |
//+------------------------------------------------------------------+
CBar *CSeries::GetBarBySeriesIndex(const uint index)
  {
   CBar *tmp=new CBar(this.m_symbol,this.m_timeframe,index); 
   if(tmp==NULL)
      return NULL;
   this.m_list_series.Sort(SORT_BY_BAR_INDEX);
   int idx=this.m_list_series.Search(tmp);
   delete tmp;
   CBar *bar=this.m_list_series.At(idx);
   return(bar!=NULL ? bar : NULL);
  }
//+------------------------------------------------------------------+

ここでは、現在の時系列オブジェクトチャートの銘柄と期間 、およびメソッドに渡されたバーインデックスを使用して、一時的なバーオブジェクトを作成します。 チャート時系列のバーインデックスは、バーインデックスで並び替えられた時系列リストで同じオブジェクトを検索するために必要です。 同じ時系列インデックスを持つバーを検索しているときに、リスト内のインデックスを取得 し(tこのインデックスは、リスト内のバーオブジェクトへのポインタを取得するために使用されます)、オブジェクトへのポインタを返します。
これで、このメソッドは、時系列リスト内の元のバーオブジェクトへのポインタを返します。リアルタイムのデータ更新中に変更できます。

今度は、CTimeSeries時系列オブジェクトクラスを改善して、新しいティックを追跡し、そのようなイベントを定義するときにデータを更新します。クラスオブジェクトは、1つの銘柄で使用されるすべてのチャート期間の時系列のセットです。これは、CTimeSeries時系列オブジェクト銘柄によって新しいティックを取得すると、オブジェクトに属するすべての期間のCSeries時系列オブジェクトデータの更新が開始されるため、オブジェクトが「新しいティック」クラスオブジェクトに最適な場所であることを意味します 。

新しいティック」オブジェクトクラスファイルを時系列オブジェクトクラスファイルに含めます。クラスのprivateセクションで、「新しいティック」クラスオブジェクトを定義します。
クラスのpublicセクションで、現在の時系列オブジェクト銘柄に新しいティックフラグを返すメソッドを追加します

//+------------------------------------------------------------------+
//|                                                   TimeSeries.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Series.mqh"
#include "..\Ticks\NewTickObj.mqh"
//+------------------------------------------------------------------+
//| Symbol timeseries class                                          |
//+------------------------------------------------------------------+
class CTimeSeries : public CBaseObj
  {
private:
   string            m_symbol;                                             // Timeseries symbol
   CNewTickObj       m_new_tick;                                           // "New tick" object
   CArrayObj         m_list_series;                                        // List of timeseries by timeframes
   datetime          m_server_firstdate;                                   // The very first date in history by a server symbol
   datetime          m_terminal_firstdate;                                 // The very first date in history by a symbol in the client terminal
//--- Return (1) the timeframe index in the list and (2) the timeframe by the list index
   char              IndexTimeframe(const ENUM_TIMEFRAMES timeframe) const { return IndexEnumTimeframe(timeframe)-1;                            }
   ENUM_TIMEFRAMES   TimeframeByIndex(const uchar index)             const { return TimeframeByEnumIndex(uchar(index+1));                       }
//--- Set the very first date in history by symbol on the server and in the client terminal
   void              SetTerminalServerDate(void)
                       {
                        this.m_server_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_SERVER_FIRSTDATE);
                        this.m_terminal_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_TERMINAL_FIRSTDATE);
                       }
public:
//--- Return (1) oneself, (2) the full list of timeseries, (3) specified timeseries object and (4) timeseries object by index
   CTimeSeries      *GetObject(void)                                       { return &this;                                                      }
   CArrayObj        *GetListSeries(void)                                   { return &this.m_list_series;                                        }
   CSeries          *GetSeries(const ENUM_TIMEFRAMES timeframe)            { return this.m_list_series.At(this.IndexTimeframe(timeframe));      }
   CSeries          *GetSeriesByIndex(const uchar index)                   { return this.m_list_series.At(index);                               }
//--- Set/return timeseries symbol
   void              SetSymbol(const string symbol)                        { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol);  }
   string            Symbol(void)                                    const { return this.m_symbol;                                              }
//--- Set the history depth (1) of a specified timeseries and (2) of all applied symbol timeseries
   bool              SetRequiredUsedData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool              SetRequiredAllUsedData(const uint required=0,const int rates_total=0);
//--- Return the flag of data synchronization with the server data of the (1) specified timeseries, (2) all timeseries
   bool              SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool              SyncAllData(const uint required=0,const int rates_total=0);
//--- Return the very first date in history by symbol (1) on the server, (2) in the client terminal and (3) the new tick flag
   datetime          ServerFirstDate(void)                           const { return this.m_server_firstdate;                                    }
   datetime          TerminalFirstDate(void)                         const { return this.m_terminal_firstdate;                                  }
   bool              IsNewTick(void)                                       { return this.m_new_tick.IsNewTick();                                }
//--- Create (1) the specified timeseries list and (2) all timeseries lists
   bool              Create(const ENUM_TIMEFRAMES timeframe,const uint required=0);
   bool              CreateAll(const uint required=0);
//--- Update (1) the specified timeseries list and (2) all timeseries lists
   void              Refresh(const ENUM_TIMEFRAMES timeframe,
                             const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0);
   void              RefreshAll(const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0);

//--- Compare CTimeSeries objects (by symbol)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Display (1) description and (2) short symbol timeseries description in the journal
   void              Print(const bool created=true);
   void              PrintShort(const bool created=true);
   
//--- Constructors
                     CTimeSeries(void){;}
                     CTimeSeries(const string symbol);
  };
//+------------------------------------------------------------------+

IsNewTick()メソッドは、m_new_tick「新しいティック」オブジェクトから新しいティックのデータをリクエストした結果を返します。

データが返される銘柄を「新しいティック」クラスオブジェクトに知らせるには、クラスコンストラクタで「新しいティック」クラスオブジェクトの銘柄を設定し、すぐに 現在のティック価格を読み取るためのデータを更新する必要があります。

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTimeSeries::CTimeSeries(const string symbol) : m_symbol(symbol)
  {
   this.m_list_series.Clear();
   this.m_list_series.Sort();
   for(int i=0;i<21;i++)
     {
      ENUM_TIMEFRAMES timeframe=this.TimeframeByIndex((uchar)i);
      CSeries *series_obj=new CSeries(this.m_symbol,timeframe);
      this.m_list_series.Add(series_obj);
     }
   this.SetTerminalServerDate();
   this.m_new_tick.SetSymbol(this.m_symbol);
   this.m_new_tick.Refresh();
  }
//+------------------------------------------------------------------+

データ同期フラグを返すメソッドの時系列使用フラグを確認します。フラグがオフの場合、時系列はプログラムで使用されず、処理されるべきではありません

//+------------------------------------------------------------------+
//| Return the flag of data synchronization                          |
//| with the server data                                             |
//+------------------------------------------------------------------+
bool CTimeSeries::SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRST_SET_SYMBOL));
      return false;
     }
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   if(series_obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ),this.m_symbol," ",TimeframeDescription(timeframe));
      return false;
     }
   if(!series_obj.IsAvailable())
      return false;
   return series_obj.SyncData(required,rates_total);
  }
//+------------------------------------------------------------------+
//| Return the flag of data synchronization                          |
//| of all timeseries with the server data                           |
//+------------------------------------------------------------------+
bool CTimeSeries::SyncAllData(const uint required=0,const int rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRST_SET_SYMBOL));
      return false;
     }
   bool res=true;
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL || !series_obj.IsAvailable())
         continue;
      res &=series_obj.SyncData(required,rates_total);
     }
   return res;
  }
//+------------------------------------------------------------------+

時系列作成メソッドで時系列使用フラグを強制的に設定します。

//+------------------------------------------------------------------+
//| Create a specified timeseries list                               |
//+------------------------------------------------------------------+
bool CTimeSeries::Create(const ENUM_TIMEFRAMES timeframe,const uint required=0)
  {
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   if(series_obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ),this.m_symbol," ",TimeframeDescription(timeframe));
      return false;
     }
   if(series_obj.RequiredUsedData()==0)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA));
      return false;
     }
   series_obj.SetAvailable(true);
   return(series_obj.Create(required)>0);
  }
//+------------------------------------------------------------------+
//| Create all timeseries lists                                      |
//+------------------------------------------------------------------+
bool CTimeSeries::CreateAll(const uint required=0)
  {
   bool res=true;
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL || series_obj.RequiredUsedData()==0)
         continue;
      series_obj.SetAvailable(true);
      res &=(series_obj.Create(required)>0);
     }
   return res;
  }
//+------------------------------------------------------------------+

時系列の更新メソッド(「新しいバー」イベントが検出された場合)で、上記のCSeries 時系列オブジェクトのSendEvent()メソッドを使用して、イベントに関するメッセージの送信を制御プログラムチャートに追加します。

//+------------------------------------------------------------------+
//| Update a specified timeseries list                               |
//+------------------------------------------------------------------+
void CTimeSeries::Refresh(const ENUM_TIMEFRAMES timeframe,
                          const datetime time=0,
                          const double open=0,
                          const double high=0,
                          const double low=0,
                          const double close=0,
                          const long tick_volume=0,
                          const long volume=0,
                          const int spread=0)
  {
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   if(series_obj==NULL || series_obj.DataTotal()==0)
      return;
   series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread);
   if(series_obj.IsNewBar(time))
     {
      series_obj.SendEvent();
      this.SetTerminalServerDate();
     }
  }
//+------------------------------------------------------------------+
//| Update all timeseries lists                                      |
//+------------------------------------------------------------------+
void CTimeSeries::RefreshAll(const datetime time=0,
                          const double open=0,
                          const double high=0,
                          const double low=0,
                          const double close=0,
                          const long tick_volume=0,
                          const long volume=0,
                          const int spread=0)
  {
   bool upd=false;
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL || series_obj.DataTotal()==0)
         continue;
      series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread);
      if(series_obj.IsNewBar(time))
        {
         series_obj.SendEvent();
         upd &=true;
        }
     }
   if(upd)
      this.SetTerminalServerDate();
  }
//+------------------------------------------------------------------+


\MQL5\Include\DoEasy\Collections\TimeSeriesCollection.mqhのCTimeSeriesCollection時系列コレクションクラスを改善しましょう。

時系列コレクションタイプをCListObjクラスにします。
これにはCListObjクラスファイルをインクルードしてコレクションリストタイプをCArrayObjから CListObjに変えます

//+------------------------------------------------------------------+
//|                                         TimeSeriesCollection.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Objects\Series\TimeSeries.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
//+------------------------------------------------------------------+
//| Symbol timeseries collection                                     |
//+------------------------------------------------------------------+
class CTimeSeriesCollection : public CObject
  {
private:
   CListObj                m_list;                    // List of applied symbol timeseries
//--- Return the timeseries index by symbol name
   int                     IndexTimeSeries(const string symbol);
public:

クラスのpublicセクションで、指定した時系列バーをグラフの時系列インデックスで返すメソッド指定した時系列の新しいバーを開くフラグを返すメソッド 、および現在の銘柄に属していない時系列を更新するメソッドを宣言します。

//--- Return the flag of data synchronization with the server data of the (1) specified timeseries of the specified symbol,
//--- (2) the specified timeseries of all symbols, (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   bool                    SyncData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool                    SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0);
   bool                    SyncData(const string symbol,const uint required=0,const int rates_total=0);
   bool                    SyncData(const uint required=0,const int rates_total=0);

//--- Return the bar of the specified timeseries of the specified symbol of the specified position 
   CBar                   *GetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const bool from_series=true);
//--- Return the flag of opening a new bar of the specified timeseries of the specified symbol
   bool                    IsNewBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time=0);

//--- Create (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols,
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols

   void                    RefreshOther(void);

//--- Display (1) the complete and (2) short collection description in the journal
   void                    Print(const bool created=true);
   void                    PrintShort(const bool created=true);
   
   
//--- Constructor
                           CTimeSeriesCollection();
  };
//+------------------------------------------------------------------+

クラスコンストラクタで、時系列オブジェクトのリストに時系列コレクションIDを設定します

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTimeSeriesCollection::CTimeSeriesCollection()
  {
   this.m_list.Clear();
   this.m_list.Sort();
   this.m_list.Type(COLLECTION_SERIES_ID);
  }
//+------------------------------------------------------------------+

以下は、時系列インデックスによってバーオブジェクトを返すメソッドと指定された時系列リストからの新しいバーイベントを返すメソッドの実装です。

//+-----------------------------------------------------------------------+
//| Return the bar of the specified timeseries                            |
//| of the specified symbol of the specified position                     |
//| from_series=true - by the timeseries index, false - by the list index |
//+-----------------------------------------------------------------------+
CBar *CTimeSeriesCollection::GetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const bool from_series=true)
  {
//--- Get the timeseries object index in the timeseries collection list by a symbol name
   int idx=this.IndexTimeSeries(symbol);
   if(idx==WRONG_VALUE)
      return NULL;
//--- Get the pointer to the timeseries object from the collection list of timeseries objects by the obtained index
   CTimeSeries *timeseries=this.m_list.At(idx);
   if(timeseries==NULL)
      return NULL;
//--- Get the specified timeseries from the symbol timeseries object by the specified timeframe
   CSeries *series=timeseries.GetSeries(timeframe);
   if(series==NULL)
      return NULL;
//--- Depending on the from_series flag, return the pointer to the bar
//--- either by the chart timeseries index or by the bar index in the timeseries list
   return(from_series ? series.GetBarBySeriesIndex(index) : series.GetBarByListIndex(index));
  }
//+------------------------------------------------------------------+
//| Return new bar opening flag                                      |
//| for a specified timeseries of a specified symbol                 |
//+------------------------------------------------------------------+
bool CTimeSeriesCollection::IsNewBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time=0)
  {
//--- Get the timeseries object index in the timeseries collection list by a symbol name
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return false;
//--- Get the pointer to the timeseries object from the collection list of timeseries objects by the obtained index
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return false;
//--- Get the specified timeseries from the symbol timeseries object by the specified timeframe
   CSeries *series=timeseries.GetSeries(timeframe);
   if(series==NULL)
      return false;
//--- Return the result of checking the new bar of the specified timeseries
   return series.IsNewBar(time);
  }
//+------------------------------------------------------------------+

現在の銘柄時系列のすべての時系列リストを更新するメソッドを実装します。

//+------------------------------------------------------------------+
//| Update all timeseries except the current symbol                  |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::RefreshOther(void)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      if(timeseries.Symbol()==::Symbol() || !timeseries.IsNewTick())
         continue;
      timeseries.RefreshAll();
     }
  }
//+------------------------------------------------------------------+

すべての時系列オブジェクトのリストによるループで、時系列オブジェクトを取得します。オブジェクト銘柄がプログラムが起動されるチャートの銘柄と等しい場合そのような時系列オブジェクトはスキップされます

このメソッドは、以下で説明する時系列更新メソッドと同様に、新しいティックフラグのチェックを備えています。 新しいティックがない場合時系列はスキップされ、そのデータは更新されません。

//+------------------------------------------------------------------+
//| Update the specified timeseries of the specified symbol          |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::Refresh(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                    const datetime time=0,
                                    const double open=0,
                                    const double high=0,
                                    const double low=0,
                                    const double close=0,
                                    const long tick_volume=0,
                                    const long volume=0,
                                    const int spread=0)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return;
   if(!timeseries.IsNewTick())
      return;
   timeseries.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread);
  }
//+------------------------------------------------------------------+
//| Update the specified timeseries of all symbols                   |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::Refresh(const ENUM_TIMEFRAMES timeframe,
                                    const datetime time=0,
                                    const double open=0,
                                    const double high=0,
                                    const double low=0,
                                    const double close=0,
                                    const long tick_volume=0,
                                    const long volume=0,
                                    const int spread=0)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      if(!timeseries.IsNewTick())
         continue;
      timeseries.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread);
     }
  }
//+------------------------------------------------------------------+
//| Update all timeseries of the specified symbol                    |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::Refresh(const string symbol,
                                    const datetime time=0,
                                    const double open=0,
                                    const double high=0,
                                    const double low=0,
                                    const double close=0,
                                    const long tick_volume=0,
                                    const long volume=0,
                                    const int spread=0)
  {
   int index=this.IndexTimeSeries(symbol);
   if(index==WRONG_VALUE)
      return;
   CTimeSeries *timeseries=this.m_list.At(index);
   if(timeseries==NULL)
      return;
   if(!timeseries.IsNewTick())
      return;
   timeseries.RefreshAll(time,open,high,low,close,tick_volume,volume,spread);
  }
//+------------------------------------------------------------------+
//| Update all timeseries of all symbols                             |
//+------------------------------------------------------------------+
void CTimeSeriesCollection::Refresh(const datetime time=0,
                                    const double open=0,
                                    const double high=0,
                                    const double low=0,
                                    const double close=0,
                                    const long tick_volume=0,
                                    const long volume=0,
                                    const int spread=0)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTimeSeries *timeseries=this.m_list.At(i);
      if(timeseries==NULL)
         continue;
      if(!timeseries.IsNewTick())
         continue;
      timeseries.RefreshAll(time,open,high,low,close,tick_volume,volume,spread);
     }
  }
//+------------------------------------------------------------------+


最後のステップは、CEngineライブラリのメインオブジェクトクラスのファイルに必要な改善を加えることです。

クラスファイルを\MQL5\Include\DoEasy\Engine.mqhで開きます。
クラスのprivateセクションで、ライブラリに基づいて、プログラムのタイプを格納するための変数を宣言します

//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Collection of historical orders and deals
   CMarketCollection    m_market;                        // Collection of market orders and deals
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CSymbolsCollection   m_symbols;                       // Symbol collection
   CTimeSeriesCollection m_series;                       // Timeseries collection
   CResourceCollection  m_resource;                      // Resource list
   CTradingControl      m_trading;                       // Trading management object
   CArrayObj            m_list_counters;                 // List of timer counters
   int                  m_global_error;                  // Global error code
   bool                 m_first_start;                   // First launch flag
   bool                 m_is_hedge;                      // Hedge account flag
   bool                 m_is_tester;                     // Flag of working in the tester
   bool                 m_is_market_trade_event;         // Account trading event flag
   bool                 m_is_history_trade_event;        // Account history trading event flag
   bool                 m_is_account_event;              // Account change event flag
   bool                 m_is_symbol_event;               // Symbol change event flag
   ENUM_TRADE_EVENT     m_last_trade_event;              // Last account trading event
   int                  m_last_account_event;            // Last event in the account properties
   int                  m_last_symbol_event;             // Last event in the symbol properties
   ENUM_PROGRAM_TYPE    m_program;                       // Program type

クラスのpublicセクションで、NewTick EAイベントを処理するメソッドを宣言します。

//--- (1) NewTick event timer and (2) handler
   void                 OnTimer(void);
   void                 OnTick(void);

クラスのpublicセクションで、チャートの時系列インデックスで指定された銘柄の指定された時系列のバーオブジェクトを返すメソッドを宣言 し、指定された銘柄の指定された時系列の新しいバーを開くフラグを返すメソッドを宣言します。

//--- Create (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols,
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
   bool                 SeriesCreate(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0)
                          { return this.m_series.CreateSeries(symbol,timeframe,required);          }
   bool                 SeriesCreate(const ENUM_TIMEFRAMES timeframe,const uint required=0)
                          { return this.m_series.CreateSeries(timeframe,required);                 }
   bool                 SeriesCreate(const string symbol,const uint required=0)
                          { return this.m_series.CreateSeries(symbol,required);                    }
   bool                 SeriesCreate(const uint required=0)
                          { return this.m_series.CreateSeries(required);                           }

//--- Return the bar of the specified timeseries of the specified symbol of the specified position
   CBar                *SeriesGetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const bool from_series=true)
                          { return this.m_series.GetBar(symbol,timeframe,index,from_series);                   }
//--- Return the flag of opening a new bar of the specified timeseries of the specified symbol
   bool                 SeriesIsNewBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time=0)
                          { return this.m_series.IsNewBar(symbol,timeframe,time);                  }

//--- Update (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols,
//--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols

同じクラスセクションで、指定された銘柄、時系列、およびチャート時系列(バーインデックス)でのポジションについて標準のバープロパティを返すメソッドを宣言します

//--- Return (1) Open, (2) High, (3) Low, (4) Close, (5) Time, (6) TickVolume,
//--- (7) RealVolume, (8) Spread of the specified bar of the specified symbol of the specified timeframe
   double               SeriesOpen(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   double               SeriesHigh(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   double               SeriesLow(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   double               SeriesClose(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   datetime             SeriesTime(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   long                 SeriesTickVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   long                 SeriesRealVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);
   int                  SeriesSpread(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index);

//--- Set the following for the trading classes:
//--- (1) correct filling policy, (2) filling policy,
//--- (3) correct order expiration type, (4) order expiration type,
//--- (5) magic number, (6) comment, (7) slippage, (8) volume, (9) order expiration date,
//--- (10) the flag of asynchronous sending of a trading request, (11) logging level, (12) number of trading attempts

クラスコンストラクタで、実行中のプログラムのタイプを設定し、時間枠コレクションタイマーのカウンターを作成します

//+------------------------------------------------------------------+
//| CEngine constructor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),
                     m_last_trade_event(TRADE_EVENT_NO_EVENT),
                     m_last_account_event(WRONG_VALUE),
                     m_last_symbol_event(WRONG_VALUE),
                     m_global_error(ERR_SUCCESS)
  {
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE);
   this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE);
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID1,COLLECTION_SYM_COUNTER_STEP1,COLLECTION_SYM_PAUSE1);
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID2,COLLECTION_SYM_COUNTER_STEP2,COLLECTION_SYM_PAUSE2);
   this.CreateCounter(COLLECTION_REQ_COUNTER_ID,COLLECTION_REQ_COUNTER_STEP,COLLECTION_REQ_PAUSE);
   this.CreateCounter(COLLECTION_TS_COUNTER_ID,COLLECTION_TS_COUNTER_STEP,COLLECTION_TS_PAUSE);
   
   ::ResetLastError();
   #ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   #endif 
   //---
  }
//+------------------------------------------------------------------+

ライブラリのOnTimer()ハンドラーに、時系列コレクションタイマーの操作を追加します(過剰なコードは削除されます)。

//+------------------------------------------------------------------+
//| CEngine timer                                                    |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
//--- Timer of the collections of historical orders and deals, as well as of market orders and positions
//...

//--- Account collection timer
//...
     
//--- Timer 1 of the symbol collection (updating symbol quote data in the collection)
//...

//--- Timer 2 of the symbol collection (updating all data of all symbols in the collection and tracking symbl and symbol search events in the market watch window)
//...

//--- Trading class timer
//...
     
//--- Timeseries collection timer
   index=this.CounterIndex(COLLECTION_TS_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If the pause is over, work with the timeseries list (except for the current symbol timeseries)
            if(counter.IsTimeDone())
               this.m_series.RefreshOther();
           }
         //--- In case of the tester, work with the timeseries list by tick (except for the current symbol timeseries)
         else
            this.m_series.RefreshOther();
        }
     }
  }
//+------------------------------------------------------------------+

コレクションタイマーカウンターとタイマー自体を使用して、CEngineライブラリのメインオブジェクトを作成するときに考慮されました.。他のすべてはコードへのコメントで説明されています。
タイマーは、プログラムが起動されたチャートの銘柄と銘柄が一致しない時系列のみを処理することに注意してください。
タイマーでは、「非ネイティブ」銘柄の「新しいティック」イベントを登録するときに時系列を更新します。したがって、これらはタイマーで検出されるイベントです。
EAのOnTick()ハンドラーから起動される>OnTick()メソッドは、現在の銘柄の時系列を更新するために使用されます。

//+------------------------------------------------------------------+
//| NewTick event handler                                            |
//+------------------------------------------------------------------+
void CEngine::OnTick(void)
  {
//--- If this is not a EA, exit
   if(this.m_program!=PROGRAM_EXPERT)
      return;
//--- Update the current symbol timeseries
   this.SeriesRefresh(NULL,PERIOD_CURRENT);
  }
//+------------------------------------------------------------------+

指定された時系列の指定されたバーのメインプロパティを受け取るためのメソッドを実装します。

//+------------------------------------------------------------------+
//| Return the specified bar's Open                                  |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
double CEngine::SeriesOpen(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.Open() : 0);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's High                                  |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
double CEngine::SeriesHigh(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.High() : 0);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's Low                                   |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
double CEngine::SeriesLow(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.Low() : 0);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's Close                                 |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
double CEngine::SeriesClose(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.Close() : 0);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's Time                                  |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
datetime CEngine::SeriesTime(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.Time() : 0);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's TickVolume                            |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
long CEngine::SeriesTickVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.VolumeTick() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's RealVolume                            |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
long CEngine::SeriesRealVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.VolumeReal() : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the specified bar's Spread                                |
//| of the specified symbol of the specified timeframe               |
//+------------------------------------------------------------------+
int CEngine::SeriesSpread(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index)
  {
   CBar *bar=this.m_series.GetBar(symbol,timeframe,index);
   return(bar!=NULL ? bar.Spread() : INT_MIN);
  }
//+------------------------------------------------------------------+

ここではすべてが単純です。チャート時系列の指定されたインデックス(0 —現在のバー)から時系列銘柄時間枠によってバーオブジェクトを受け取り適切なバープロパティを返します

これらは、プログラムで使用される時系列価格データの自動更新を作成し、イベントを制御プログラムチャートに送信し、プログラムで作成された時系列からデータを受信するために今日必要なすべての改善です。

テスト

次の方法でテストを実行してみましょう。3つの銘柄の現在の時間枠に対して3つの時系列を作成し、時系列コレクションオブジェクト(CTimeSeriesCollection)からゼロバーオブジェクト(CBarクラス)を取得し、バーオブジェクトの短い名前とバーオブジェクトパラメータの説明を返すメソッドを使用して、チャートコメントにバーデータを表示します。 2番目のコメント行は、ゼロバーデータを同様の形式で含めることです。ただし、この場合、データはCEngineライブラリのメインオブジェクトのメソッドを使用して生成され、指定された時間枠の指定された銘柄の指定されたバーのデータを返します。

データはテスターでリアルタイムに更新され、チャート上でEAが起動されます。
また、「新しいバー」イベントを制御プログラムチャートに送信するCSeriesクラスオブジェクトからのイベントの受信処理を実装し、銘柄チャートで起動されたプログラムでこれらのイベントを受信することを観察します。

テストを実行するには、前の記事のEAを\MQL5\Experts\TestDoEasy\Part38\TestDoEasyPart38.mq5として保存します。

EAのOnTick()ハンドラーを次の方法で確認します。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();       // Working in the timer
      PressButtonsControl();  // Button pressing control
      EventsHandling();       // Working with events
     }
//--- Handle the NewTick event in the library
   engine.OnTick();

//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();    // Trailing positions
      TrailingOrders();       // Trailing of pending orders
     }
   
//--- Bet the zero bar of the current timeseries
   CBar *bar=engine.SeriesGetBar(NULL,PERIOD_CURRENT,0);
   if(bar==NULL)
      return;
//--- Create a string of parameters of the current bar similar to the one
//--- displayed by the bar object description:
//--- bar.Header()+": "+bar.ParameterDescription()
   string parameters=
     (TextByLanguage("Бар \"","Bar \"")+Symbol()+"\" "+TimeframeDescription((ENUM_TIMEFRAMES)Period())+"[0]: "+TimeToString(bar.Time(),TIME_DATE|TIME_MINUTES|TIME_SECONDS)+
      ", O: "+DoubleToString(engine.SeriesOpen(NULL,PERIOD_CURRENT,0),Digits())+
      ", H: "+DoubleToString(engine.SeriesHigh(NULL,PERIOD_CURRENT,0),Digits())+
      ", L: "+DoubleToString(engine.SeriesLow(NULL,PERIOD_CURRENT,0),Digits())+
      ", C: "+DoubleToString(engine.SeriesClose(NULL,PERIOD_CURRENT,0),Digits())+
      ", V: "+(string)engine.SeriesTickVolume(NULL,PERIOD_CURRENT,0)+
      ", Real: "+(string)engine.SeriesRealVolume(NULL,PERIOD_CURRENT,0)+
      ", Spread: "+(string)engine.SeriesSpread(NULL,PERIOD_CURRENT,0)
     );
//--- Display the data received from the bar object in the first line of the chart comment,
//--- while the second line contains the methods of receiving timeseries price data
   Comment(bar.Header(),": ",bar.ParameterDescription(),"\n",parameters);
  }
//+------------------------------------------------------------------+

ここではすべてが簡単です。コードブロックは、DoEasyライブラリを操作するときの標準テンプレートです。現在の実装は、各ティックでライブラリによって処理されるNewTickイベントハンドラーを呼び出す機能です(現在、作成された時系列の更新を行います)。欠落しているすべての時間枠(宣言されているがCreate()メソッドによって作成されていない)はスキップされます(ライブラリによって更新されません)。今後、現在の時系列データを更新するには、EAのOnTick()ハンドラーからこのメソッドを呼び出す必要があります。

次に、現在の銘柄と期間の時系列からバーオブジェクトを受け取り取得したバーデータの説明を表す文字列を作成し、コメントに2行を表示します。
最初の行はbarオブジェクトメソッドを使用して表示されます
2番目のものは、リクエストされたバーデータを返すライブラリのメインオブジェクトのメソッドによって取得されたデータで構成されています

OnInitDoEasy()ライブラリ初期化関数は、使用されるすべての銘柄の時系列を作成するためにわずかに変更されたコードブロックを備えています。

//--- Implement displaying the list of used timeframes only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedTFs!=TIMEFRAMES_MODE_CURRENT)
      ArrayPrint(array_used_periods);
#endif 
//--- Create timeseries of all used symbols
   CArrayObj *list_timeseries=engine.GetListTimeSeries();
   if(list_timeseries!=NULL)
     {
      int total=list_timeseries.Total();
      for(int i=0;i<total;i++)
        {
         CTimeSeries *timeseries=list_timeseries.At(i);
         int total_periods=ArraySize(array_used_periods);
         for(int j=0;j<total_periods;j++)
           {
            ENUM_TIMEFRAMES timeframe=TimeframeByDescription(array_used_periods[j]);
            timeseries.SyncData(timeframe);
            timeseries.Create(timeframe);
           }
        }
     }
//--- Check created timeseries - display descriptions of all created timeseries in the journal
//--- (true - only created ones, false - created and declared ones)
   engine.GetTimeSeriesCollection().PrintShort(true); // Short descriptions
   //engine.GetTimeSeriesCollection().Print(true);      // Full descriptions


ここでは、すべての時系列のリストを取得し、時系列リストによるループでループインデックスによって次の時系列オブジェクトを取得します。次に、使用された時間枠の数によるループで、 時系列データと履歴データを同期した後、必要な時系列リストを作成します。

OnDoEasyEvent()ライブラリイベントを処理する関数で、時系列イベントを処理するためのコードブロックを追加します(冗長なコードは削除されました)。

//+------------------------------------------------------------------+
//| Handling DoEasy library events                                   |
//+------------------------------------------------------------------+
void OnDoEasyEvent(const int id,
                   const long &lparam,
                   const double &dparam,
                   const string &sparam)
  {
   int idx=id-CHARTEVENT_CUSTOM;
//--- Retrieve (1) event time milliseconds, (2) reason and (3) source from lparam, as well as (4) set the exact event time
   ushort msc=engine.EventMSC(lparam);
   ushort reason=engine.EventReason(lparam);
   ushort source=engine.EventSource(lparam);
   long time=TimeCurrent()*1000+msc;
   
//--- Handling symbol events
//...  
     
//--- Handling account events
//...
     
//--- Handling market watch window events
//...
     
//--- Handling timeseries events
   else if(idx>SERIES_EVENTS_NO_EVENT && idx<SERIES_EVENTS_NEXT_CODE)
     {
      //--- "New bar" event
      if(idx==SERIES_EVENTS_NEW_BAR)
        {
         Print(TextByLanguage("Новый бар на ","New Bar on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",TimeToString(lparam));
        }
     }
     
//--- Handling trading events
//...

  }
//+------------------------------------------------------------------+

ここで、取得したイベントIDが時系列イベントID内にありsこれが「新規バー」イベントである場合そのイベントに関するメッセージをターミナルの操作ログに表示します

EAをコンパイルし、そのパラメーターを次のように設定します。

  • 指定された銘柄リストを使用するために、使用される銘柄リストのモードを設定します。
  • 使用されている銘柄のリスト(カンマ-区切り記号)に、3つの銘柄のみを残します。そのうちの1つはEURUSDで、
  • 使用される時間枠のリストのモードで、現在の時間枠のみでの作業を選択します。次に例を示します。


チャートで起動します。しばらくすると、操作ログは現在の銘柄チャートで使用されている銘柄に関する「新しいバー」イベントメッセージを表示します。

New bar on EURUSD M5: 2020.03.11 12:55
New bar on EURAUD M5: 2020.03.11 12:55
New bar on AUDUSD M5: 2020.03.11 12:55
New bar on EURUSD M5: 2020.03.11 13:00
New bar on AUDUSD M5: 2020.03.11 13:00
New bar on EURAUD M5: 2020.03.11 13:00

EURUSDなどの設定で選択された銘柄の1つのチャートのビジュアルテスターモードでEAを起動し、チャートのコメントでゼロバーデータがどのように変化するかを確認します。


ご覧のように、異なる方法で取得されたデータを含む両方のラインは、受信したゼロバープロパティの値が同じであり、各ティックでリアルタイムに更新されます。

次の段階

次の記事では、本稿の完了時に検出された現在のライブラリバージョンのいくつかの欠点を修正し、指標の一部として機能するようにライブラリを準備することで、引き続き時系列での作業の概念を開発します。

現在のバージョンのライブラリのすべてのファイルは、テスト用EAファイルと一緒に以下に添付されているので、テストするにはダウンロードしてください。
質問や提案はコメント欄にお願いします。

目次に戻る

シリーズのこれまでの記事:

DoEasyライブラリの時系列(第35部): バーオブジェクトと銘柄の時系列リスト
DoEasyライブラリの時系列(第36部): すべての使用銘柄期間の時系列オブジェクト
DoEasyライブラリの時系列(第37部): すべての使用銘柄期間の時系列オブジェクト


MetaQuotes Software Corp.によりロシア語から翻訳された
元の記事: https://www.mql5.com/ru/articles/7695

添付されたファイル |
MQL5.zip (3698.04 KB)
MQL4.zip (3697.98 KB)
DoEasyライブラリの時系列(第37部): すべての使用銘柄期間の時系列オブジェクト DoEasyライブラリの時系列(第37部): すべての使用銘柄期間の時系列オブジェクト

本稿は、プログラムで使用されるすべての銘柄の指定された時間枠の時系列コレクションの開発についてです。時系列コレクション、コレクションの時系列パラメータを設定するメソッド、および開発された時系列に履歴データを最初に入力するメソッドを開発します。

トレードシグナルの多通貨監視(その4)。機能強化とシグナル検索システムの改善 トレードシグナルの多通貨監視(その4)。機能強化とシグナル検索システムの改善

このパートでは、トレードシグナルの検索・編集システムを拡張し、カスタムインジケータの使用可能性やプログラムのローカリゼーションを追加することを紹介します。 以前、シグナルを検索するための基本的なシステムを作ったことがありますが、小さなインジケータとシンプルな検索ルールのセットをベースにしていました。

MQLプログラムのグラフィカルインターフェイスのマークアップツールとしてのMQL 第2部 MQLプログラムのグラフィカルインターフェイスのマークアップツールとしてのMQL 第2部

本論文では、MQLプログラムのウィンドウインタフェースを記述するための新しい概念をMQLの構造体を用いて確認します。 MQLマークアップに基づいてGUIを自動的に作成することで、要素をキャッシュして動的に生成したり、イベントを処理するためのスタイルや新しいスキームを制御したりする関数が追加されます。 標準のコントロールライブラリの強化版が添付されています。

連続的なウォークフォワード最適化(その6):オートオプティマイザの論理部分と構造 連続的なウォークフォワード最適化(その6):オートオプティマイザの論理部分と構造

記事3と4以前、我々は自動ウォークフォワード最適化の作成を検討しました。 今回は、オートオプティマイザツールの内部構造について進めていきます。 この記事は、作成したプロジェクトをさらに稼働したい方、修正したい方はもちろん、プログラムのロジックを理解したい方にも役立つ内容となっています。 今回の記事では、プロジェクトの内部構造とオブジェクト間の関係を示すUML図を掲載します。 また、最適化開始までの過程が記述されていますが、オプティマイザの実装過程が記述されていない状態です。