
DoEasyライブラリでの価格(第60部): 銘柄ティックデータのシリーズリスト
内容
概念
前回の記事で、ティックデータを使用するための機能の作成を開始しました。特に、ティックデータオブジェクトのクラスを作成しました。ここでは、そのようなオブジェクトを格納するためのリストを作成します。このようなリストは、プログラムで使用される各銘柄で利用できます。デフォルトでは、銘柄のティックデータリストのサイズは当日を対象としますが、当然、プログラムが一連のティックデータを持つ日数を設定することは可能です。
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部): 単一ティックのデータを格納するオブジェクト
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/8912
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
このDoEasyシリーズは興味深かった。残念ながら、私たちはお互いを知らない。あなたはユーモアを期待している。でも何のために?
車輪の再発明はできない!そしてこの「DoEasy」シリーズの作者は、まさにそれをやりたがっている。デタラメはメタ・トレード・エンジンの「列挙」を「カプセル化」することから始まる。私の意見では、ここではリストとカプセル化について多くの時間が浪費されている。通常のOOPはリストに入るのではなく、アルゴリズムに入る!それがここでは完全に欠落している。著者は管理しやすいテキストについて常に語っているが、そうではない!ボリュームには価値があるのが普通だ。しかし、私にはない。
例題はデバッグ・モードでしか利用できない--つまり見るためだけのものだ。だから、今のところ時間の無駄だ。
今、60巻まで来た!これでは何の価値もない。たとえ著者がそのようなものを提示したとしても。某ソフトウェア事務所がリストの作成に追われているという事実を除けば、どこに内容があるのだろう?
視点は重要ではない。サーバーのデータは毎分公開されている(CopyRates!!)。そこにすべてがある。ブローカーにとっては数分しかない。
私は批判を承知している。非常に少ないアルゴリズムが多くのテキストに詰め込まれている!そして、「C++が "表現として最良の方法 "であるかどうか」議論されることがあります!
そして私は、車輪を再発明する必要はないということを伝えたかったのです!
mfG
マルク・トルクミット
このDoEasyシリーズは興味深かった。残念ながら、私たちはお互いを知らない。あなたはユーモアを期待している。でも何のために?
車輪の再発明はできない!そしてこの「DoEasy」シリーズの著者は、まさにそれをやりたがっている。デタラメはメタ・トレード・エンジンの「列挙」を「カプセル化」することから始まる。私の意見では、ここではリストとカプセル化について多くの時間が浪費されている。通常のOOPはリストに入るのではなく、アルゴリズムに入る!それがここでは完全に欠落している。著者は管理しやすいテキストについて常に語っているが、そうではない!ボリュームには価値があるのが普通だ。しかし、私にはそれがない。
例題はデバッグ・モードでしか利用できない--つまり見るためだけのものだ。従って、ここまでは時間のロスである。
現在60巻まで来ている!これでは何の価値もない。たとえ著者がそのようなものを提示したとしても。某ソフトウェア事務所がリストの作成に追われているという事実を除けば、どこに内容があるのだろうか?
視点は重要ではない。サーバーのデータは毎分公開されている(CopyRates!!)。そこにすべてがある。ブローカーにとっては数分しかない。
私は批判を承知している。非常に少ないアルゴリズムが多くのテキストに詰め込まれている!そして、「C++が "表現として最良の方法 "であるかどうか」は議論される可能性がある!
そして、車輪を再発明する必要はないということを伝えたかった!
mfG
マーク・トルクミット
口調には注意と敬意を払ってください。作者はターミナルの動作について多くの知識を持っています。データは分単位ではなくティック単位でサーバーから送られてきます。したがって、誰かがティックを配列で最新に保つことは理にかなっている。
さらに、独自の取引を監視するためのグラフィカル・オブジェクトの作成に関する部分もある。興味のある人は、重要なパーツをコピーすることができる。
このDoEasyシリーズは興味深かった。残念ながら、私たちはお互いを知らない。ユーモアを期待してしまう。でも何のために?
車輪の再発明はできない!そしてこの「DoEasy」シリーズの著者は、まさにそれをやりたがっている。デタラメはメタ・トレード・エンジンの「列挙」を「カプセル化」することから始まる。私の意見では、ここではリストとカプセル化について多くの時間が浪費されている。通常のOOPはリストに入るのではなく、アルゴリズムに入る!それがここでは完全に欠落している。著者は管理しやすいテキストについて常に語っているが、そうではない!ボリュームには価値があるのが普通だ。しかし、私にはそれがない。
例題はデバッグ・モードでしか利用できない--つまり見るためだけのものだ。従って、ここまでは時間のロスである。
現在60巻まで来ている!これでは何の価値もない。たとえ著者がそのようなものを提示したとしても。某ソフトウェア事務所がリストの作成に追われているという事実を除けば、どこに内容があるのだろうか?
サーバーのデータは分単位のサイクルの中にある。そこにすべてがある。ブローカーには数分しかなく、その間にすべての顧客が招待され、サービスを受ける。
私は批判を承知している。非常に少ないアルゴリズムが多くのテキストに詰め込まれている!そして、「C++が "表現として最良の方法 "であるかどうか」は議論される可能性がある!
そして、車輪を再発明する必要はないということを伝えたかった!
mfG
マーク・トルクミット
フィードバックをありがとう。
今のところ、記事はライブラリの開発プロセスについて語っています。ライブラリは、ターミナルを操作するための様々な側面をカバーするものでなければならないので、小さなものではありません。様々なライブラリオブジェクトのプロパティは、MQL5言語の関数のパラメータの列挙を繰り返しており、これは自然なことですが、新しいものがプロパティに追加されています。これは、これらのプロパティでオブジェクトを正しくソートするために必要です。ソートは、特定のプロパティでオブジェクトをすばやく見つけるために必要です。もちろん、標準的なインデックスから各プロパティのインデックスを数学的に計算することもできますが、オブジェクトのプロパティとして独自の列挙を使用する方がはるかに明確で、柔軟性があり、ミスが少なくなります。
オブジェクト・リストは、そのプロパティに素早くアクセスするために必要であり、ロジックやアルゴリズムを作成するための基礎となります。近い将来、グラフィック・オブジェクトの大きなセクションが計画されており、あなたのプログラムで独自のグラフィック・インターフェースを作成するために使用できる、すでに作成されたすべての利用可能なライブラリ・オブジェクトが装備される予定です。完全にインタラクティブ。
完全に完成したオブジェクト・ベースから様々なアルゴリズムを作成することは、ライブラリ記述の最後の段階で計画されています。
ハンドルとペダルだけが作られた「車を運転する」のは奇妙なことでしょう。
---------
Спасибо за отзыв.
На данный момент в статьях рассказывается о процессе разработки библиотеки.Библиотека должна охватывать разные аспекты работы с терминалом, поэтому она не маленькая.MQL5では、MQL5を使用することで、MQL5を使用することができます。нужноのдля правильной сортировки объектов по этим свойствам.А сортировка нужна для быстрого поиска объекта по заданному свойству.Можно конечно использовать математические вычисления индексов каждого свойства от стандартных, но куда более наглядно、более гибко и менеее ошибочно использовать собственные перечисления в качестве свойств объектов.
このような場合、объектов нужны для быстрого доступа к их свойствам, и являются базой для создания логики и алгоритмов.В ближайшее время планируется большой раздел по графическим объектам, которыми будут наделены все доступные уже созданные объекты библиотеки、このような場合、このような邦訳が必要です。Полностью интерактивные.
Боданиениеのразличных алгоритмов из полностью готовой базы объектов запланировано на самый последний этап описания библиотеки .
Было бы странно "управлять автомобилем", для которого сделано только рулевое колесо и педали.