: | : | :期货程序化 | :期货程序化研究 | :期货量化学习 | :期货量化 |
返回列表 发帖

信号脚本计算机制(第一部分)

信号脚本计算机制(第一部分)

信号脚本计算机制(第一部分)

(原创:Alex)

这篇关于信号脚本计算机制的帖子,会系统阐述信号脚本的各种计算方式,每种计算方式的驱动机制不一样,通常的驱动因素是基于行情,而除了行情的驱动还有一些其它的驱动因素;每种驱动因素都会导致信号脚本执行计算,而计算过程中变量的存储和调用的机制又是如何进行的,另外,每种计算又是如何委托发单的,这两点是本文的重点;除了这两个重点之外,还需要阐述一下相关的知识点,如最大bar数量、回溯、bar的状态,以此来更深刻的理解上面提到的两个重点。

尽管本文阐述的是信号脚本的计算机制,但是对于指标脚本,也可以进行类推理解,这里不再深入阐述;另外,本文是基于时间周期图表,并不涉及其它周期图表,也不涉及跨周期调用子图的阐述,因为这样会使整篇文章的主线模糊不容易理解;关于非时间周期图表,如tick周期,大家可以根据这篇帖子进行类推理解,后续若有时间会写一篇关于非时间周期图表的帖子;由于跨周期调用子图的阐述涉及的知识点更多,而且需要以本文作为铺垫,所以跨周期调用子图的内容会安排在这篇帖子之后进行编写。

一、最大bar数量


1. 最大bar数量

  如图1所示,策略的最大bar数量在策略属性中进行设置,默认为50;信号脚本会从图表左边开始的第51bar开始计算,依次向图表右边进行执行计算,这是策略(信号)基于bar的一个计算顺序,从左边向右边逐根bar执行计算。那么很明显的问题是,为什么要设置最大bar数量,这个有什么用?答案是用于回溯,因为信号基于当根bar(假设是图表上第51bar)执行计算时,有时需要回溯当根bar前若干根bar上的信息进行计算判断(假设此时当根bar为图表上第51bar,即图中的2151分钟的收盘时间线位置,而信号基于第51bar进行计算时,需要调用图表上第49bar上的收盘价信息,那么此时就需要进行回溯);退一步考虑,若最大bar数量设置为0,而当根bar为图表上第1bar,并且信号基于当根bar的计算时需要调用前1bar上的信息,例如收盘价信息,但是此时当根bar就是图表上第1bar,从而当根bar前面没有bar可用于回溯调用。这就是最大bar数量的用途所在。

1. 最大bar数量,相对编号和绝对编号

下方当我们涉及到最大bar数量时,我们始终将策略属性中的最大bar数量设置成默认的50,为方便叙述起见。引入了最大bar数量之后,就引入了两个概念,分别是bar的相对编号和绝对编号。Bar的编号是每根bar的一个位置,位置分为相对位置和绝对位置;bar的绝对编号就是绝对位置,和最大bar数量没有关系,它是基于图表中第1bar依次进行编号的,即图表中第1bar的绝对编号为1,第51bar的绝对编号为51bar的相对编号就是相对位置,它是基于图表中最大bar数量之后的bar依次进行编号的,一句话就是,bar的相对编号=bar的绝对编号—最大bar数量。


关键字currentbar返回的数值就是当根bar的相对编号,关键字maxbarsback反应的数值就是策略属性中设置的最大bar数量;基于这两个关键字,当根bar的绝对编号就等于两个关键字的返回值之和。

2. 回溯


2. 回溯

如图2所示,我们以相对编号为1bar为当根bar进行分析,也就图中收盘时间为2151的时间线所在的位置为当前策略(信号)正在执行计算的位置,可以看出,当根barbar编号=51,这个是它的绝对编号,图表显示出来的bar的编号都是绝对编号。基于当根bar进行计算时,需要调用前几根bar的信号,例如调用前几根bar上的收盘价进行分析,相应的代码如下:

//信号代码

if currentbar=1 then begin

print("currentbar=",currentbar,",time[0]=",time[0],",close[0]=",close[0]);


print("time[1]=",time[1],",close[1]=",close[1]);


print("time[2]=",time[2],",close[2]=",close[2]);


print("time[3]=",time[3],",close[3]=",close[3]);


print("time[4]=",time[4],",close[4]=",close[4]);


print("time[5]=",time[5],",close[5]=",close[5]);


print("time[6]=",time[6],",close[6]=",close[6]);


print("time[7]=",time[7],",close[7]=",close[7]);


print("time[8]=",time[8],",close[8]=",close[8]);

end;

//输出

currentbar=   1.00,time[0]=2151.00,close[0]=3354.00

time[1]=2150.00,close[1]=3353.00

time[2]=2149.00,close[2]=3353.00

time[3]=2148.00,close[3]=3352.00

time[4]=2147.00,close[4]=3352.00

time[5]=2146.00,close[5]=3351.00

time[6]=2145.00,close[6]=3350.00

time[7]=2144.00,close[7]=3353.00

time[8]=2143.00,close[8]=3353.00


在当根bar的位置调用当根bar的收盘价可以使用close[0]或者close即可,对应到图2中的“0”标记;调用前第1bar的收盘价可以使用close[1]即可,对应到图2中的“1”标记;调用前第2bar的收盘价可以使用close[2]即可,对应到图2中的“2”标记;调用前第3bar的收盘价可以使用close[3]即可,对应到图2中的“3”标记,其它以此类推即可,但是最多只能回溯前第50bar的收盘价,因为回溯的数量不能超过策略属性中的最大bar数值50

但是并不是所有的关键字都有回溯的功能,比如关键字currentbar就不能进行回溯,currentbar[1]始终返回的数值等于currentbar返回的数值;关键字currentbar不能进行回溯,但是使用currentbar[2]这种形式并不会报错,只是currentbar[2]始终等于currentbar,对于currentbar[3]currentbar[4]也始终等于currentbar的返回值。其它不能回溯的关键字在这里不一一叙述,请参考关键字currentbar的使用情况;我们可以将其数值赋值给变量,然后通过变量的回溯功能来回溯前第Nbar上的currentbar的数值,代码如下:

//信号代码:

value1=currentbar;

print("currentbar=",currentbar,",value1=",value1,",value1[1]=",value1[1]);

//输出

currentbar=   1.00,value1=   1.00,value1[1]=   0.00

currentbar=   2.00,value1=   2.00,value1[1]=   1.00

currentbar=   3.00,value1=   3.00,value1[1]=   2.00

3. symbol系列关键字

symbol系列关键字有symbol_closesymbol_currentbarsymbol_lengthsymbol_timesymbol_datesymbol_downtickssymbol_highsymbol_lowsymbol_opensymbol_openintsymbol_tickidsymbol_tickssymbol_timesymbol_time_ssymbol_uptickssymbol_volume,但是本文并不打算将symbol系列关键字全部进行阐述清楚,我们只选择如下4个进行阐述,其它的symbol类关键字以此类推即可;之所以阐述symbol系列关键字,是因为它们在使用上不受最大bar数量的限制,这点是关键的地方。

1. Symbol系列关键字

Symbol关键字名称

简介

Symbol_close

返回bar的收盘价

Symbol_currentbar

返回bar的绝对编号


情境一:

//信号代码

if currentbar=1 then


close[51];

由于这段代码的存在,会弹出一个窗口报错提示“指标运算试图参考的bar的数量(51)比当前设置的“指标运算参考的最大bar数量”多,这是因为最大bar数量设置为50,而关键字close调用了前第51bar上的收盘价,回溯的数量已经超过了最大bar数量50的限制,这是不允许的。

情境二:

//信号代码

if currentbar=1 then


symbol_close[51];


这段代码的存在仍然会弹出一个与情境一相同的窗口报错提示,但是这里我们的解释与上面不一样;报错的原因是因为关键字symbol_close调用(回溯)不存在的bar上的收盘价信息而导致报错提示;因为相对编号为1bar的前面最多只有50bar,所以最多只能调用(回溯)前第50bar上的数据,这点与最大bar数量限制没有关系。

情境三:

//信号代码

if currentbar=10 then


close[51];

这段代码的存在会弹出一个与情境一相同的窗口报错提示,原因与情境一完全一样,是因为回溯的数量超过了最大bar数量50的限制。

情境四:

//信号代码

if currentbar=10 then


symbol_close[51];


这段代码的存在不会弹出窗口报错提示,原因是因为相对编号为10bar的前面有59bar,这个数量大于当前回溯的数量51,所以不会报错。

通过这4个情境,我们已经将symbol_closeclose的区别阐述清楚了,前者不受策略属性中的最大bar数量的限制,而后者会受最大bar数量的限制。对于其它的symbol类关键字,请参考symbol_closeclose之间的区别。

关键字Symbol_currentbar的返回值等于currentbarmaxbarsback的返回值之和,它表示的是当根bar的绝对编号,这是与currentbar的唯一区别;symbol_currentbarcurrentbar这两个关键字都不能回溯。

二、Bar的状态

每根bar都有自己的状态,因为有开始也有结束,开始了还没有结束,结束了还没有开始,总而言之每根bar只有4种状态,分别为开盘状态,收盘状态,bar内状态,bar外状态;bar内状态与bar外状态是相对于开盘状态和收盘状态而言的,所谓的bar内状态就是当根bar已经开盘了,但是当根bar还没有收盘,所谓的bar外状态就是当根bar已经收盘了,但是下一根bar还没有开盘,那么这段空间就是当根barbar外状态。那么什么是当根bar的开盘状态,什么又是当根bar的收盘状态?在回答这个问题之前,首先反问大家一个问题,就是对于时间周期的bar(为方便叙述,下方未特殊说明,时间周期都是默认指1分钟周期)比如收盘时间为2152bar,这一分钟的bar是由215100215159这个时间段内的tick合成而来的,但是当接收到时间为215159的一笔tick时,是否应该判断当根bar已经结束了呢?不应该,因为每一秒的tick不至一笔(内盘是一秒最多两笔,但是外盘不一定),所以最准确的办法应该是当接收到一笔时间超过215159tick时才能确定当根bar已经收盘结束了,这种情境是通过下一根bar的第一笔tick来确定的,因为下一根bar的第一笔tick的时间已经不在当根bar的时间范围内;另一种情境是,若215159之后一直没有接收到下一根bar的第一笔tick,应该怎么处理呢?这种情境就需要MC底层根据时间进行判断一下,MC底层的判断机制215159秒之后5分钟之内没有收到下一根bar的第一笔tick也会判断当根bar收盘;对于bar的开盘状态,当接收到第一笔tick即判断当根bar开盘,例如接收到215100215159这个时间范围内的第一笔tick时,即判断当根bar(收盘时间为2152)开盘。(注意:每笔tick行情都会挟带时间信息,而MC是根据每笔tick的时间信息进行判断)

基于bar的状态,对应有一个关键字来进行判断;关键字barstatus的返回值有4种,分别为012-1,分别代表当根bar的开盘状态、bar内状态、bar的收盘状态、bar外状态。

之前写过一篇帖子“This barnext bar的区别”,里面阐述了概念开盘tickbartick、收盘tick,开盘tick与这里的开盘状态是一致的,收盘tick与这里的收盘状态是一致的,bartick是这里的bar内状态有些区别,请区别进行理解。

1. 收盘tick

我们前面说过,收盘tickbar收盘状态是等价的,因为策略(信号)接收到收盘tick时,策略(信号)才会判断当根bar收盘(即bar收盘状态),而收盘tickMC内部产生的,这种产生要区分两种情境:1. 下一根bar的第一笔tick(即下一根bar的开盘tick)接收时,MC内部会产生当根bar的收盘tick,判断当根bar收盘;2. 五分钟内没有收到下一根bar的第一笔tick时,MC内部也会产生当根bar的收盘tick,判断当根bar收盘。

测试情境1

//信号代码:

print("time_s=",time_s,",barstatus=",barstatus,",currenttime_s=",currenttime_s);

//输出

time_s=215200.00,barstatus=   2.00,currenttime_s=215202.00

测试情境2

//信号代码:

print("time_s=",time_s,",barstatus=",barstatus,",currenttime_s=",currenttime_s);

//输出

time_s=215200.00,barstatus=   2.00,currenttime_s=215700.00

将上面这段代码写入信号脚本(是否开启bar内模式都可以,bar内模式测试更清楚),然后将该信号插入到图表中(是否开启自动交易都可以);情境1是通常的情况,215202时接收到下一根bar的第一笔tick,然后策略(信号)判断当根bar收盘,基于当根bar进行计算;情境2是当5分钟内都没有接收到下一根bar的第一笔tick时,策略(信号)判断当根bar收盘,基于当根bar进行计算。对比一下time_scurrenttime_s的输出值进行对比。

注意事项:

l
情境2一般会出现在不活跃商品合约上,交易所收盘之后也会出现这种情境。

l
内盘期货1500之后收盘,正常情况下,会在1505时判断当根bar(即最后一根bar)收盘,但是若在1500之后对图表重新进行回补,那么策略(信号)也会判断最后一根bar收盘,对应的当然是barstatus返回2。重新回补可以对超过收盘时间没有收盘的bar 执行收盘。

三、变量的类型

1. 变量

信号中的变量除了value1~value99(这99个变量都是时序类型的数值变量,从value1value2value3,一直到value99)和condition1~condition99(这99个变量都是时序类型的布尔类型变量,从condition1condition2condition3,一直到condition99)这198个变量不需要声明之外,其它的变量都需要声明才可以在信号中使用,举例如下:

variable: var0(0), var1(false), var2("");

上面分别声明了三个变量var0var1var2,变量分为数值类型变量、布尔类型变量、字符串类型变量,这三个变量分别对应这三种类型,每个变量的类型是根据变量声明时括号中的初始值来判断的,而变量声明时需要指定初始值,如变量var0的初始值是数值0,那么变量var0就是数值类型变量,变量var1的初始值是布尔类型,那么变量var1就是布尔类型变量,变量var2的初始值是字符串类型,那么变量var3就是字符串类型变量。关于更详细的变量声明,可以看一下公式编译器中的关键字variable的说明。

MC的变量有两种类型,分别是时序类型和数值变量,两者的本质区别在于是否能回溯,这与它们的名称也是相符合的;通俗的讲,时序类型的变量就是在每根bar上都绑定一个值(即在内存中为每根bar上的时序变量都分配一块区域,用于存储每根bar上的变量值),然后后续回溯的时候可以方便调用,而数值类型的变量始终只会保存最新的一个数值(即在内存中只分配一块区域,该区域只存储最新的变量值);那么问题是,变量什么时候进行存储,这个点是关键需要理解的地方。另外,除了时序类型和数值类型两大类型的变量之外,还有一种复合类型的变量,也就是兼具时序类型变量和数值类型变量的特点,为不容易混淆,我们在后面再阐述。

2. 时序类型

为方便讲解,这里使用的是bar内模式,从而信号会基于每笔tick执行计算一次,也就是说信号会在同一根bar上计算若干次。

//信号代码

[IntrabarOrderGeneration=true]

variable: var_seq(0);

print("currentbar=",currentbar,", 3 ",",var_seq=",var_seq,",var_seq[1]=",var_seq[1]);

var_seq=var_seq+1;

print("currentbar=",currentbar,", 5 ",",var_seq=",var_seq);

var_seq=var_seq+1;

print("currentbar=",currentbar,", 7 ",",var_seq=",var_seq, ",barstatus=",barstatus);

//部分输出

currentbar=   5.00, 3 ,var_seq=   8.00,var_seq[1]=   8.00

currentbar=   5.00, 5 ,var_seq=   9.00

currentbar=   5.00, 7 ,var_seq=  10.00,barstatus=   1.00

currentbar=   5.00, 3 ,var_seq=   8.00,var_seq[1]=   8.00

currentbar=   5.00, 5 ,var_seq=   9.00

currentbar=   5.00, 7 ,var_seq=  10.00,barstatus=   2.00

currentbar=   6.00, 3 ,var_seq=  10.00,var_seq[1]=  10.00

currentbar=   6.00, 5 ,var_seq=  11.00

currentbar=   6.00, 7 ,var_seq=  12.00,barstatus=   0.00

currentbar=   6.00, 3 ,var_seq=  10.00,var_seq[1]=  10.00

currentbar=   6.00, 5 ,var_seq=  11.00

currentbar=   6.00, 7 ,var_seq=  12.00,barstatus=   1.00

除特别声明的变量之外,MC中默认的变量都是时序类型变量,如上面的变量var-_seq,信号每次执行计算开始时都会为时序类型变量var_seq分配一个临时的内存区域,用于存储var_seq的最新的值,临时内存区域中初始存储的值是最新的值,也就是前一根bar上绑定的变量值,因为此时前一根bar上绑定的值是变量最新的值;当var_seq被调用时, var_seq会调用临时存储的最新值,然后将变量var_seq最新的计算结果存储到临时内存区域中;当信号每次执行计算结束时,若当根bar是收盘状态(即barstatus=2),那么信号会将临时存储区域中的最新值绑定到当根bar上,若当根bar不是收盘状态,那么信号不对时序类型变量var_seq作绑定处理,之后将该临时内存区域释放掉,下次信号执行计算开始时会重新为时序类型变量var_seq分配一个新的临时的内存区域。总而言之,信号会在当根bar的收盘状态时将时序类型变量在临时存储区域中的最新值绑定到当根bar上,用于后续的变量回溯调用(回测调用的数量会受最大bar的数量限制),而这个临时的存储区域会在每次信号计算开始时重新分配。

对于上面的测试情境,由于内容限制,我们只截取了4次计算的输出结果:

l
第一次计算开始时分配一块临时内存区域,将前一根bar上绑定的最新的变量值存储到临时内存区域中;在第3行代码中调用变量var_seq的值时,会调用临时内存区域中的最新的值,即8;第4行代码,执行语句“var_seq=var_seq+1;”,首先调用在等式右边调用变量var_seq的值,这个值取临时内存区域中存储的最新的值,累加1之后存储到临时内存区域;第5行代码,调用变量var_seq时,是取临时内存区域中存储的计算值,然后输出9;第6行代码,执行语句“var_seq=var_seq+1;”,等式右边会调用临时内存区域中的计算值9,然后累加1之后,再将10存储到临时内存区域中;第7行代码,调用临时内存区域中的值,然后输出,值为10;由于当根bar的状态是bar内状态(即barstatus=1),所以信号不对临时内存区域中存储的计算值进行绑定处理,直接将临时内存区域释放。

l
第二次计算开始时分配一块临时内存区域,临时内存区域的初始值存储的是前一根bar上绑定的变量值,即8;在第3行代码中调用变量var_seq的值时,会调用临时内存区域中变量var_seq的值,即8;第4行代码,执行语句“var_seq=var_seq+1;”,首先在等式右边调用变量var_seq的值,这个值取临时内存区域中存储的最新的值,累加1之后存储到临时内存区域,即9;第5行代码,调用变量var_seq的值时会调用临时内存区域中存储的值9,也就是调用最新的值,然后输出9;第6行代码,执行语句var_seq=var_seq+1;”,首先在等式右边调用变量var_seq的值9,也就是调用临时内存区域中存储的最新的值9,然后累加1之后存储到临时内存区域中,即将10存储到临时内存区域中;第7行代码,调用变量var_seq的最新值,也就是调用临时内存区域中的最新值10,然后输出10;此时信号执行计算结束,由于此时当根bar的状态是收盘状态(即barstatus=2),那么信号会将临时内存区域中最新的值10绑定到当根bar上,那么在后面就可以调用这根bar上绑定的变量var_seq的值。

l
第三次计算开始时分配一块临时内存区域,临时内存区域的初始存储的值是最新的值,而此时最新的值是前一根bar上绑定的值,即10;第3行代码中,调用变量var_seq的最新的值,会调用临时内存区域中变量var_seq值,即10,然后输出10;第4行代码,执行语句“var_seq=var_seq+1;”,会调用临时内存区域中最新的值10,然后累加1之后再将最新的值存储到临时内存区域中,即存储11;第5行代码,调用临时内存区域中的最新值11,然后输出11;第6行代码,执行语句var_seq=var_seq+1;”,会调用临时内存区域中最新的值11,然后累加1之后将最新的值12存储到临时内存区域中;第7行代码,调用临时内存区域中最新的值12,然后输出12;此时信号执行计算结束,由于此时当根bar的状态是开盘状态(即barstatus=0),那么策略(信号)不会将临时内存区域中的最新值12绑定到当根bar上,直接释放临时内存区域。

l
第四次计算,可以参考上面的三次计算的原理,不再多述。

3. 数值类型

前一节,我们说过,MC中默认的变量都是时序类型变量,也就是说数值类型变量需要特别声明,那么如何声明一个数值类型变量呢?MC有一个关键字recalcpersist,用于声明数值类型变量,代码如下:

variable: recalcpersist var_numeric(0);

{在每个数值类型变量声明之前加上关键字recalcpersist,那么该变量就是数值类型变量,如上面的变量var_numeric就是一个数值类型变量}

数值类型变量与时序类型变量有两个地方不同:1. 不会在每根bar上绑定一个值用于回溯调用;2. 不会分配临时内存区域,而是会分配一个永久内存区域,这里的永久只是相对于临时而言的,“永久”也有它的生命周期。当将信号插入到图表中,就会为信号中的数值类型变量分配一块永久内存区域,当将信号从图表上删除该永久内存区域才会释放,当将信号重新插入到图表中去时,又要重新分配一块永久内存区域;当将信号重新编译时,信号会为数值类型变量重新分配一块永久内存区域,这里是重新分配,也就是之前存储的数值被释放了,不会保留到当前的内存区域中去;除了以上两种情况之外,对于其它情况,永久内存区域都一至不变,每次信号执行计算时,都会调用永久内存区域中存储的数值类型变量值,然后将最新的计算结果再存储到永久内存区域中去。(注意:将信号在图表上的状态由“开启”变成“关闭”不是将信号从图表上移除,所以这种情况永久内存区域没有释放)

//信号代码

[IntrabarOrderGeneration=true]

variable: recalcpersist var_numeric(0);

print("currentbar=",currentbar,", 3 ",",var_numeric=",var_numeric,",var_numeric[1]=",var_numeric[1]);

var_numeric=var_numeric+1;

print("currentbar=",currentbar,", 5 ",",var_numeric=",var_numeric);

var_numeric=var_numeric+1;

print("currentbar=",currentbar,", 7 ",",var_numeric=",var_numeric,",barstatus=",barstatus);

//部分输出

currentbar=   1.00, 3 ,var_numeric=   0.00,var_numeric[1]=   0.00

currentbar=   1.00, 5 ,var_numeric=   1.00

currentbar=   1.00, 7 ,var_numeric=   2.00,barstatus=   0.00

currentbar=   1.00, 3 ,var_numeric=   2.00,var_numeric[1]=   2.00

currentbar=   1.00, 5 ,var_numeric=   3.00

currentbar=   1.00, 7 ,var_numeric=   4.00,barstatus=   1.00

currentbar=   1.00, 3 ,var_numeric=   4.00,var_numeric[1]=   4.00

currentbar=   1.00, 5 ,var_numeric=   5.00

currentbar=   1.00, 7 ,var_numeric=   6.00,barstatus=   1.00

currentbar=   1.00, 3 ,var_numeric=   6.00,var_numeric[1]=   6.00

currentbar=   1.00, 5 ,var_numeric=   7.00

currentbar=   1.00, 7 ,var_numeric=   8.00,barstatus=   2.00

currentbar=   2.00, 3 ,var_numeric=   8.00,var_numeric[1]=   8.00

currentbar=   2.00, 5 ,var_numeric=   9.00

currentbar=   2.00, 7 ,var_numeric=  10.00,barstatus=   0.00

currentbar=   2.00, 3 ,var_numeric=  10.00,var_numeric[1]=  10.00

currentbar=   2.00, 5 ,var_numeric=  11.00

currentbar=   2.00, 7 ,var_numeric=  12.00,barstatus=   1.00


这部分的信号代码与上一节唯一不同的地方只是将时序类型变量替换成数值类型变量而已,其它地方没有变化,可以通过相互比较进行理解。由于内容的限制,这里也只是列举前6次计算的输出结果,通过结果进行详细的说明原理。

l
信号为数值类型变量var_numeric分配了一块永久内存区域,第一次分配或者重新分配永久内存区域时,都会将类型类型变量的初始值(即声明变量时括号中的值,这里是0)存储到永久内存区域中去,作为初始的最新值。

l
第一次计算时,第3行代码中会调用变量var_numeric的变量值,而此时永久内存区域中存储的最新的值是数值类型变量的初始值0,所以输出var_numeric的值为0;第4行代码执行语句“var_numeric=var_numeric+1;”,等式右边会调用永久内存区域中的最新的值0,然后经过累加1之后,将最新值1存储到永久内存区域中;第5行代码,调用永久内存区域中的最新值1,然后输出;第6行代码执行语句“var_numeric=var_numeric+1;”,等式右边会调用永久内存区域的最新的值1,然后经过累加之后,将最新的值2存储到永久内存区域中;第7行代码,调用永久内存区域中的最新的值2,然后输出。

l
2次计算和第3次计算不再叙述,可以参考第1次计算的分析过程。第4次计算时,第3行代码会调用永久内存区域中的最新的值6,然后输出6;第4行代码执行语句“var_numeric=var_numeric+1;”,等式右边调用永久内存区域中的最新的值6,然后累加1之后将最新值7存储到永久内存区域中;第5行代码会调用永久内存区域中的最新的值7,然后输出7;第6行代码执行语句“var_numeric=var_numeric+1;”,等式右边调用永久内存区域中的最新的值7,然后累加1之后将最新的计算结果8存储到永久内存区域中去;第7行代码调用永久内存区域中的最新的值8,然后输出。此时当根bar的状态是收盘状态,但是信号并不会将永久内存区域中的最新的值绑定到当根bar上,也就是说数值类型变量只会有一个存储区域用于存储最新的值,基于此,var_numeric[1]var_numeric[2]var_numeric[N]始终取的是永久内存区域中的最新的值,即始终等于var_numeric的值。

l
5次计算和第6次计算不再过多叙述,请参考上面的分析过程。

4. 复合类型

上面我们叙述了时序类型变量和数值类型变量两种类型,但是这里需要提一下,MC有一种复合类型的变量,兼具了时序类型变量和数值类型变量的特性,这点会在下面的内容中详细阐述,那么它是如何进行声明的呢?因为前面说过,所有的变量默认都是时序类型变量,其它类型的变量,如数值类型变量和复合类型变量都需要在变量声明的时候特殊处理一下。复合类型变量声明的代码如下:

variable: intrabarpersist var_intra(0);

{也就是在变量声明时,在变量的前面加上关键字intrabarpersist,也许大家之前都认为,这个关键字只能用于bar内模式,但是其实不是,这个关键字的用法就是声明该变量是复合类型的变量}

我们知道,当信号插入到图表上时,信号会从相对编号为1bar上开始执行计算,然后基于相对编号为2bar执行计算,以此类型一直向图表的右边的bar执行计算,这叫重新计算,所谓的重新计算就信号会从相对编号为1bar上开始执行计算,然后一直向图表的右边的bar执行计算;而引发信号的重新计算的方式有很多,如对图表重新回补、重新插入信号到图表上、开启自动交易、重新编译、右键图表上选择“重新计算所有策略”、信号的状态由“关闭”转换为“开启”,这些都会引发信号基于图表的bar的重新计算。信号每次对图表进行重新计算时,初始时都会为复合类型变量在内存中分配一块内存区域,这块内存区域不是永久内存区域,也不是临时内存区域,它的生命周期只限于“重新计算”,也就是每次“重新计算”时,该内存区域都会重新分配,之前的内存区域中存储的值不会保留到最新的内存区域中,这就是复合类型变量在内存区域上与时序类型变量、数值类型变量不一样的地方;除了内存区域的不同,另一个地方就是,信号会在bar的收盘状态时将内存区域中的最新的值绑定到当根bar上,以便后续的回溯调用。

通过上面对于时序类型变量及数值类型变量的叙述,复合类型变量的理解就变得简的多,所以这部分只是简单的案例解释,为方便对比,这里仍然以bar内模式为例。

//信号代码

[IntrabarOrderGeneration=true]

variable: intrabarpersist var_intra(0);

print("currentbar=",currentbar,", 3 ",",var_intra=",var_intra,",var_intra[1]=",var_intra[1]);

var_intra=var_intra+1;

print("currentbar=",currentbar,", 5 ",",var_intra=",var_intra);

var_intra=var_intra+1;

print("currentbar=",currentbar,", 7 ",",var_intra=",var_intra,",barstatus=",barstatus);

//部分输出

currentbar=   1.00, 3 ,var_intra=   0.00,var_intra[1]=   0.00

currentbar=   1.00, 5 ,var_intra=   1.00

currentbar=   1.00, 7 ,var_intra=   2.00,barstatus=   0.00

currentbar=   1.00, 3 ,var_intra=   2.00,var_intra[1]=   0.00

currentbar=   1.00, 5 ,var_intra=   3.00

currentbar=   1.00, 7 ,var_intra=   4.00,barstatus=   1.00

currentbar=   1.00, 3 ,var_intra=   4.00,var_intra[1]=   0.00

currentbar=   1.00, 5 ,var_intra=   5.00

currentbar=   1.00, 7 ,var_intra=   6.00,barstatus=   1.00

currentbar=   1.00, 3 ,var_intra=   6.00,var_intra[1]=   0.00

currentbar=   1.00, 5 ,var_intra=   7.00

currentbar=   1.00, 7 ,var_intra=   8.00,barstatus=   2.00

currentbar=   2.00, 3 ,var_intra=   8.00,var_intra[1]=   8.00

currentbar=   2.00, 5 ,var_intra=   9.00

currentbar=   2.00, 7 ,var_intra=  10.00,barstatus=   0.00

currentbar=   2.00, 3 ,var_intra=  10.00,var_intra[1]=   8.00

currentbar=   2.00, 5 ,var_intra=  11.00

currentbar=   2.00, 7 ,var_intra=  12.00,barstatus=   1.00


l
每次重新计算时,信号会为复合类型变量var_intra分别一块内存区域,该内存区域中会存储变量的初始值作为最新的值(即变量声明时,变量括号中的值,此时是0)。

l
1次计算时,第3行代码会调用复合类型变量的最新的值,会取内存区域中的最新的值0,然后输出;第4行代码,调用内存区域中的最新的值0,然后累加1之后再存储到内存区域中;第5行代码,调用内存区域中的最新的值1,然后输出;第6行,累累1之后,将2存储到内存区域中;第7行代码,会调用内存区域中最新的值2,然后输出2;信号执行计算结束,由于当根bar的状态是开盘bar(即barstatus=0),而非收盘状态,所以信号不对复合类型变量执行绑定操作,也就是不将内存区域中最新的值2绑定到当根bar上。

l
2次计算和第3次计算,请参考上面的分析过程。

l
4次计算时,第7行代码,调用内存区域中最新的值8,然后输出8;信号执行计算结束,此时当根bar的状态是收盘状态(即barstatus=2),所以信号将内存区域中最新的值8绑定到当根bar上,以此后续可以回溯调用这根bar上的变量值。

6次计算时,第3行代码,调用了复合类型变量var_intra的值和var_intra[1]的值,而var_intra会取内存区域中最新的值,而var_intra[1]会取前一根bar上绑定的值,即8


论坛官方微信、群(期货热点、量化探讨、开户与绑定实盘)
 
期货论坛 - 版权/免责声明   1.本站发布源码(包括函数、指标、策略等)均属开放源码,用意在于让使用者学习程序化语法撰写,使用者可以任意修改语法內容并调整参数。仅限用于个人学习使用,请勿转载、滥用,严禁私自连接实盘账户交易
  2.本站发布资讯(包括文章、视频、历史记录、教材、评论、资讯、交易方案等)均系转载自网络主流媒体,内容仅为作者当日个人观点,本网转载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。本网不对该类信息或数据做任何保证。不对您构成任何投资建议,不能依靠信息而取代自身独立判断,不对因使用本篇文章所诉信息或观点等导致的损失承担任何责任。
  3.本站发布资源(包括书籍、杂志、文档、软件等)均从互联网搜索而来,仅供个人免费交流学习,不可用作商业用途,本站不对显示的内容承担任何责任。请在下载后24小时内删除。如果喜欢,请购买正版,谢谢合作!
  4.龙听期货论坛原创文章属本网版权作品,转载须注明来源“龙听期货论坛”,违者本网将保留追究其相关法律责任的权力。本论坛除发布原创文章外,亦致力于优秀财经文章的交流分享,部分文章推送时若未能及时与原作者取得联系并涉及版权问题时,请及时联系删除。联系方式:http://www.qhlt.cn/thread-262-1-1.html
如何访问权限为100/255贴子:/thread-37840-1-1.html;注册后仍无法回复:/thread-23-1-1.html;微信/QQ群:/thread-262-1-1.html;网盘链接失效解决办法:/thread-93307-1-1.html

返回列表