Pine Script笔记归档

1.简介

量化交易平台 TrendingView  (opens new window)是一个支持多种资产的投资平台,很多人在上面分享对于股票,外汇,数据货币等资产的投资观点,难得的是在上面能找到很多人的交易策略。

TrendingView 使用的是自己开发的Pine 语言作为脚本,这一点和MT4 开发的mql4 很像。用户可以自己编写脚本和策略,并与其他人分享。Pine 直观给我的印象比Mql4 更加简单,更加关注于策略本身,而不是编程技巧。

此外,Pine语言编辑器没有那么强大的debug 功能,这对于一开始上手练习来说,不是那么方便。不过它一直在更新,发展的很快。

TrendingView 对接了很多经纪商,使得它支持的交易品种很丰富,而且它的图表功能很强大。

2.脚本结构

指明用的Pine 脚本版本

1
//@version=4  

Pine 可以分为study脚本 和 strategy 脚本(指标&策略) study 脚本必须包含 plot,plotshape,barcolor,line.new 等输出 strategy 脚本包含 strategy.* 即交易函数

3.换行

1
2
3
4
5
6
7
8
9
10
11
//例子1 换行需要空格
a = open+
high+
low
// 例子2 换行中不能有注释
a = open+
high // 此处加注释会出问题
// 例子3 函数内换行,空行必须要超过一个Tab(或者4个空格)
label.new(bar_index, na, yloc=yloc.abovebar, text=t,
color=hist ? color.green : color.red)
// 这里空格必须超过4个

4.运算符

  • 算术: + - * / %

    1
    1/2 = 0 1/2.0 = 0.5
  • 比较: == !=

  • 逻辑: not and or

  • 三元运算符:

    1
    2
    3
    condition ? result1 : result2
    iff(condition, result1, result2)
    //有房?嫁:有车?: 嫁:帅?嫁: 不嫁

[] 运算符(History reference operator) close 代表最新的价格,close[1]代表了历史价格。

1
close = close[0] //显示的是最新的收盘价

除此之外,Pine脚本里面还有一个变量 bar_index,记录着bar的数目,编号自左向右,从0开始。bar_index = (bar数量)N-1。

5.函数

Pine 脚本中包含了大量的自建函数,用户还可以自定义函数、

  • 单行函数

    1
    f(x,y) => x+y

    Pine Script 的函数不支持递归
    即,不允许在函数中再次调用自己本身

  • 多行函数

    1
    2
    3
    4
    geom_average(x, y) =>
    a = x*x
    b = y*y
    sqrt(a + b)
    • Pine Script 需要(一个Tab 或者4空格,TrendingView 会自动用4个空格来替换掉Tab)来划定函数的范围
    • 最后一行的表达式或 变量作为函数的输出结果

5.1.函数的注意事项

当在函数块中使用函数或者历史数据信息的时候要注意。因为所使用的历史信息是每一次连续调用生成的。

如果函数并不是在每一根柱线上都调用,那么数据生成就会出现错误。

6.变量声明&语句statement

6.1.var

  • Pine 语言中变量定义的方式有两种: = 和 var

    1
    2
    3
    4
    5
    a = 1 // a为整形
    float a = 1 // a为浮点型
    var a = 0
    var int a = 0
    b = na //出错

    变量定义的时候,需要指明变量的类型(或者 等式右侧表达式能指明类型亦可)
    na 没有特定的类型,所以赋值时会出错

  • var 关键词
    var 是用于分配和一次性初始化变量的关键词。

不含var 关键词的变量在每次数据更新的时候都会覆盖变量的值。使用了var 关键词的变量,在数据更新中,可以“保持状态”。 举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//@version=4
study("Var keyword example")
var a = close
var b = 0.0
var c = 0.0
var green_bars_count = 0
if close > open
var x = close
b := x
green_bars_count := green_bars_count + 1
if green_bars_count >= 10
var y = close
c := y
plot(a)
plot(b)
plot(c)

变量 ‘a’ 保持系列中每个柱线的第一根柱线的收盘价。

变量 ‘b’保持系列中第一个“绿色”价格棒的收盘价。

变量 ‘c’保持系列中第十个“绿色”条的收盘价。

即a,b,c 都是一个常数。

去除var 的话,a,b,c 会随着价格变化而变化

6.2.if语句

1
2
3
4
5
6
7
8
9
10
// This code compiles
x = if close > open
close
else
open
// This code doesn't compile
x = if close > open
close
else
"open"

需要注意的是,与python不同,Pine要求,then 和 else语句返回的值的类型是相同的。在上面的第二个例子中,close 和 “open” 一个是float Series,另一个是string,不同类型的话,编译会出错。

1
2
3
4
  x = if close > open
close
// If current close > current open, then x = close.
// Otherwise the x = na.

if 语句中可以忽略else,但是系统会默认赋值(na,false,””)

6.3.for语句

1
2
for i = 1 to length-1
sum := sum + price[i]

7.执行模型

Pine代码是根据价格信息计算的。但是价格信息并不是完整加载的,用户可以一直向左滑动图表,直到最早的一根柱子(Pro 用户可以在图表上加载10000左右,免费用户可以加载5000根柱子)

8.实时数据的计算

Pine指标计算实时数据的时候和计算历史数据略有不同,因为实时数据会有addtional commit(?)和rollback action(?)

在实时数据的处理过程中,柱线的每一次变动都会引起Pine 指标的计算

rollback : 在每一根柱线更新时发生
commit : 在每一根柱线关闭时发生
对于判断柱线的状态,Pine中有一系列的自建函数 barstate.* 来显示当前柱线的状态。

9.Context switching and the security function

security 函数可以用于按照特定要求请求数据

1
2
3
4
//@version=4
study("Example security 1", overlay=true)
ibm_15 = security("NYSE:IBM", "15", close)
plot(ibm_15)
  • symbol (string)商品代码。

商品代码可以包含数据提供商信息,也可以不含

如 “NYSE:IBM”,”BATS:IBM”,”IBM”(如不提供,默认使用BATS)

syminfo.ticker (opens new window)and syminfo.tickerid (opens new window)是表示当前图标上的商品代码,syminfo.ticker是不含数据供应商信息,syminfo.tickerid是包含供应商信息。Pine教程建议使用后者,为了避免数据的模糊性

  • resolution (string)分辨率/timeframe时间周期

    • 分钟级:1,5,10,21,60,120,等等
    • 日级: D,1D,2D 等等
    • 周级:W,1W,2W
    • 月级:M,1M,2M
    • timeframe.period 记录当前图标时间周期
  • expression (series)计数并从 security (opens new window)调用返回的表达式。

如果仅仅是获取收盘价数据,我们可以用security('EURUSD','D',close)

但是,expression能给我们提供更加丰富的操作,比如,我们需要知道,EURUSD相对于GBPUSD 上涨的幅度

1
2
3
4
5
6
7
//@version=4
study(title = "Advance Decline Ratio", shorttitle="ADR")
ratio(t1, t2, source) =>
s1 = security(t1, timeframe.period, source)
s2 = security(t2, timeframe.period, source)
s1 / s2
plot(ratio("GBPUSD", "EURUSD", close-open))

在security数据应用到当前图表上的时候,有两个控制,一个是gaps,另一个是lookahead

  • gaps (const bool)默认值为barmerge.gaps_off (opens new window)。可以理解为数据平滑的操作,因为数据中会存在空值(na),在gaps_off的情况下,na会被离它最近的非空值所替代,也就不会出现间隔(gap)的情况

  • lookahead (const bool)默认值为barmerge.lookahead_off (opens new window)
    合并所请求数据位置的策略。 请求的条形图与当前的条形图按照k线开盘时间合并。 这种合并策略可能导致从“未来”获取数据计算历史的不良影响。 这在回溯测试策略中不被接受,但在指标中可使用。

    1
    2
    3
    4
    5
    6
    //@version=4
    study('My Script', overlay=true)
    a = security(syminfo.tickerid, '60', low, lookahead=barmerge.lookahead_off)
    plot(a, color=color.red)
    b = security(syminfo.tickerid, '60', low, lookahead=barmerge.lookahead_on)
    plot(b, color=color.lime)

10.bar state.* 变量

  • barstate.isfirst 当前k线为k线组的第一条k线

  • barstate.islast 当前k线为k线组的最后一条k线

  • barstate.ishistory 当前k线为历史k线

  • batstate.isrealtime 当前k线为实时k线

  • barstate.isnew 新K线的第一次更新

  • batstate.isconfirmed =当前k线的最后(关闭)更新
    不建议在security (opens new window)表达式中使用barstate.isconfirmed

所有的历史柱线都曾被认为是新的柱线,因为脚本是依次执行的。当柱线第一更开盘价生成的时候,认为此柱线是新的。

11.会话和时间信息

Pine 提供方法来生成 交易区间,时间和日期的信息。

time(变量): 返回的是时间戳格式

time(函数):time(resolution, session) → series 返回的是按照session 格式返回的时间,如果不在session时间段的话便会返回na值

1
2
3
4
//@version=4
study("Time", overlay=true)
t1 = time(timeframe.period, "0000-0000")
bgcolor(t1 ? color.blue : na)

交易区间的格式有:

  • 0000-0000:1234567 24小时交易,时间从午夜0点开始
  • 0000-0000:23456 工作日24小时交易
  • 1700-1700:24小时交易,时间从17点开始
  • 0930-1700:146 交易时间为09:30~17:00,交易时间在周日(1),周三(4),周五(6)
  • 24x7 等价于 0000-0000:1234567
    1
    2
    3
    4
    5
    6
    7
    // 判断是否为30min的新柱线
    //@version=4
    study("new 30 min bar")
    is_newbar(res) =>
    t = time(res)
    not na(t) and (na(t[1]) or t > t[1])
    plot(is_newbar("30") ? 1 : 0)

用到的函数变量和类型:

  • time:UNIX格式的当前k线时间
  • timenow:UNIX格式的当前时间
  • syminfo.timezone:时区

当前K线用到的变量:

  • year/month/weekofyear
  • dayofmonth
  • dayofweek(sunday,monday 等)
  • hour/minute/secondzh

创建时间:

12.策略编写

12.1backtesting & forwardtesting

strategy脚本是可以产生交易订单的Pine 脚本。利用strategy 可以做策略回测(backtesting)和 模拟交易(forwardtesting)

无论backtesting 还是forwardtesting,计算都是默认发生在K线收盘的时候,但是在forwardtesting 的时候,可以选择在每一个tick发生的时候,都运行一次。

做法一是调整strategy的 Setting/Properties,或者修改代码,添加strategy(... ,calc_on_every_tick=true ) ,此外还可以选择在每笔订单完成之后计算strategy(... , calc_on_order_fills=true)

12.2.经纪商模拟

仅仅只有OHLC数据的话,K线内数据的生成有一套逻辑,如果最高价更接近开盘价,生成顺序是 open->high->low->close,此外还假设价格是没有gaps的

12.3.订单生成命令

12.3.1.strategy.entry 订单生成函数

这是进入市场的命令。 如果具有相同ID的订单已经挂起,则可修改订单。 如果没有指定ID的订单,则会发出新的订单。

要取消/停用预挂单,应使用命令strategy.cancel (opens new window)strategy.cancel_all (opens new window)

与函数strategy.order (opens new window)相比,strategy.entry (opens new window)功能受金字塔影响,可以正确反转市场位置。 如果“Limit”和“stop”参数均为“NaN”,则订单类型为市场订单。

1
strategy.entry(id, long, qty, limit, stop, oca_name, oca_type, comment, when) → void
12.3.2.strategy.exit 订单退出函数
1
strategy.exit(id, from_entry, qty, qty_percent, profit, limit, loss, stop, trail_price, trail_points, trail_offset, oca_name, comment, when) → void

这是一个退出指定进场或整个市场地位的命令,重点区分它和strategy.close 的不同

  • id(string): 订单的标识符。
  • from_entry(string): 这里填入要平仓的订单的标识符,默认为空。
  • qty: 平仓手数(弄清楚合约的大小)
  • qty_percent: 平台的比例
  • profit: 获利点数(一定搞清楚单位是点还是步)
  • limit: 与profit 相似,limit约定获利的价格
  • loss:止损点数
  • stop:与loss 相似,stop约定止损的价格
  • tail.*: 指明跟踪指数
12.3.3.strategy.order

这条命令可以生成开仓也可以生成平仓命令,但是它不受金字塔影响。它的作用就是弥补strategy.entry 和 strategy.exit 函数的不灵活性。

12.4.风险管理

strategy.risk.* 一系列函数,可以帮助进行风险管理。当风险管理规则被激活的时候,没有订单会生成。

1
2
3
4
5
6
//@version=4
strategy("multi risk demo", overlay=true, pyramiding=10, calc_on_order_fills = true)
if year > 2014
strategy.entry("LE", strategy.long)
strategy.risk.max_intraday_filled_orders(5)
strategy.risk.max_intraday_filled_orders(2)
  • strategy.risk.max_intraday_filled_orders(2)
    限制一天成交的最大的交易单数,一旦达到,所有未成交订单全部取消,成交订单关闭。并且一直关闭交易直到本交易日结束。

13.指标重绘

历史数据仅仅包含OHLC,不包含线内的运动。这会导致的问题是,历史数据上的回测和实时数据不一致的情况。

另外一个担心是,未来函数的使用。这里尤其要关注security 函数,此函数可能会错误的引入未来的信息。

14.绘图

Pine V4 中存在两种绘图类型:label 和 line。

注:用户的绘图和 编程绘图是不一样的,编程得到的绘图是不能用鼠标修改的。

和指标绘图函数(plot,plotshape,plotchar) 不一样的是,绘图函数可以在图表右侧没有K线的地方。

14.1.label

1
label.new(x, y, text, xloc, yloc, color, style, textcolor, size) → series[label]
1
2
3
4
//@version=4
study("My Script", overlay=true)
label.new(bar_index, high, style=label.style_none,
text="x=" + tostring(bar_index) + "\ny=" + tostring(high))
  • x的位置是用bar_index 标识的,此时xloc 的默认值为xloc.barindex

  • y的位置是最高价

  • xloc取值:xloc.bar_index(默认) 和 xloc.bar_time

  • yloc取值:

    • yloc.price 传入此函数,需要输入y值
    • yloc.abovebar,yloc.belowbar 启动时,y值会失效。标签在图表上部或者下部
  • style: 很多种,可能用到比较多的有label.style_none,无底色

14.2.line

1
line.new(x1, y1, x2, y2, xloc, extend, color, style, width) → series[line]
  • extend: extend.none/extend.right/extend.left

15.尾巴