一、简介1. 系列函数功能介绍 OpenD、HighD、LowD和CloseD系列函数都是通过调用OHLCPeriodsAgo或者OHLCPeriodsAgoGen函数来返回指定时间段内的指定价格,例如HighD(0)返回当天的最高价,HighD(1)返回前一天的最高价,以此类推,但是最多只能返回当天之前的第50天的最高价,也就是说HighD(51)不允许使用,HighD(50)允许使用,最多向历史引用50个数据对该系列所有函数都适用;由于OHLCPeriodsAgo和OHLCPeriodsAgoGen两个函数都返回开高低收4个价格,所以它们的参数需要用到numericref传址类型来输出多个结果。
由于指定时间段内的开、高、低和收盘价格的计算原理是相同的,所以我这里只对最高价的相关内容进行阐述,开盘价、最低价和收盘价格可以根据最高价的内容进行类推,在此不再赘述;HighS、HighD、HighW、HighM、HighY函数都是返回指定时间段的收盘价,不同的是,它们返回的是不同的时间周期的收盘价,五个函数后面的大写字母分别是S、D、W、M和Y,五个字母分别代表Session(时段)、Day(日)、Week(周)、Month(月)、Year(年),由于HighD和HighW、HighM、HighY函数的原理相同,所以这里我着重阐述HighS和HighD的相关内容,HighW、HighM、HighY函数的相关内容可以根据HighD进行类推;OpenD、HighD、LowD和CloseD系列函数的功能的简单介绍可以看表1 OpenD、HighD、LowD和CloseD系列函数。
关于日期的定义,表1中出现两个名词,一个是交易所时段,另一个是物理时段。交易所时段的一天,指的是将前一天晚上的夜盘计算到当天的日盘上,HighS返回指定日(比如2017-08-03号)的最高价,那么实际返回的就是指定日前一天夜盘(2017-08-02号晚上)和指定日(2017-08-03号)白天的商品合约的最高价格,这是对于内盘期货而言是这样计算的;对于股票,由于没有夜盘,所以HighS只计算日盘的最高价格;对于外盘期货,HighS的计算也是按照交易所时段来计算的,这个具体看外盘交易所如何规定交易日的时间范围。物理时段的一天,很好理解,就是从指定日的00:00:00到23:59:59的时间范围内的最高价格;物理时段的一周、一月和一年都是基于物理时段的一天进行累加,所以HighW返回的是指定周的最高价,HighM返回的是指定月的最高价,HighY返回的是指定年的最高价。
2. 内容安排 本文分成五个大部分,由浅由深的阐述OpenD、HighD、LowD和CloseD系列函数的功能介绍、计算原理、灵活运用、代码解析及函数扩展与构造;如果您只是想基本了解该系列函数的基本用法,您只需要看第一部分;如果您想在使用中避免一些误区,那么您需要看前三个部分;如果您想理解该系列函数的代码逻辑以便于完全掌握它们,甚至通过学习该系列函数的构造原理,您需要构造相同逻辑的其它功能函数,那么您需要看完这篇文章。 表1 OpenD、HighD、LowD和CloseD系列函数 MC函数名称 | 函数功能 | OHLCPeriodsAgo | 通过传址参数,输出指定时间段内的开、高、低和收盘价格 | OpenS | 调用OHLCPeriodsAgo函数,返回指定日(交易所时段)的开盘价 | OpenD | 调用OHLCPeriodsAgo函数,返回指定日(物理时段)的开盘价 | OpenW | 调用OHLCPeriodsAgo函数,返回指定周(物理时段)的开盘价 | OpenM | 调用OHLCPeriodsAgo函数,返回指定月(物理时段)的开盘价 | OpenY | 调用OHLCPeriodsAgo函数,返回指定年(物理时段)的开盘价 | HighS | 调用OHLCPeriodsAgo函数,返回指定日(交易所时段)的最高价 | HighD | 调用OHLCPeriodsAgo函数,返回指定日(物理时段)的最高价 | HighW | 调用OHLCPeriodsAgo函数,返回指定周(物理时段)的最高价 | HighM | 调用OHLCPeriodsAgo函数,返回指定月(物理时段)的最高价 | HighY | 调用OHLCPeriodsAgo函数,返回指定年(物理时段)的最高价 | LowS | 调用OHLCPeriodsAgo函数,返回指定日(交易所时段)的最低价 | LowD | 调用OHLCPeriodsAgo函数,返回指定日(物理时段)的最低价 | LowW | 调用OHLCPeriodsAgo函数,返回指定周(物理时段)的最低价 | LowM | 调用OHLCPeriodsAgo函数,返回指定月(物理时段)的最低价 | LowY | 调用OHLCPeriodsAgo函数,返回指定年(物理时段)的最低价 | CloseS | 调用OHLCPeriodsAgo函数,返回指定日(交易所时段)的收盘价 | CloseD | 调用OHLCPeriodsAgo函数,返回指定日(物理时段)的收盘价 | CloseW | 调用OHLCPeriodsAgo函数,返回指定周(物理时段)的收盘价 | CloseM | 调用OHLCPeriodsAgo函数,返回指定月(物理时段)的收盘价 | CloseY | 调用OHLCPeriodsAgo函数,返回指定年(物理时段)的收盘价 | OHLCPeriodsAgoGen | 相比OHLCPeriodsAgo函数,该函数只是多了一个参数(子图编号) | HighDGen | 调用OHLCPeriodsAgoGen函数,返回子图指定日(物理时段)的最高价 | LowDGen | 调用OHLCPeriodsAgoGen函数,返回子图指定日(物理时段)的最低价 | 二、计算原理 HighS和HighD的计算原理实际上是通过逐个比较图表上指定日内的bar的最高价,来确定指定日的最高价(例如,图表的周期为1分钟,那么通过逐个比较前一天的每一根bar的最高价,最后将前一天所有的bar的最高价中的最大值通过HighD(1)返回),所以图表的周期不能大于1日;若图表的周期是2日,那么通过HighD和HighS返回的其实是指定bar的最高价,例如HighD返回的是当根bar的最高价,HighD(1)返回的是前一根bar的最高价,以此类推,因为HighD是的通过条件语句date<>date[1]进行判断的(就是当根bar的日期和前一根bar的日期进行比较,来区分不同的日),有点不一样的是,HighS是通过条件语句sessionlastbar[1]进行判断的;若图表的周期是1周及以上,那么HighD和HighS返回的是-1(还有两种情况是返回-1的,那就是当引用的数据超过50,例如HighD(51)返回-1,或者当数据不够时,也会返回-1,因为默认值是-1),这个判断是通过条件语句condition1=PeriodsAgo > 50 or BarType >2 or BarType > 4 进行判断的(bartype关键字是判断图表的周期类型,详细的可以看一下这个关键字的解释),当这个条件语句condition1成立的时候,返回值为-1。
所以在使用函数HighS和HighD时,图表的周期设为日内(秒、分、时)、ticks、合约或者1日;在使用函数HighW时,图表的周期需要设为日内(秒、分、时)、ticks、合约、1日或者1周(这里不建议使用周期是3日,因为这样会使某根bar上的数据分别来自两周,而函数HighW的计算中会根据bar的收盘时间来区分不同的周,所以最终会导致HighD返回错误的最高价,类似的情况在使用其它函数的时候也会出现);在使用函数HighM时,图表的周期需要设为日内(秒、分、时)、ticks、合约、1日、1周或者1月;在使用函数HighY时,图表的周期需要设为日内(秒、分、时)、ticks、合约、1日、1周、1月、2月、3月、4月、6月或者1年。 三、灵活运用1. 关键字sessionlastbar 前面已经阐述了交易所时段是根据关键字sessionlastbar来识别的,那么sessionlastbar是如何判断交易时段结束,这个可以在MC的报价管理器中交易时段设置中看到;现在举例上海期货交易所商品合约shfe.rb hot的交易时段(对应报价管理器交易时段),如表2 shfe.rb hot交易时段所示。
Sessionlastbar识别的是“结束”,两个“结束”之间的时间范围是一个“交易所时段”,而这个和实际的交易所时段是有一定的差别的;实际的交易日时段会将周五夜盘的行情计算到下周一的白天行情中,而sessionlastbar会将星期五的夜盘行情单独计算为一个交易日时段,星期一白天的行情单独计算为一个交易日时段。由于HighS是利用sessionlastbar关键字对交易日时段进行区分的,所以HighS并不能完全取到每一个实际交易日时段的最高价。 表2 shfe.rbhot交易时段
开盘 | 时间 | 收盘 | 时间 | 时段结束 | 星期一 | 09:00 | 星期一 | 13:30 | | 星期一 | 13:30 | 星期一 | 15:00 | 结束 | 星期一 | 21:00 | 星期一 | 23:00 | | 星期二 | 09:00 | 星期二 | 13:30 | | 星期二 | 13:30 | 星期二 | 15:00 | 结束 | 星期二 | 21:00 | 星期二 | 23:00 | | 星期三 | 09:00 | 星期三 | 13:30 | | 星期三 | 13:30 | 星期三 | 15:00 | 结束 | 星期三 | 21:00 | 星期三 | 23:00 | | 星期四 | 09:00 | 星期四 | 13:30 | | 星期四 | 13:30 | 星期四 | 15:00 | 结束 | 星期四 | 21:00 | 星期四 | 23:00 | | 星期五 | 09:00 | 星期五 | 13:30 | | 星期五 | 13:30 | 星期五 | 15:00 | 结束 | 星期五 | 21:00 | 星期五 | 23:00 | 结束 | 2. 子图运行机制 在图表上(为方便起见,这里将最大bar设置为0,不开启bar内)可以插入一个子图(多个子图是同样的道理),主图上的bar和子图的bar在图表上是以收盘时间进行先后排列的,如果主图上的bar(为方便叙述记为A)和子图上的bar(为方便叙述记为B)的收盘时间相同,那么这两个bar会在图表上同一条垂直线上排列;如果A的收盘时间小于B,那么A在B的左边排列,B在A的右边排列。如果在代码中不出现data2,那么实际上主图上有多少根bar,代码就计算多少次,也就是说代码执行地计算不考虑子图data2的存在;如果代码中出现data2,那么实际上代码的计算会考虑图表上所有的bar的数目,每一根bar收盘都会计算一次,在同一垂直线上bar(分别来自不同的子图和主图)只会计算一次。
这个部分会很难理解,因为还涉及“重新计算“和”更新“的概念,需要大家多多测试代码(利用print输出数据进行观察),详细的解释参考图1和图2进行进一步学习 3. OHLCPeriodsAgoGen 和HighDGen函数 OHLCPeriodsAgoGen和OHLCPeriodsAgo的区别在于使用了子图的数据,而HighDGen函数是调用OHLCPeriodsAgoGen函数,返回子图指定日(物理时段)的最高价。尽管HighDGen函数是代码计算中逐个比较子图中每一根bar的最高价然后取最高价返回,但是这其中会涉及两种计算,一种是“重新计算”,另一种是“更新”,只有当代码的计算是基于主图的bar进行计算(也就是“更新”),此时的比较才是有意义的,这时候比较出来的最高价才会通过HighDGen函数返回。例如,主图是1小时周期,子图是1分钟周期,假设2017-08-02号有7根小时bar,那么只是在7次基于主图bar计算时取的子图的7个最高价进行比较,然后通过HighDGen函数返回7个中的最高值。这部分内容,有兴趣可以自己研究一下。 四、代码简析 这部分以OHLCPeriodsAgo函数内部代码进行解析,该系列其它函数根据这个函数的解析就很好理解了。HighS、HighD、HighW、HighM和HighY函数都是调用OHLCPeriodsAgo函数返回相应时段的最高价,而OHLCPeriodsAgo函数内部是通过参数PeriodType对这5个函数进行区分计算(PeriodType为0时,表示Session;PeriodType为1时,表示Day;PeriodTypeo为2时,表示Week;PeriodType为3时,表示Month;PeriodType为4时,表示Year)另外一个重要的参数是PeriodsAgo,这个是向历史引用的参数,最多只能向历史引用50个数据。代码的解析以HighD为例。 inputs: PeriodType( numericsimple ), PeriodsAgo( numericsimple ), oPeriodOpen( numericref ), oPeriodHigh( numericref ), oPeriodLow( numericref ), oPeriodClose( numericref ) ; {oPeriodOpen、oPeriodHigh 、oPeriodLow和oPeriodClose都是传址参数,它们分别用于输出开盘价、最高价、最低价和收盘价} variables: var0( 0 ), sess_last_bar(false) ; arrays: arr0[ 4, 50 ]( -1 ) ; {OHLCPeriodsAgo函数在PeriodType和PeriodsAgo这个参数确定的情况下会返回4个价格(开盘价、最高价、最低价和收盘价),而数组arr0被用于存储这4个价格,每个价格会有50个历史数据被存储在这个数组中,方便调用,默认初始值为-1;因为多维数据不能动态调用数组大小,所以最多只能向历史引用50个数据} sess_last_bar = sessionlastbar; condition1 = PeriodsAgo > 50 or BarType > IFF(PeriodType<>0, PeriodType + 1, PeriodType + 2) or BarType > 4 ; {这部分是判断语句,当condition1成立时,所有都返回-1;也就是说,它限制向历史引用的数据超过50,它只允许图表的周期类型是ticks、合约、秒、分、时、日、周、月、季和年,它不允许图表的周期大于需要返回的交易时段(这里的逻辑并不很严谨)} if condition1 then begin oPeriodOpen = -1 ; oPeriodHigh = -1 ; oPeriodLow = -1 ; oPeriodClose = -1 ; OHLCPeriodsAgo = -1 ; end else begin if PeriodType = 0 then Condition1 = sess_last_bar[1] else if PeriodType = 1 then Condition1 = Date <> Date[1] else if PeriodType = 2 then Condition1 = DayOfWeek( Date ) < DayOfWeek( Date[1] ) else if PeriodType = 3 then Condition1 = Month( Date ) <> Month( Date[1] ) else if PeriodType = 4 then Condition1 = Year( Date ) <> Year( Date[1] ) ; {通过PeriodType来判断session、Day、Week、Month和Year} condition1 = CurrentBar = 1 or Condition1 ; {当currentbar=1成立时或者当根bar的日期和前一根bar的日期不一致时,那么首先将当根bar的最高价赋值给arr0[2,var0],因为是新的交易日的开始,所以必须存储到数组的新的存储单元中,那么这个是通过var0循环减一达到目的的} if condition1 then begin var0 = var0 - 1 ; if var0 = -1 then var0 = 50 ; arr0[ 1, var0 ] = O ; arr0[ 2, var0 ] = H ; arr0[ 3, var0 ] = L ; arr0[ 4, var0 ] = C ; end else begin condition1 = H > arr0[ 2, var0 ] ; if condition1 then arr0[ 2, var0 ] = H ; condition1 = L < arr0[ 3, var0 ] ; if condition1 then arr0[ 3, var0 ] = L ; arr0[ 4, var0 ] = C ; end ; {当Date=Date[1]时,执行else语句,也就是逐个将当前bar的最高价与arr0[2,var0]进行比较,然后将最大的值重新存储在arr0[2,var0]中,不断更新} oPeriodOpen = arr0[ 1, Mod( var0 + PeriodsAgo, 51 ) ] ; oPeriodHigh = arr0[ 2, Mod( var0 + PeriodsAgo, 51 ) ] ; oPeriodLow = arr0[ 3, Mod( var0 + PeriodsAgo, 51 ) ] ; oPeriodClose = arr0[ 4, Mod( var0 + PeriodsAgo, 51 ) ] ; {执行完if-else语句之后,再更新oPeriodHigh;这里使用到数学中的求余的技巧,举例一下就比较清楚了;当PeriodsAgo为0时,那么oPeriodHigh的值是不断更新变化的,当然HighD(PeriodsAgo)也是不断更新变化的;当PeriodsAgo为1时,那么oPeriodHigh的值取的是arr0[2,var0+1]的值,这个值不会每根bar都变化了,它是前一日的最高价} OHLCPeriodsAgo = 1 ; end ; if false then Value1 = OHLCPeriodsAgo[1] ; 五、函数扩展与构造这个是计算指定日(物理时段)的成交量的函数代码,由于使用的是动态数组,所以它向历史引用的数据不受数量的限制;这个代码在附件中,有兴趣的可以下载一下。 |