欢迎光临散文网 会员登陆 & 注册

发明者量化PINE语言入门教程-变量声明、条件、循环结构

2022-10-17 16:40 作者:发明者量化  | 我要投稿

变量声明

我们之前已经学习过了“标识符”的概念,“标识符”就是作为变量的名称来给变量命名的。所以也说:变量是保存值的标识符。那么如何声明一个变量呢?声明变量又有哪些规则?

  • 声明模式:
    在声明变量时最先写的就是「声明模式」,变量的声明模式有三种即:
    1、使用关键字var
    2、使用关键字varip
    3、什么都不写。

    varvarip关键字其实我们在之前的「赋值运算符」章节已经学习过了,这里不再赘述。如果变量的声明模式什么都不写,例如语句:i = 1,其实我们之前也讲过,这样声明的变量并且赋值,是在每个K线BAR上都执行的。

  • 类型
    FMZ上的Pine语言对于类型要求并不严苛,一般可以省略。不过为了兼容Trading View上的脚本策略,声明变量时也是可以带类型的。例如:

在Trading View上的类型是要求比较严苛的,如果使用以下代码在Trading View上则会报错:



  • 标识符
    标识符即变量名称,标识符的命名在之前章节已经讲过,可以回看:https://www.fmz.com/bbs-topic/9390#标识符

总结一下,声明一个变量可以写作:



这里使用赋值运算符:=在变量声明时给变量赋值。赋值时,值可以是字符串、数值、表达式、函数调用、if forwhileswitch等结构(这些结构关键字、语句用法我们后续课程中会详细讲解,其实我们已经在之前的课程里学会了简单的if语句赋值,可以回顾看看)。

这里我们着重讲解一下input函数,这个函数是我们在设计编写策略时会很频繁用到的一个函数。也是设计策略时非常关键的函数。

input函数:

在FMZ上的input函数和在Trading View上的有些不同,不过该函数都是作为策略参数的赋值输入使用。下面我们来通过一个例子详细说明input函数在FMZ上的使用:

在声明变量时给变量赋值,经常使用的就是input函数,在FMZ上input函数会在FMZ策略界面自动画出用于设置策略参数的控件。FMZ上支持的控件目前有数值输入框、文本输入框、下拉框、布尔值勾选。并且可以设置策略参数分组、设置参数的提示文本信息等功能。

我们介绍input函数的几个主要参数:

  • defval :作为input函数设置的策略参数选项的默认值,支持Pine语言的内置变量、数值、字符串

  • title :策略在实盘/回测的策略界面上显示的参数名称。

  • tooltip :策略参数的提示信息,当鼠标悬停在策略参数上会显示出这个参数设置的文本信息。

  • group :策略参数分组名称,可以给参数分组。

除了单独的变量声明、赋值,Pine语言中还有声明一组变量并且赋值的写法:

最常见的就是我们使用ta.macd函数计算MACD指标时,由于MACD指标是一个多线的指标,计算出三组数据。所以就可以写为:

我们使用以上代码就很容易画出MACD图表,不止是内置函数可以返回多个变量,编写的自定义函数也可以返回多个数据。

使用if等结构作为多个变量赋值的写法也和上面的自定义函数方式类似,有兴趣也可以试下。

条件结构


一些函数是无法写在条件分支的本地代码块里的,主要有以下几个函数:

barcolor(), fill(), hline(), indicator(), plot(), plotcandle(), plotchar(), plotshape()

Trading View上会编译报错。FMZ上限制不是那么严苛,但是也建议遵循Trading View上的规范书写。例如这样虽然在FMZ上不报错,不过不建议这样写。

if语句

讲解例子:

重点:判断用的表达式,返回布尔值。注意缩进。最多只能有一个else分支。所有分支表达式都不为真,也没有else分支,则返回na。

由于当K线BAR为阴线时,即close < open时,if语句后的表达式为假(false),则不执行if的本地代码块。这个时候也没有else分支,if语句就返回了na。x被赋值为na。在画图上就无法画出这个点,我们通过回测画图也可以观察到。


switch语句

switch语句也是一种分支结构的语句,用来设计根据某些条件执行不同的路径。switch语句一般来说有以下几个关键知识点。

1、switch语句和if语句一样,也可以返回值。
2、和其它语言中的switch语句不一样,执行switch结构时,只执行其代码中的一个本地块,所以break声明是不必要的(即不需要写break之类的关键字)。
3、switch的每个分支都可以写一个本地代码块,这个本地代码块的最后一行即为返回值(它可以是一个值的元组)。如果没有任何分支被的本地代码块被执行,则返回na。
4、switch结构中的表达式判断位置,可以写字符串、变量、表达式或函数调用。
5、switch允许指定一个返回值,该值作为结构中没有其它情况执行时使用的默认值。

switch分为两种形式,我们逐一来看例子,了解其用法。

1、带有表达式的 switch ,例子讲解:

之前我们学习了input函数,这里我们继续学习两个和input类似的函数:input.stringinput.int函数。
input.string用来返回字符串,input.int函数用来返回整型数值。例子中其实就新增了一个options参数的用法,options参数可以传入一个可选值组成的数组。例如例子中的options=["EMA", "SMA", "RMA", "WMA"]options=[5, 10, 20](注意一个是字符串类型,一个是数值类型)。这样策略界面上的控件就不是需要输入具体数值了,而是控件变为下拉框,选择options参数中提供的这些选项。

变量func的值就为一个字符串,变量func作为switch的表达式(可以是变量、函数调用、表达式),来确定执行switch中的哪个分支。如果变量func无法和switch中的任一个分支上的表达式匹配(即相等),则执行默认的分支代码块,会执行runtime.error("error")函数导致策略抛出异常停止。

我们上面的测试代码中在switch的默认分支代码块的最后一行runtime.error之后,我们并没有加入[na, na]这样的代码来兼容返回值,在trading view上是需要考虑该问题的,如果类型不一致会报错。但是在FMZ上由于没有严格要求类型,所以是可以省略这种兼容代码的。所以在FMZ上不用考虑if、switch分支返回值的类型兼容问题。

在FMZ上不会报错,在trading view上会报错。因为if分支返回的类型不一致。

2、没有表达式的switch

接下来我们看switch的另一种使用方式,即不带表达式的写法。

测试代码例子就可以看到,switch会匹配执行分支条件上为真的本地代码块。一般来说switch语句之后的分支条件必须是互斥的。就是说例子中up和down不能同时为true。因为switch只能执行一个分支的本地代码块,有兴趣可以把代码中这行语句:up = close > open // up = close < open 更换成注释里的内容,回测观察下结果。会发现switch分支只能执行第一个分支。除此之外还需要注意尽量不要把函数调用写在switch的分支中,函数无法在每个BAR上被调用可能引起一些数据计算的问题(除非如同「带有表达式的 switch」例子中,执行分支是确定的,在策略运行中是不会被更改的)。


循环结构

for语句

for语句使用非常简单,for循环可以最终返回一个值(或者返回多个值,以[a, b, c]这样的形式)。如同以上伪代码中赋值给「返回值」位置的变量。for语句之后跟随一个「计数」变量用于控制循环次数、引用其它值等。「计数」变量在循环开始之前被赋值为「初始计数」,然后根据「步长」设置递增,当「计数」变量大于「最终计数」时循环停止。

for循环中使用的break关键字:当执行了break语句后,循环就停止了。
for循环中使用的continue关键字:当执行了continue语句后,循环会忽略continue之后的代码,直接执行下一轮循环。for语句返回最后一次循环执行时的返回值。如果没有任何代码执行则返回空值。

下面我们用一个简单例子来展示:

for ... in 语句

for ... in语句有两种形式,以下面的伪代码来说明。

可以看到两种形式的主要差别就在于for关键字之后跟随的内容,一种是使用一个变量作为引用数组元素的变量。一种是使用一个包含索引变量,数组元素变量的元组的结构来引用。其它的返回值规则,使用break、continue等规则和for循环一致。我们也通过一个简单的例子来说明使用。

当需要使用索引时,就使用for [i, ele] in testArray的写法。

for循环应用

当可以使用Pine语言提供的内置函数完成一些循环逻辑计算时,可以使用循环结构直接编写,也可以使用内置函数处理。我们举两个例子。

1、计算均值

使用循环结构设计时:

例子中使用了for循环求和,然后计算均值。

直接使用内置函数计算均线:

直接使用内置函数ta.sma,计算均线指标,显然对于计算均线使用内置函数更加简单。在图表上对比可以看到计算出的结果完全一致。

2、求和

还是使用上面的例子来说明。

使用循环结构设计时:

对于计算数组所有的元素的和可以使用循环来处理,也可以使用内置函数array.sum来计算。
直接使用内置函数计算求和:

可以看到算出的数据,使用plot画在图表上显示完全一致。

那既然用内置函数就可以完成这些工作,为什么还要设计循环?使用循环主要是基于这3点的应用:
1、对于数组的一些操作、计算。
2、回顾历史,例如,找出有多少过去的高点高于当前BAR的高点。由于当前BAR的高 点仅在脚本运行的BAR上已知,因此需要一个循环来及时返回并分析过去的BAR。
3、使用Pine语言的内置函数无法完成对过去BAR的计算的情况。


while 语句

while语句让循环部分的代码一直执行,直到while结构中的判断条件为假(false)。

while的其它规则和for循环类似,循环体本地代码块最后一行是返回值,可以返回多个值。当「循环条件」为真时执行循环,条件为假时停止循环。循环体中也可以使用break、continue语句。

我还是用计算均线的例子来演示:

可以看到while循环使用也是非常简单的,还可以设计一些计算逻辑是无法用内置函数代替的,例如计算阶乘:


发明者量化PINE语言入门教程-变量声明、条件、循环结构的评论 (共 条)

分享到微博请遵守国家法律