English Русский Español Deutsch 日本語 Português
preview
让新闻交易变得容易(第一部分):创建一个数据库

让新闻交易变得容易(第一部分):创建一个数据库

MetaTrader 5示例 |
914 1
Kabelo Frans Mampa
Kabelo Frans Mampa

简介

在这篇文章中,我们将学习如何创建一个数据库,用于存储来自MQL5经济数据日历的数据。这些数据将用于后续的文章中来交易新闻事件。我们还将探索如何执行基本的SQL查询,以从这个数据库中检索某些有组织的信息。整个过程将在MQL5 IDE中完成。

交 易员们密切关注新闻来源,以获取可能影响市场的信息。这包括地缘政治事件、企业利润公告、政治事件以及GDP增长或就业数据等经济报告。交易员迅速对重大 新闻发布做出反应,以从由此产生的市场变化中获利。根据对新闻的解读,可能需要买入或卖出相关资产。在本文中,我们将重点关注经济事件,因为它们可以通过 MQL5经济数据日历轻松获取。


为什么要创建一个数据库

数据库样表

数据库是一种以结构化方式存储和访问的数据集合。数据库能够高效地管理和存储海量数据,从而支持数据分析、存储和管理等各种活动。在MQL5中,我们使用 SQLite数据库,这些数据库由SQLite数据库引擎创建和管理。SQLite数据库的文件可以有任何扩展名,但通常它们是具 有.sqlite、.sqlite3或.db扩展名的单个文件。这些文件包含数据库中所有的数据和结构,包括表、触发器、索引和其他元数据。

数据库非常适合处理大型数据集,并简化了从特定日期或事件中检索数据的过程,无需复杂的循环。此外,在策略测试器中无法访问MQL5经济日历。因此,如果您想基于经济新闻测试您的策略,该怎么做呢?

答案是使用数据库,有关数据库的更多信息,请查看这个讲义更推荐查阅这篇 MQL5 文章。此外,我将添加一个优秀的 SQLite教程。


MQL财经日历中的货币

序号 品种 名称
 1. NZD 新西兰元
 2. EUR 欧元
 3. JPY 日元
 4. CAD 加元
 5. AUD 澳元
 6. CNY 人民币
 7. SGD 新加坡元
 8. BRL 巴西里拉
 9. MXN 墨西哥比索
 10. ZAR 南非兰特
 11. HKD 港币
 12. INR 印度卢比
 13. NOK 挪威克朗
 14. USD 美元
 15. GBP 英镑
 16. CHF 瑞士法郎
 17. KRW 韩元
 18. SEK 瑞典克朗 

上面的表格并没有特定的顺序。

MQL5财经日历上有几种货币,但其中一些通常并不常被经纪商使用,或者交易者很难接触到。例如,巴西雷亚尔和韩元就并不被广泛使用。即使你设法找到能交易这些非常规货币的经纪商,其点差通常也不利于交易。这是我的亲身经历。


创建一个数据库

Daylight Savings Concept

在我们创建数据库之前,首先必须考虑夏令时(Daylight Saving Time, DST)及其对我们的回测可能产生的影响。 夏令时 (DST) 是指在一年中较温暖的月份,通常是从春季到秋季,将时钟拨快一小时,以更好地利用日光并节约能源。

在实行夏令时的地区,春秋过渡期间当地时间可能会变化一小时。这会对金融市场开市和闭市的时间产生影响。例如,如果夏令时开始,时钟拨快一小时,市场可能会 在当地时间更早的时候开市。相反,当夏令时结束,时钟拨回一小时(正常时间),市场开市时间在当地时间看起来会更晚。我们还必须认识到,世界上不同的地区 可能会根据自己的季节来设定不同的夏令时。

例如美国的夏令时通常每年从三月的第二个星期日开始,到同年十一月的第一个星期日结束。欧洲国家也有自己的夏令时,通常每年从三月的最后一个星期日开始,到十月的最后一个星期日结束。澳大利亚是另一个实行夏令时的国家,其时间表 通常从十月的第一个星期日开始,到次年四月的第一个星期日结束。 

为了考虑夏令时调整,一些交易所和金融市场可能会修改他们的交易时间表或时区。交易所可能会发布修订后的交易时间以考虑当地时间的变化,或者根据他们实行的夏令时时间表调整他们的时区,以确保市场参与者的一致性和清晰性。

据我观察,例如,如果一个经纪商实行美国夏令时,那么在夏令时开始之前其所在时区可能是GMT+2,而当美国夏令时开始时,其时区将变为GMT+3。你不会 注意到在美国夏令时之前或期间,美国新闻发布与经纪商服务器时间之间的时间差异,因为它们会是同步的。所以,我们将使用美国非农就业报告(Non- Farm Payrolls, NFP)事件作为例子来说明。假设NFP在经纪商服务器时间的下午2点发布,在夏令时之前的时间将保持在下午2点,尽管时区已经变为GMT+3。

英国夏令时交易时段变化

另 一方面,如果英国就业新闻发布时间在英国和美国夏令时(DST)开始前是上午8点,而当美国夏令时开始时经纪商更改了时区,且英国就业新闻发布在英国(欧 盟)夏令时开始之前,那么在经纪商的时区上,英国就业新闻发布时间将是上午7点。当英国夏令时开始并与美国夏令时重叠时,英国就业新闻发布时间将恢复为上 午8点。 

我知道这很让人困惑,幸运的是,我们将把这些信息整合到我们的数据库中,以便我们准确记录事件发生的日期,供日后进 行回测。我们必须确定经纪商是使用美国夏令时、英国(欧盟)夏令时、澳大利亚夏令时,还是根本不使用夏令时。我们还将为每种夏令时类型创建财经日历表,这 样我们就可以更改用于教育目的的回测的时间表。 


实现

我们将实现三个夏令时类:

  • DaylightSavings_AU
  • DaylightSavings_UK
  • DaylightSavings_US
在本文中,我将只讨论其中一个(夏令时类型),因为它们彼此非常相似,而所有的(夏令时类型)都将包含在项目文件中。

class CDaylightSavings_AU:CObject
  {

private:

   CTimeManagement   Time;
                     CDaylightSavings_AU(datetime startdate,datetime enddate);
   CObject           *List() { return savings;}//Gets the list of Daylight savings time
   datetime          StartDate;
   datetime          EndDate;
   CArrayObj         *savings;
   CArrayObj         *getSavings;
   CDaylightSavings_AU      *dayLight;

public:

                     CDaylightSavings_AU(void);
                    ~CDaylightSavings_AU(void);
   bool              isDaylightSavings(datetime Date);//This function checks if a given date falls within Daylight Savings Time.
   bool              DaylightSavings(int Year,datetime &startDate,datetime &endDate);//Check if Daylight Savings Dates are available for a certain Year
   void              adjustDaylightSavings(datetime EventDate,string &AdjustedDate);//Will adjust the date's time zone depending on Daylight Savings
  };

我们创建如下的一个类

CDaylightSavings_AU

继承自

CObject

这个类将包括创建一个列表,用于存储从2007年开始的所有夏令时日期,因为2007年是MQL5经济日历中存储的最早日期。

CDaylightSavings_AU::CDaylightSavings_AU(void)
  {
   savings = new CArrayObj();
//Daylight savings dates to readjust dates in the database for accurate testing in the strategy tester
   savings.Add(new CDaylightSavings_AU(D'2006.10.29 03:00:00',D'2007.03.25 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2007.10.28 03:00:00',D'2008.04.06 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2008.10.05 03:00:00',D'2009.04.05 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2009.10.04 03:00:00',D'2010.04.04 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2010.10.03 03:00:00',D'2011.04.03 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2011.10.02 03:00:00',D'2012.04.01 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2012.10.07 03:00:00',D'2013.04.07 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2013.10.06 03:00:00',D'2014.04.06 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2014.10.05 03:00:00',D'2015.04.05 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2015.10.04 03:00:00',D'2016.04.03 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2016.10.02 03:00:00',D'2017.04.02 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2017.10.01 03:00:00',D'2018.04.01 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2018.10.07 03:00:00',D'2019.04.07 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2019.10.06 03:00:00',D'2020.04.05 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2020.10.04 03:00:00',D'2021.04.04 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2021.10.03 03:00:00',D'2022.04.03 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2022.10.02 03:00:00',D'2023.04.02 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2023.10.01 03:00:00',D'2024.04.07 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2024.10.06 03:00:00',D'2025.04.06 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2025.10.05 03:00:00',D'2026.04.05 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2026.10.04 03:00:00',D'2027.04.04 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2027.10.03 03:00:00',D'2028.04.02 02:00:00'));
   savings.Add(new CDaylightSavings_AU(D'2028.10.01 03:00:00',D'2029.04.01 02:00:00'));
  }

我们将使用bool函数

bool              isDaylightSavings(datetime Date);//This function checks if a given date falls within Daylight Savings Time.

确定一个日期参数是否在夏令时日期范围内

bool CDaylightSavings_AU::isDaylightSavings(datetime Date)
  {
// Initialize a list to store daylight savings periods.
   getSavings = List();
// Iterate through all the periods in the list.
   for(int i=0; i<getSavings.Total(); i++)
     {
      // Access the current daylight savings period.
      dayLight = getSavings.At(i);
      // Check if the given date is within the current daylight savings period.
      if(Time.DateIsInRange(dayLight.StartDate,dayLight.EndDate,Date))
        {
         // If yes, return true indicating it is daylight savings time.
         return true;
        }
     }
// If no period matches, return false indicating it is not daylight savings time.
   return false;
  }

我们还将用另一个bool函数

bool              DaylightSavings(int Year,datetime &startDate,datetime &endDate);//Adjusts time when it is daylight savings an hour behind if outside

为了确定某个年份是否存在于我们之前初始化的夏令时日期列表中,我们将把起始日期和结束日期添加到变量 startDate 和 endDate 中。

bool CDaylightSavings_US::DaylightSavings(int Year,datetime &startDate,datetime &endDate)
  {
  // Initialize a list to store daylight savings periods.
   getSavings = List();
   bool startDateDetected=false,endDateDetected=false;
// Iterate through all the periods in the list.
   for(int i=0; i<getSavings.Total(); i++)
     {
      dayLight = getSavings.At(i);
      if(Year==Time.ReturnYear(dayLight.StartDate))//Check if a certain year's date is available within the Daylight Savings start dates in the List
        {
         startDate = dayLight.StartDate;
         startDateDetected = true;
        }
      if(Year==Time.ReturnYear(dayLight.EndDate))//Check if a certain year's date is available within the Daylight Savings end dates in the List
        {
         endDate = dayLight.EndDate;
         endDateDetected = true;
        }
      if(startDateDetected&&endDateDetected)//Check if both Daylight Savings start and end dates are found for a certain Year
        {
         return true;
        }
     }

   startDate = D'1970.01.01 00:00:00';//Set a default start date if no Daylight Saving date is found
   endDate = D'1970.01.01 00:00:00';//Set a default end date if no Daylight Saving date is found
   return false;
  }

这个类中最终的公共函数

void              adjustDaylightSavings(datetime EventDate,string &AdjustedDate);//Will adjust the date's time -zone depending on Daylight Savings

我们将通过EventDate修改一个字符串变量AdjustedDate

void CDaylightSavings_AU::adjustDaylightSavings(datetime EventDate,string &AdjustedDate)
  {
   if(isDaylightSavings(TimeTradeServer()))//Check if the current trade server time is already within the Daylight Savings Period
     {
      if(isDaylightSavings(EventDate))//Checks if the event time is during daylight savings
        {
         AdjustedDate = TimeToString(EventDate);//storing normal event time
        }
      else
        {
         AdjustedDate = TimeToString((datetime)(EventDate-Time.HoursS()));//storing event time and removing an hour for DST
        }
     }
   else
     {
      if(isDaylightSavings(EventDate))//Checks if the event time is during daylight savings
        {
         AdjustedDate = TimeToString((datetime)(Time.HoursS()+EventDate));//storing event time and adding an hour for DST
        }
      else
        {
         AdjustedDate = TimeToString(EventDate);//storing normal event time
        }
     }
  }

我们现在将继续进行此项目中的下一个头文件,该文件专门用于时间操作。

CTimeManagement

本项目中的一个类

class CTimeManagement
  {

private:

   MqlDateTime       today;//private variable
   MqlDateTime       timeFormat;//private variable

public:

   bool              DateIsInRange(datetime FirstTime,datetime SecondTime,datetime compareTime);//Checks if a date is within two other dates
   bool              DateIsInRange(datetime Start,datetime End,datetime CompareStart,datetime CompareEnd);//Check if two dates(Start&End) are within CompareStart & CompareEnd
   bool              DateisToday(datetime TimeRepresented);//Checks if a date is within the current day
   int               SecondsS(int multiple=1);//Returns seconds
   int               MinutesS(int multiple=1);//Returns Minutes in seconds
   int               HoursS(int multiple=1);//Returns Hours in seconds
   int               DaysS(int multiple=1);//Returns Days in seconds
   int               WeeksS(int multiple=1);//Returns Weeks in seconds
   int               MonthsS(int multiple=1);//Returns Months in seconds
   int               YearsS(int multiple=1);//Returns Years in seconds
   int               ReturnYear(datetime time);//Returns the Year for a specific date
   datetime          TimeMinusOffset(datetime standardtime,int timeoffset);//Will return a datetime type of a date with an subtraction offset in seconds
   datetime          TimePlusOffset(datetime standardtime,int timeoffset);//Will return a datetime type of a date with an addition offset in seconds
  };

这个函数之前称为 

Time.DateIsInRange(dayLight.StartDate,dayLight.EndDate,Date)

这是一个简单的函数,用于检查一个单独的日期是否在另外两个日期之间。

bool CTimeManagement::DateIsInRange(datetime FirstTime,datetime SecondTime,datetime compareTime)
  {
   if(FirstTime<=compareTime&&SecondTime>compareTime)
     {
      return true;
     }
   return false;
  }

不同参数的同名函数

bool		DateIsInRange(datetime Start,datetime End,datetime CompareStart,datetime CompareEnd);

也是一个简单的函数,用于检查两个日期是否在另外两个日期之间。

bool CTimeManagement::DateIsInRange(datetime Start,datetime End,datetime CompareStart,datetime CompareEnd)
  {
   if(Start<=CompareStart&&CompareEnd<End)
     {
      return true;
     }
   return false;
  }

我们需要声明一个如下的函数来判断某个日期是否是今天。

bool              DateisToday(datetime TimeRepresented);//Checks if a date is within the current day

将当前时间的年月日和时间类型变量“TimeRepresented”进行比较

bool CTimeManagement::DateisToday(datetime TimeRepresented)
  {
   MqlDateTime TiM;
   TimeToStruct(TimeRepresented,TiM);
   TimeCurrent(today);
   if(TiM.year==today.year&&TiM.mon==today.mon&&TiM.day==today.day)
     {
      return true;
     }
   return false;
  }

为了获得带有时区偏移的日期时间,我们需要用到两个函数

datetime          TimeMinusOffset(datetime standardtime,int timeoffset);//Will return a datetime type of a date with an subtraction offset in seconds

and

datetime          TimePlusOffset(datetime standardtime,int timeoffset);//Will return a datetime type of a date with an addition offset in seconds

当我们想要得到一个带有负一小时(或任何时间单位)偏移时间的日期时,我们将使用

datetime CTimeManagement::TimeMinusOffset(datetime standardtime,int timeoffset)
  {
   standardtime-=timeoffset;
   return standardtime;
  }

同理,如果我们想得到正偏移时间的日期时,我们将使用

datetime CTimeManagement::TimePlusOffset(datetime standardtime,int timeoffset)
  {
   standardtime+=timeoffset;
   return standardtime;
  }

我决定创建一个包含文件,用于存储全局变量、结构体以及枚举类型,将来他们将广泛用于不同的类和EA中

这个包含文件被命名为CommonVariables,并用于存储公共变量

string broker=AccountInfoString(ACCOUNT_COMPANY);//Getting brokers name via AccountInfoString
int Str = StringReplace(broker," ","");//Removing or replacing any spaces in the broker's name with an empty string
int Str1 = StringReplace(broker,".","");//Removing or replacing any dots in the broker's name with an empty string
int Str2 = StringReplace(broker,",","");//Removing or replacing any commas in the broker's name with an empty string
#define BROKER_NAME                    broker//Broker's Name
#define NEWS_TRADING_FOLDER            "NewsTrading"//Name of main folder in common/files
#define NEWS_CALENDAR_FOLDER           StringFormat("%s\\NewsCalendar",NEWS_TRADING_FOLDER)//name of subfolder in NewsTrading
#define NEWS_CALENDAR_BROKER_FOLDER    StringFormat("%s\\%s",NEWS_CALENDAR_FOLDER,BROKER_NAME)//Name of subfolder in NewsCalendar
#define NEWS_DATABASE_FILE             StringFormat("%s\\Calendar.sqlite",NEWS_CALENDAR_BROKER_FOLDER)//Name of sqlite file in subfolder in "Broker's Name"
#define NEWS_TEXT_FILE                 StringFormat("%s\\CalendarOpen.txt",NEWS_CALENDAR_BROKER_FOLDER)//Name of text file to indicate Calendar is open.

struct Calendar
  {
   ulong             EventId;//Event Id
   string            CountryName;//Event Country
   string            EventName;//Event Name
   string            EventType;//Event Type
   string            EventImportance;//Event Importance
   string            EventDate;//Event Date
   string            EventCurrency;//Event Currency
   string            EventCode;//Event Code
   string            EventSector;//Event Sector
   string            EventForecast;//Event Forecast Value
   string            EventPreval;//Event Previous Value
   string            EventImpact;//Event Impact
   string            EventFrequency;//Event Frequency
  };

enum DST_type
  {
   US_DST,//US Daylight Savings
   UK_DST,//UK(EU) Daylight Savings
   AU_DST,//AU Daylight Savings
   DST_NONE//No Daylight Savings Available
  };

当我们在Common文件夹中创建文件时,我希望通过文件夹来组织文件,这样便于在Common文件夹中进行协作。

所以我创建了一个类

class CFolders

在Common文件夹中按NewsTrading/NewsCalendar/Broker顺序创建文件夹。我们将让类的构造函数执行这项任务。

class CFolders
  {
private:
   bool              CreateFolder(string FolderPath);//Will create a folder with the FolderPath string parameter

public:
                     CFolders(void);//Class's constructor
  };

构造函数内部

CFolders::CFolders(void)
  {
   if(CreateFolder(NEWS_TRADING_FOLDER))//Will create the NewsTrading Folder
     {
      if(CreateFolder(NEWS_CALENDAR_FOLDER))//Will create the NewsCalendar Folder
        {
         if(!CreateFolder(NEWS_CALENDAR_BROKER_FOLDER))//Will create the Broker Folder
           {
            Print("Something went wrong with creating folder: ",NEWS_CALENDAR_BROKER_FOLDER);
           }
        }
     }
  }

调用构造函数创建各文件夹

bool CFolders::CreateFolder(string FolderPath)
  {
//--- attempt to create a folder relative to the MQL5\Files path
   if(FolderCreate(FolderPath,FILE_COMMON))
     {
      //--- successful execution
      return true;
     }
   else
     {
      PrintFormat("Failed to create the folder %s. Error code %d",FolderPath,GetLastError());
     }
//--- execution failed
   return false;
  }

K线图非常重要,我们需要收集特定K线的高点、低点、开盘价和收盘价的信息。为此,我们将创建另一个类来获取这些信息,将用于项目中的其他的程序。类名是:

class CCandleProperties

现在我们声明这个类的函数

class CCandleProperties
  {
private:
   CTimeManagement   Time;

public:
   double            Open(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle OpenPrice
   double            Close(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle ClosePrice
   double            High(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle HighPrice
   double            Low(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle LowPrice
   bool              IsLargerThanPreviousAndNext(datetime CandleTime,int Offset,string SYMBOL);//Determine if one candle is larger than two others
  };

我们快速看下

double            Open(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Open Price

在这个函数中,基于CandleIndex参数和K线的时间周期以及交易品种,我们将返回K线的开盘价。

double CCandleProperties::Open(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL)
  {
   return iOpen(((SYMBOL==NULL)?Symbol():SYMBOL),Period,CandleIndex);//return candle open price
  }

为了验证特定日期是否发生了经济事件,因为经纪商可能会调整其时区。我们将获取经济事件的日期,并根据该 日期查看该时间点的十五分钟K线图,然后计算K线图的高度(iHigh-iLow),并将其与事件发生前一小时和事件发生后一小时的十五分钟K线图高度进 行比较。由于经济事件通常会导致市场价格 剧烈波动 ,因此当事件发生时,我们会注意到有一个非常长的K线产生。我们将使用十五分钟周期的K线图,以便给市场足够的时间来形成这个长K线。如果经纪商调整了其 时区,那么存储在MQL5经济日历中的事件日期可能与经纪商K线图上特定时间的价格波动不匹配,但这种波动可能会在指定日期之前一小时或之后一小时出现。 我们将使用以下函数来进行验证:

bool              IsLargerThanPreviousAndNext(datetime CandleTime,int Offset,string SYMBOL);//Determine if one candle is larger than two others

这个函数将检查特定日期的K线图高度是否大于指定日期之前和之后带有时间偏移的K线图高度。

bool CCandleProperties::IsLargerThanPreviousAndNext(datetime CandleTime,int Offset,string SYMBOL)
  {
   int CandleIndex = iBarShift(SYMBOL,PERIOD_M15,CandleTime);//Assign candle index of candletime
   int CandleIndexMinusOffset = iBarShift(SYMBOL,PERIOD_M15,Time.TimeMinusOffset(CandleTime,Offset));//Assign candle index of candletime minus time offset 
   int CandleIndexPlusOffset = iBarShift(SYMBOL,PERIOD_M15,Time.TimePlusOffset(CandleTime,Offset));//Assign candle index of candletime plus time offset
   double CandleHeight = High(CandleIndex,PERIOD_M15,SYMBOL)-Low(CandleIndex,PERIOD_M15,SYMBOL);//Assign height of M15 candletime in pips
   double CandleHeightMinusOffset = High(CandleIndexMinusOffset,PERIOD_M15,SYMBOL)-Low(CandleIndexMinusOffset,PERIOD_M15,SYMBOL);//Assign height of M15 candletime  minus offset in Pips
   double CandleHeightPlusOffset = High(CandleIndexPlusOffset,PERIOD_M15,SYMBOL)-Low(CandleIndexPlusOffset,PERIOD_M15,SYMBOL);//Assign height of M15 candletime plus offset in Pips
   //--Determine if candletime height is greater than candletime height minus offset and candletime height plus offset
   if(CandleHeight>CandleHeightMinusOffset&&CandleHeight>CandleHeightPlusOffset)
     {
      return true;//Candletime is likely when the news event occurred
     }
   return false;//Candletime is unlikely when the real news data was released
  }

我们来看下News类,这个类将直接用于从MQL5经济日历获取数据,

将这些数据存储到数据库中用于测试和最终的交易。 

class CNews
  {
   //Private Declarations Only accessible by this class/header file
private:
   CTimeManagement   Time;//TimeManagement Object declaration
   CDaylightSavings_UK  Savings_UK;//DaylightSavings Object for the UK and EU
   CDaylightSavings_US  Savings_US;//DaylightSavings Object for the US
   CDaylightSavings_AU  Savings_AU;//DaylightSavings Object for the AU
   CCandleProperties Candle;//CandleProperties Object
   string            CurrencyBase,CurrencyProfit,EURUSD;//String variables declarations for working with EURUSD
   bool              EurusdIsSelected,EurusdIsFound,is_Custom;//Boolean variables declarations for working with EURUSD
   bool              timeIsShifted;//Boolean variable declaration will be used to determine if the broker changes it's time zone
   datetime          DaylightStart,DaylightEnd;//Datetime variables declarations for start and end dates for Daylight Savings
   //Structure Declaration for DST
   struct DST
     {
      bool           result;
      datetime       date;
     };
   bool              AutoDetectDST(DST_type &dstType);//Function will determine Broker DST
   DST_type          DSTType;//variable of DST_type enumeration declared in the CommonVariables class/header file
   bool              InsertIntoTable(int db,DST_type Type,Calendar &Evalues[]);//Function for inserting Economic Data in to a database's table
   void              CreateAutoDST(int db);//Function for creating and inserting Recommend DST for the Broker into a table
   bool              CreateTable(int db,string tableName,bool &tableExists);//Function for creating a table in a database
   void              CreateRecords(int db);//Creates a table to store records of when last the Calendar database was updated/created
   bool              UpdateRecords();//Checks if the main Calendar database needs an update or not
   void              EconomicDetails(Calendar &NewsTime[]);//Gets values from the MQL5 economic Calendar

   //Public declarations accessable via a class's Object
public:
                    ~CNews(void);//Deletes a text file created when the Calendar database is being worked on
   void              CreateEconomicDatabase();//Creates the Calendar database for a specific Broker
   datetime          GetLastestNewsDate();//Gets the latest/newest date in the Calendar database
  };

这里有很多内容需要详细解释,我们首先来看一下 

bool              AutoDetectDST(DST_type &dstType);//Function will determine Broker DST

从这个函数的名称可以看出,其主要目的是获取经纪商使用的夏令时计划,我们将通过枚举类型来获取这个信息

enum DST_type
  {
   US_DST,//US Daylight Savings
   UK_DST,//UK(EU) Daylight Savings
   AU_DST,//AU Daylight Savings
   DST_NONE//No Daylight Savings Available
  };

在函数AutoDetectDST中通过引用传递,如果经纪商的夏令时计划未知或无法识别,此函数将返回false。

bool CNews::AutoDetectDST(DST_type &dstType)
  {
   MqlCalendarValue values[];//Single array of MqlCalendarValue type
   string eventtime[];//Single string array variable to store NFP(Nonfarm Payrolls) dates for the 'United States' from the previous year
   int lastyear = Time.ReturnYear(Time.TimeMinusOffset(iTime(Symbol(),PERIOD_CURRENT,0),Time.YearsS()));//Will store the previous year into an integer
   datetime lastyearstart = StringToTime(StringFormat("%s.01.01 00:00:00",(string)lastyear));//Will store the start date for the previous year
   datetime lastyearend = StringToTime(StringFormat("%s.12.31 23:59:59",(string)lastyear));//Will store the end date for the previous year

   if(CalendarValueHistory(values,lastyearstart,lastyearend,"US"))//Getting last year's calendar values for CountryCode = 'US'
     {
      for(int x=0; x<(int)ArraySize(values); x++)
        {
         if(values[x].event_id==840030016)//Get only NFP Event Dates
           {
            ArrayResize(eventtime,eventtime.Size()+1,eventtime.Size()+2);//Increasing the size of eventtime array by 1
            eventtime[eventtime.Size()-1] = TimeToString(values[x].time);//Storing the dates in an array of type string
           }
        }
     }

   datetime ShiftStart=D'1970.01.01 00:00:00',ShiftEnd=D'1970.01.01 00:00:00';//datetime variables to store the broker's time zone shift(change)
   DST previousresult,currentresult;//Variables of structure type DST declared at the beginning of the class

   EURUSD="";//String variable assigned empty string
   EurusdIsSelected = false;//Boolean variable assigned value false
   EurusdIsFound = false;//Boolean variable assigned value false

   for(int i=0;i<SymbolsTotal(true);i++)//Will loop through all the Symbols inside the Market Watch
     {
      string SymName = SymbolName(i,true);//Assign the Symbol Name of index 'i' from the list of Symbols inside the Market Watch
      CurrencyBase = SymbolInfoString(SymName,SYMBOL_CURRENCY_BASE);//Assign the Symbol's Currency Base
      CurrencyProfit = SymbolInfoString(SymName,SYMBOL_CURRENCY_PROFIT);//Assign the Symbol's Currency Profit
      SymbolExist(SymName,is_Custom);//Get the boolean value into 'is_Custom' for whether the Symbol Name is a Custom Symbol(Is not from the broker)

      //-- Check if the Symbol outside the Market Watch has a SYMBOL_CURRENCY_BASE of EUR
      //-- and a SYMBOL_CURRENCY_PROFIT of USD, and this Symbol is not a Custom Symbol(Is not from the broker)
      if(CurrencyBase=="EUR"&&CurrencyProfit=="USD"&&!is_Custom)
        {
         EURUSD = SymName;//Assigning the name of the EURUSD Symbol found inside the Market Watch
         EurusdIsFound = true;//EURUSD Symbol was found in the Trading Terminal for your Broker
         break;//Will end the for loop
        }
     }

   if(!EurusdIsFound)//Check if EURUSD Symbol was already Found in the Market Watch
     {
      for(int i=0; i<SymbolsTotal(false); i++)//Will loop through all the available Symbols outside the Market Watch
        {
         string SymName = SymbolName(i,false);//Assign the Symbol Name of index 'i' from the list of Symbols outside the Market Watch
         CurrencyBase = SymbolInfoString(SymName,SYMBOL_CURRENCY_BASE);
         CurrencyProfit = SymbolInfoString(SymName,SYMBOL_CURRENCY_PROFIT);
         SymbolExist(SymName,is_Custom);//Get the boolean value into 'is_Custom' for whether the Symbol Name is a Custom Symbol(Is not from the broker)

         //-- Check if the Symbol outside the Market Watch has a SYMBOL_CURRENCY_BASE of EUR
         //-- and a SYMBOL_CURRENCY_PROFIT of USD, and this Symbol is not a Custom Symbol(Is not from the broker)
         if(CurrencyBase=="EUR"&&CurrencyProfit=="USD"&&!is_Custom)
           {
            EurusdIsSelected = SymbolSelect(SymName,true);//Adding the EURUSD Symbol to the Market Watch
            if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
              {
               EURUSD = SymName;//Assigning the name of the EURUSD Symbol found outside the Market Watch
               EurusdIsFound = true;//EURUSD Symbol was found in the Trading Terminal for your Broker
               break;//Will end the for loop
              }
           }
        }
     }

   if(!EurusdIsFound)//Check if EURUSD Symbol was Found in the Trading Terminal for your Broker
     {
      Print("Cannot Find EURUSD!");
      Print("Cannot Create Database!");
      Print("Server DST Cannot be Detected!");
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   for(uint i=0;i<eventtime.Size();i++)
     {
      currentresult.result = Candle.IsLargerThanPreviousAndNext((datetime)eventtime[i],Time.HoursS(),EURUSD);//Store the result of if the event date is the larger candlestick
      currentresult.date = (datetime)eventtime[i];//Store the eventdate from eventtime[i]
      timeIsShifted = ((currentresult.result!=previousresult.result&&i>0)?true:false);//Check if there is a difference between the previous result and the current result

      //--- Print Event Dates and if the event date's candle is larger than the previous candle an hour ago and the next candle an hour ahead
      Print("Date: ",eventtime[i]," is Larger: ",Candle.IsLargerThanPreviousAndNext((datetime)eventtime[i],Time.HoursS(),EURUSD)," Shifted: ",timeIsShifted);

      if(timeIsShifted)//Check if the Larger candle has shifted from the previous event date to the current event date in eventtime[i] array
        {
         if(ShiftStart==D'1970.01.01 00:00:00')//Check if the ShiftStart variable has not been assigned a relevant value yet
           {
            ShiftStart=currentresult.date;//Store the event date for when the time shift began
           }
         ShiftEnd=previousresult.date;//Store the event date timeshift
        }
      previousresult.result = currentresult.result;//Store the previous result of if the event date is the larger candlestick
      previousresult.date = currentresult.date;//Store the event date from eventtime[i]
     }

   if(ShiftStart==D'1970.01.01 00:00:00'&&eventtime.Size()>0)//Check if the ShiftStart variable has not been assigned a relevant value and the event dates are more than zero
     {
      Print("Broker ServerTime unchanged!");
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return true;//Returning True, Broker's DST schedule was found successfully
     }

   if(Savings_AU.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For AU DST");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = AU_DST;//Assigning enumeration value AU_DST, Broker has AU DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For AU");
      Print("Year: %d Cannot Be Found!",lastyear);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   if(Savings_UK.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For UK DST");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = UK_DST;//Assigning enumeration value UK_DST, Broker has UK/EU DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For UK");
      Print("Year: %d Cannot Be Found!",lastyear);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   if(Savings_US.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For US DST");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = US_DST;//Assigning enumeration value US_DST, Broker has US DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For US");
      Print("Year: %d Cannot Be Found!",lastyear);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
     {
      SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
     }
   Print("Cannot Detect Broker ServerTime Configuration!");
   dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
   return false;//Returning False, Broker's DST schedule was not found
  }

上面的函数有些冗长,让我们把它分解一下。在第一部分中,我们将收集前一年的非农就业报告(NFP)的日期。因此,如果当前年份是2024年,我们将收集2023年的所有日期,因为我们需要分析整年的数据,以便成功获取经纪商可能的夏令时计划。这些日期将存储在我们的单个字符串数组eventtime中。 

MqlCalendarValue values[];//Single array of MqlCalendarValue type
   string eventtime[];//Single string array variable to store NFP(Nonfarm Payrolls) dates for the 'United States' from the previous year
   int lastyear = Time.ReturnYear(Time.TimeMinusOffset(iTime(Symbol(),PERIOD_CURRENT,0),Time.YearsS()));//Will store the previous year into an integer
   datetime lastyearstart = StringToTime(StringFormat("%s.01.01 00:00:00",(string)lastyear));//Will store the start date for the previous year
   datetime lastyearend = StringToTime(StringFormat("%s.12.31 23:59:59",(string)lastyear));//Will store the end date for the previous year

   if(CalendarValueHistory(values,lastyearstart,lastyearend,"US"))//Getting last year's calendar values for CountryCode = 'US'
     {
      for(int x=0; x<(int)ArraySize(values); x++)
        {
         if(values[x].event_id==840030016)//Get only NFP Event Dates
           {
            ArrayResize(eventtime,eventtime.Size()+1,eventtime.Size()+2);//Increasing the size of event time array by 1
            eventtime[eventtime.Size()-1] = TimeToString(values[x].time);//Storing the dates in an array of type string
           }
        }
     }

接下来,我们将在EURUSD(欧元/美元)货币对 上使用这些日期数据,因为XAUUSD(黄金)和其他指数如US30(道琼斯指数)即使没有新闻事件也波动很大,而EURUSD在大多数情况下相对稳定, 但在经济事件发生时会变得波动剧烈。这使得在经济事件发生时更容易检测到,因为市场可能会由于事件而出现剧烈波动。这也是我们将重点关注NFP(非农就业 报告)事件的原因,因为它们通常会引发市场价格的大幅波动。这也是我们将重点关注NFP(非农就业报告)事件的原因,因为它们通常会引发市场价格的大幅波 动。基于上述原因,我们采用EURUSD货币对。首先,我们将检查经纪商提供的所有可用货币对,找到并选择EURUSD。如果我们找不到EURUSD,我 们将返回false。 

   EURUSD="";//String variable assigned empty string
   EurusdIsSelected = false;//Boolean variable assigned value false
   EurusdIsFound = false;//Boolean variable assigned value false

   for(int i=0;i<SymbolsTotal(true);i++)//Will loop through all the Symbols inside the Market Watch
     {
      string SymName = SymbolName(i,true);//Assign the Symbol Name of index 'i' from the list of Symbols inside the Market Watch
      CurrencyBase = SymbolInfoString(SymName,SYMBOL_CURRENCY_BASE);//Assign the Symbol's Currency Base
      CurrencyProfit = SymbolInfoString(SymName,SYMBOL_CURRENCY_PROFIT);//Assign the Symbol's Currency Profit
      SymbolExist(SymName,is_Custom);//Get the boolean value into 'is_Custom' for whether the Symbol Name is a Custom Symbol(Is not from the broker)

      //-- Check if the Symbol outside the Market Watch has a SYMBOL_CURRENCY_BASE of EUR
      //-- and a SYMBOL_CURRENCY_PROFIT of USD, and this Symbol is not a Custom Symbol(Is not from the broker)
      if(CurrencyBase=="EUR"&&CurrencyProfit=="USD"&&!is_Custom)
        {
         EURUSD = SymName;//Assigning the name of the EURUSD Symbol found inside the Market Watch
         EurusdIsFound = true;//EURUSD Symbol was found in the Trading Terminal for your Broker
         break;//Will end the for loop
        }
     }

   if(!EurusdIsFound)//Check if EURUSD Symbol was already Found in the Market Watch
     {
      for(int i=0; i<SymbolsTotal(false); i++)//Will loop through all the available Symbols outside the Market Watch
        {
         string SymName = SymbolName(i,false);//Assign the Symbol Name of index 'i' from the list of Symbols outside the Market Watch
         CurrencyBase = SymbolInfoString(SymName,SYMBOL_CURRENCY_BASE);
         CurrencyProfit = SymbolInfoString(SymName,SYMBOL_CURRENCY_PROFIT);
         SymbolExist(SymName,is_Custom);//Get the boolean value into 'is_Custom' for whether the Symbol Name is a Custom Symbol(Is not from the broker)

         //-- Check if the Symbol outside the Market Watch has a SYMBOL_CURRENCY_BASE of EUR
         //-- and a SYMBOL_CURRENCY_PROFIT of USD, and this Symbol is not a Custom Symbol(Is not from the broker)
         if(CurrencyBase=="EUR"&&CurrencyProfit=="USD"&&!is_Custom)
           {
            EurusdIsSelected = SymbolSelect(SymName,true);//Adding the EURUSD Symbol to the Market Watch
            if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
              {
               EURUSD = SymName;//Assigning the name of the EURUSD Symbol found outside the Market Watch
               EurusdIsFound = true;//EURUSD Symbol was found in the Trading Terminal for your Broker
               break;//Will end the for loop
              }
           }
        }
     }

   if(!EurusdIsFound)//Check if EURUSD Symbol was Found in the Trading Terminal for your Broker
     {
      Print("Cannot Find EURUSD!");
      Print("Cannot Create Database!");
      Print("Server DST Cannot be Detected!");
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

在找到EURUSD货币对之后,我们将遍历所有 NFP日期,并为每个日期找到对应的十五分钟K线图。然后,我们将比较K线图的高度与事件发生前一小时和后一小时的偏移日期,以检测事件可能发生的时间。 如果事件日期与K线图中的波动性不匹配,我们将存储第一次出现这种不匹配的事件日期以及它结束时的日期到变量ShiftStart和ShiftEnd中。

for(uint i=0;i<eventtime.Size();i++)
     {
      currentresult.result = Candle.IsLargerThanPreviousAndNext((datetime)eventtime[i],Time.HoursS(),EURUSD);//Store the result of if the event date is the larger candlestick
      currentresult.date = (datetime)eventtime[i];//Store the event date from eventtime[i]
      timeIsShifted = ((currentresult.result!=previousresult.result&&i>0)?true:false);//Check if there is a difference between the previous result and the current result

      //--- Print Event Dates and if the event date's candle is larger than the previous candle an hour ago and the next candle an hour ahead
      Print("Date: ",eventtime[i]," is Larger: ",Candle.IsLargerThanPreviousAndNext((datetime)eventtime[i],Time.HoursS(),EURUSD)," Shifted: ",timeIsShifted);

      if(timeIsShifted)//Check if the Larger candle has shifted from the previous event date to the current event date in eventtime[i] array
        {
         if(ShiftStart==D'1970.01.01 00:00:00')//Check if the ShiftStart variable has not been assigned a relevant value yet
           {
            ShiftStart=currentresult.date;//Store the event date for when the time shift began
           }
         ShiftEnd=previousresult.date;//Store the event date timeshift
        }
      previousresult.result = currentresult.result;//Store the previous result of if the event date is the larger candlestick
      previousresult.date = currentresult.date;//Store the event date from eventtime[i]
     }

一旦我们有了ShiftStart和 ShiftEnd日期,我们将检查这些日期是否与任何夏令时(DST)开始和结束日期相对应。如果存在匹配,我们将把夏令时计划赋值给变量 dstType,并返回true。如果我们没有得到ShiftStart日期(即ShiftStart等于'1970.01.01 00:00:00')且eventtime数组的大小大于零,那么我们知道经纪商不遵循任何夏令时(DST)计划。

if(ShiftStart==D'1970.01.01 00:00:00'&&eventtime.Size()>0)//Check if the ShiftStart variable has not been assigned a relevant value and the event dates are more than zero
     {
      Print("Broker ServerTime unchanged!");
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return true;//Returning True, Broker's DST schedule was found successfully
     }

   if(Savings_AU.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For AU DST");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = AU_DST;//Assigning enumeration value AU_DST, Broker has AU DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For AU");
      Print("Year: %d Cannot Be Found!",lastyear);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   if(Savings_UK.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For UK DST");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = UK_DST;//Assigning enumeration value UK_DST, Broker has UK/EU DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For UK");
      Print("Year: %d Cannot Be Found!",lastyear);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   if(Savings_US.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For US DST");
         if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
           {
            SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
           }
         dstType = US_DST;//Assigning enumeration value US_DST, Broker has US DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For US");
      Print("Year: %d Cannot Be Found!",lastyear);
      if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
        {
         SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
        }
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   if(EurusdIsSelected)//Check if this program added EURUSD Symbol to Market Watch
     {
      SymbolSelect(EURUSD,false);//Remove EURUSD Symbol from Market Watch
     }
   Print("Cannot Detect Broker ServerTime Configuration!");
   dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
   return false;//Returning False, Broker's DST schedule was not found
  }

一旦我们检测到了夏令时(DST)计划,我们需要存储这些信息以供后续在实现时使用。当我们测试经济事件时,我们就会知道应该使用哪个日历了。

void              CreateAutoDST(int db);//Function for creating and inserting Recommend DST for the Broker into a table

一条记录将储存到表AutoDST中。

void CNews::CreateAutoDST(int db)
  {
   bool failed=false;//boolean variable

   if(!DatabaseTableExists(db,"AutoDST"))//Checks if the table 'AutoDST' exists in the databse 'Calendar'
     {
      //--- create the table AutoDST
      if(!DatabaseExecute(db,"CREATE TABLE AutoDST(DST STRING NOT NULL);"))//Will attempt to create the table 'AutoDST'
        {
         Print("DB: create the AutoDST table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return;//Exits the function if creating the table failed
        }
     }
   else
     {
      return;//Exits the function if the table AutoDST table already exists
     }

//Sql query/request to insert the recommend DST for the Broker using the DSTType variable to determine which string data to insert
   string request_text=StringFormat("INSERT INTO 'AutoDST'(DST) VALUES ('%s')",((DSTType==US_DST)?"Data_US":
                                    (DSTType==UK_DST)?"Data_UK":(DSTType==AU_DST)?"Data_AU":"Data_None"));

   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat("INSERT INTO 'AutoDST'(DST) VALUES ('%s')",((DSTType==US_DST)?"Data_US":
                  (DSTType==UK_DST)?"Data_UK":(DSTType==AU_DST)?"Data_AU":"Data_None"));//Will print the sql query if failed
      failed=true;//assign true if the request failed
     }

   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
     }
  }

我们需要一个函数来创建我们的日历表,这样我们就可以重用这个函数来根据不同的夏令时(DST)计划调整我们的日历了。

bool              CreateTable(int db,string tableName,bool &tableExists);//Function for creating a table in a database

在 这个CreateTable函数中,我们将创建四个表:Data_UK用于英国夏令时(DST),Data_US用于美国夏令时,Data_AU用于澳大 利亚夏令时,以及Data_None用于不遵循任何夏令时的情况。字符串tableName将作为表后缀名称的参数,例如'US'。

bool CNews::CreateTable(int db,string tableName,bool &tableExists)
  {
   if(DatabaseTableExists(db,StringFormat("Data_%s",tableName)))//Checks if a table 'Data_%s' exists in the database 'Calendar'
     {
      tableExists=true;//Assigns true to tableExists variable

      if(!DatabaseExecute(db,StringFormat("DROP TABLE Data_%s",tableName)))//We will drop the table if the table already exists
        {
         //If the table failed to be dropped/deleted
         PrintFormat("Failed to drop table Data_%s with code %d",tableName,GetLastError());
         DatabaseClose(db);//Close the database
         return false;//will terminate execution of the rest of the code below and return false, when the table cannot be dropped
        }
     }

   if(!DatabaseTableExists(db,StringFormat("Data_%s",tableName)))//If the database table 'Data_%s' doesn't exist
     {
      //--- create the table 'Data' with the following columns
      if(!DatabaseExecute(db,StringFormat("CREATE TABLE Data_%s("
                                          "ID INT NOT NULL,"
                                          "EVENTID  INT   NOT NULL,"
                                          "COUNTRY  STRING   NOT NULL,"
                                          "EVENTNAME   STRING   NOT NULL,"
                                          "EVENTTYPE   STRING   NOT NULL,"
                                          "EVENTIMPORTANCE   STRING   NOT NULL,"
                                          "EVENTDATE   STRING   NOT NULL,"
                                          "EVENTCURRENCY  STRING   NOT NULL,"
                                          "EVENTCODE   STRING   NOT NULL,"
                                          "EVENTSECTOR STRING   NOT NULL,"
                                          "EVENTFORECAST  STRING   NOT NULL,"
                                          "EVENTPREVALUE  STRING   NOT NULL,"
                                          "EVENTIMPACT STRING   NOT NULL,"
                                          "EVENTFREQUENCY STRING   NOT NULL,"
                                          "PRIMARY KEY(ID));",tableName)))//Checks if the table was successfully created
        {
         Print("DB: create the Calendar table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return false;//Function returns false if creating the table failed
        }
     }
   return true;//Function returns true if creating the table was successful
  }

我们现在需要将数据插入这些创建的表格中,但是首先我们要获取数据。我们下一个函数

void              EconomicDetails(Calendar &NewsTime[]);//Gets values from the MQL5 economic Calendar

将通过引用在NewsTime Calendar数组中检索所有可用的经济事件

void CNews::EconomicDetails(Calendar &NewsTime[])
  {
   int Size=0;//to keep track of the size of the events in the NewsTime array
   MqlCalendarCountry countries[];
   int count=CalendarCountries(countries);//Get the array of country names available in the Calendar
   string Country_code="";

   for(int i=0; i<count; i++)
     {
      MqlCalendarValue values[];
      datetime date_from=0;//Get date from the beginning
      datetime date_to=(datetime)(Time.MonthsS()+iTime(Symbol(),PERIOD_D1,0));//Date of the next month from the current day

      if(CalendarValueHistory(values,date_from,date_to,countries[i].code))
        {
         for(int x=0; x<(int)ArraySize(values); x++)
           {
            MqlCalendarEvent event;
            ulong event_id=values[x].event_id;//Get the event id

            if(CalendarEventById(event_id,event))
              {
               ArrayResize(NewsTime,Size+1,Size+2);//Readjust the size of the array to +1 of the array size
               StringReplace(event.name,"'","");//Removing or replacing single quotes(') from event name with an empty string

               NewsTime[Size].CountryName = countries[i].name;//storing the country's name from the specific event
               NewsTime[Size].EventName = event.name;//storing the event's name
               NewsTime[Size].EventType = EnumToString(event.type);//storing the event type from (ENUM_CALENDAR_EVENT_TYPE) to a string
               NewsTime[Size].EventImportance = EnumToString(event.importance);//storing the event importance from (ENUM_CALENDAR_EVENT_IMPORTANCE) to a string
               NewsTime[Size].EventId = event.id;//storing the event id
               NewsTime[Size].EventDate = TimeToString(values[x].time);//storing normal event time
               NewsTime[Size].EventCurrency = countries[i].currency;//storing event currency
               NewsTime[Size].EventCode = countries[i].code;//storing event code
               NewsTime[Size].EventSector = EnumToString(event.sector);//storing event sector from (ENUM_CALENDAR_EVENT_SECTOR) to a string

               if(values[x].HasForecastValue())//Checks if the event has a forecast value
                 {
                  NewsTime[Size].EventForecast = (string)values[x].forecast_value;//storing the forecast value into a string
                 }
               else
                 {
                  NewsTime[Size].EventForecast = "None";//storing 'None' as the forecast value
                 }

               if(values[x].HasPreviousValue())//Checks if the event has a previous value
                 {
                  NewsTime[Size].EventPreval = (string)values[x].prev_value;//storing the previous value into a string
                 }
               else
                 {
                  NewsTime[Size].EventPreval = "None";//storing 'None' as the previous value
                 }

               NewsTime[Size].EventImpact =  EnumToString(values[x].impact_type);//storing the event impact from (ENUM_CALENDAR_EVENT_IMPACT) to a string
               NewsTime[Size].EventFrequency =  EnumToString(event.frequency);//storing the event frequency from (ENUM_CALENDAR_EVENT_FREQUENCY) to a string
               Size++;//incrementing the Calendar array NewsTime
              }
           }
        }
     }
  }


一旦我们有了数据,我们将其插入日历表中。

bool              InsertIntoTable(int db,DST_type Type,Calendar &Evalues[]);//Function for inserting Economic Data in to a database's table

函数InsertIntoTable实现这个功能。他有三个参数。

1. 这个输入参数是数据库的整数值。

int db

2. 这个输入参数是夏令时(DST)计划。

DST_type Type

3. 这个数组引用是我们从之前的EconomicDetails函数中检索到的日历事件的输入参数。

Calendar &Evalues[]

在这个函数中,我们将根据夏令时(DST)计划更改单个事件日期。如果事件日期在DST计划内,我们将给事件日期增加一个小时;如果服务器当前时间在DST计划内,我们将从事件日期中减去一个小时。然后将所有经济事件数据存储到日历表Data_%s中。

bool CNews::InsertIntoTable(int db,DST_type Type,Calendar &Evalues[])
  {
   string tableName;//will store the table name suffix
   for(uint i=0; i<Evalues.Size(); i++)//Looping through all the Economic Events
     {
      string Date;//Will store the date for the economic event
      switch(Type)//Switch statement to check all possible 'case' scenarios for the variable Type
        {
         case DST_NONE://if(Type==DST_NONE) then run code below
            Date = Evalues[i].EventDate;//Assign the normal Economic EventDate
            tableName = "None";//Full table name will be 'Data_None'
            break;//End switch statement
         case US_DST://if(Type==US_DST) then run code below
            Savings_US.adjustDaylightSavings(StringToTime(Evalues[i].EventDate),Date);//Assign by Reference the Economic EventDate adjusted for US DST(Daylight Savings Time)
            tableName = "US";//Full table name will be 'Data_US'
            break;//End switch statement
         case UK_DST://if(Type==UK_DST) then run code below
            Savings_UK.adjustDaylightSavings(StringToTime(Evalues[i].EventDate),Date);//Assign by Reference the Economic EventDate adjusted for UK DST(Daylight Savings Time)
            tableName = "UK";//Full table name will be 'Data_UK'
            break;//End switch statement
         case AU_DST://if(Type==AU_DST) then run code below
            Savings_AU.adjustDaylightSavings(StringToTime(Evalues[i].EventDate),Date);//Assign by Reference the Economic EventDate adjusted for AU DST(Daylight Savings Time)
            tableName = "AU";//Full table name will be 'Data_AU'
            break;//End switch statement
         default://if(Type==(Unknown value)) then run code below
            Date = Evalues[i].EventDate;//Assign the normal Economic EventDate
            tableName = "None";//Full table name will be 'Data_None'
            break;//End switch statement
        }

      string request_text =
         StringFormat("INSERT INTO 'Data_%s'(ID,EVENTID,COUNTRY,EVENTNAME,EVENTTYPE,EVENTIMPORTANCE,EVENTDATE,EVENTCURRENCY,EVENTCODE,"
                      "EVENTSECTOR,EVENTFORECAST,EVENTPREVALUE,EVENTIMPACT,EVENTFREQUENCY)"
                      "VALUES (%d,%d,'%s','%s', '%s', '%s', '%s','%s','%s','%s','%s','%s','%s','%s')",
                      tableName,
                      i,
                      Evalues[i].EventId,
                      Evalues[i].CountryName,
                      Evalues[i].EventName,
                      Evalues[i].EventType,
                      Evalues[i].EventImportance,
                      Date,
                      Evalues[i].EventCurrency,
                      Evalues[i].EventCode,
                      Evalues[i].EventSector,
                      Evalues[i].EventForecast,
                      Evalues[i].EventPreval,
                      Evalues[i].EventImpact,
                      Evalues[i].EventFrequency);//Inserting all the columns for each event record

      if(!DatabaseExecute(db, request_text))//Checks whether the event was inserted into the table 'Data_%s'
        {
         Print(GetLastError());
         PrintFormat("INSERT INTO 'Data_%s'(ID,EVENTID,COUNTRY,EVENTNAME,EVENTTYPE,EVENTIMPORTANCE,EVENTDATE,EVENTCURRENCY,EVENTCODE,"
                      "EVENTSECTOR,EVENTFORECAST,EVENTPREVALUE,EVENTIMPACT,EVENTFREQUENCY)"
                      "VALUES (%d,%d,'%s','%s', '%s', '%s', '%s','%s','%s','%s','%s','%s','%s','%s')",
                      tableName,
                      i,
                      Evalues[i].EventId,
                      Evalues[i].CountryName,
                      Evalues[i].EventName,
                      Evalues[i].EventType,
                      Evalues[i].EventImportance,
                      Date,
                      Evalues[i].EventCurrency,
                      Evalues[i].EventCode,
                      Evalues[i].EventSector,
                      Evalues[i].EventForecast,
                      Evalues[i].EventPreval,
                      Evalues[i].EventImpact,
                      Evalues[i].EventFrequency);//Will print the sql query to check for any errors or possible defaults in the query/request

         return false;//Will end the loop and return false, as values failed to be inserted into the table
        }
     }
   return true;//Will return true, all values were inserted into the table successfully
  }

一旦我们创建了表格并将经济数据插入其中,我们就需要记录执行此操作的时间戳。这是为了知道何时更新数据表。

void              CreateRecords(int db);//Creates a table to store records of when last the Calendar database was updated/created

我们将创建一个名为Records的表,并在每次创建或更新日历表时,将当前服务器时间存储到这个表中。

void CNews::CreateRecords(int db)
  {
   bool failed=false;

   if(!DatabaseTableExists(db,"Records"))//Checks if the table 'Records' exists in the databse 'Calendar'
     {
      //--- create the table
      if(!DatabaseExecute(db,"CREATE TABLE Records(RECORDEDTIME INT NOT NULL);"))//Will attempt to create the table 'Records'
        {
         Print("DB: create the Records table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return;//Exits the function if creating the table failed
        }
     }

//Sql query/request to insert the current time into the 'RECORDEDTIME' column in the table 'Records'
   string request_text=StringFormat("INSERT INTO 'Records'(RECORDEDTIME) VALUES (%d)",(int)TimeCurrent());

   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat("INSERT INTO 'Records'(RECORDEDTIME) VALUES (%d)",(int)TimeCurrent());
      failed=true;//assign true if the request failed
     }

   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
     }
  }

 现在我们基本上已经有了所有的函数,但我还想重点关注剩下的三个函数。第一个

bool              UpdateRecords();//Checks if the main Calendar database needs an update or not

当 名为Records的表中的最大记录(即最新的记录)是在当前日期内时(意味着日历表已经在当天创建或更新过,当天无需再进行进一步的更新),这个函数将 返回false。如果以下任一条件成立:日历表不存在、日历数据库不存在,或者名为Records的表中的最大记录(即最新的记录)不在当前日期(天) 内,那么,这个函数将返回true。

bool CNews::UpdateRecords()
  {
//--- open/create
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READONLY|DATABASE_OPEN_COMMON);//try to open database Calendar

   if(db==INVALID_HANDLE)//Checks if the database was able to be opened
     {
      //if opening the database failed
      if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if the database Calendar exists in the common folder
        {
         return true;//Returns true when the database was failed to be opened and the file doesn't exist in the common folder
        }
     }

   if(!DatabaseTableExists(db,"Records"))//If the database table 'Records' doesn't exist
     {
      DatabaseClose(db);
      return true;
     }

   int recordtime=0;//will store the maximum date recorded in the database table 'Records'
   string request_text="SELECT MAX(RECORDEDTIME) FROM Records";//Sql query to determine the lastest or maximum date recorded
   int request=DatabasePrepare(db,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead()
   if(request==INVALID_HANDLE)//Checks if the request failed to be completed
     {
      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return true;
     }

   for(int i=0; DatabaseRead(request); i++)//Will read all the results from the sql query/request
     {
      if(!DatabaseColumnInteger(request, 0, recordtime))//Will assign the first column value to the variable 'recordtime'
        {
         Print(i, ": DatabaseRead() failed with code ", GetLastError());
         DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
         DatabaseClose(db);//Closes the database
         return true;
        }
     }

   DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
   DatabaseClose(db);//Closes the database

   if(!Time.DateisToday((datetime)recordtime))//Checks if the recorded time/date is today(current day)
     {
      return true;
     }

   return false;
  }

第二个我想关注的函数是

datetime          GetLastestNewsDate();//Gets the lastest/newest date in the Calendar database

这 个函数与上一个名为UpdateRecords的函数类似,但区别在于GetLastestNewsDate只会返回Records表中的最大记录时间 (即最新的记录时间)。这个函数稍后将用于通知用户,如果他们在策略测试器中尝试测试一个大于此日期的日期,将会发生什么情况。我们将告诉用户/交易者没 有经济事件可用于测试。

datetime CNews::GetLastestNewsDate()
  {
//--- open the database 'Calendar' in the common folder
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READONLY|DATABASE_OPEN_COMMON);

   if(db==INVALID_HANDLE)//Checks if 'Calendar' failed to be opened
     {

      if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if 'Calendar' database exists
        {
         return 0;//Will return the earliest date which is 1970.01.01 00:00:00
        }
     }

   string eventtime="1970.01.01 00:00:00";//string variable with the first/earliest possible date in MQL5
//Sql query to determine the lastest or maximum recorded time from which the database was updated.
   string request_text="SELECT MAX(RECORDEDTIME) FROM Records";
   int request=DatabasePrepare(db,request_text);

   if(request==INVALID_HANDLE)
     {

      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return true;

     }

   for(int i=0; DatabaseRead(request); i++)//Will read all the results from the sql query/request
     {
      if(!DatabaseColumnText(request, 0,eventtime))//Will assign the first column(column 0) value to the variable 'eventtime'
        {

         Print(i, ": DatabaseRead() failed with code ", GetLastError());
         DatabaseFinalize(request);//Finalize request
         DatabaseClose(db);//Closes the database 'Calendar'
         return 0;//Will end the for loop and will return the earliest date which is 1970.01.01 00:00:00
        }
     }

   DatabaseFinalize(request);
   DatabaseClose(db);//Closes the database 'Calendar'

   return StringToTime(eventtime);//Returns the string eventtime converted to datetime
  }

现在,我们将继续实现一个函数,该函数将创建日历数据库,并调用我们之前创建的所有其他函数,以便在这个数据库中创建表并向这些表中插入值。

void              CreateEconomicDatabase();//Creates the Calendar database for a specific Broker

当EA加载到图表上这个函数将被调用来创建我们的数据库。

void CNews::CreateEconomicDatabase()
  {
   Print("Please wait...");

   if(FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Check if the database exists
     {
      if(!UpdateRecords())//Check if the database is up to date
        {
         return;//will terminate execution of the rest of the code below
        }
     }
   else
     {
      if(!AutoDetectDST(DSTType))//Check if AutoDetectDST went through all the right procedures
        {
         return;//will terminate execution of the rest of the code below
        }
     }

   if(FileIsExist(NEWS_TEXT_FILE,FILE_COMMON))//Check if the database is open
     {
      return;//will terminate execution of the rest of the code below
     }

   Calendar Evalues[];//Creating a Calendar array variable
   bool failed=false,tableExists=false;
   int file=INVALID_HANDLE;
   datetime lastestdate=D'1970.01.01 00:00:00';
//--- open/create the database 'Calendar'
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE| DATABASE_OPEN_COMMON);//will try to open/create in the common folder

   if(db==INVALID_HANDLE)//Checks if the database 'Calendar' failed to open/create
     {
      Print("DB: ",NEWS_DATABASE_FILE, " open failed with code ", GetLastError());
      return;//will terminate execution of the rest of the code below
     }
   else
     {
      file=FileOpen(NEWS_TEXT_FILE,FILE_WRITE|FILE_ANSI|FILE_TXT|FILE_COMMON);//try to create a text file 'NewsDatabaseOpen' in common folder
      if(file==INVALID_HANDLE)
        {
         DatabaseClose(db);//Closes the database 'Calendar' if the News text file failed to be created
         return;//will terminate execution of the rest of the code below
        }
     }

   DatabaseTransactionBegin(db);//Starts transaction execution
   Print("Please wait...");

//-- attempt to create the calendar tables
   if(!CreateTable(db,"None",tableExists)||!CreateTable(db,"US",tableExists)
      ||!CreateTable(db,"UK",tableExists)||!CreateTable(db,"AU",tableExists))
     {
      FileClose(file);//Closing the file 'NewsDatabaseOpen.txt'
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);//Deleting the file 'NewsDatabaseOpen.txt'
      return;//
     }

   EconomicDetails(Evalues);//Retrieving the data from the Economic Calendar

   if(tableExists)//Checks if there is an existing table within the Calendar Database
     {
      //if there is an existing table we will notify the user that we are updating the table.
      PrintFormat("Updating %s",NEWS_DATABASE_FILE);
     }
   else
     {
      //if there isn't an existing table we will notify the user that we about to create one
      PrintFormat("Creating %s",NEWS_DATABASE_FILE);
     }

//-- attempt to insert economic event data into the calendar tables
   if(!InsertIntoTable(db,DST_NONE,Evalues)||!InsertIntoTable(db,US_DST,Evalues)
      ||!InsertIntoTable(db,UK_DST,Evalues)||!InsertIntoTable(db,AU_DST,Evalues))
     {
      failed=true;//Will assign true if inserting economic vaules failed in any of the Data tables
     }

   if(failed)//Checks if the event/s failed to be recorded/inserted into the database table 'Data_%s'
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
      FileClose(file);//Close the text file 'NEWS_TEXT_FILE'
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);//Delete the text file, as we are reverted/rolled-back the database
      ArrayRemove(Evalues,0,WHOLE_ARRAY);//Removes the values in the array

     }
   else//if all the events were recorded or inserted into the tables 'Data_%s'
     {
      if(tableExists)
        {
         //Let the user/trader know that the database was updated
         PrintFormat("%s Updated",NEWS_DATABASE_FILE);
        }
      else
        {
         //Let the user/trader know that the database was created
         PrintFormat("%s Created",NEWS_DATABASE_FILE);
        }

      CreateRecords(db);//Will create the 'Records' table and insert the  current time
      CreateAutoDST(db);//Will create the 'AutoDST' table and insert the broker's DST schedule
      FileClose(file);//Close the text file 'NEWS_TEXT_FILE'
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);//Delete the text file, as we are about to close the database
      ArrayRemove(Evalues,0,WHOLE_ARRAY);//Removes the values in the array
     }
//--- all transactions have been performed successfully - record changes and unlock the database
   DatabaseTransactionCommit(db);
   DatabaseClose(db);//Close the database
  }

我们现在来看看 EA,它将加载我们创建的“NewsTrading”项目中所有不同类和文件程序。

//+------------------------------------------------------------------+
//|                                                  NewsTrading.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "News.mqh"
CNews NewsObject;
#include "TimeManagement.mqh"
CTimeManagement CTM;
#include "WorkingWithFolders.mqh"
CFolders Folder();//Calling the class's constructor
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   if(!MQLInfoInteger(MQL_TESTER))//Checks whether the program is in the strategy tester
     {
      //Checks whether the database file exists and whether it has been modified in the current date
      if((!CTM.DateisToday((datetime)FileGetInteger(NEWS_DATABASE_FILE,FILE_MODIFY_DATE,true)))||(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON)))
        {
         /*
         In the Do while loop below, the code will check if the terminal is connected to the internet.
如果程序停止,循环将中断。如果程序没有停止且终端连接到互联网,将从News.mqh头文件的对象NewsObject中调用CreateEconomicDatabase函数,一旦调用,循环将中断。
         */
         bool done=false;
         do
           {
            if(IsStopped())
              {
               done=true;
              }

            if(!TerminalInfoInteger(TERMINAL_CONNECTED))
              {
               Print("Waiting for connection...");
               Sleep(500);
               continue;
              }
            else
              {
               Print("Connection Successful!");
               NewsObject.CreateEconomicDatabase();//calling the database create function
               done=true;
              }
           }
         while(!done);
        }
     }
   else
     {
      //Checks whether the database file exists
      if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))
        {
         Print("Necessary Files Do not Exist!");
         Print("Run Program outside of the Strategy Tester");
         Print("Necessary Files Should be Created First");
         return(INIT_FAILED);
        }
      //Checks whether the lastest database date includes the time and date being tested
      datetime lastestdate = CTM.TimePlusOffset(NewsObject.GetLastestNewsDate(),CTM.DaysS());//Day after the lastest recorded time in the database
      if(lastestdate<TimeCurrent())
        {
         Print("Necessary Files OutDated!");
         Print("Database Dates End at: ",lastestdate);
         Print("Dates after %s will not be available for backtest",lastestdate);
         Print("To Update Files:");
         Print("Run Program outside of the Strategy Tester");
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

现在剩下的就是编译文件以及EA,并将EA添加到图表上,开始对我们编写的代码进行分析。

最重要的是,看看经济日历能提供什么信息。

这些是我们的项目文件

项目文件


MQL5 经济日历目录

“导航器”窗口

一旦所有内容都编译完成,我们现在将打开交易终端,找到“导航器”窗口,并打开我们的“NewsTrading”文件夹。

将EA加载到图表上

我们现在将把EA附加到你选择的图表上。在本文中,我们将不启用“允许算法交易”,因为程序并不实施任何交易操作。我们点击OK加载EA。

EA打印的信息

一旦EA加载到图表上,你应该会在终端底部的“专家”选项卡中看到打印的文本,通知用户数据库已成功创建。

CommonFolderIDE

现在你需要找到交易终端上方的按钮,一旦找到该按钮,按下它,你会看到另一个窗口独立打开。

MT5 交易终端在菜单的左上方找到“文件”按钮,并选择打开MetaQuotes公共数据文件夹(Common Data Folder)。

文件目录 

你的文件目录应该与上面的图像相似,它会显示这个名为“Files”文件夹中的文件。

NewsTrading文件夹

NewsCalendar文件夹

Broker文件夹

你会看到NewsTrading文件夹已经创建了,以及其他许多文件夹

目录

Calendar Database 文件

一旦打开所有文件夹,你将找到你的经纪商单独的日历SQLite数据库。

我们回到MQL5 IDE中。

FolderIcon


在界面的左上角选择文件夹图标,移到Common Folder上。

数据库文件

接下来,我们将定位经纪人的Calendar SQLite数据库并打开它。

打开数据库

在导航窗口中,你应该能够看到数据库的内容。我们右键点击第一个表,并选择“打开表”。

AutoDST表

表将被打开,并显示来自“AutoDST”的所有记录。在这个表中,始终只会有一条记录,因为这就是我们的经纪商推荐的夏令时(DST)日历表。

Data_AU表

一旦我们打开名为“Data_AU”的表,该表将显示其中的所有记录以及所有列。

日历表连接

SQL语句

SELECT  None.EVENTNAME, DATE(REPLACE(None.EVENTDATE,'.','-')) as Date, TIME(REPLACE(None.EVENTDATE,'.','-')) as Time_None,

TIME(REPLACE(US.EVENTDATE,'.','-')) as Time_US, TIME(REPLACE(UK.EVENTDATE,'.','-')) as Time_UK, TIME(REPLACE(AU.EVENTDATE,'.','-')) as Time_AU

FROM 'Data_None' None

INNER JOIN 'Data_US' US on US.ID=None.ID

INNER JOIN 'Data_UK' UK on UK.ID=None.ID

INNER JOIN 'Data_AU' AU on AU.ID=None.ID

WHERE Date BETWEEN '2023-01-01' AND '2024-01-01' AND None.EVENTID='840100001';

目 的:此查询旨在从名为“Data_None”的表中,根据2023年1月1日至2024年1月1日之间的日期,显示事件ID为“840100001”的事 件名称(作为“Date”列显示日期,“Time_None”列显示时间)。同时,从“Data_UK”、“Data_US”和“Data_AU”这三个 表中选取相同的事件ID,但仅显示这些表中事件的时间,分别作为“Time_UK”、“Time_US”和“Time_AU”列。在这个查询中,我们想要 展示2023年全年中,同一事件在不同日历表之间的时间差异。

在上面的SQL查询中,我们为表“Data_None”设置了别名“None”,为表“Data_US”设置了别名“US”,同样地,我们也为表“Data_UK”设置了别名“UK”,以及为表“Data_AU”设置了别名“AU"。

我们SELECT

None.EVENTNAME

从表“'Data_None”中。

SQL函数

REPLACE(None.EVENTDATE,'.','-')

None.EVENTDATE值中的字符('.')被替换为字符('-')这是因为SQL

DATE()

函数只接受使用短横线作为字符分隔符的日期时间格式,而不接受使用点作为字符分隔符的日期时间格式。这个函数将时间变量转化为数值。

时间

这个SQL函数将datetime转换为time类型

不同的国家

SQL语句 

SELECT Distinct(COUNTRY) FROM 'Data_None';

目的:此查询为我们提供了在日历表“Data_None”中有记录的所有唯一(不重复)国家。

ExportResults

如果你想保留SQL查询的结果以供后续分析,可以右键点击其中一个结果,然后选择导出CSV文件。这个CSV文件之后可以被转换成Excel文件。

用于后续更加高效的执行分析。 

新西兰

SQL语句 

SELECT * FROM 'Data_None' where COUNTRY='New Zealand';

目的:这个查询语句让我们获取特定国家所有的记录。新西兰财政部

Unique Event IDs

SQL语句 

SELECT Distinct(EVENTID), COUNTRY, EVENTNAME,EVENTTYPE, EVENTSECTOR, EVENTIMPORTANCE, EVENTFREQUENCY,
EVENTCURRENCY, EVENTCODE FROM 'Data_None' 
where COUNTRY='New Zealand';

目的:此查询检索特定国家的所有唯一事件ID,并选择特定列,如“EVENTNAME”和“EVENTSECTOR”等。

但是特别注意,不包括“EVENTDATE”,因为每个“EVENTID”都有一个不同的“EVENTDATE”。在查询语句中包括"EVENTDATE"会返回错误。

特定的EventSector

SQL语句 

SELECT Distinct(EVENTID), COUNTRY, EVENTNAME, EVENTTYPE, EVENTIMPORTANCE FROM 'Data_None' 
where COUNTRY='New Zealand' and EVENTSECTOR='CALENDAR_SECTOR_PRICES';

目的:此查询检索特定“COUNTRY”和“EVENTSECTOR”下的所有唯一事务ID。

重要的事件

SQL语句

SELECT COUNTRY,EVENTNAME, EVENTIMPORTANCE FROM 'Data_None' 
where EVENTIMPORTANCE='CALENDAR_IMPORTANCE_HIGH'
UNION
SELECT COUNTRY,EVENTNAME, EVENTIMPORTANCE FROM 'Data_None' 
where EVENTIMPORTANCE='CALENDAR_IMPORTANCE_HIGH';

目的:此查询检索所有事件重要性为“CALENDAR_IMPORTANCE_HIGH”的唯一记录。


总结

从这篇文章中,我们可以学到很多东西。我希望在阅读完这篇文章后,你能学到一些新知识并获得一些新想法。在本文中,我们探讨了为什么使用数据库是正确的选择,以及使用数据库的一些好处。我们还了解了MQL5经济日历提供的所有货币以及夏令时(DST)的概念。此外,我们还编写了多个关键文件来创建日历数据库,并决定是更新现有数据库还是创建一个新数据库。

最后,我们学习了如何在创建数据库文件后找到它们,并使用基本的SQL查询从表中提取特定数据以进行操作。在下一篇文章中我们将详细了解风险管理。再见。

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/14324

附加的文件 |
最近评论 | 前往讨论 (1)
amrali
amrali | 22 4月 2024 在 09:27

文章写得不错,代码也编写得很好。

我喜欢您在欧元兑美元图表上通过价格对美国非农就业数据的反应来获取经纪商 DST 类型的方法,它工作得非常完美。

我还在多个经纪商(DST_NONE、DST_UK 和 DST_US)上测试了美国非农就业(NFP)事件的 DST 调整,结果显示它们在 CDaylightSavings_AU::adjustDaylightSavings() 方法及其他两个类中的同类方法中得到了正确计算。不过,通过数学公式直接计算 DST 切换时间,而不是通过 CArrayObj() 线性搜索硬编码值,可以大大提高识别日光时间过程的性能。请参见此处

还要注意的是,SymbolInfoString(SymName,SYMBOL_CURRENCY_BASE) 在某些经纪商服务器上可能会失败,因为他们没有正确配置该属性(他们"错误地 " 将 EURUSD 的基准货币设置为美元而不是欧元),因此使用 StringSubstr(SymName,0,3) 更为安全。

谢谢。

在MQL5中构建自适应的自动化交易系统(EA) 在MQL5中构建自适应的自动化交易系统(EA)
建立前瞻性的EA,并根据任何市场进行调整。
使用 LSTM 神经网络创建时间序列预测:规范化价格和令牌化时间 使用 LSTM 神经网络创建时间序列预测:规范化价格和令牌化时间
本文概述了一种使用每日范围对市场数据进行归一化并训练神经网络以增强市场预测的简单策略。开发的模型可以与现有的技术分析框架结合使用,也可以单独使用,以帮助预测整体市场方向。任何技术分析师都可以进一步完善本文中概述的框架,以开发适用于手动和自动交易策略的模型。
神经网络变得简单(第 74 部分):自适应轨迹预测 神经网络变得简单(第 74 部分):自适应轨迹预测
本文介绍了一种相当有效的多个体轨迹预测方法,其可适配各种环境条件。
分歧问题:深入探讨人工智能的复杂性可解释性 分歧问题:深入探讨人工智能的复杂性可解释性
在这篇文章中,我们将探讨理解人工智能如何工作的挑战。人工智能模型经常会以难以解释的方式做出决策,这就是所谓的 "分歧问题"。这个问题是提高人工智能透明度和可信度的关键。