MQL5 编程基础:时间 2
 (2016-04-25 22:19:42)
	
			
					(2016-04-25 22:19:42)		| 分类: 工作修行 | 
确定日开始时间以及自日开始以来流逝的时间量
根据某给定时间来确定日开始时间最显而易见的方式,就是将时间分解成其各个分量,将小时、分钟和秒归零,然后再把它们加起来。但是,还有一种更简单的方式。一天有 86400 秒。我们需要获取一个时间除以一天秒数所得结果的整数,再乘以一天的秒数:
datetimetm=TimeCurrent(); tm=(tm/86400)*86400; //--- output result Alert("Day start time: "+(string)tm); 
注意! 
MathFloor(tm/86400)*86400
乘法运算之后,则应利用 
MathRound(MathFloor(tm/86400)*86400)
采用 integer 变量时,余数会被自动截掉。处理时间的过程中,很少会用到双精度或浮点型变量;如果用到它们,极有可能表明方法从根本上错了。
要确定自日开始起流逝的秒数,我们只需取时间除以 86400 的余数:
datetimetm=TimeCurrent(); long seconds=tm%86400; //--- output result Alert("Time elapsed since the day start: "+(string)seconds+" sec."); 
也可以用类似的方法,将获取的以秒计算的时间,转换为小时、分钟和秒。作为一个函数实施:
intTimeFromDayStart(datetime aTime,int &aH,int &aM,int &aS) { //--- Number of seconds elapsed since the day start (aTime�400), //--- divided by the number of seconds in an hour is the number of hours aH=(int)((aTime%86400)/3600); //--- Number of seconds elapsed since the last hour (aTime600), //--- divided by the number of seconds in a minute is the number of minutes aM=(int)((aTime%3600)/60); //--- Number of seconds elapsed since the last minute aS=(int)(aTime%60); //--- Number of seconds since the day start return(int(aTime%86400)); } 
第一个传递的参数是时间。其它参数均用于返回值:aH - 小时,aM - 分钟,aS - 秒。此函数本身会返回自日开始起的总秒数。我们来看看这个函数:
datetimetm=TimeCurrent(); int t,h,m,s; t=TimeFromDayStart(tm,h,m,s); //--- output resultAlert("Time elapsed since the day start ",t," s, which makes ",h," h, ",m," m, ",s," s "); 
您也可以计算确定日开始柱的指标中待使用的天数:
boolNewDay=(time[i]/86400)!=(time[i-1]/86400); 
假设各柱由左至右索引,其中 time[i] 为当前柱的时间,而 time[i-1] 则为前一柱的时间。
 
确定周开始时间以及自周开始以来流逝的时间量
与确定日开始相比,确定周开始时间稍微复杂一些。尽管一周的天数是恒定的,而且也能计算出一周的秒数时长(604800 秒),但是只是计算出自时间戳记开始起流逝的整周数、再乘以周的持续时长是不够的。
问题在于,大多数国家的一周都是从周一开始,而有一些国家(美国、加拿大、以色列等)却是从周日开始。但我们还记得,时间测量的时间戳从周四开始。如果周四是一周的第一天,那么有这些简单的计算就足够了。
出于方便考虑,我们将以第一个时间戳日对应 0 值为例,来研究确定周开始的特殊性。我们需要找到这样一个值:当将添加到时间时,会改变时间戳的首日 (1970.01.01 00:00),从零开始计数,到第四日,即我们需要添加四日的持续时长。如果一周从周一开始,则周四是第四日,所以我们需要添加三日的持续时长。但如果一周从周日开始,则周四是第五日,所以我们需要添加四日的持续时长。
我们编写一个函数来计算周数:
longWeekNum(datetime aTime,bool aStartsOnMonday=false) { //--- if the week starts on Sunday, add the duration of 4 days (Wednesday+Tuesday+Monday+Sunday), // if it starts on Monday, add 3 days (Wednesday, Tuesday, Monday) if(aStartsOnMonday) { aTime+=259200; // duration of three days (86400*3) } else { aTime+=345600; // duration of four days (86400*4) } return(aTime/604800); } 
此函数可在确定新一周的第一个柱的指标中发挥作用:
boolNewWeek=WeekNum(time[i])!=WeekNum(time[i-1]); 
假设各柱由左至右索引,其中 time[i] 为当前柱的时间,而 time[i-1] 则为前一柱的时间。
现在,我们可以计算周开始的时间。由于为了计算一周的天数,我们假设时间戳的开始提前三(或四)天,现在我们需要执行反向的时间纠正:
longWeekStartTime(datetime aTime,bool aStartsOnMonday=false) { long tmp=aTime; longCorrector; if(aStartsOnMonday) { Corrector=259200; // duration of three days (86400*3) } else { Corrector=345600; // duration of four days (86400*4) } tmp+=Corrector; tmp=(tmp/604800)*604800; tmp-=Corrector; return(tmp); } 
此函数会返回一个 
现在,我们拥有了周开始的时间,就可以计算自周开始起流逝的秒数了:
longSecondsFromWeekStart(datetime aTime,bool aStartsOnMonday=false) { return(aTime-WeekStartTime(aTime,aStartsOnMonday)); } 
秒数可被转换为日、小时、分钟和秒。尽管计算自日开始起的小时、分钟和秒不难,但像这种情况采用 TimeToStruct() 函数会更简单:
longsfws=SecondsFromWeekStart(TimeCurrent()); MqlDateTime stm; TimeToStruct(sfws,stm); stm.day--; Alert("Time elapsed since the week start "+(string)stm.day+" d, "+(string)stm.hour+" h, "+(string)stm.min+" m, "+(string)stm.sec+" s"); 
请注意,stm.day 值被减了 1。月的号数是从 1 计数,因为我们需要确定所有日数。有些人可能觉得,从实用角度看,这部分内容没什么用处。但您对于上述函数的了解作为处理时间的一项经验,十分有价值。
 
确定自某个给定日期、年开始或月开始以来的周数
注意 MqlDateTime 结构的各个字段,尤其是 day_of_year 字段,有人会喜欢创建确定自年开始和月开始起周数的函数。最好是编写一个确定自某给定日期起周数的一般函数。此函数的运行原理,与确定自时间戳开始起的周数所使用的函数类似:
longWeekNumFromDate(datetime aTime,datetime aStartTime,bool aStartsOnMonday=false) { long Time,StartTime,Corrector; MqlDateTime stm; Time=aTime; StartTime=aStartTime; //--- determine the beginning of the reference epoch StartTime=(StartTime/86400)*86400; //--- determine the time that elapsed since the beginning of the reference epoch Time-=StartTime;//--- determine the day of the week of the beginning of the reference epoch TimeToStruct(StartTime,stm); //--- if the week starts on Monday, numbers of days of the week are decreased by 1, // and the day with number 0 becomes a day with number 6 if(aStartsOnMonday) { if(stm.day_of_week==0) { stm.day_of_week=6; } else { stm.day_of_week--; } } //--- calculate the value of the time corrector Corrector=86400*stm.day_of_week; //--- time correction Time+=Corrector; //--- calculate and return the number of the week return(Time/604800); } 
基于此函数,我们编写两个确定自年开始、自月开始起周数的函数。为此,我们首先需要确定年开始的时间和月开始的时间。将时间分解成其分量,调整某些字段的值,并将各分量重新转换为时间标记。
- 
确定年开始时间的函数: datetime YearStartTime(datetime aTime) { MqlDateTime stm; TimeToStruct(aTime,stm); stm.day=1; stm.mon=1; stm.hour=0; stm.min=0; stm.sec=0; return(StructToTime(stm)); } 
- 
确定月开始时间的函数: datetime MonthStartTime(datetime aTime) { MqlDateTime stm; TimeToStruct(aTime,stm); stm.day=1; stm.hour=0; stm.min=0; stm.sec=0; return(StructToTime(stm)); } 
现在,下面就是确定自年开始、月开始起周数的函数。
- 
自年开始起: long WeekNumYear(datetime aTime,bool aStartsOnMonday=false) { return(WeekNumFromDate(aTime,YearStartTime(aTime),aStartsOnMonday)); } 
- 
自月开始起: long WeekNumMonth(datetime aTime,bool aStartsOnMonday=false) { return(WeekNumFromDate(aTime,MonthStartTime(aTime),aStartsOnMonday)); } 
最终,我们开始着手纯粹的实务。
 
创建试验工具集
前面提到过,有些交易中心的报价包含周日柱,也有的在整个周末都持续提供报价。我们要确保必要的函数在所有情况下都正常运行。当然,我们可以到互联网上找到一些适用的交易中心,并利用演示账户上的报价来测试各函数的运行情况。但是,除了寻找正确的交易中心外,我们还必须在图表中寻找适当的位置来运行所需测试。
我们来创建自己的测试函数测试区域。而周五、周末和周一则是我们的关注重点。我们将根据需要,创建一个包含周五、周一和周末柱时间的数组。共有 4 个选项:
- 无周末柱。
- 周日末尾的一些柱,即 4 个。
- 持续的周末报价。
- 周六柱,但没有周日柱。
为避免数组变得过大,我们将采用 H1 时间框架。数组最大尺寸将是 96 个元素(每天 24 个柱乘 4 天),而数组本身将在利用图形对象绘制时匹配图表。所以,我们会通过启动某指标时第一次执行 OnCalculate() 函数的类似方式,得到一种带有时间和可在循环中的数组上进行迭代的、类似指标缓冲区的东西。由此,我们将能够实现函数运行的可视化。
此工具以随附于本文的一个脚本的形式实施(sTestArea.mq5 文件)。准备工作则于脚本的 OnStart() 函数内执行。此函数代码最开头的 Variant 变量允许您选取上面列出的 4 个选项中的 1 个。而在 OnStart() 函数下方,您可以看到类似于指标的 OnCalculate() 函数的 LikeOnCalculate() 函数。此函数有两个参数:rates_total - 柱数,以及 time[] - 带柱时间的数组。
此外,我们会继续在此函数内工作,就像我们在编写一个指标一样。您可以通过调用 SetMarker() 函数,由此函数设定一个标记。传递给 SetMarker() 函数的参数分别为:柱索引、缓冲区索引(标记显示的行)及标记颜色。
图 4 所示为脚本性能结果,Variant 变量为 2,且在每个柱下设置两个标记行(用相关时间戳记标记柱)。所有图表元素的颜色设置均不可见。
https://c.mql5.com/2/5/005.png 
图 4. sTestArea.mq5 脚本性能
柱时间戳记根据周几取色:周五 - 红色,周六 - 洋红色,周日 - 绿色,周一 - 蓝色。现在,我们可以继续编写对周末柱需要特殊方法、且能够可视化监控其工作的各种函数了。
 
支点指标 - 备选 1
我们首先尝试创建一个简单的支点指标。要计算支点线,我们需要清楚昨日的收盘价格,以及昨日的最高和最低价格。该指标值是作为上述三值的平均进行计算。我们会找出一日内的新高新低,计算新的一日开始时的支点值,并进一步绘制并显示全天的价格。
我们将提供该指标运行的两种版本:
- 新的每一天,都计算支点(假设没有周末柱)。如果有周末柱,则周六和周日柱会被区别对待。
- 周六柱属于周五,而周日柱则属于周一(整个周末都持续提供报价、以及仅存在周日柱的情况均适用)。这里,您要记住的是,周末很可能没有柱。
在第一种版本中,只确定新的一天的开始就足够了。我们将当前时间 (aTimeCur) 和前一时间 (aTimePre) 传递给此函数,计算自时间戳开始起的天数,而且如果它们不匹配,我们就推断新的一天已经开始:
boolNewDay1(datetime aTimeCur,datetime aTimePre) { return((aTimeCur/86400)!=(aTimePre/86400)); } 
第二种版本。如果周六已开始,则日开始应被忽略。如果周日已开始,我们则定义日开始(这自然只是另一天)。如果周一继周日后开始,则略过日开始。如果一周中其它任何一天(比如周六或周五)先于周一,则定义日开始。所得函数如下:
boolNewDay2(datetime aTimeCur,datetime aTimePre) { MqlDateTime stm; //--- new day if(NewDay1(aTimeCur,aTimePre)) { TimeToStruct(aTimeCur,stm); switch(stm.day_of_week) { case 6: // Saturday return(false); break; case 0: // Sunday return(true); break; case 1: // Monday TimeToStruct(aTimePre,stm); if(stm.day_of_week!=0) { // preceded by any day of the week other than Sunday return(true); } else { return(false); } 

 加载中…
加载中…