
让新闻交易轻松上手(第4部分):性能增强
概述
在上一篇文章中,我们探讨了基于新闻事件的影响实施交易的过程。我们在这一任务中取得了成功,但其回测速度相对较慢,是这文章中最后一段代码的一个显著缺点。这主要是因为在回测策略时,频繁地访问内存中的数据库。为了解决该问题,我们将减少在回测过程中访问数据库的次数。我们将从内存中的数据库获取当前日所需的所有信息,这意味着在理想情况下每天只访问一次数据库。
另一种将用来提高性能的方法是根据小时对新闻事件进行聚类,这意味着对于一天中的每一小时,我们将会有一个数组,只存储特定小时的事件信息。当需要当前小时的事件信息(如果有)时,我们将使用switch语句来访问与当前时间相关小时的事件信息数组。这些方法将显著减少EA的运行时间,尤其是在特定的一天或一小时内有许多新闻事件发生时。在本文中,我们将编写构成这些解决方案的基础代码模块,以便在后续文章中使用,从而避免只写出一篇冗长的文章。
时间变量类
之前,此类被用于声明一个固定大小为2000个索引的数组,用来存储K线的时间,并将在K线属性类中检查存储的K线时间是否等于当前K线时间,以确定是否形成了新的K线图。这一次,我们将扩展这个类,声明时间的枚举值,并添加函数,将整数值转换为声明的枚举值之一,以便在包含这个类的其他类中有一个可控的方法来处理秒、分钟和小时。
该类的结构如下:
//+------------------------------------------------------------------+ //| NewsTrading | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/kaaiblo | //+------------------------------------------------------------------+ //--- Enumeration For Hours in a Day enum HOURLY { H1=1,//01 H2=2,//02 H3=3,//03 H4=4,//04 H5=5,//05 H6=6,//06 H7=7,//07 H8=8,//08 H9=9,//09 H10=10,//10 H11=11,//11 H12=12,//12 H13=13,//13 H14=14,//14 H15=15,//15 H16=16,//16 H17=17,//17 H18=18,//18 H19=19,//19 H20=20,//20 H21=21,//21 H22=22,//22 H23=23,//23 H24=0//00 }; //--- Enumeration For Minutes in an Hour enum MINUTELY { M0,//00 M1,//01 M2,//02 M3,//03 M4,//04 M5,//05 M6,//06 M7,//07 M8,//08 M9,//09 M10,//10 M11,//11 M12,//12 M13,//13 M14,//14 M15,//15 M16,//16 M17,//17 M18,//18 M19,//19 M20,//20 M21,//21 M22,//22 M23,//23 M24,//24 M25,//25 M26,//26 M27,//27 M28,//28 M29,//29 M30,//30 M31,//31 M32,//32 M33,//33 M34,//34 M35,//35 M36,//36 M37,//37 M38,//38 M39,//39 M40,//40 M41,//41 M42,//42 M43,//43 M44,//44 M45,//45 M46,//46 M47,//47 M48,//48 M49,//49 M50,//50 M51,//51 M52,//52 M53,//53 M54,//54 M55,//55 M56,//56 M57,//57 M58,//58 M59//59 }; //--- Enumeration For Seconds Pre-event datetime enum PRESECONDLY { Pre_S30=30//30 }; //--- Enumeration For Seconds in a Minute enum SECONDLY { S0,//00 S1,//01 S2,//02 S3,//03 S4,//04 S5,//05 S6,//06 S7,//07 S8,//08 S9,//09 S10,//10 S11,//11 S12,//12 S13,//13 S14,//14 S15,//15 S16,//16 S17,//17 S18,//18 S19,//19 S20,//20 S21,//21 S22,//22 S23,//23 S24,//24 S25,//25 S26,//26 S27,//27 S28,//28 S29,//29 S30,//30 S31,//31 S32,//32 S33,//33 S34,//34 S35,//35 S36,//36 S37,//37 S38,//38 S39,//39 S40,//40 S41,//41 S42,//42 S43,//43 S44,//44 S45,//45 S46,//46 S47,//47 S48,//48 S49,//49 S50,//50 S51,//51 S52,//52 S53,//53 S54,//54 S55,//55 S56,//56 S57,//57 S58,//58 S59//59 }; //+------------------------------------------------------------------+ //|TimeVariables class | //+------------------------------------------------------------------+ class CTimeVariables { private: //--- Array to store candlestick times datetime CandleTime[2000]; public: CTimeVariables(void); //--- Set datetime value for an array index void SetTime(uint index,datetime time); //--- Get datetime value for an array index datetime GetTime(uint index); //--- Convert Integer to the Enumeration HOURLY HOURLY Hourly(uint Hour); //--- Convert Integer to the Enumeration MINUTELY MINUTELY Minutely(uint Minute); //--- Convert Integer to the Enumeration SECONDLY SECONDLY Secondly(uint Second); }; //+------------------------------------------------------------------+ //|Constructor | //+------------------------------------------------------------------+ CTimeVariables::CTimeVariables() { //--- Set default datetime values for all indexes in array CandleTime for(uint i=0; i<CandleTime.Size(); i++) { CandleTime[i]=D'1970.01.01'; } } //+------------------------------------------------------------------+ //|Set datetime value for an array index in array CandleTime | //+------------------------------------------------------------------+ void CTimeVariables::SetTime(uint index,datetime time) { if(index>=0&&index<CandleTime.Size()) { CandleTime[index] = time; } } //+------------------------------------------------------------------+ //|Get the datetime value for an array index in array CandleTime | //+------------------------------------------------------------------+ datetime CTimeVariables::GetTime(uint index) { return (index>=0&&index<CandleTime.Size())?CandleTime[index]:datetime(0); } //+------------------------------------------------------------------+ //|Convert Integer to the Enumeration HOURLY | //+------------------------------------------------------------------+ HOURLY CTimeVariables::Hourly(uint Hour) { return (Hour>23)?HOURLY(0):HOURLY(Hour); } //+------------------------------------------------------------------+ //|Convert Integer to the Enumeration MINUTELY | //+------------------------------------------------------------------+ MINUTELY CTimeVariables::Minutely(uint Minute) { return (Minute>59)?MINUTELY(0):MINUTELY(Minute); } //+------------------------------------------------------------------+ //|Convert Integer to the Enumeration SECONDLY | //+------------------------------------------------------------------+ SECONDLY CTimeVariables::Secondly(uint Second) { return (Second>59)?SECONDLY(0):SECONDLY(Second); } //+------------------------------------------------------------------+
以下代码定义了一个名为HOURLY的枚举类型。枚举(enum)是一种用户自定义的数据类型,它由一组命名的整数常量组成。当您想要用有意义的名称来表示一组特定的值,从而使代码更易于阅读时,通常会使用枚举。
enum HOURLY { H1=1, // Represents Hour 01 H2=2, // Represents Hour 02 H3=3, // Represents Hour 03 H4=4, // Represents Hour 04 H5=5, // Represents Hour 05 H6=6, // Represents Hour 06 H7=7, // Represents Hour 07 H8=8, // Represents Hour 08 H9=9, // Represents Hour 09 H10=10, // Represents Hour 10 H11=11, // Represents Hour 11 H12=12, // Represents Hour 12 H13=13, // Represents Hour 13 H14=14, // Represents Hour 14 H15=15, // Represents Hour 15 H16=16, // Represents Hour 16 H17=17, // Represents Hour 17 H18=18, // Represents Hour 18 H19=19, // Represents Hour 19 H20=20, // Represents Hour 20 H21=21, // Represents Hour 21 H22=22, // Represents Hour 22 H23=23, // Represents Hour 23 H24=0 // Represents Hour 00 (Midnight) };
值设定:
枚举中的每个值都对应一天中的特定小时,从第一个小时的 H1=1 开始,接着是 H2=2,依此类推,直到第23个小时的 H23=23。H24=0 用于表示午夜(00:00小时)。在代码中,您可以使用像H1、H2这样的名称,这样在处理与时间相关的数据时,代码会更加直观易懂。相比于直接使用小时的原始数字,我更倾向于使用HOURLY枚举。
使用示例:
HOURLY current_hour = H10; // Setting the current hour to 10:00 AM if (current_hour == H10) { Print("The current hour is 10:00 AM"); }
在以上示例中,current_hour被赋值为H10,之后代码检查当前小时是否为H10,并据此打印一条消息。这样做使得代码更易于阅读和理解,特别是在处理大量基于时间的操作时。
分钟的枚举 — MINUTELY。
该枚举定义了每小时中每一分钟的常量,从 M0(00)到 M59(59),因为每小时有60分钟。
enum MINUTELY
{
M0, M1, M2, M3, ..., M59
};
我们不再使用原始的整数(例如,0、1、2),而是使用有意义的标签,如M0、M1等。
例如:
- M0代表第00分钟。
- M30代表第30分钟。
事件前秒数的枚举 — PRESECONDLY。
该枚举定义了相对于事件前时间的秒数。这里,Pre_S30专门用于表示新闻事件发生前30秒。它将作为一个固定值,用于提前进入新闻事件。例如,如果有一个新闻事件在下午14:00发生,我们只会在下午13:59:30考虑进入交易。这意味着之前在事件前5秒进入交易的选项将不可用。
我所做的这个更改有利有弊。
主要弊端:
- 定制化程度降低:这是一个缺点,因为用户/交易者可能只想在事件前5秒进入交易,由于在高影响力事件发生前偶尔会出现波动,这意味着您的止损可能仅仅基于在事件发生前进入交易的早晚而被触发。因此,在新闻交易中,几秒钟的时间就可能对您的盈利水平产生巨大的影响。
主要优势:
- 固定时间表:这是一个优点,因为我们可以设置EA只在30秒的间隔内检查是否进入交易,这将极大地提高回测速度。这样做之所以能减少回测运行时间,仅仅是因为我们不需要那么频繁地使用计算机的资源。如果我们想在事件前5秒进入交易,EA就必须每5秒或1秒检查一次是否是进入交易的正确时间,这会消耗计算机的资源,使得回测运行得更慢。
另一个需要考虑的因素是流动性。随着我们接近高影响的新闻事件,受影响的资产变得越缺乏流动性。这就意味着在此情况下进入交易通常不那么有利。
以下这些因素使得在流动性不足的资产/市场中进入交易变得不利:
- 滑点:资产或交易品种的价格变化极为频繁,以至于用户/交易者希望进入/退出市场的价格不再可用,而使用了一个不那么有利/不如预期的价格。不可预测性是滑点的主要缺点。
- 点差:点差往往更大,这限制了交易者的潜在盈利能力。
- 报价失效/不可用:交易可能会完全终止,这意味着交易者失去了从资产/市场的潜在有利变动中获益的能力。
在新闻事件前30秒进入交易,与在新闻事件前5秒进入或在30秒后进入相比,我们更有可能避免流动性不足。
enum PRESECONDLY { Pre_S30 = 30 // 30 seconds before an event };
秒的枚举 — SECONDLY。
与MINUTELY类似,该枚举代表每分钟的每一秒,从S0(00秒)到S59(59秒)。
enum SECONDLY
{
S0, S1, S2, ..., S59
};
例如:
- S0代表00秒。
- S30代表30秒。
以下函数将整数(小时)转换为对应的HOURLY枚举值。它确保传递的值(在Hour变量中)是有效的(在0到23之间),之后返回对应的HOURLY枚举值。
- 返回类型:HOURLY — 意味着函数返回一个HOURLY枚举值,这是我们之前讨论过的。该枚举包含一天24小时对应的值,其中H1=1,H2=2,...,H23=23,H24=0。
- 函数名称:Hourly。属于CTimeVariables类的一个方法,由作用域解析运算符(::)表示。因此,它是CTimeVariables类的一部分。
- 参数:
- uint Hour:这是一个无符号整数,表示小时,作为参数传递给函数。
HOURLY CTimeVariables::Hourly(uint Hour) { return (Hour>23)?HOURLY(0):HOURLY(Hour); }
函数内的逻辑:
这行代码使用了三元运算符(?:),它是if-else语句的简写。三元运算符检查一个条件,并根据条件的真假返回两个值中的一个。
条件:(Hour > 23)
- 其检查Hour值是否超过23。由于有效小时数的范围是从0到23(一天有24小时),任何大于23的值都是无效的。
- 如果Hour > 23(无效小时),函数将返回HOURLY(0),这对应于H24=0(午夜或00:00)。
如果为假:HOURLY(Hour)
- 如果Hour在有效范围内(0到23),它将Hour整数转换为其对应的HOURLY枚举值。例如,如果Hour = 10,它返回HOURLY(10),这对应于H10。
return (Hour>23)?HOURLY(0):HOURLY(Hour);
例如:
- 如果您调用Hourly(10),函数将返回HOURLY(10)(这是上午10:00的枚举值)。
- 如果您调用Hourly(25),由于25不是一个有效的小时数,函数将返回HOURLY(0)(这对应于00:00或午夜)。
要点:
- 处理无效小时:该函数确保如果Hour值超出有效范围(大于23),则默认为HOURLY(0),相当于午夜。
- 转换:该函数高效地将整数小时转换为HOURLY枚举值,使其更容易用在基于时间的逻辑中。
将整数转换为MINUTELY的实用函数。
该函数将整数(从0到59)转换为MINUTELY枚举值。如果输入的整数超过59(无效的分钟数),函数默认为M0(第0分钟)。
MINUTELY CTimeVariables::Minutely(uint Minute) { return (Minute>59)?MINUTELY(0):MINUTELY(Minute); }
如果分钟数超过59,则会回退并返回M0以防止出错。
将整数转换为SECONDLY的实用函数。
这个函数执行类似的秒数转换任务。它将整数(从0到59)转换为SECONDLY枚举值。如果整数超过59,则默认为S0(第0秒)。
SECONDLY CTimeVariables::Secondly(uint Second) { return (Second>59)?SECONDLY(0):SECONDLY(Second); }
如果秒数超过59,则会返回S0以便正确地处理无效输入。
要点:
时间精度:TimeVariables类简化了交易系统中的时间管理,使得处理分钟和秒级别的精度变得更加容易,这对于新闻交易策略至关重要。
灵活性:通过使用枚举,开发人员可以轻松地根据新闻事件调整交易执行时间,而无需为每种情况手动编写时间。
实际应用:在新闻交易中,能够抓住由经济发布引起的市场波动等时间敏感的机会,可以显著提高交易表现。
预测与见解:
不同的市场条件将影响TimeVariables类的有效程度。例如:
- 在高波动性市场中,特别是在重大新闻事件期间(如非农就业报告或央行公告),精确到秒变得至关重要,因为大的价格变动可能在毫秒级发生。
- 在低波动性的条件下,使用分钟级别的精度可能就足以进行交易,因为价格变动通常较慢。
减少数据库访问
在上一篇文章中,我们使用内存中的数据库来存储事件信息,这将帮助我们的EA根据事件时间知晓何时开仓。访问数据库的过程如下所示。
- 在新的一天开始时,理想情况下我们会加载当天将要发生或已经发生的全部新闻事件。这些数据将被存储到一个结构体数组中,运行程序后将在图表上显示事件对象,显示事件的时间和名称等信息。
- 当事件发生或正在发生时,EA将再次访问内存中的数据库,以检索当天接下来发生的事件信息(如果有)。
上述过程并不是最优的,因为如果同一天有多个不同时间的事件,数据库将被频繁访问以获取接下来发生的事件信息。一种简单的解决方案是使用每天更新的同一个结构体数组,并遍历该数组,直到找到事件日期与当前日期匹配的事件,这样我们就可以在正确的时间开仓,而不会为了获取下一个事件信息而多次访问数据库。
这个简单的解决方案肯定会提高EA的运行速度,但在遍历同一个结构体数组以检查日期匹配时,会出现另一个问题,即我们可能会有已经发生的事件日期,检查这些多余的日期将限制EA的运行速度,同样,对于那些比当前日期晚得多的事件日期也是如此。
例如,假设我们有以下数组中的时间。
time[0] = '14:00' time[1] = '14:15' time[2] = '14:30' time[3] = '14:45' time[4] = '14:50' time[5] = '14:55' time[6] = '15:00' time[7] = '15:05' time[8] = '15:10' time[9] = '15:15' time[10] = '15:30' time[11] = '15:45' time[12] = '15:55'
如果当前时间是上午11点,那么检查下午2点的时间是否匹配是没有意义的,并且当事件时间仍然无法到达时,每次图表上出现新的tick都会不断地遍历这个数组,将进一步影响EA的运行速度。鉴于此,为了解决该问题,一个简单的方法是至少将数组按一天中的不同小时分开,EA只会遍历与当前时间相匹配的小时的数组。这样,EA将节省时间,只在事件时间至少与当前日期在同一小时内时才进行检查。
案例研究:
让我们考虑一个实际的例子,以突出这种优化如何提高性能:
- 场景:一位交易者使用EA监控美国的重大新闻发布。如非农就业报告(NFP),该报告安排在14:30发布。该EA根据新闻结果来设置买入止损或卖出止损订单。如果没有优化,EA在整个白天每秒都会持续访问数据库,检查下一个事件。到NFP发布时,由于太过频繁的数据库查询,EA可能会在对新闻做出反应时出现延迟,从而降低了它抓住最佳交易入场点的机会。
- 减少数据库访问的解决方案:而不是持续访问数据库,EA在00:00加载当天的所有事件,并按小时对事件进行分段。当时间接近14:00时,EA只检查14:00小时数组内的事件,并跳过此时间窗口之外的任何事件检查。当14:30到来时,EA已准备好立即对NFP发布做出反应,没有任何延迟,在正确的时间设置交易。
我们将创建一个名为TimeSeries的新文件夹,该文件夹将包含两个类,这两个类将为一天中的每个小时创建数组,并检索每小时的数组值。
按小时划分时间的类
以下代码定义了一个名为CTimeByHour的类,该类旨在管理和检索一天中每个小时的时间和事件数据。该类使用了几种组件,如结构体、数组以及面向对象编程(OOP)的概念。该类的目的是为一天中的每个小时创建数组对象,即24小时意味着我们将声明24个数组对象。这些数组对象将存储整数变量Hour、整数变量Minute以及日历结构变量myEData(此变量用来存储特定小时和分钟内发生的事件的所有信息)。声明的结构体TimeDate将存储日期的小时和分钟,其中数组myTimeData将与结构体数组myEvents并行存储数据。
//+------------------------------------------------------------------+ //| TimeByHour.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #include <Object.mqh> #include <Arrays\ArrayObj.mqh> #include "../TimeVariables.mqh" #include "../CommonVariables.mqh" #include "../TimeManagement.mqh" //--- Structure to store time data in Hour and Minute struct TimeDate { int Hour; int Minute; } myTimeData[]; //--- Structure array to store event data in parallel with myTimeData array Calendar myEvents[]; //+------------------------------------------------------------------+ //|TimeByHour class | //+------------------------------------------------------------------+ class CTimeByHour:public CObject { private: //--- classes' object declarations CTimeManagement CTime; CTimeVariables CTV; protected: //--- class constructor with parameters CTimeByHour(HOURLY myHour,MINUTELY myMinute,Calendar &myEventData): //--- Assign integer variables Hour and Minute with time data from myHour and myMinute respectively Hour(int(myHour)),Minute(int(myMinute)) { //--- Assign variable myEData with event info from variable myEventData myEData = myEventData; } virtual void myTime(Calendar &myNews[]); //--- Array object declarations for each hour of the day CArrayObj *myH1,*myH2,*myH3,...,*myH24; //--- Integer variables to store time data in hour and minute format int Hour; int Minute; //--- Calendar structure variable to store event info Calendar myEData; public: //--- class constructor without parameters CTimeByHour(void) { } //--- Array object variable CArrayObj *getmyTime; //--- Retrieve array object for an individual hour CObject *getTime(HOURLY myHour) { switch(myHour) { case H1: //--- retrieve array obj for 01 Hour return myH1; break; case H2: //--- retrieve array obj for 02 Hour return myH2; break; case H3: //--- retrieve array obj for 03 Hour return myH3; break; // ... default: //--- retrieve array obj for 24|00 Hour return myH24; break; } } //--- class pointer variable CTimeByHour *myClass; //--- class destructor ~CTimeByHour(void) { //--- delete all pointer variables delete getmyTime; delete myClass; delete myH1; delete myH2; delete myH3; // ... } //--- Function to retrieve timedata and calendar info for a specific hour of the day via parameters passed by reference void GetDataForHour(HOURLY myHour,TimeDate &TimeData[],Calendar &Events[]) { //--- Clean arrays ArrayRemove(TimeData,0,WHOLE_ARRAY); ArrayRemove(Events,0,WHOLE_ARRAY); //--- retrieve array object for the specific hour getmyTime = getTime(myHour); // Iterate through all the items in the list. for(int i=0; i<getmyTime.Total(); i++) { // Access class obj via array obj index myClass = getmyTime.At(i); //Re-adjust arrays' sizes ArrayResize(TimeData,i+1,i+2); ArrayResize(Events,i+1,i+2); //--- Assign values to arrays' index TimeData[i].Hour = myClass.Hour; TimeData[i].Minute = myClass.Minute; Events[i] = myClass.myEData; } } }; //+------------------------------------------------------------------+
结构:
- TimeDate:一个用于存储时间数据的结构体,其中包含整型字段Hour(小时)和Minute(分钟)。
- myTimeData[]:一个TimeDate结构体数组,用于存储多个小时的时间数据。
struct TimeDate { int Hour; int Minute; } myTimeData[];
- myEvents[]:一个Calendar类型的数组,旨在与myTimeData并行存储事件数据。
Calendar myEvents[];
CTimeByHour类声明:
- CTimeByHour:一个扩展自CObject的类。该类按小时管理时间和事件数据。
class CTimeByHour:public CObject
私有成员:
- CTimeManagement和CTimeVariables:这些是从TimeManagement.mqh和TimeVariables.mqh中包含的自定义类(CTimeManagement、CTimeVariables)的对象,用于管理与时间相关的数据和变量。
private:
CTimeManagement CTime;
CTimeVariables CTV;
构造函数:
- 这是该类的参数化构造函数。使用HOURLY和MINUTELY枚举初始化两个整数变量(Hour和Minute),并将事件信息(myEventData)分配给myEData。
protected: CTimeByHour(HOURLY myHour, MINUTELY myMinute, Calendar &myEventData): Hour(int(myHour)), Minute(int(myMinute)) { myEData = myEventData; }
数据成员:
- myH1...myH24:指向CArrayObj对象的指针,每个指针对应一天中的一个特定小时(01到24)。每个CArrayObj都包含一个特定小时的对象数组。
- Hour和Minute:用于存储时间的整数变量。
- myEData:一个Calendar对象,用于存储事件信息。
CArrayObj *myH1, *myH2, ..., *myH24; int Hour; int Minute; Calendar myEData;
公有方法:
- CTimeByHour(void):一个默认构造函数,不初始化任何内容。
- getmyTime:一个指向数组对象的指针,该数组对象包含特定小时的时间数据。
public: CTimeByHour(void) {} CArrayObj *getmyTime;
获取小时的数组对象:
- getTime(HOURLY myHour):一个使用switch语句的方法,根据HOURLY枚举获取一天中特定小时的CArrayObj对象。每个case对应一个小时(例如,H1、H2……H24)。
CObject *getTime(HOURLY myHour) { switch(myHour) { case H1: return myH1; case H2: return myH2; ... case H24: return myH24; } }
析构函数:
- ~CTimeByHour(void):析构函数通过调用delete释放CArrayObj指针(myH1...myH24)以及其他类指针所分配的动态内存。
~CTimeByHour(void) { delete getmyTime; delete myClass; delete myH1, myH2, ..., myH24; }
获取特定小时的数据:
- GetDataForHour:该方法获取特定小时(myHour)的时间和事件数据。
- ArrayRemove:清空数组(TimeData[]、Events[])。
- getmyTime = getTime(myHour):获取指定小时的数组对象。
- for循环:遍历检索到的CArrayObj中的所有条目(即每条记录的时间和事件数据)。
- ArrayResize:动态调整数组(TimeData[]、Events[])的大小以适应新数据。
- myClass:当前正在处理的数组中的对象。
对于每个对象,该方法将Hour、Minute和myEData分配到TimeData[]和Events[]数组中对应的索引位置。
void GetDataForHour(HOURLY myHour, TimeDate &TimeData[], Calendar &Events[]) { ArrayRemove(TimeData, 0, WHOLE_ARRAY); ArrayRemove(Events, 0, WHOLE_ARRAY); getmyTime = getTime(myHour); for(int i = 0; i < getmyTime.Total(); i++) { myClass = getmyTime.At(i); ArrayResize(TimeData, i + 1); ArrayResize(Events, i + 1); TimeData[i].Hour = myClass.Hour; TimeData[i].Minute = myClass.Minute; Events[i] = myClass.myEData; } }
按天划分时间的类
这个类将负责为在TimeByHour头文件中声明的数组对象分配值,以及检索这些值,并对存储在相关数组对象中的特定小时和分钟进行排序。代码首先导入其他文件:TimeByHour.mqh,用于处理小时级别的时间数据,以及CommonVariables.mqh,包含共享的常量和变量。CTimeByDay类继承自CTimeByHour。该类按天处理时间数据,并允许与CTimeByHour管理的特定小时的时间数据进行交互。
//+------------------------------------------------------------------+ //| TimeByDay.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #include "TimeByHour.mqh" #include "../CommonVariables.mqh" //+------------------------------------------------------------------+ //|TimeByDay class | //+------------------------------------------------------------------+ class CTimeByDay:private CTimeByHour { private: //--- Function to clear all array objects void Clear(); //--- Function to clean array dates in accordance to the current minute void DatePerMinute(TimeDate &TData[],Calendar &EData[],MINUTELY min,TimeDate &TimeData[],Calendar &EventData[]) { //--- Iterate through all the idexes in TData array for(uint i=0;i<TData.Size();i++) { //--- Check if Minutes match if(TData[i].Minute==int(min)) { //--- Resize arrays ArrayResize(TimeData,TimeData.Size()+1,TimeData.Size()+2); ArrayResize(EventData,EventData.Size()+1,EventData.Size()+2); //--- Assign data from each array to the other TimeData[TimeData.Size()-1] = TData[i]; EventData[EventData.Size()-1] = EData[i]; } } } public: //--- Function to set time for array objects based on calendar array myNews void SetmyTime(Calendar &myNews[]) { //--- Clear previous data stored in array objects Clear(); //--- clean arrays in parallel declared in TimeByHour header file ArrayRemove(myTimeData,0,WHOLE_ARRAY); ArrayRemove(myEvents,0,WHOLE_ARRAY); //--- Set new values to each array object accordingly myTime(myNews); } //--- Function to get time for the specific hour and minute for news events void GetmyTime(HOURLY myHour,MINUTELY myMinute,TimeDate &TimeData[],Calendar &Events[]) { //--- clean arrays in parallel declared in TimeByHour header file ArrayRemove(myTimeData,0,WHOLE_ARRAY); ArrayRemove(myEvents,0,WHOLE_ARRAY); //--- Declare temporary arrays to get news data for a specific hour TimeDate myTData[]; Calendar myData[]; //--- Get Data for the specific hour of the day GetDataForHour(myHour,myTData,myData); //--- Filter the Data for a specific Minute of the hour DatePerMinute(myTData,myData,myMinute,TimeData,Events); //--- Clear data from the temporary array variables ArrayRemove(myTData,0,WHOLE_ARRAY); ArrayRemove(myData,0,WHOLE_ARRAY); } public: //--- Class constructor CTimeByDay(void) { //--- Initialize array objects myH1 = new CArrayObj(); myH2 = new CArrayObj(); myH3 = new CArrayObj(); //... } //--- Class destructor ~CTimeByDay(void) { } }; //+------------------------------------------------------------------+ //|Add data to Array Objects for each Hour of the day | //+------------------------------------------------------------------+ void CTimeByHour::myTime(Calendar &myNews[]) { //--- Iterate through myNews calendar array for(uint i=0;i<myNews.Size();i++) { //--- Assign datetime from myNews calendar array datetime Date = datetime(myNews[i].EventDate); //--- Assign HOURLY Enumeration value from datetime variable Date HOURLY myHour = CTV.Hourly(CTime.ReturnHour(Date)); //--- Assign MINUTELY Enumeration value from datetime variable Date MINUTELY myMinute = CTV.Minutely(CTime.ReturnMinute(Date)); //--- Switch statement to identify each value scenario for myHour switch(myHour) { case H1: //--- add array obj values for 01 Hour myH1.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; case H2: //--- add array obj values for 02 Hour myH2.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; case H3: //--- add array obj values for 03 Hour myH3.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; //... default: //--- add array obj values for 24|00 Hour myH24.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; } } } //+------------------------------------------------------------------+ //|Clear Data in Array Objects | //+------------------------------------------------------------------+ void CTimeByDay::Clear(void) { //--- Empty all array objects myH1.Clear(); myH2.Clear(); myH3.Clear(); //... } //+------------------------------------------------------------------+
私有函数:
- 该函数用于清空(或重置)所有存储小时时间数据的数组对象。
void Clear();
以下函数过滤时间数据(TData[])和日历事件数据(EData[]),以保留与特定分钟(由min参数表示)匹配的条目。
- 其遍历TData[],对于每个元素,检查分钟是否与min匹配。如果匹配,将调整数组TimeData[]和EventData[]的大小,并复制TData[]和EData[]中的对应数据到两个数组中。
void DatePerMinute(TimeDate &TData[], Calendar &EData[], MINUTELY min, TimeDate &TimeData[], Calendar &EventData[]);
公有函数:
重置以下函数,并为一天分配新的时间和事件数据。使用CTimeByHour中的myTime方法,该方法根据传递给myNews[]的新闻事件在小时级别处理时间数据。
- 首先,它使用Clear()清空之前存储的时间和事件数据。
- 然后,它从并行数组(myTimeData和myEvents)中移除所有数据,并使用从CTimeByHour继承的myTime()函数设置新值。
void SetmyTime(Calendar &myNews[]);
以下函数检索特定小时(myHour)和分钟(myMinute)的时间数据。
- 它首先清空数组myTimeData和myEvents。
- 声明临时数组myTData[]和myData[]以存储时间和事件数据。
- 调用GetDataForHour(),用指定小时的数据填充临时数组。
- 使用DatePerMinute()进一步过滤特定分钟的数据。
void GetmyTime(HOURLY myHour, MINUTELY myMinute, TimeDate &TimeData[], Calendar &Events[]);
构造函数:
- 构造函数初始化一天中每小时(1到24)的数组对象。这些数组对象将存储特定小时的时间和事件数据。
CTimeByDay(void) { // Initialize array objects for each hour myH1 = new CArrayObj(); myH2 = new CArrayObj(); // (Initializes for all 24 hours) }
CTimeByHour::myTime()
该函数在CTimeByHour类中定义,并由CTimeByDay类继承。它用于处理myNews[]数组,并将事件与一天中的特定小时关联起来。
- 对于myNews[]中的每个事件,提取事件的小时和分钟。
- 使用switch语句来决定哪个小时的数组对象(例如,myH1、myH2等)应该存储时间和事件数据。
- 每个事件的时间作为一个CTimeByHour对象添加到对应的数组对象中。
for(uint i=0; i<myNews.Size(); i++) { datetime Date = datetime(myNews[i].EventDate); HOURLY myHour = CTV.Hourly(CTime.ReturnHour(Date)); MINUTELY myMinute = CTV.Minutely(CTime.ReturnMinute(Date)); // Switch case to handle different hours }
Clear() Function
- 该函数会清空所有小时级数组对象(如 myH1、myH2 等),实质上是重置为每个小时存储的时间数据。
void CTimeByDay::Clear(void) { // Clears all array objects myH1.Clear(); myH2.Clear(); // (Clears all 24 hours) }
结论
在本文中,我们展示了通过以下方法来提升EA性能的办法:将事件时间按一天中的每小时划分到不同的数组中,并减少对内存中日历数据库的访问频率。通过将时间值构造为枚举类型(如MINUTELY,SECONDLY),使得代码更容易管理和解读,从而降低了发生逻辑错误的风险。
枚举类型MINUTELY、SECONDLY和PRESECONDLY分别代表分钟、秒和事件前的时间,它们提供了更优的可读性和对时间间隔的控制。转换函数使得以整数作为输入变得简单,并确保其被转换为有意义的枚举值。CTimeByHour类提供了一种机制,用于存储、检索和管理一天中每小时的时间和事件数据。这些方法将在后续的文章中实现。
要点:
- 高效的数据库访问:每天仅加载一次事件并存储在内存中,从而减少了不必要的数据库查询。
- 时间分段:通过将事件划分到每小时的范围中,EA只需检查相关事件,从而提高了速度并降低了CPU负载。
- 可扩展性:所提出的方法对于事件数量较多的日子也具有可扩展性,确保在整个交易日内保持一致的性能。
- 提升响应速度:通过专注于相关时间框架内的事件,EA能够更快地对市场事件做出反应,这对于基于新闻的策略至关重要。
感谢您的阅读,我期待在下一篇文章中为您提供更多有价值的内容。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/15878




您好、
谢谢您的文章,您能帮我添加一个过滤器(filter.csv),其中只包含我想交易的新闻吗?
您好、
谢谢您的文章,您能帮我添加一个过滤器(filter.csv),其中只包含我想交易的新闻吗?
您好 Hamid Rabia,感谢您对本文的关注。新闻过滤这一主题将在接下来的文章中介绍,请您耐心等待。