TB交易开拓者股指期货量化交易模型编写操作符保留字语法表达式学习
TB交易开拓者股指期货量化交易模型编写操作符保留字语法表达式学习
操作符
操作符是一些象征具体操作运算行为的符号,例如操作符"+"代表对两个数求和,这些操作符适用于数值型、字符串、布尔型的数据。
TradeBlazer公式为您提供了多种操作运算符,便于您对保留字的操作和生成更复杂的数据类型、逻辑型、字符串类型的值。下面有四种不同类型的操作符可用于逻辑表达式、数值表达式、字符串表达式中。
数学操作符
数值型表达式的操作符有几种,如下表所示:
操作符 | 说明 |
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 求模 |
() | 括号 |
这些数学操作按其特定的优先级来进行计算,"*"(乘法)最先,其次是"/"(除法)和"%"(求模),加和减最后,如果有多个乘法/除法(或者是加法或减法),那么计算顺序是从左边到右边。
例如,在数值型的表达式中:
High+2*range/2;
它首先计算的是range(此处range是指High-Low)与2的积,接着计算与2的商(除法),最后求2*range/2与最高价(High)的和。
如果要找到一个Bar的中间位置,可以尝试写成如下语句:
High+Low/2;
然而在上面语句中,首先运算的是以最低价(Low)除于2,然后再与最高价求和。最后的值不是我们所需要的,并且和原来预想中的值是不一致的。
为了处理上述这样的情况,我们在运算符号中引入了括号"()",可以用括号来操作和控制运算的规则,先计算括号里面的表达式,不考虑外面的操作符和常量。因此,获取某一个Bar上的中间位置(MidPoint)的语句可如下:
(High+Low)/2;
该语句就是返回最高价和最低价之和的1/2,即Bar的中间位置。
对于除法,有一些特别的提示,众所周知,0不能作为除数,否则将会导致系统溢出。TradeBlazer公式在对脚本进行执行时,碰到除法符号时,都需要对除数进行是否为0的检查,以保证脚本能够正确的执行下去,当遇到除数为0的情况下,我们将会返回一个无效值。
上面描述到求中间位置(MidPoint)的表达式,其实我们可以用另外一种形式来代替它:
(High+Low)*0.5;
对于A/B这样的一个表达式,因为不知道B是否为0,所以总是需要对其进行有效性验证,计算的速度会受到一定的影响,因此,对于除法表达式,我们强烈建议使用时尽可能转换为乘法处理,即提高执行速度,也可以避免未知的错误发生。
字符串操作符
"+"是唯一的可以应用于控制字符串表达式的数学操作符,它可用于连接两个文本字符串为一个字符串。如下:
"这是一个字符串表达式,"+"返回值为数值型。";
"This is expression A"+" and this is expression B.";
上面字符串表达式的输出值,应该是"这是一个字符串表达式,返回值为数值型。"和"This is expression A and this is expression B."。
关系操作符
逻辑运算符使用下列标准的比较符号,大于、小于、等于、小于等于、大于等于和不等于。
下列的关系操作符号都可以应用到逻辑表达式中。
操作符 | 说明 |
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
<>或!= | 不等于 |
== | 等于 |
应用上述的关系运算符,我们可以对两个数值或字符串表达式进行对比,在下列的语句中,我们就是找到一个Bar,它的当前Bar收盘价要高于前一个Bar最高价:
Close>High[1];
在字符串的比较运算中,首先是把每一个字符用它的ASCII来代替,其次对两个表达式中的字符逐一比较其ASCII值,从第一个开始,直到两个表达式中的所有字符都已经被计算完为止。例如:
"abcd" < "zyxw";
在这个例子中,我们对把第一个字符串表达式中的字符和第二个表达式中的字符进行比较运算,字母"a"的ASCII值是小于"z"的,同样其它的字符也是一样,所有该表达式的值为True。
逻辑操作符
逻辑运算符常常用于比较两个True/False的表达式,共有三个逻辑操作符:AND(&&),OR(||),NOT(!)。
下表列出AND逻辑操作符的应用情况:
表达式1 | 表达式2 | 表达式1 AND 表达式2 |
True | True | True |
True | False | False |
False | True | False |
False | False | False |
下表列出OR逻辑操作符的应用情况:
表达式1 | 表达式2 | 表达式1 OR 表达式2 |
True | True | True |
True | False | True |
False | True | True |
False | False | False |
下表列出NOT逻辑操作符的应用情况:
表达式1 | NOT表达式1 |
True | False |
False | True |
在上面的表格中,应用OR可以增加表达式的值为True的可能性,仅仅只要两个表达式中,只要有一个的值为True,那么整个表达式的值就为True。
其实在应用的过程中,还包含有一些复杂的组合运算。为了获得一个的关键反转Bar,可以使用如下的表达式:
Low < Low[1] AND Close > High[1];
在上面的表达式中,我们使用了AND逻辑运算符,因而要表达式的值为True,那么当前Bar的最低价一定要小于前一个Bar的最低价,而且当前Bar的收盘价还必须高于前一个Bar的最高价。只有当这两个条件都满足的时候,表达式的值才为True。
再看下面一个例子:
High > 10 OR Vol > 5000;
在上面的表达式中,如果要其值为True,那么只需要任意一个条件满足即值为True,那么表达式的值便为True,如果当前bar的最高价大于10,或者成交量大于5000,那么表达式的值便为True。而如果需要表达式的值为False时,则两个条件都必须为False,表达式的值才为False。
逻辑操作符的优先级低于数学操作符和关系操作符。逻辑操作符也遵循先括号的原则,如果没有括号,那么其运算顺序也是从左边到右边。
对于逻辑表达式中不同条件的先后顺序,可能会产生不同的运算逻辑,执行的效率也会有所不同。
以Con1 AND Con2这样的表达式举例,系统从左到右进行逻辑判断,当Con1为True时,需要继续判断Con2是否为True,只有当Con1,Con2都为True时,整个表达式才为True。但是只要当Con1为False时,就不再需要判断Con2的值,而是直接返回False。
因此,以下的两个表达式在执行效率方面是有差异的:
5 < 4 AND Close > Open; Close > Open AND 5 < 4;
第一条语句的执行速度大部分情况下都比第二条要快。
对于Con1 OR Con2表达式,情况也比较类似,当Con1为False时,需要继续判断Con2是否为False,只有当Con1,Con2都为False时,整个表达式才为False。但是只要当Con1为True时,就不再需要判断Con2的值,而是直接返回True。
以下两条语句的执行效率也是不一样的:
5 > 4 OR Close > Open; Close > Open OR 5 > 4;
通过上述的说明,我们应该知道,逻辑表达式的组合时,应该尽可能的把容易判别整个表达式逻辑的条件放在前面,以减少整个表达式的计算时间。
保留字
保留字都有自己独特的意思或用途,主要是一些功能关键字,系统函数,以及数据类型等。
下面分类列举出系统主要的保留字。
数据类型
包含3种基本类型,共9种数据类型能够,详细说明参见数据类型。
运算符
类型 | 保留字 |
算术运算符 | + - * / % ^ |
关系运算符 | > >= < <= == != <> |
逻辑运算符 | AND/&& OR/|| NOT/! |
括号 | (){} [] |
其它 | . , |
功能关键字
保留字 | 说明 |
Params | 用该关键字宣告参数定义的起始,参数必须填写默认值。 |
Vars | 用该关键字宣告变量定义的起始(可以赋初值), 变量不填写初值时,系统将自动为其填充初值。 |
If | 条件语句。 |
Else | 条件语句。 |
Begin | 用该关键字宣告程序主体的起始。 |
End | 用该关键字宣告程序主体的结束。 |
For | 循环语句。 |
To | 循环语句。 |
DownTo | 循环语句。 |
While | 循环语句。 |
Break | 循环语句。 |
Continue | 循环语句。 |
True | 真。 |
False | 假。 |
数据源
保留字 | 说明 |
Data0-Data49 | 支持50个数据源。 |
数据输出
保留字 | 说明 |
PlotBool | 输出布尔型值。 |
PlotNumeric | 输出数值型值。 |
PlotString | 输出字符串值。 |
UnPlot | 取消指定位置的输出。 |
Alert | 报警输出。 |
Buy | 多头建仓操作。 |
Sell | 多头平仓操作。 |
SellShort | 空头建仓操作。 |
BuyToCover | 空头平仓操作。 |
... | 其他系统函数。 |
表达式
表达式是操作符和保留字的有机组合,任意表达式都代表了一个值,表达式的值只能是以下的三种类型:
-
数值型: 即Numeric,NumericRef,NumericSeries三种数据类型。
-
布尔型: 即Bool,BoolRef,BoolSeries三种数据类型。
-
字符串: 即String,StringRef,StringSeries三种数据类型。
当您使用TradeBlazer公式的过程中,便可应用三种广泛数据类型的表达式,来完成您自己的程序。
数值型的表达式可以是一个数字,当然,他们也可以是一个数值型的保留字,例如:保留字"Close"。或者是通过运算符号组合的一个计算表达式,该表达式的结果是数值型。下文中的例子都是数值型的表达式。
154; Vol; (High+Low)/2;
布尔型的表达式能够返回一个值True/False,或者说它可以对表达式的值进行评估,然后返回其值,要么为True,要么为False。逻辑表达式永远要涉及到比较运算,下面我们有一些逻辑表达式,它也就是对一个表达式的值是True还是False进行判断。
Close > Open; 5 < 2;
字符串的表达式可以是单个字符串,也可以字符串的组合,或者返回值为字符串的系统函数,如下,就是一个字符串的表达式:
"This is a test string expression" ; "Hello"+"World" ; Trim(" I Love This Game! ");
控制语句
TradeBlazer公式支持两大类的控制语句:条件语句和循环语句。
条件语句
条件语句包括以下四类表达方式:
If
If语句是一个条件语句,当特定的条件满足后执行一部分操作。
语法如下:
If (Condition) { TradeBlazer公式语句; }
Condition是一个逻辑表达式,当Condition为True的时候,TradeBlazer公式语句将会被执行,Condition可以是多个条件表达式的逻辑组合,Condition必须用()括起来。
TradeBlazer公式语句是一些语句的组合,如果TradeBlazer公式语句是单条,您可以省略{},二条或者二条以上的语句必须使用{}。
例如,您可以计算图表中上升缺口(当前Bar的开盘价高于上一个Bar的最高价)出现了多少次,只要在图表中使用If语句,当找到一个满足条件的Bar时,即条件为真时,变量加1,脚本如下:
Vars NumericSeries Counter(0); Begin If ( Open > High[1]) { Counter = Counter[1] + 1; ... } ... End
在TradeBlazer公式中,If语句被广泛使用,当条件满足的时候,在满足条件的Bar上面进行标记。例如,下面的语句就是公式应用的例子:
If(High > High[1] AND Low < Low[1]) { PlotNumeric("Outside Bar",High); }
If语句在不是用括号的情况,只执行下面的第一条语句,如下的语句,Alert不会只在条件为True时执行,而是每次都执行。
If(High > High[1] AND Low < Low[1]) PlotNumeric("Outside Bar",High); Alert("Outside Bar");
要想Alert只在条件为True时执行,您需要按照下面的格式编写:
If(High > High[1] AND Low < Low[1]) { PlotNumeric("Outside Bar",High); Alert("Outside Bar"); }
If-Else
If-Else语句是对指定条件进行判断,如果条件满足执行If后的语句。否则执行Else后面的语句。
语法如下:
参数
参数是一个预先声明的地址,用来存放输入参数的值,在声明之后,您就可以在接下来的公式中使用该参数的名称来引用其值。
参数的值在公式的内部是不能够被修改,在整个程序中一直保持不变,不能对参数进行赋值操作(引用参数是个特例)。参数的好处在于您可以在调用公式应用的时候才指定相应的参数,而不需要重新编译。
参数的修改很简单,在超级图表调用指标的过程中,您可以打开指标的属性设置框,切换到参数页面,手动修改各项参数的值,然后应用即可,交易开拓者将根据新的参数设置计算出新的结果,在超级图表中反映出来。
另外,参数的一个额外的优点是,我们可以通过修改公式应用不同的参数,测试交易策略的性能优劣,达到优化参数的目的。
参数类型
用户函数的参数类型可以包含TradeBlazer公式的九种类型,而公式应用只能使用三种简单的基本类型。
三种简单类型参数通过传值的方式将参数值传入公式,公式内部通过使用参数名称,将参数值用来进行计算或赋值。
引用参数是在调用的时候传入一个变量的地址,在用户函数内部会修改参数的值,在函数执行完毕,上层调用的公式会通过变量获得修改后的值,引用参数对于需要通过用户函数返回多个值的情况非常有用。
序列参数可以通过回溯获取以前Bar的值,具体介绍可参见参数回溯。
参数声明
在使用参数之前,必须对参数进行声明,TradeBlazer公式使用关键字"Params"来进行参数宣告,并指定参数类型。可以选择赋默认值,也可以不赋默认值。如果某个参数没有赋予默认值,则这个参数之前的其他参数的默认值都将被忽略。
参数定义的语法如下:
Params 参数类型 参数名1(初值); 参数类型 参数名2(初值); 参数类型 参数名3(初值);
下面是一些参数定义的例子:
Params Bool bTest(False); //定义布尔型参数bTest,默认值为False; Numberic Length(10); //定义数值型参数Length,默认值为10; NumericSeries Price(0); //定义数值型序列参数Price,默认值为0; NumericRef output(0); //定义数值型引用参数output,默认值为0; String strTmp("Hello"); //定义字符串参数strTmp,默认值为Hello;
参数名称的命名规范详细说明参见命名规则。
整个公式中只能出现一个Params宣告,并且要放到公式的开始部分,在变量定义之前。
参数的默认值
在声明参数时,通常会赋给参数一个默认值。例如上例中的False,10,0等就是参数的默认值。用户函数的默认值是在当用户函数被其他公式调用,省略参数时作为参数的输入值,其他五种公式的默认值是用于图表,报价等模块调用公式时默认的输入值。
参数的默认值的类型在定义的时候指定,默认值在公式调用的时候传入作为参数进行计算。只能够对排列在后面的那些参数提供默认参数,例如:
Params Numeric MyVal1; Numeric MyVal2(0); Numeric MyVal3(0);
您不能够使用以下方式对参数的默认值进行设定:
Params Numeric MyVal1(0); Numeric MyVal2(0); Numeric MyVal3;
参数使用
在声明参数之后,我们可以在脚本正文中通过参数名称使用该参数,在使用的过程中要注意保持数据类型的匹配,示例如下:
Params NumericSeries Price(1); Vars NumericSeries CumValue(0); Begin CumValue = CumValue[1] + Price; Return CumValue; End
在以上的公式中,首先定义了一个数值型序列参数Price,并将其默认值设置为1。接着定义了一个变量CumValue。脚本正文中,将CumValue的上一个Bar值加上Price,并将值赋给CumValue,最后返回CumValue。
通过上述的公式可以看到,我们只需要调用参数名,就可以使用参数的值进行计算了,如果要对序列参数进行回溯,请参见参数回溯。
引用参数
TradeBlazer公式的用户函数可以通过返回值,返回函数的计算结果,返回值只能是三种简单类型。当我们需要通过函数进行计算,返回多个值的时候,单个的返回值就不能满足需求了。在这种情况下,我们提出了引用参数的概念,引用参数是在调用的时候传入一个变量的地址,在用户函数内部会修改参数的值,在函数执行完毕,上层调用的公式会通过变量获得修改后的值。因为引用参数的使用是没有个数限制,因此,我们可以通过引用参数返回任意多个值。
Params NumericSeries Price(0); NumericRef oHigher(0); NumericRef oLower(0); Vars Numeric Tmp(0); Begin Tmp = Average(Price,10); oHigher = IIf(Tmp > High,Tmp,High); oLower = IIf(Tmp < Low,Tmp,Low); Return Tmp; End
Vars Numeric AvgValue; Numeric HigherValue; Numeric LowerValue; Begin AvgValue = MyFunc(Close,HigherValue,LowerValue); ... End
变量
变量是一个存储值的地址,当变量被声明之后,就可以在脚本中使用变量,可以对其赋值,也可以在其他地方引用变量的值进行计算,要对变量进行操作,直接使用变量名称即可。
变量的主要用处在于它可以存放计算或比较的结果,以方便在之后的脚本中直接引用运算的值,而无需重现计算过程。
例如,我们定义一个变量Y,我们把一个收盘价(Close)乘上8%的所得的值存储在Y中,即Y = Close *8%。那么一旦计算出Close * 8%的值,便赋给变量Y。而无需在公式中输入计算过程,只需调用变量名称即可引用变量的值。
变量有助于程序的优化,这是TradeBlazer公式必须重复调用一些数据,这些数据可能是某些函数(如:Bar数据),或通过表达式执行计算和比较的值。因此,在表达式频繁使用的地方使用变量可提高程序的运行速度和节约内存空间。
使用变量也可以避免输入错误,使程序的可读性提高,示例如下:
If(Close > High[1] + Average(Close,10)*0.5) { Buy(100, High[1] + Average(Close,10)*0.5); }
如果使用变量,则整个代码变得简洁:
Value1 = High[1] + Average(Close,10)*0.5; If (Close > Value1) { Buy(100,Value1); }
如果一些表达式的组合经常在不同的公式中被调用,这个时候变量就不能实现功能,变量只能在单个公式的内部使用,这个时候我们需要建立用户函数来完成这些功能,详细说明参见用户函数。
变量类型
TradeBlazer公式支持九种数据类型,但对于变量定义,引用类型是无效的,剩余六种数据类型中分为简单和序列两大类,简单类型变量是单个的值,不能对其进行回溯,序列类型变量是和Bar长度一致的数据排列,我们可以通过回溯来获取当前Bar以前的任意值。
变量声明
在使用变量之前,必须对变量进行声明,TradeBlazer公式使用关键字"Vars"来进行变量宣告,并指定变量类型。可以选择赋默认值,也可以不赋默认值。
变量定义的语法如下:
Vars 变量类型 变量名1(初值); 变量类型 变量名2(初值); 变量类型 变量名3(初值);
下面是一些变量定义的例子:
Vars NumericSeries MyVal1(0); //定义数值型序列变量MyVal1,默认值为0; Numeric MyVal2(0); //定义数值型变量MyVal2,默认值为0; Bool MyVal3(False); //定义布尔型变量MyVal3,默认值为False; String MyVal4("Test"); //定义字符串变量MyVal4,默认值为Test。
变量定义的个数没有限制,变量名称的命名规范详细说明参见命名规则。
整个公式中只能出现一个Vars宣告,并且要放到公式的开始部分,在参数定义之后,正文之前。
变量的默认值
在声明变量时,通常会赋给变量一个默认值。例如上例中的0,False,"Test"等就是变量的默认值。如果某个变量没有赋予默认值,系统将会自动给该变量赋予默认值。数值型变量的默认值为0,布尔型变量的默认值为False,字符串的默认值为空串。
变量的默认值是在当公式在执行时,给该变量赋予的初值,使该变量在引用时存在着有效的值。在该公式每个Bar的执行过程中,改变量的默认值都会被重新赋值。
变量赋值
变量声明完成之后,您可以在脚本正文中给变量指定一个值。
语法如下:
Name = Expression;
"Name"是变量的名称,表达式的类型可以是数值型、布尔型、字符串中的任何一种。不过表达式的类型一定要和变量的数据类型相匹配。如果变量被指定为是数值型的,那么表达式一定要是数值型的表达式。
例如:下面的语句将Close的10周期平均值赋值给变量Value1:
Value1 = Average(Close , 10);
在下面这个语句中,声明了一个名为"KeyReversal"的逻辑型变量,然后又把计算的值赋给它。
Vars Bool KeyReversal(False); Begin KeyReversal = Low < Low[1] AND Close > High[1]; ... End
变量使用
变量定义、赋值之后,在表达式中直接使用变量名就可以引用变量的值。例如在下面的语句中计算了买入价格后,把值赋给数值型变量EntryPrc,在买入指令中便可直接应用变量名,通过变量名便可引用变量的值:
Vars Numeric EntryPrc(0); Begin EntryPrc = Highest(High,10); If (MarkerPosition <> 1) { Buy(1,EntryPrc); } End
接下来的例子,我们计算最近10个Bar最高价中的最大值(不包括当前Bar),对比当前High,然后通过If语句,产生报警信息。
Vars Bool Con1(False); Begin Con1 = High > Highest(High,10)[1]; If(Con1) { Alert("New 10-bar high"); } End
其实我们并不一定都要应用条件为True的情况,有时候我们需要判断条件为False的时候执行某些代码,如下的例子:
Vars Bool Con1(False); Begin Con1 = High < Highest(High,10)[1] AND Low > Lowest(Low,10)[1]; If(Con1==False) { Alert("New high or low"); } End
序列变量
序列变量是变量中的一种,可以对序列变量进行回溯获取以前Bar的变量数据。序列变量的声明和简单变量一样,只是定义的数据类型不同,您必须选择以下的3种类型来定义序列变量:NumericSeries/BoolSeries/StringSeries。例如:
Vars NumericSeries MyNumSVal(0); BoolSeries MyBoolVal(False); StringSeries MyStrVal("");
序列变量和简单变量一样,可以对其赋予默认值。
序列变量定义之后,您可以象简单变量一样的对其使用,不会有任何的不同。除了支持全部简单变量的功能之外,序列变量还可以通过"[nOffset]"来回溯以前的变量值,详细说明参见变量回溯。
对于序列变量,TradeBlazer公式在内部针对其回溯的特性作了很多的特殊处理,也需要为序列变量保存相应的历史数据,因此,和简单变量相比,执行的速度和占用内存空间方面都作了一些牺牲。因此,尽管您可以定义一个序列变量,把它当作简单变量来使用,但是,我们强烈建议您只将需要进行回溯的变量定义为序列变量。