商品期货量化交易-TradingviewPine语言基础课程(八): 条件和循环结构

Author: ianzeng123, Created: 2023-02-13 10:08:22, Updated:


大家好,本节课我们要讲述的内容为Pine语言语法结构的最后一块拼图–条件和循环结构。假设通过你在期货市场上通过多年的学习,形成了一套完整的指标信号计算方法,和使用指标信号进行期货市场进场出场的判断方法,但是这套交易方法是基于手工操作的,大量的准确的指标运算和交易判断方法需要耗费你极大的精神成本,如果有一套代码可以胜任这项工作,仅仅需要你少量的精力去学习,可以大大减轻你的时间压力和工作压力,如此性价比的投入,你愿意尝试一下吗?

上节课的内容我们学习了运算符,可以帮助你进行准确的指标计算。本节课我们所学的内容是条件和循环结构,通过学习本节内容,可以帮助你利用指标进行交易逻辑的判断。在学完本节内容后,基本上Pine语言交易逻辑的梳理和代码的编写工作你都可以胜任了。

条件结构

Pine语言中的条件结构是 if 和 switch。它们可用于:

  • 返回一个值或元组,然后可以将其分配给一个或多个(元组)变量。
  • 不返回值,重新赋值变量或者执行函数(比如交易函数)。

if语句

if语言相信大家都很熟悉,“如果,就”。在if条件里准确的对信号加以描述,模型就可以执行相应的操作。其基本语法结构为:

单层判断

if condition1 \\conditon后不需要加任何符号
    action1

双层判断

if condition1 
    action1
else
    action2

多层判断

if condition1 
    action1
else if condition2
    action2
else
    action3

if语句可用来给变量赋值或者执行逻辑操作,下面给大家举例示范下:

\\ 赋值
x = if close > close[1]
    '涨'
else if close == close[1]
    '平'
else
    '跌'

runtime.log(x)

\\ 操作

if close > close[1]
    runtime.log('涨') 
else if close == close[1]
    runtime.log('平')
else
    runtime.log('跌') 

需要注意的是:

  • 在tradingview上,某些 Pine语言内置函数无法从条件结构的本地块中调用,比如一些画图函数 plot(), plotbar(), plotcandle(), plotchar(), plotshape()等。 FMZ上限制不是那么严苛,但是也建议遵循Trading View上的规范书写。例如这样虽然在FMZ上不报错,不过不建议这样写。
  • 虽然允许在本地块中进行函数调用, 它们的功能与在脚本的全局范围内的功能相同(比如input)。
  • 条件结构中的局部块必须缩进四个空格或制表符。
  • 在赋值判断时,所有分支表达式都不为真,也没有else分支,则返回na。
x = if close > open
    close
runtime.log('x:',x)
  • 条件结构和循坏结构都是可以根据你的交易逻辑互相嵌入的。

switch语句

switch可以来说是if语句的另外一种呈现形式。switch也可以用来赋值或者进行逻辑操作。一般来说switch语句之后的分支条件必须是互斥的。就是说各分支的条件不能同时成立。因为switch只能执行一个分支的本地代码块。

switch的每个分支都可以写一个本地代码块,这个本地代码块的最后一行即为返回值(它可以是一个值的元组)。如果没有任何分支被的本地代码块被执行,则返回na。

带有表达式的switch

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

indic = input('close', title="指标名称", tooltip="选择要使用的指标函数名称", options=['close', 'open', 'high', 'low', 'null'])


switch indic
    'close' =>
        runtime.log('收盘价:', close)
    'open' =>
        runtime.log('开盘价:', open)
    'high' =>
        runtime.log('最高价:', high)
    'low' =>
        runtime.log('最低价:', low)
    =>
        runtime.error('没有指标')
        

不带有表达式的switch

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

up = close > open
down = close < open

switch
    up  => 
        runtime.log('涨')
    down => 
        runtime.log('跌')

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

请注意,在tradingview上,各个分支的返回值需要是一致的,比如统一的变量type类型(数字,字符,元组等),但是在FMZ上没有明确的要求,可以根据需要设置需要的返回值,FMZ实现了类型的兼容。

循环结构

虽然Pine语言中有很多内置函数可以帮助我们进行循环的计算,但是循环的存在是有充分理由的,因为即使在 Pine 语言中,它们在某些情况下也是必需的。这些情况通常包括:

  • 数组(array)的操作。
  • 回顾历史,使用只能 在当前柱上已知,例如,找出有多少过去的高点高于当前柱线的高点。由于当前柱线的高点仅在运行脚本的柱上已知,循环是必要的,可以回到过去并分析过去的柱线。
  • 使用Pine语言的内置函数无法完成对过去BAR的计算的情况。

for语句

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

for 起始计数 to 最终计数 by 步长 //FMZ暂时不支持反向操作
    语句 // 注释:语句里可以有break、continue
    操作

x = for 起始计数 to 最终计数 by 步长 
    语句 
    返回值//循坏过后最终的值

下面举例示范下:

//操作
for i = 0 to 10       
    runtime.log("i:", i)
runtime.error("stop")

//赋值
ret = for i = 0 to 10       
    runtime.log("i:", i)
    i                       // 如果这行不写,就返回空值,因为没有可返回的变量
    
runtime.log("ret:", ret)
runtime.error("stop")

在if语句中是可以插入break(跳出整个循环)这个循环和continue(跳过本次条件)。

for i = 0 to 10       
    runtime.log("i:", i)
    if i == 3
      break

for i = 0 to 10       
    runtime.log("i:", i)
    if i == 3
      continue

for…in 语句

for.in语句主要针对对象为数组,主要有以下两种形式:

for 数组元素 in 数组 
    语句                        // 注释:语句里可以有break,continue

for [索引变量, 索引变量对应的数组元素] in 数组
    语句                        // 注释:语句里可以有break,continue

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

testArray = array.from(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
for ele in testArray            
    runtime.log("ele:", ele)

runtime.error("stop")

for [i, ele] in testArray            
    runtime.log("ele:", ele, ", i:", i)

runtime.error("stop")

while语句

while语句让循环部分的代码一直执行,直到while结构中的判断条件为假(false)。和if语句一致,while语句也可以进行赋值或者逻辑操作,while语句的基本结构为

while 判断条件
    操作                    // 注释:语句里可以有break,continue


返回值 = while 判断条件
    语句                    // 注释:语句里可以有break,continue
    语句                    // 注释:最后一条语句为返回值

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

i = 0

while i <= 10 
    runtime.log('i',i)
    i += 1
runtime.error("stop")

x = while i <= 10 
    runtime.log('i',i)
    i += 1
    i//循环体结束后最后的值

runtime.log('x:',x)
runtime.error("stop")

Pine语言最后一块语法拼图已经讲解完毕,我们下节课再见。


更多内容