
DoEasy 函数库中的时间序列(第六十部分):品种即时报价数据的序列列表
内容
概述
在上一篇文章中,我开始创建操控即时报价数据的功能。 特别是,我创建了即时报价数据对象类。 在此,我将创建存储该类对象的列表。 这样的列表在程序中可用于所有用到的每个品种。 默认情况下,品种的即时报价数据列表尺寸会覆盖当天数据量。 自然地,也可以在程序里设置所需天数的即时报价数据的集合。
既然 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; } //+------------------------------------------------------------------+
我已多次讲述过这种方法的操作。 在第三部分文章中可找到有关它们的更多信息。
即时报价数据序列对象类
现在,我们来编写即时报价数据的对象类列表。 就像函数库中的所有其他内容一样,列表将是基于指向 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); }; //+------------------------------------------------------------------+
之前准备的 “New tick” 和 “Tick data” 对象类列表也包含在该类中。 在下一篇文章中更新即时报价数据列表时,我们将需要 “New tick” 对象,而 “Tick data” 对象则是要被放置到列表中的对象类。
在类的私密部分中,声明所有必需的类成员变量。 这些变量用于存储对象参数的数值,对象列表类的对象(即时报价列表本身),以及下一篇文章中实时更新列表期间所需的 “New tick” 对象。
该类的公开部分含有标准的函数库对象操作方法,访问即时报价数据列表对象属性的方法,访问列表中指定即时报价数据对象属性的简化方法,以及创建和更新列表的方法。
我们来研究对象方法。
在类的参数型构造函数中,清除列表,依据即时报价时间(以毫秒为单位)为已排序列表设置标志,和所需的的天数,我们需要利用 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); } //+------------------------------------------------------------------+
依据传递给该方法的时间(以毫秒为单位),获取相符的即时报价对象列表,并返回它们的最后一个。
如同刚研究的方法中一样,不同的即时报价可能具有相同的时间,因此返回最后一个即时报价,因为只有它才被认为是最相关的。
后续获取即时报价对象的方法将彼此相同,并有三个重载方法 — 按索引、按时间和按毫秒时间获取列表。 我们来研究这三种获取即时报价价格的方法,并展示其他相同方法的清单。
该方法依据列表索引返回即时报价对象的出价(Bid):
//+------------------------------------------------------------------+ //| Return tick's Bid by index in the list | //+------------------------------------------------------------------+ double CTickSeries::Bid(const uint index) { CDataTick *tick=this.GetTickByListIndex(index); return(tick!=NULL ? tick.Bid() : 0); } //+------------------------------------------------------------------+
依据传递给方法的索引从列表中获取对象,并从获取的对象返回出价(Bid)或 0(如果无法获取该对象)
该方法以毫秒为单位返回列表中最后一个即时报价对象的出价(Bid):
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
依据传递给方法的时间(以毫秒为单位)从列表中获取对象,并从获取的对象返回出价(Bid)或 0(如果无法获取该对象)
该方法依据时间返回列表中最后一个即时报价对象的出价(Bid):
//+------------------------------------------------------------------+ //| Return tick's Bid by time | //+------------------------------------------------------------------+ double CTickSeries::Bid(const datetime time) { CDataTick *tick=this.GetTick(time); return(tick!=NULL ? tick.Bid() : 0); } //+------------------------------------------------------------------+
依据传递给方法的时间获取最后一个对象(上面已经研究过获取方法),然后从获取的对象返回出价(Bid),或返回 0(如果未能获取该对象)。
从列表中获取即时报价对象属性值的其余方法与上述三种方法相同。 将它们的分析留待独立研究:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
每种方法都由三个相同的重载方法表示,从而可以依据其在列表中的索引、时间(以日期格式和毫秒为单位)获取即时报价对象。
返回即时报价序列的字符串名称的方法:
//+------------------------------------------------------------------+ //| 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 结构格式的即时报价。 使用数组创建一个新的 tick 对象,若成功创建该对象则将其放置到列表之中。 循环完成后,返回放置到即时报价数据列表中的总数量。
至此即时报价数据列表的创建完毕。
列表创建和数据检索测试
出于测试目的,在程序启动期间,简单地基于当前品种、当日数据创建一个即时报价对象列表。 在得到的列表中,找到要加(Ask)最高和出价(Bid)最低的即时报价,并在日志中显示检测到的即时报价对象数据。 为此,借用上一篇文章中的 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)); } //+------------------------------------------------------------------+
创建当前品种当日即时报价序列,并在其中搜索两个所需即时报价对象,以便在日志中显示其数据的代码块已详细加以注释。 因此,我相信,在学习时不会出现任何问题。 如果您有任何疑问,请随时在下面的评论中提问。
该函数是从 OnInit() 处理函数计算得出的。 因此,启动该程序时会将一次性创建该列表。 立即在列表里查找含有当日最高要价(Ask)和出价(Bid)的两个即时报价数据对象。 显示数据需要一些时间。 如果本地没有可显示的即时报价数据,则会激活下载。
编译 EA,于任何品种的图表上启动它,并在设置中初步定义采用当前品种和当前时间帧。 当初始化 EA 时,将显示有关 EA 参数的数据,所创建时间序列的数据,以及(稍后)创建的即时报价序列上的数据。 下面显示的是找到的当日两个要价(Ask)最高和出价(Bid)最低的即时报价数据:
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 函数库中的时间序列(第三十五部分):柱线对象和品种时间序列列表
DoEasy 函数库中的时间序列(第三十六部分):所有用到的品种周期的时间序列对象
DoEasy 函数库中的时间序列(第三十七部分):时间序列集合 - 按品种和周期的时间序列数据库
DoEasy 函数库中的时间序列(第三十八部分):时间序列集合 - 实时更新以及从程序访问数据
DoEasy 函数库中的时间序列(第三十九部分):基于函数库的指标 - 准备数据和时间序列事件
DoEasy 函数库中的时间序列(第四十部分):基于函数库的指标 - 实时刷新数据
DoEasy 函数库中的时间序列(第四十一部分):多品种多周期指标样品
DoEasy 函数库中的时间序列(第四十二部分):抽象指标缓冲区对象类
DoEasy 函数库中的时间序列(第四十三部分):指标缓冲区对象类
DoEasy 函数库中的时间序列(第四十四部分):指标缓冲区对象集合类
DoEasy 函数库中的时间序列(第四十五部分):多周期指标缓冲区
DoEasy 函数库中的时间序列(第四十六部分):多周期、多品种指标缓冲区
DoEasy 函数库中的时间序列(第四十七部分):多周期、多品种标准指标
DoEasy 函数库中的时间序列(第四十八部分):在单一子窗口里基于一个缓冲区的多周期、多品种指标
DoEasy 函数库中的时间序列(第四十九部分):多周期、多品种、多缓冲区标准指标
DoEasy 函数库中的时间序列(第五十部分):多周期、多品种带位移的标准指标
DoEasy 函数库中的时间序列(第五十一部分):复合多周期、多品种标准指标
DoEasy 函数库中的时间序列(第五十二部分):多周期、多品种单缓冲区标准指标的跨平台性质
DoEasy 函数库中的时间序列(第五十三部分):抽象基准指标类
DoEasy 函数库中的时间序列(第五十四部分):抽象基准指标类的衍生类
DoEasy 函数库中的时间序列(第五十五部分):指标集合类
DoEasy 函数库中的时间序列(第五十六部分):自定义指标对象,从集合中的指标对象获取数据
DoEasy 函数库中的时间序列(第五十七·部分):指标缓冲区数据对象
DoEasy 函数库中的时间序列(第五十八部分):指标缓冲区数据的时间序列
DoEasy 函数库中的价格(第五十九部分):存储一个即时报价数据的对象
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/8912
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。




我觉得这个 DoEasy 系列很有趣。不幸的是,我们互不相识。你期待幽默。但为了什么?
你不能重新发明轮子!而 "DoEasy "系列的作者就想这么做。废话从 Meta Trade 引擎的 "封装""枚举 "开始。在我看来,很多时间都浪费在了列表和封装上。正常的 OOP 并不涉及列表,而是涉及算法!这里完全没有这一点。作者一直在谈论可管理的文本,但事实并非如此!正常情况下,"THE volume "应该有一个值。但我没有。
示例只能在调试模式下使用,因此只能用于查看。所以到目前为止,这都是在浪费时间。
我现在已经读到第 60 卷了!即使作者介绍了这样的内容,也没有任何价值。除了一个软件办公室忙着生成列表的事实之外,内容在哪里?
视角并不重要--服务器数据每时每刻都在提供(CopyRates!!!)。这就是一切的归宿。对于经纪人来说,只有几分钟的时间--在此期间,所有客户都会被邀请/服务。
我知道我的批评。大量文字中包含的算法非常少!C++ 是否是 "最好的表达方式 "还有待商榷!
我想告诉您的是,不必重新发明轮子!
mfG
马克-托尔克米特
我觉得这个 DoEasy 系列很有趣。不幸的是,我们互不相识。你期待幽默。但为什么呢?
你不能重新发明轮子!而 "DoEasy "系列的作者就想这么做。废话从 Meta Trade 引擎的 "封装""枚举 "开始。在我看来,很多时间都浪费在了列表和封装上。正常的 OOP 并不涉及列表,而是涉及算法!这里完全没有这一点。作者一直在谈论可管理的文本,但事实并非如此!正常情况下,"THE volume "应该有一个值。但我没有。
示例只能在调试模式下使用,因此只能用于查看。因此,到目前为止,时间已经浪费了。
我现在已经读到第 60 卷了!这没有任何价值--即使作者提出了这样的东西。除了一个软件办公室一直在忙着生成列表之外,内容在哪里?
视角并不重要--服务器数据每时每刻都在提供(CopyRates!!!)。这就是一切的归宿。对于经纪人来说,只有几分钟的时间--在此期间,所有客户都会得到邀请/服务。
我知道我的批评。大量文字中包含的算法非常少!C++ 是否是 "最好的表达方式 "还有待商榷!
我想告诉大家的是,不必重新发明轮子!
mfG
马克-托克米特
请注意语气,尊重他人。作者对终端的行为非常了解。服务器上的数据是逐个刻度的,而不是逐分钟的。因此,在数组中保持 ticks 的更新是有意义的。
此外,部分内容涉及创建图形对象以监控自营交易。相关人员可以复制出重要的部分,而不必自己创建,以免出现各种错误。
我觉得这个 DoEasy 系列很有趣。不幸的是,我们互不相识。你期待幽默。但为什么呢?
你不能重新发明轮子!而 "DoEasy "系列的作者就想这么做。废话从 Meta Trade 引擎的 "封装""枚举 "开始。在我看来,很多时间都浪费在了列表和封装上。正常的 OOP 并不涉及列表,而是涉及算法!这里完全没有这一点。作者一直在谈论可管理的文本,但事实并非如此!正常情况下,"THE volume "应该有一个值。但我没有。
示例只能在调试模式下使用,因此只能用于查看。因此,到目前为止,时间已经浪费了。
我现在已经读到第 60 卷了!这没有任何价值--即使作者提出了这样的东西。除了一个软件办公室一直在忙着生成列表之外,内容在哪里?
视角并不重要--服务器数据处于分钟周期(CopyRates!!!)。一切都在这里进行。对于经纪人来说,只有几分钟的时间--在此期间,所有客户都被邀请/服务。
我知道我的批评。大量的文字中只包含了很少的算法!C++ 是否是 "最好的表达方式 "还有待商榷!
我想告诉大家的是,不必重新发明轮子!
mfG
马克-托克米特
感谢您的反馈。
目前,这些文章讨论的是程序库的开发过程。该库应涵盖终端工作的各个方面,因此规模并不小。各种库对象的属性重复了 MQL5 语言函数参数的枚举,这很自然,但属性中又添加了新的参数。要根据这些属性对对象进行正确排序,就必须这样做。要根据特定属性快速查找对象,必须进行排序。当然,您也可以使用标准指数对每个属性的指数进行数学计算,但使用自己的枚举作为对象的属性会更清晰、更灵活、更不容易出错。
对象列表是快速访问其属性所必需的,也是创建逻辑和算法的基础。在不久的将来,我们还计划推出一个关于图形对象的大型部分,其中将包含所有已创建的可用库对象,您可以利用它们在程序中创建自己的图形界面。完全交互式。
计划在库描述的最后阶段,从完全完成的对象库中创建各种算法。
只有方向盘和踏板的 "汽车 "是很奇怪的。
---------
Спасибо за отзыв.
在这里,你可以看到一个 "车轮 "和一个" ped ped",只有在这个 "车轮 "和" ped ped "中才会有转向轮和踏板。Библиотека должна охватывать разные аспекты работы с терминалом, поэтому она не маленькая.Свойства различных объектов библиотеки повторяют перечисления параметров функций языка MQL5, и это естественно, но к свойствам добавляются новые.在此基础上,我们将继续努力,为您提供更优质的服务。А сортировка нужна для быстрого поиска объекта по заданному свойству.Можно конечно использовать математические вычисления индексов каждого свойства от стандартных, но куда более наглядно、более гибко и менеее ошибочно использовать собственные перечисления в качестве свойств объектов.
在此基础上,我们还将继续努力,在全球范围内,为实现 "全球海洋观测系统 "的目标而不懈努力。В ближайшее время планируется большой раздел по графическим объектам, которыми будут наделены все доступные уже созданные объекты библиотеки、在这些地区,您还可以使用"......"、"......"、"...... "和"...... "等词语。Полностью интерактивные.
Создание различных алгоритмов из полностью готовой базы объектов запланировано на самый последний этап описания библиотеки .
在 "управлять автомобилем", для которого сделано только рулевое колесо и педали.