内容
- 概述
- 改进库类
- 即时报价数据序列对象类
- 列表创建和数据检索测试
- 下一步是什么?
概述
在上一篇文章中,我开始创建操控即时报价数据的功能。 特别是,我创建了即时报价数据对象类。 在此,我将创建存储该类对象的列表。 这样的列表在程序中可用于所有用到的每个品种。 默认情况下,品种的即时报价数据列表尺寸会覆盖当天数据量。 自然地,也可以在程序里设置所需天数的即时报价数据的集合。
既然 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 函数库中的价格(第五十九部分):存储一个即时报价数据的对象