DoEasyライブラリでの価格(第60部): 銘柄ティックデータのシリーズリスト

Artyom Trishkin | 26 4月, 2021

内容


概念

前回の記事で、ティックデータを使用するための機能の作成を開始しました。特に、ティックデータオブジェクトのクラスを作成しました。ここでは、そのようなオブジェクトを格納するためのリストを作成します。このようなリストは、プログラムで使用される各銘柄で利用できます。デフォルトでは、銘柄のティックデータリストのサイズは当日を対象としますが、当然、プログラムが一連のティックデータを持つ日数を設定することは可能です。

MQL5でいつでも取得できるのに、なぜカスタムティックデータリストが必要なのでしょうか。これは、必要なデータを検索したり、データをすばやく簡単に比較して受信したりすることができます。この機会は、ライブラリでリストを作成し、それらを使用するという概念そのものによって提供されます。

プログラムによって適用される各銘柄に対して作成されたリストをティックデータのコレクションに結合します。これにより、任意の銘柄のティックデータを使用し、さまざまな銘柄のティックストリームの分析を実行するのがより便利になります。


ライブラリクラスの改善

まず、新しいライブラリメッセージを\MQL5\Include\DoEasy\Data.mqhを追加しましょう。新しいメッセージのインデックスを追加します

//--- CTick
   MSG_TICK_TEXT_TICK,                                // Tick
   MSG_TICK_TIME_MSC,                                 // Time of the last update of prices in milliseconds
   MSG_TICK_TIME,                                     // Time of the last update of prices
   MSG_TICK_VOLUME,                                   // Volume for the current Last price
   MSG_TICK_FLAGS,                                    // Flags
   MSG_TICK_VOLUME_REAL,                              // Volume for the current Last price with greater accuracy
   MSG_TICK_SPREAD,                                   // Spread
   MSG_LIB_TEXT_TICK_CHANGED_DATA,                    // Changed data on tick:
   MSG_LIB_TEXT_TICK_FLAG_BID,                        // Bid price change
   MSG_LIB_TEXT_TICK_FLAG_ASK,                        // Ask price change
   MSG_LIB_TEXT_TICK_FLAG_LAST,                       // Last deal price change
   MSG_LIB_TEXT_TICK_FLAG_VOLUME,                     // Volume change
   
//--- CTickSeries
   MSG_TICKSERIES_TEXT_TICKSERIES,                    // Tick series
   MSG_TICKSERIES_ERR_GET_TICK_DATA,                  // Failed to get tick data
   MSG_TICKSERIES_FAILED_CREATE_TICK_DATA_OBJ,        // Failed to create tick data object
   MSG_TICKSERIES_FAILED_ADD_TO_LIST,                 // Failed to add tick data object to list
   MSG_TICKSERIES_TEXT_IS_NOT_USE,                    // Tick series not used. Set the flag using SetAvailable()
   MSG_TICKSERIES_REQUIRED_HISTORY_DAYS,              // Requested number of days
   
  };
//+------------------------------------------------------------------+

また、新しく追加されたインデックスに対応するテキストも追加します。

//--- CTick
   {"Тик","Tick"},
   {"Время последнего обновления цен в миллисекундах","Last price update time in milliseconds"},
   {"Время последнего обновления цен","Last price update time"},
   {"Объем для текущей цены Last","Volume for the current Last price"},
   {"Флаги","Flags"},
   {"Объем для текущей цены Last c повышенной точностью","Volume for the current \"Last\" price with increased accuracy"},
   {"Спред","Spread"},
   {"Изменённые данные на тике:","Changed data on a tick:"},
   {"Изменение цены Bid","Bid price change"},
   {"Изменение цены Ask","Ask price change"},
   {"Изменение цены последней сделки","Last price change"},
   {"Изменение объема","Volume change"},
   
//--- TickSeries
   {"Тиковая серия","Tick series"},
   {"Ошибка получения тиковых данных","Error getting tick data"},
   {"Не удалось создать объект тиковых данных","Failed to create tick data object"},
   {"Не удалось добавить объект тиковых данных в список","Failed to add tick data object to the list"},
   {"Тиковая серия не используется. Нужно установить флаг использования при помощи SetAvailable()","Tick series are not used. Need to set the use flag using SetAvailable()"},
   {"Запрошенное количество дней: ","Number of days requested: "},
   
  };
//+---------------------------------------------------------------------+

デフォルトでは当日のティックデータが保存されます。\MQL5\Include\DoEasy\Defines.mqhで、新しい定数(マクロ置換)を導入して、ライブラリがティックを保存する日数を設定しましょう。

//--- Timeseries parameters
#define SERIES_DEFAULT_BARS_COUNT      (1000)                     // Required default amount of timeseries data
#define PAUSE_FOR_SYNC_ATTEMPTS        (16)                       // Amount of pause milliseconds between synchronization attempts
#define ATTEMPTS_FOR_SYNC              (5)                        // Number of attempts to receive synchronization with the server
//--- Tick series parameters
#define TICKSERIES_DEFAULT_DAYS_COUNT  (1)                        // Required number of days for tick data in default series

\MQL5\Include\DoEasy\Objects\Series\SeriesDE.mqhで時系列クラスコードを再確認しているときに間違いに気づきました。作成したバーオブジェクトが何らかの理由でリストに追加されない場合、削除されていません。これにより、メモリリークが発生する可能性があります。したがって、リストに追加されなかった場合に備えて、オブジェクトの削除を追加しましょう。

//+------------------------------------------------------------------+
//| Create the timeseries list                                       |
//+------------------------------------------------------------------+
int CSeriesDE::Create(const uint required=0)
  {
//--- If the required history depth is not set for the list yet,
//--- display the appropriate message and return zero,
   if(this.m_amount==0)
     {
      ::Print(DFUN,this.m_symbol," ",TimeframeDescription(this.m_timeframe),": ",CMessage::Text(MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA));
      return 0;
     }
//--- otherwise, if the passed 'required' value exceeds zero and is not equal to the one already set, 
//--- while being lower than the available bar number,
//--- set the new value of the required history depth for the list
   else if(required>0 && this.m_amount!=required && required<this.m_bars)
     {
      //--- If failed to set a new value, return zero
      if(!this.SetRequiredUsedData(required,0))
         return 0;
     }
//--- For the rates[] array we are to receive historical data to,
//--- set the flag of direction like in the timeseries,
//--- clear the bar object list and set the flag of sorting by bar index
   MqlRates rates[];
   ::ArraySetAsSeries(rates,true);
   this.m_list_series.Clear();
   this.m_list_series.Sort(SORT_BY_BAR_TIME);
   ::ResetLastError();
//--- Get historical data of the MqlRates structure to the rates[] array starting from the current bar in the amount of m_amount,
//--- if failed to get data, display the appropriate message and return zero
   int copied=::CopyRates(this.m_symbol,this.m_timeframe,0,(uint)this.m_amount,rates),err=ERR_SUCCESS;
   if(copied<1)
     {
      err=::GetLastError();
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_SERIES_DATA)," ",this.m_symbol," ",TimeframeDescription(this.m_timeframe),". ",
                   CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(err),CMessage::Retcode(err));
      return 0;
     }
//--- Historical data is received in the rates[] array
//--- In the rates[] array loop,
   for(int i=0; i<copied; i++)
     {
      //--- create a new bar object out of the current MqlRates structure by the loop index
      ::ResetLastError();
      CBar* bar=new CBar(this.m_symbol,this.m_timeframe,rates[i]);
      if(bar==NULL)
        {
         ::Print
           (
            DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ)," ",this.Header()," ",::TimeToString(rates[i].time),". ",
            CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(::GetLastError())
           );
         continue;
        }
      //--- If failed to add bar object to the list,
      //--- display the appropriate message with the error description in the journal
      //--- and remove the newly created object
      if(!this.m_list_series.Add(bar))
        {
         err=::GetLastError();
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_ADD_TO_LIST)," ",bar.Header()," ",::TimeToString(rates[i].time),". ",
                      CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(err),CMessage::Retcode(err));
         delete bar;
        }
     }
//--- Return the size of the created bar object list
   return this.m_list_series.Total();
  }
//+------------------------------------------------------------------+

作成したリストからティックオブジェクトを検索、並べ替え、選択できるようにするには、このリストとティックデータを使用するためのメソッドを\MQL5\Include\DoEasy\Services\Select.mqhに追加します。

ティックデータオブジェクトクラスのファイルをインクルードします

//+------------------------------------------------------------------+
//|                                                       Select.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 <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\PendRequest\PendRequest.mqh"
#include "..\Objects\Series\SeriesDE.mqh"
#include "..\Objects\Indicators\Buffer.mqh"
#include "..\Objects\Indicators\IndicatorDE.mqh"
#include "..\Objects\Indicators\DataInd.mqh"
#include "..\Objects\Ticks\DataTick.mqh"
//+------------------------------------------------------------------+

クラス本体の最後に、ティックデータを使用するメソッドの宣言を追加します

//+------------------------------------------------------------------+
//| Methods of work with indicator data                              |
//+------------------------------------------------------------------+
   //--- Return the list of indicator data with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the indicator data index in the list with the maximum value of (1) integer, (2) real and (3) string property of data
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property);
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property);
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property);
   //--- Return the indicator data index in the list with the minimum value of (1) integer, (2) real and (3) string property of data
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property);
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property);
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with tick data                                |
//+------------------------------------------------------------------+
   //--- Return the list of tick data with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByTickDataProperty(CArrayObj *list_source,ENUM_TICK_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByTickDataProperty(CArrayObj *list_source,ENUM_TICK_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByTickDataProperty(CArrayObj *list_source,ENUM_TICK_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the tick data index in the list with the maximum value of (1) integer, (2) real and (3) string property of data
   static int        FindTickDataMax(CArrayObj *list_source,ENUM_TICK_PROP_INTEGER property);
   static int        FindTickDataMax(CArrayObj *list_source,ENUM_TICK_PROP_DOUBLE property);
   static int        FindTickDataMax(CArrayObj *list_source,ENUM_TICK_PROP_STRING property);
   //--- Return the tick data index in the list with the minimum value of (1) integer, (2) real and (3) string property of data
   static int        FindTickDataMin(CArrayObj *list_source,ENUM_TICK_PROP_INTEGER property);
   static int        FindTickDataMin(CArrayObj *list_source,ENUM_TICK_PROP_DOUBLE property);
   static int        FindTickDataMin(CArrayObj *list_source,ENUM_TICK_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

宣言されたメソッドをクラス本体の外側に実装します。

//+------------------------------------------------------------------+
//| Methods of working with tick data lists                          |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of tick data with one of integer                 |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByTickDataProperty(CArrayObj *list_source,ENUM_TICK_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CDataTick *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of tick data with one of real                    |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByTickDataProperty(CArrayObj *list_source,ENUM_TICK_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CDataTick *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of tick data with one of string                  |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByTickDataProperty(CArrayObj *list_source,ENUM_TICK_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CDataTick *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the tick data in the list                                 |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindTickDataMax(CArrayObj *list_source,ENUM_TICK_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataTick *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataTick *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the tick data in the list                                 |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindTickDataMax(CArrayObj *list_source,ENUM_TICK_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataTick *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataTick *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the tick data in the list                                 |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindTickDataMax(CArrayObj *list_source,ENUM_TICK_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataTick *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataTick *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the tick data in the list                                 |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindTickDataMin(CArrayObj* list_source,ENUM_TICK_PROP_INTEGER property)
  {
   int index=0;
   CDataTick *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataTick *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the tick data in the list                                 |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindTickDataMin(CArrayObj* list_source,ENUM_TICK_PROP_DOUBLE property)
  {
   int index=0;
   CDataTick *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataTick *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the tick data in the list                                 |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindTickDataMin(CArrayObj* list_source,ENUM_TICK_PROP_STRING property)
  {
   int index=0;
   CDataTick *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataTick *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+

そのようなメソッドの動作は何度も説明しました。詳細については、第3部をご覧ください。


ティックデータシリーズオブジェクトクラス

それでは、ティックデータのリストオブジェクトクラスを記述しましょう。ライブラリ内の他のすべてと同様に、リストはCObjectクラスとその子孫のインスタンスへのポインタの動的配列のクラスに基づく配列になります。

ティック履歴を保存する指定日数に応じて、必要な日の開始を計算します。その日からのCopyTicksRange()を使用して、既存のすべてのティックをリストに追加します。次の記事では、これらのリストのリアルタイムで更新して、コレクションに関連するティックデータベースが常に含まれるようにします。

ティックデータリストオブジェクトクラスの構造は、銘柄時系列リストクラスに似ています。唯一の違いは、ここではバーオブジェクトの代わりにティックオブジェクトが最小データストレージ単位として使用されていることです。クラスの構成は、ライブラリでは標準です。それでは、その本体を完全に検討し、後でいくつかの詳細とメソッドを明確にしていきます。

//+------------------------------------------------------------------+
//|                                                   TickSeries.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 "..\..\Services\Select.mqh"
#include "NewTickObj.mqh"
#include "DataTick.mqh"
//+------------------------------------------------------------------+
//| "Tick data series" class                                         |
//+------------------------------------------------------------------+
class CTickSeries : public CBaseObj
  {
private:
   string            m_symbol;                                          // Symbol
   uint              m_amount;                                          // Amount of applied tick series data
   uint              m_required;                                        // Required number of days for tick series data
   CArrayObj         m_list_ticks;                                      // List of tick data
   CNewTickObj       m_new_tick_obj;                                    // "New tick" object

public:
//--- Return (1) itself, (2) list of tick data and (3) "New tick" object of the tick series
   CTickSeries      *GetObject(void)                                    { return &this;               }
   CArrayObj        *GetList(void)                                      { return &m_list_ticks;       }
   CNewTickObj      *GetNewTickObj(void)                                { return &this.m_new_tick_obj;}

//--- Return the list of tick objects by selected (1) double, (2) integer and (3) string property fitting a compared condition
   CArrayObj        *GetList(ENUM_TICK_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByTickDataProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_TICK_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByTickDataProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_TICK_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByTickDataProperty(this.GetList(),property,value,mode); }
//--- Return the object of tick data by (1) index in the list, (2) time and (4) list size
   CDataTick        *GetTickByListIndex(const uint index);
   CDataTick        *GetTick(const datetime time); 
   CDataTick        *GetTick(const ulong time_msc); 
   int               DataTotal(void)                              const { return this.m_list_ticks.Total();       }

//--- The comparison method for searching identical tick series objects by a symbol
   virtual int       Compare(const CObject *node,const int mode=0) const 
                       {   
                        const CTickSeries *compared_obj=node;
                        return(this.Symbol()==compared_obj.Symbol() ? 0 : this.Symbol()>compared_obj.Symbol() ? 1 : -1);
                       } 
//--- Return the tick series name
   string            Header(void);
//--- Display (1) the tick series description and (2) the tick series short description in the journal
   void              Print(void);
   void              PrintShort(void);

//--- Constructors
                     CTickSeries(void){;}
                     CTickSeries(const string symbol,const uint required=0);

//+------------------------------------------------------------------+ 
//| Methods of working with objects and accessing their properties   |
//+------------------------------------------------------------------+
//--- Set (1) a symbol and (2) a number of used tick series data
   void              SetSymbol(const string symbol);                     
   void              SetRequiredUsedBars(const uint required=0);

//--- Return (1) symbol, (2) number of used, (3) requested tick data and (4) new tick flag
   string            Symbol(void)                                 const { return this.m_symbol;                   }
   ulong             AvailableUsedData(void)                      const { return this.m_amount;                   }
   ulong             RequiredUsedDays(void)                       const { return this.m_required;                 }
   bool              IsNewTick(void)                                    { return this.m_new_tick_obj.IsNewTick(); }

//--- Return (1) Bid, (2) Ask, (3) Last, (4) volume with increased accuracy,
//--- (5) spread, (6) volume, (7) tick flags, (8) time, (9) time in milliseconds by index in the list
   double            Bid(const uint index);
   double            Ask(const uint index);
   double            Last(const uint index);
   double            VolumeReal(const uint index);
   double            Spread(const uint index);
   long              Volume(const uint index);
   uint              Flags(const uint index);
   datetime          Time(const uint index);
   long              TimeMSC(const uint index);
   
//--- Return (1) Bid, (2) Ask, (3) Last, (4) volume with increased accuracy,
//--- (5) spread, (6) volume, (7) tick flags by tick time in milliseconds
   double            Bid(const ulong time_msc);
   double            Ask(const ulong time_msc);
   double            Last(const ulong time_msc);
   double            VolumeReal(const ulong time_msc);
   double            Spread(const ulong time_msc);
   long              Volume(const ulong time_msc);
   uint              Flags(const ulong time_msc);
   
//--- Return (1) Bid, (2) Ask, (3) Last, (4) volume with increased accuracy,
//--- (5) spread, (6) volume and (7) tick flags by tick time
   double            Bid(const datetime time);
   double            Ask(const datetime time);
   double            Last(const datetime time);
   double            VolumeReal(const datetime time);
   double            Spread(const datetime time);
   long              Volume(const datetime time);
   uint              Flags(const datetime time);

//--- (1) Create and (2) update the timeseries list
   int               Create(const uint required=0);
   void              Refresh(void);
  };
//+------------------------------------------------------------------+

以前に準備された「新しいティック」および「ティックデータ」オブジェクトクラスのリストがクラスに含まれます。「新しいティック」オブジェクトは次の記事でティックデータリストを更新するときに必要になりますが、「ティックデータ」オブジェクトはリストに配置されるオブジェクトのクラスです。

必要なすべてのクラスメンバー変数は、クラスのprivateセクションで宣言されます。これらの変数は、オブジェクトパラメータの値、オブジェクトリストクラスのオブジェクト(ティックリスト自体)、およびリアルタイムリストの更新中に次の記事で必要になる「新しいティック」オブジェクトを格納するためのものです。

クラスのpublicセクションには、ライブラリオブジェクトで標準のオブジェクト使用メソッドティックデータリストオブジェクトのプロパティにアクセスするためのメソッド、リストに配置された指定されたティックデータオブジェクトのプロパティへの簡単なアクセスのためのメソッドおよびリストを作成および更新するためのメソッドがあります。

オブジェクトメソッドについて考えてみましょう。

パラメトリッククラスコンストラクタでリストをクリアし、 ミリ秒単位のティック時間と、SetRequiredUsedDays()メソッドを使用してリストにティック履歴を含める必要がある日数並び替えられたリストのフラグを設定します。

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CTickSeries::CTickSeries(const string symbol,const uint required=0) : m_symbol(symbol)
  {
   this.m_list_ticks.Clear();
   this.m_list_ticks.Sort(SORT_BY_TICK_TIME_MSC);
   this.SetRequiredUsedDays(required);
  }
//+------------------------------------------------------------------+

以下は、ティックリスト銘柄を設定するメソッドです。

//+------------------------------------------------------------------+
//| Set a symbol                                                     |
//+------------------------------------------------------------------+
void CTickSeries::SetSymbol(const string symbol)
  {
   if(this.m_symbol==symbol)
      return;
   this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol);
  }
//+------------------------------------------------------------------+

メソッドに渡された銘柄がすでに設定されている場合は、メソッドを終了します。それ以外、NULLまたは空の文字列が渡された場合は、現在の銘柄を設定します。それ以外の場合は、メソッドに渡される銘柄を設定します。

以下は、ティックデータが必要な日数を設定するメソッドです。

//+------------------------------------------------------------------+
//| Set the number of required tick data                             |
//+------------------------------------------------------------------+
void CTickSeries::SetRequiredUsedDays(const uint required=0)
  {
   this.m_required=(required<1 ? TICKSERIES_DEFAULT_DAYS_COUNT : required);
  }
//+------------------------------------------------------------------+

メソッドにゼロ以下が渡された場合は、デフォルトの日数を設定します。それ以外の場合は、渡されて日数を設定します。

以下は、リストインデックスによってリストからティックオブジェクトを返すメソッドです。

//+------------------------------------------------------------------+
//| Return the tick object by its index in the list                  |
//+------------------------------------------------------------------+
CDataTick *CTickSeries::GetTickByListIndex(const uint index)
  {
   return this.m_list_ticks.At(index);
  }
//+------------------------------------------------------------------+

メソッドに渡されたインデックスによってリストにあるオブジェクトを返すだけです。無効なインデックスが指定された場合、またはリストが空の場合、At()メソッドNULLを返すことに注意してください。

以下は、リストからティックオブジェクトをその時間によって返すメソッドです。

//+------------------------------------------------------------------+
//| Return the last tick object by its time                          |
//+------------------------------------------------------------------+
CDataTick *CTickSeries::GetTick(const datetime time)
  {
   CArrayObj *list=GetList(TICK_PROP_TIME,time,EQUAL);
   if(list==NULL) return NULL;
   return list.At(list.Total()-1);
  }
//+------------------------------------------------------------------+

メソッドに渡された時間に対応する時間を持つティックオブジェクトのリストを取得し、最後のものを返します。
異なるティックは同時に発生する可能性があるため、最後のティックが返されます。

以下は、ミリ秒単位の時間でリストからティックオブジェクトを返すメソッドです。

//+------------------------------------------------------------------+
//| Return the last tick object by its time in milliseconds          |
//+------------------------------------------------------------------+
CDataTick *CTickSeries::GetTick(const ulong time_msc)
  {
   CArrayObj *list=GetList(TICK_PROP_TIME_MSC,time_msc,EQUAL);
   if(list==NULL) return NULL;
   return list.At(list.Total()-1);
  }
//+------------------------------------------------------------------+

メソッドに渡されたものに対応するミリ秒の時間でティックオブジェクトのリストを取得し、最後のものを返します。
今検討したメソッドと同様に、異なるティックの時間は同じミリ秒である可能性があるため、最も関連性が高いと見なされる最後のティックが返されます。

ティックオブジェクトを取得するための後続のメソッドは互いに同一であり、3つのオーバーロード(インデックス、時間、ミリ秒単位の時間によるリストの取得)があります。買呼値のティックを取得するためのこれらの3つのメソッドを検討し、他の同じメソッドのコードを見てみましょう。

以下は、リスト内のインデックスによってティックオブジェクトの買呼値を返すメソッドです。

 tick.Bid() : 0);
  } 
//+------------------------------------------------------------------+

メソッドに渡されたインデックスによってリストからオブジェクトを取得し、取得したオブジェクトから買呼値または0を返します(オブジェクトの取得に失敗した場合)。

以下は、リスト内の最後のティックオブジェクトの買呼値をミリ秒単位の時間で返すメソッドです。

//+------------------------------------------------------------------+
//| Return tick's Bid by time in milliseconds                        |
//+------------------------------------------------------------------+
double CTickSeries::Bid(const ulong time_msc)
  {
   CDataTick *tick=this.GetTick(time_msc);
   return(tick!=NULL ? tick.Bid() : 0);
  } 
//+------------------------------------------------------------------+

メソッドに渡された時間(ミリ秒単位)で最後のオブジェクトを取得し(取得メソッドは上記で検討されています)、取得したオブジェクトから買呼値または0(オブジェクトの取得に失敗した場合)を返します。

以下は、リスト内の最後のティックオブジェクトの買呼値をその時間によって返すメソッドです。

//+------------------------------------------------------------------+
//| Return tick's Bid by time                                        |
//+------------------------------------------------------------------+
double CTickSeries::Bid(const datetime time)
  {
   CDataTick *tick=this.GetTick(time);
   return(tick!=NULL ? tick.Bid() : 0);
  } 
//+------------------------------------------------------------------+

メソッドに渡された時間までに最後のオブジェクトを取得し(取得メソッドは上記で検討されています)、取得したオブジェクトから買呼値または0(オブジェクトの取得に失敗した場合)を返します。

リストからティックオブジェクトプロパティの値を取得する残りのメソッドは、上記で検討した3つのメソッドと同じです。分析は各々で行ってください。

//+------------------------------------------------------------------+
//| Return tick's Ask by index in the list                           |
//+------------------------------------------------------------------+

double CTickSeries::Ask(const uint index)
  {
   CDataTick *tick=this.GetTickByListIndex(index);
   return(tick!=NULL ? tick.Ask() : 0);
  } 
//+------------------------------------------------------------------+
//| Return tick's Ask by time in milliseconds                        |
//+------------------------------------------------------------------+
double CTickSeries::Ask(const ulong time_msc)
  {
   CDataTick *tick=this.GetTick(time_msc);
   return(tick!=NULL ? tick.Ask() : 0);
  } 
//+------------------------------------------------------------------+
//| Return tick's Ask by time                                        |
//+------------------------------------------------------------------+
double CTickSeries::Ask(const datetime time)
  {
   CDataTick *tick=this.GetTick(time);
   return(tick!=NULL ? tick.Ask() : 0);
  } 
//+------------------------------------------------------------------+
//| Return tick's Last by index in the list                          |
//+------------------------------------------------------------------+
double CTickSeries::Last(const uint index)
  {
   CDataTick *tick=this.GetTickByListIndex(index);
   return(tick!=NULL ? tick.Last() : 0);
  } 
//+------------------------------------------------------------------+
//| Return tick's Last by time in milliseconds                       |
//+------------------------------------------------------------------+
double CTickSeries::Last(const ulong time_msc)
  {
   CDataTick *tick=this.GetTick(time_msc);
   return(tick!=NULL ? tick.Last() : 0);
  } 
//+------------------------------------------------------------------+
//| Return tick's Last by time                                       |
//+------------------------------------------------------------------+
double CTickSeries::Last(const datetime time)
  {
   CDataTick *tick=this.GetTick(time);
   return(tick!=NULL ? tick.Last() : 0);
  } 
//+-------------------------------------------------------------------------+
//| Return the volume with the increased tick accuracy by index in the list |
//+-------------------------------------------------------------------------+
double CTickSeries::VolumeReal(const uint index)
  {
   CDataTick *tick=this.GetTickByListIndex(index);
   return(tick!=NULL ? tick.VolumeReal() : 0);
  } 
//+--------------------------------------------------------------------------+
//|Return the volume with the increased tick accuracy by time in milliseconds|
//+--------------------------------------------------------------------------+
double CTickSeries::VolumeReal(const ulong time_msc)
  {
   CDataTick *tick=this.GetTick(time_msc);
   return(tick!=NULL ? tick.VolumeReal() : 0);
  } 
//+------------------------------------------------------------------+
//| Return the volume with the increased tick accuracy by time       |
//+------------------------------------------------------------------+
double CTickSeries::VolumeReal(const datetime time)
  {
   CDataTick *tick=this.GetTick(time);
   return(tick!=NULL ? tick.VolumeReal() : 0);
  } 
//+------------------------------------------------------------------+
//| Return the tick spread by index in the list                      |
//+------------------------------------------------------------------+
double CTickSeries::Spread(const uint index)
  {
   CDataTick *tick=this.GetTickByListIndex(index);
   return(tick!=NULL ? tick.Spread() : 0);
  } 
//+------------------------------------------------------------------+
//| Return tick's spread by time in milliseconds                     |
//+------------------------------------------------------------------+
double CTickSeries::Spread(const ulong time_msc)
  {
   CDataTick *tick=this.GetTick(time_msc);
   return(tick!=NULL ? tick.Spread() : 0);
  } 
//+------------------------------------------------------------------+
//| Return tick's spread by time                                     |
//+------------------------------------------------------------------+
double CTickSeries::Spread(const datetime time)
  {
   CDataTick *tick=this.GetTick(time);
   return(tick!=NULL ? tick.Spread() : 0);
  } 
//+------------------------------------------------------------------+
//| Return the tick volume by index in the list                      |
//+------------------------------------------------------------------+
long CTickSeries::Volume(const uint index)
  {
   CDataTick *tick=this.GetTickByListIndex(index);
   return(tick!=NULL ? tick.Volume() : 0);
  } 
//+------------------------------------------------------------------+
//| Return tick's volume by time in milliseconds                     |
//+------------------------------------------------------------------+
long CTickSeries::Volume(const ulong time_msc)
  {
   CDataTick *tick=this.GetTick(time_msc);
   return(tick!=NULL ? tick.Volume() : 0);
  } 
//+------------------------------------------------------------------+
//| Return tick's volume by time                                     |
//+------------------------------------------------------------------+
long CTickSeries::Volume(const datetime time)
  {
   CDataTick *tick=this.GetTick(time);
   return(tick!=NULL ? tick.Volume() : 0);
  } 
//+------------------------------------------------------------------+
//| Return the tick flags by index in the list                       |
//+------------------------------------------------------------------+
uint CTickSeries::Flags(const uint index)
  {
   CDataTick *tick=this.GetTickByListIndex(index);
   return(tick!=NULL ? tick.Flags() : 0);
  } 
//+------------------------------------------------------------------+
//| Return the tick flags by time in milliseconds                    |
//+------------------------------------------------------------------+
uint CTickSeries::Flags(const ulong time_msc)
  {
   CDataTick *tick=this.GetTick(time_msc);
   return(tick!=NULL ? tick.Flags() : 0);
  } 
//+------------------------------------------------------------------+
//| Return the tick flags by time                                    |
//+------------------------------------------------------------------+
uint CTickSeries::Flags(const datetime time)
  {
   CDataTick *tick=this.GetTick(time);
   return(tick!=NULL ? tick.Flags() : 0);
  } 
//+------------------------------------------------------------------+

各メソッドは、3つの同一のオーバーロードメソッドで表され、リスト内のインデックスと時間(日付形式とミリ秒の両方)でティックオブジェクトを取得できます。

以下は、ティックシリーズの文字列名を返すメソッドです。

//+------------------------------------------------------------------+
//| Return the tick series name                                      |
//+------------------------------------------------------------------+
string CTickSeries::Header(void)
  {
   return CMessage::Text(MSG_TICKSERIES_TEXT_TICKSERIES)+" \""+this.m_symbol+"\"";
  }
//+------------------------------------------------------------------+

文字列は以下の形式で返します。

Tickseries "symbol name"

以下は例です。

Tick series "EURUSD"

以下は、ティックシリーズの完全な説明を操作ログに表示するメソッドです。

//+------------------------------------------------------------------+
//| Display the tick series description in the journal               |
//+------------------------------------------------------------------+
void CTickSeries::Print(void)
  {
   string txt=
     (
      CMessage::Text(MSG_TICKSERIES_REQUIRED_HISTORY_DAYS)+(string)this.RequiredUsedDays()+", "+
      CMessage::Text(MSG_LIB_TEXT_TS_AMOUNT_HISTORY_DATA)+(string)this.DataTotal()
     );
   ::Print(this.Header(),": ",txt);
  }
//+------------------------------------------------------------------+

このメソッドは、ティックがシリーズに保存されている日数と、リストに実際に保存されているティックの数の説明を示す文字列を作成します。
その後、ティックシリーズのヘッダーと作成された文字列が表示されます。以下は例です。

Tick series "EURUSD": Requested number of days: 1, Historical data created: 256714

以下は、ティックシリーズの簡潔な説明を操作ログに表示するメソッドです。

//+------------------------------------------------------------------+
//| Display the brief tick series description in the journal         |
//+------------------------------------------------------------------+
void CTickSeries::PrintShort(void)
  {
   ::Print(this.Header());
  }
//+------------------------------------------------------------------+

ティックシリーズの文字列名を操作ログに表示します。

以下は、ティックシリーズを作成するメソッドです。

//+------------------------------------------------------------------+
//| Create the series list of tick data                              |
//+------------------------------------------------------------------+
int CTickSeries::Create(const uint required=0)
  {
//--- If the tick series is not used, inform of that and exit
   if(!this.m_available)
     {
      ::Print(DFUN,this.m_symbol,": ",CMessage::Text(MSG_TICKSERIES_TEXT_IS_NOT_USE));
      return false;
     }
//--- Declare the ticks[] array we are to receive historical data to,
//--- clear the list of tick data objects and set the flag of sorting by time in milliseconds
   MqlTick ticks_array[];
   this.m_list_ticks.Clear();
   this.m_list_ticks.Sort(SORT_BY_TICK_TIME_MSC);
   ::ResetLastError();
   int err=ERR_SUCCESS;
//--- Calculate the day start time in milliseconds the ticks should be copied from
   MqlDateTime date_str={0};
   datetime date=::iTime(m_symbol,PERIOD_D1,this.m_required);
   ::TimeToStruct(date,date_str);
   date_str.hour=date_str.min=date_str.sec=0;
   date=::StructToTime(date_str);
   long date_from=(long)date*1000;
   if(date_from<1) date_from=1;
//--- Get historical data of the MqlTick structure to the tick[] array
//--- from the calculated date to the current time and save the obtained number in m_amount.
//--- If failed to get data, display the appropriate message and return zero
   this.m_amount=::CopyTicksRange(m_symbol,ticks_array,COPY_TICKS_ALL,date_from);
   if(this.m_amount<1)
     {
      err=::GetLastError();
      ::Print(DFUN,CMessage::Text(MSG_TICKSERIES_ERR_GET_TICK_DATA),": ",CMessage::Text(err),CMessage::Retcode(err));
      return 0;
     }
//--- Historical data is received in the rates[] array
//--- In the ticks[] array loop
   for(int i=0; i<(int)this.m_amount; i++)
     {
      //--- create a new object of tick data out of the current MqlTick structure data from the ticks[] array by the loop index
      ::ResetLastError();
      CDataTick* tick=new CDataTick(this.m_symbol,ticks_array[i]);
      if(tick==NULL)
        {
         ::Print
           (
            DFUN,CMessage::Text(MSG_TICKSERIES_FAILED_CREATE_TICK_DATA_OBJ)," ",this.Header()," ",::TimeMSCtoString(ticks_array[i].time_msc),". ",
            CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(::GetLastError())
           );
         continue;
        }
      //--- If failed to add a new tick data object to the list
      //--- display the appropriate message with the error description in the journal
      //--- and remove the newly created object
      if(!this.m_list_ticks.Add(tick))
        {
         err=::GetLastError();
         ::Print(DFUN,CMessage::Text(MSG_TICKSERIES_FAILED_ADD_TO_LIST)," ",tick.Header()," ",
                      CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(err),CMessage::Retcode(err));
         delete tick;
        }
     }
//--- Return the size of the created bar object list
   return this.m_list_ticks.Total();
  }
//+------------------------------------------------------------------+

メソッドの使用については、コードで詳しく説明しています。簡単に言うと
ティックをコピーする必要がある日の開始時刻を計算し、配列の計算された日付からティックを要求します。ティックが正常に計算されたら、取得したティック配列によって、ループ内のMqlTick構造体形式で次のティックを取得します。配列を使用して、新しいティックオブジェクトを作成し、作成が成功した場合にリストに配置します。ループが完了したら、ティックデータリストに配置されたティック数を返します。

これでティックデータリストの作成は完了です。


リストの作成とデータ取得のテスト

テストの目的で、プログラムの開始時に現在の銘柄のティックオブジェクトの当日のリストを作成するだけです。得られたリストで、売呼値が最も高く、買呼値価格が最も低いティックを見つけ、検出されたティックオブジェクトのデータを操作ログで表示します。これを行うには、前の記事からEAを取得し、\MQL5\Experts\TestDoEasy\Part60\TestDoEasyPart60.mq5として保存します。

これは単にティックデータリストのテストであり、ライブラリから直接アクセスすることはできないため、ティックデータリストオブジェクトクラスをEAファイルにインクルードします

//|                                             TestDoEasyPart60.mq5 |
//|                        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"
//--- includes
#include <DoEasy\Engine.mqh>
#include <DoEasy\Objects\Ticks\TickSeries.mqh>
//--- enums

プログラムのグローバル変数の領域で、ティックデータリストオブジェクトを宣言します

//--- global variables
CEngine        engine;
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ushort         magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           distance_pending_request;
uint           bars_delay_pending_request;
uint           slippage;
bool           trailing_on;
bool           pressed_pending_buy;
bool           pressed_pending_buy_limit;
bool           pressed_pending_buy_stop;
bool           pressed_pending_buy_stoplimit;
bool           pressed_pending_close_buy;
bool           pressed_pending_close_buy2;
bool           pressed_pending_close_buy_by_sell;
bool           pressed_pending_sell;
bool           pressed_pending_sell_limit;
bool           pressed_pending_sell_stop;
bool           pressed_pending_sell_stoplimit;
bool           pressed_pending_close_sell;
bool           pressed_pending_close_sell2;
bool           pressed_pending_close_sell_by_buy;
bool           pressed_pending_delete_all;
bool           pressed_pending_close_all;
bool           pressed_pending_sl;
bool           pressed_pending_tp;
double         trailing_stop;
double         trailing_step;
uint           trailing_start;
uint           stoploss_to_modify;
uint           takeprofit_to_modify;
int            used_symbols_mode;
string         array_used_symbols[];
string         array_used_periods[];
bool           testing;
uchar          group1;
uchar          group2;
double         g_point;
int            g_digits;

//--- "New tick" object
CNewTickObj    check_tick;
//--- Object of the current symbol tick series data
CTickSeries    tick_series;
//+------------------------------------------------------------------+

OnTick()で、ティックデータオブジェクトを使用するコードブロックを削除します。これは、前回の記事からここに残されています。ここではオブジェクトを作成しません。

//--- Create a temporary list for storing “Tick data” objects,
//--- a variable for obtaining tick data and
//--- a variable for calculating incoming ticks
   static int tick_count=0;
   CArrayObj list;
   MqlTick tick_struct;
//--- Check a new tick on the current symbol
   if(check_tick.IsNewTick())
     {
      //--- If failed to get the price - exit
      if(!SymbolInfoTick(Symbol(),tick_struct))
         return;
      //--- Create a new tick data object
      CDataTick *tick_obj=new CDataTick(Symbol(),tick_struct);
      if(tick_obj==NULL)
         return;
      //--- Increase tick counter (simply to display on the screen, no other purpose is provided)
      tick_count++;
      //--- Limit the number of ticks in the counting as one hundred thousand (again, no purpose is provided)
      if(tick_count>100000) tick_count=1;
      //--- In the comment on the chart display the tick number and its short description
      Comment("--- #",IntegerToString(tick_count,5,'0'),": ",tick_obj.Header());
      //--- If this is the first tick (which follows the first launch of EA) display its full description in the journal
      if(tick_count==1)
         tick_obj.Print();
      //--- Remove if failed to put the created tick data object in the list
      if(!list.Add(tick_obj))
         delete tick_obj;
     }

よって、OnTick()ハンドラは次のようになります。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Handle the NewTick event in the library
   engine.OnTick(rates_data);

//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Working in the timer
      PressButtonsControl();        // Button pressing control
      engine.EventsHandling();      // Working with events
     }

//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();          // Trailing positions
      TrailingOrders();             // Trailing pending orders
     }
  }
//+------------------------------------------------------------------+

リストは、DoEasyライブラリ初期化関数内のOnInit()ハンドラ、つまりコードブロックに作成されます。

//+------------------------------------------------------------------+
//| Initializing DoEasy library                                      |
//+------------------------------------------------------------------+
void OnInitDoEasy()
  {
//--- Check if working with the full list is selected
   used_symbols_mode=InpModeUsedSymbols;
   if((ENUM_SYMBOLS_MODE)used_symbols_mode==SYMBOLS_MODE_ALL)
     {
      int total=SymbolsTotal(false);
      string ru_n="\nКоличество символов на сервере "+(string)total+".\nМаксимальное количество: "+(string)SYMBOLS_COMMON_TOTAL+" символов.";
      string en_n="\nNumber of symbols on server "+(string)total+".\nMaximum number: "+(string)SYMBOLS_COMMON_TOTAL+" symbols.";
      string caption=TextByLanguage("Внимание!","Attention!");
      string ru="Выбран режим работы с полным списком.\nВ этом режиме первичная подготовка списков коллекций символов и таймсерий может занять длительное время."+ru_n+"\nПродолжить?\n\"Нет\" - работа с текущим символом \""+Symbol()+"\"";
      string en="Full list mode selected.\nIn this mode, the initial preparation of lists of symbol collections and timeseries can take a long time."+en_n+"\nContinue?\n\"No\" - working with the current symbol \""+Symbol()+"\"";
      string message=TextByLanguage(ru,en);
      int flags=(MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2);
      int mb_res=MessageBox(message,caption,flags);
      switch(mb_res)
        {
         case IDNO : 
           used_symbols_mode=SYMBOLS_MODE_CURRENT; 
           break;
         default:
           break;
        }
     }
//--- Set the counter start point to measure the approximate library initialization time
   ulong begin=GetTickCount();
   Print(TextByLanguage("--- Инициализация библиотеки \"DoEasy\" ---","--- Initializing the \"DoEasy\" library ---"));
//--- Fill in the array of used symbols
   CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,InpUsedSymbols,array_used_symbols);
//--- Set the type of the used symbol list in the symbol collection and fill in the list of symbol timeseries
   engine.SetUsedSymbols(array_used_symbols);
//--- Displaying the selected mode of working with the symbol object collection in the journal
   string num=
     (
      used_symbols_mode==SYMBOLS_MODE_CURRENT ? ": \""+Symbol()+"\"" : 
      TextByLanguage(". Количество используемых символов: ",". The number of symbols used: ")+(string)engine.GetSymbolsCollectionTotal()
     );
   Print(engine.ModeSymbolsListDescription(),num);
//--- Implement displaying the list of used symbols only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedSymbols!=SYMBOLS_MODE_CURRENT)
     {
      string array_symbols[];
      CArrayObj* list_symbols=engine.GetListAllUsedSymbols();
      for(int i=0;i<list_symbols.Total();i++)
        {
         CSymbol *symbol=list_symbols.At(i);
         if(symbol==NULL)
            continue;
         ArrayResize(array_symbols,ArraySize(array_symbols)+1,SYMBOLS_COMMON_TOTAL);
         array_symbols[ArraySize(array_symbols)-1]=symbol.Name();
        }
      ArrayPrint(array_symbols);
     }
#endif   
//--- Set used timeframes
   CreateUsedTimeframesArray(InpModeUsedTFs,InpUsedTFs,array_used_periods);
//--- Display the selected mode of working with the timeseries object collection
   string mode=
     (
      InpModeUsedTFs==TIMEFRAMES_MODE_CURRENT   ? 
         TextByLanguage("Работа только с текущим таймфреймом: ","Work only with the current Period: ")+TimeframeDescription((ENUM_TIMEFRAMES)Period())   :
      InpModeUsedTFs==TIMEFRAMES_MODE_LIST      ? 
         TextByLanguage("Работа с заданным списком таймфреймов:","Work with a predefined list of Periods:")                                              :
      TextByLanguage("Работа с полным списком таймфреймов:","Work with the full list of all Periods:")
     );
   Print(mode);
//--- 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
   engine.SeriesCreateAll(array_used_periods);

//--- Check created timeseries - display descriptions of all created timeseries in the journal
//--- (true - only created ones, false - created and declared ones)
   engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions
   //engine.GetTimeSeriesCollection().Print(true);      // Full descriptions

//--- Code block for checking the tick list creation and working with it
   Print("");
//--- Since the tick series object is created with the default constructor,
//--- set a symbol, usage flag and the number of days (the default is 1) to copy the ticks
//--- Create the tick series and printed data in the journal
   tick_series.SetSymbol(Symbol());
   tick_series.SetAvailable(true);
   tick_series.SetRequiredUsedDays();
   tick_series.Create();
   tick_series.Print();
   
   Print("");
//--- Get and display in the journal the data of an object with the highest Ask price in the daily price range
   int index_max=CSelect::FindTickDataMax(tick_series.GetList(),TICK_PROP_ASK);
   CDataTick *tick_max=tick_series.GetList().At(index_max);
   if(tick_max!=NULL)
      tick_max.Print();
//--- Get and display in the journal the data of an object with the lowest Bid price in the daily price range
   int index_min=CSelect::FindTickDataMin(tick_series.GetList(),TICK_PROP_BID);
   CDataTick *tick_min=tick_series.GetList().At(index_min);
   if(tick_min!=NULL)
      tick_min.Print();

//--- Create resource text files
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","Falling coin 1"),sound_array_coin_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Falling coins"),sound_array_coin_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Coins"),sound_array_coin_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","Falling coin 2"),sound_array_coin_04);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Button click 1"),sound_array_click_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Button click 2"),sound_array_click_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Button click 3"),sound_array_click_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","Cash machine"),sound_array_cash_machine_01);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""),img_array_spot_green);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""),img_array_spot_red);

//--- Pass all existing collections to the main library class
   engine.CollectionOnInit();

//--- Set the default magic number for all used symbols
   engine.TradingSetMagic(engine.SetCompositeMagicNumber(magic_number));
//--- Set synchronous passing of orders for all used symbols
   engine.TradingSetAsyncMode(false);
//--- Set the number of trading attempts in case of an error
   engine.TradingSetTotalTry(InpTotalAttempts);
//--- Set correct order expiration and filling types to all trading objects
   engine.TradingSetCorrectTypeExpiration();
   engine.TradingSetCorrectTypeFilling();

//--- Set standard sounds for trading objects of all used symbols
   engine.SetSoundsStandart();
//--- Set the general flag of using sounds
   engine.SetUseSounds(InpUseSounds);
//--- Set the spread multiplier for symbol trading objects in the symbol collection
   engine.SetSpreadMultiplier(InpSpreadMultiplier);
      
//--- Set controlled values for symbols
   //--- Get the list of all collection symbols
   CArrayObj *list=engine.GetListAllUsedSymbols();
   if(list!=NULL && list.Total()!=0)
     {
      //--- In a loop by the list, set the necessary values for tracked symbol properties
      //--- By default, the LONG_MAX value is set to all properties, which means "Do not track this property" 
      //--- It can be enabled or disabled (by setting the value less than LONG_MAX or vice versa - set the LONG_MAX value) at any time and anywhere in the program
      /*
      for(int i=0;i<list.Total();i++)
        {
         CSymbol* symbol=list.At(i);
         if(symbol==NULL)
            continue;
         //--- Set control of the symbol price increase by 100 points
         symbol.SetControlBidInc(100000*symbol.Point());
         //--- Set control of the symbol price decrease by 100 points
         symbol.SetControlBidDec(100000*symbol.Point());
         //--- Set control of the symbol spread increase by 40 points
         symbol.SetControlSpreadInc(400);
         //--- Set control of the symbol spread decrease by 40 points
         symbol.SetControlSpreadDec(400);
         //--- Set control of the current spread by the value of 40 points
         symbol.SetControlSpreadLevel(400);
        }
      */
     }
//--- Set controlled values for the current account
   CAccount* account=engine.GetAccountCurrent();
   if(account!=NULL)
     {
      //--- Set control of the profit increase to 10
      account.SetControlledValueINC(ACCOUNT_PROP_PROFIT,10.0);
      //--- Set control of the funds increase to 15
      account.SetControlledValueINC(ACCOUNT_PROP_EQUITY,15.0);
      //--- Set profit control level to 20
      account.SetControlledValueLEVEL(ACCOUNT_PROP_PROFIT,20.0);
     }
//--- Get the end of the library initialization time counting and display it in the journal
   ulong end=GetTickCount();
   Print(TextByLanguage("Время инициализации библиотеки: ","Library initialization time: "),TimeMSCtoString(end-begin,TIME_MINUTES|TIME_SECONDS));
  }
//+------------------------------------------------------------------+

現在の銘柄の当日のティックシリーズを作成し、その中の2つの必要なティックオブジェクトを検索して操作ログにデータを表示するためのコードブロックは詳細にコメントされているため、勉強するときに疑問は生じないはずです。ご質問がある場合は、下のコメント欄でお気軽にお問い合わせください。

この関数は、OnInit()ハンドラから計算されます。したがって、リストはプログラムの起動時に一度作成されます。当日の最高の売呼値と最低の買呼値を特徴とする2つのティックデータオブジェクトがすぐに見つかります。データの表示には時間がかかります。ティックデータがローカルに表示されない場合、それらのダウンロードがアクティブ化されます。

EAをコンパイルし、現在の銘柄と時間枠を使用するように設定で事前に定義されている銘柄チャートで起動します。EAを初期化するとき、EAパラメータのデータ、作成された時系列、および(少し後で)作成されたティックシリーズのデータが表示されます。その日の最高の売呼値最低の買呼値の2つが見つかったティックのデータを以下に表示します。

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10426.13 USD, 1:100, Hedge, MetaTrader 5 demo
--- Initializing "DoEasy" library ---
Working with the current symbol only: "EURUSD"
Working with the current timeframe only: H4
EURUSD symbol timeseries: 
- Timeseries "EURUSD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6336

Tick series "EURUSD": Requested number of days: 1, Historical data created: 276143

============= Beginning of parameter list (Tick "EURUSD" 2021.01.06 14:25:32.156) =============
Last price update time in milliseconds: 2021.01.06 14:25:32.156
Last price update time: 2021.01.06 14:25:32
Volume for the current Last price: 0
Flags: 134
Changed data on the tick:
 - Ask price change
 - Bid price change
------
Bid price: 1.23494
Ask price: 1.23494
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00000
------
Symbol: "EURUSD"
============= End of parameter list (Tick "EURUSD" 2021.01.06 14:25:32.156) =============

============= Beginning of parameter list (Tick "EURUSD" 2021.01.07 12:51:40.632) =============
Last price update time in milliseconds: 2021.01.07 12:51:40.632
Last price update time: 2021.01.07 12:51:40
Volume for the current Last price: 0
Flags: 134
Changed data on the tick:
 - Ask price change
 - Bid price change
------
Bid price: 1.22452
Ask price: 1.22454
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00002
------
Symbol: "EURUSD"
============= End of parameter list (Tick "EURUSD" 2021.01.07 12:51:40.632) =============

Library initialization time: 00:00:12.828

Initialization took 12.8 seconds — time for uploading historical tick data.

次の段階

次の記事では、プログラムで使用されるすべての銘柄のティックデータのコレクションクラスを作成し、作成されたすべてのリストのリアルタイムでの更新を実装します。

ライブラリの現在のバージョンのすべてのファイルは、テストおよびダウンロードできるように、MQL5のテストEAファイルと一緒に以下に添付されています。
ティックデータクラスは開発中であるため、この段階でカスタムプログラムで使用しないことを強くお勧めします。
質問や提案はコメント欄にお願いします。

目次に戻る

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

DoEasyライブラリの時系列(第35部): バーオブジェクトと銘柄の時系列リスト
DoEasyライブラリの時系列(第36部): すべての使用銘柄期間の時系列オブジェクト
DoEasyライブラリの時系列(第37部): すべての使用銘柄期間の時系列オブジェクト
DoEasyライブラリの時系列(第38部): 時系列コレクション-リアルタイムの更新とプログラムからのデータへのアクセス
DoEasyライブラリの時系列(第39部): ライブラリに基づいた指標 - データイベントと時系列イベントの準備
DoEasyライブラリの時系列(第40部): ライブラリに基づいた指標 - 実時間でのデータ更新
DoEasyライブラリの時系列(第41部): 複数銘柄・複数期間指標の例
DoEasyライブラリの時系列(第42部): 抽象指標バッファオブジェクトクラス
DoEasyライブラリの時系列(第43部): 指標バッファオブジェクトクラス
DoEasyライブラリの時系列(第44部): 指標バッファオブジェクトのコレクションクラス
DoEasyライブラリの時系列(第45部): 複数期間指標バッファ
DoEasyライブラリの時系列(第46部): 複数銘柄・複数期間指標バッファ
DoEasyライブラリの時系列(第47部): 複数銘柄・複数期間標準指標
DoEasyライブラリの時系列(第48部): 単一サブウィンドウでの単一バッファ複数銘柄・複数期間指標
DoEasyライブラリの時系列(第49部): 複数銘柄・複数期間の複数バッファ標準指標
DoEasyライブラリの時系列(第50部): シフト付き複数銘柄・複数期間標準指標
DoEasyライブラリの時系列(第51部): 複数銘柄・複数期間の複合標準指標
DoEasyライブラリの時系列(第52部): 複数銘柄・複数期間の単一バッファ標準指標のクロスプラットフォーム化
DoEasyライブラリの時系列(第53部): 抽象基本指標クラス
DoEasyライブラリの時系列(第54部): 抽象基本指標の子孫クラス
DoEasyライブラリの時系列(第55部): 指標コレクションクラス
DoEasyライブラリの時系列(第56部): カスタム指標オブジェクト、コレクション内指標オブジェクトからのデータ取得
DoEasyライブラリの時系列(第57部): 指標バッファデータオブジェクト
DoEasyライブラリの時系列(第58部): 指標バッファデータの時系列
DoEasyライブラリの時系列(第59部): 単一ティックのデータを格納するオブジェクト