用这么易学易用的Pine语言如果还不会写策略的话,那我就...

Author: 小小梦, Created: 2022-06-01 17:37:55, Updated: 2023-09-18 20:19:45

img

用这么易学易用的Pine语言如果还不会写策略的话,那我就…

TradingView上的开源策略数量多到爆炸,这么多优秀的策略、思路、指标不能实盘真是太可惜了。看到这一点,作为致力于把量化交易技术普及给众多交易者的FMZ自然不能抑制这种解决需求的冲动!

这个需求绝对不能忍!

于是,在编程开发的代码世界里跋山涉水、走过万水千山,历经9*9=81坑,熬过无数个不眠的夜晚,墙角堆砌起了小山一般的红牛空罐子之后。终于FMZ支持兼容了Pine语言,各种TradingView的Pine脚本都可以拿来用了。

说到Pine语言,小编我也是最近才自学。不过说实话作为量化交易的Pine语言真的是简单易用,易学。 什么?不信?看我洋洋洒洒给你写个网格策略~

/*backtest
start: 2021-06-01 00:00:00
end: 2022-05-23 00:00:00
period: 1h
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
args: [["v_input_float_1",500],["v_input_string_1",2],["v_input_float_2",0.01],["v_input_int_1",20],["v_input_int_2",500],["RunMode",1,358374],["MinStock",0.001,358374]]
*/

strategy(overlay=true)

varip beginPrice = 0
var spacing = input.float(-1, title="间距价格")
var dir = input.string("long", title="方向", options = ["long", "short", "both"])
var amount = input.float(-1, title="下单量")
var numbers = input.int(-1, title="网格数量")
var profit = input.int(-1, title="盈利价差") / syminfo.mintick

if spacing == -1 and amount == -1 and numbers == -1 and profit == -1
    runtime.error("参数错误")

if not barstate.ishistory and beginPrice == 0 
    beginPrice := close 

findTradeId(id) =>
    ret = "notFound"
    for i = 0 to strategy.opentrades - 1
        if strategy.opentrades.entry_id(i) == id 
            ret := strategy.opentrades.entry_id(i)
    ret 

// 实时K线阶段
if not barstate.ishistory
    // 检索网格
    for i = 1 to numbers
        // 做多
        direction = dir == "both" ? "long" : dir 
        plot(beginPrice-i*spacing, direction+str.tostring(i), color.green)
        if direction == "long" and beginPrice-i*spacing > 0 and beginPrice-i*spacing < close and findTradeId(direction+str.tostring(i)) == "notFound"
            strategy.order(direction+str.tostring(i), strategy.long,  qty=amount, limit=beginPrice-i*spacing)
            strategy.exit("exit-"+direction+str.tostring(i), direction+str.tostring(i), qty_percent=100, profit=profit)
        // 做空
        direction := dir == "both" ? "short" : dir 
        plot(beginPrice+i*spacing, direction+str.tostring(i), color.red)
        if direction == "short" and beginPrice+i*spacing > close and findTradeId(direction+str.tostring(i)) == "notFound"
            strategy.order(direction+str.tostring(i), strategy.short, qty=amount, limit=beginPrice+i*spacing)
            strategy.exit("exit-"+direction+str.tostring(i), direction+str.tostring(i), qty_percent=100, profit=profit)

FMZ的实盘、回测工具、众多的功能再加上Pine语言的简单易用,可算如虎添翼!算上参数设置、回测配置代码,总共代码也没超过50行。入门的同学再也不用为了写个网格头疼到炸了。。。

当然啦,这个策略是个网格策略,网格策略也有硬伤,也不是包赢的印钞机,关键看用法、参数。这点就不赘述了,我们更多来关注如何轻松的写策略,实现自己的交易逻辑,自己写策略交易赚钱,不求人的感觉太爽了!!

代码讲解

我来给各位看官讲解讲解,代码简单易懂,用这么易学易用的Pine语言如果还不会写策略的话,那我就…详细的给您讲讲!

开头的/*backtest*/包裹住的内容是FMZ的回测配置代码,这个是FMZ的功能,并非Pine语言的内容。当然,您可以不写这部分内容,回测的时候就需要手动点击参数控件来设置回测配置和参数了。

/*backtest
start: 2021-06-01 00:00:00
end: 2022-05-23 00:00:00
period: 1h
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
args: [["v_input_float_1",500],["v_input_string_1",2],["v_input_float_2",0.01],["v_input_int_1",20],["v_input_int_2",500],["RunMode",1,358374],["MinStock",0.001,358374]]
*/

接下来的代码:

strategy(overlay=true)

varip beginPrice = 0
var spacing = input.float(-1, title="间距价格")
var dir = input.string("long", title="方向", options = ["long", "short", "both"])
var amount = input.float(-1, title="下单量")
var numbers = input.int(-1, title="网格数量")
var profit = input.int(-1, title="盈利点数") / syminfo.mintick
  • strategy(overlay=true): 用来设置脚本的一些选项,overlay=true,就是给参数overlay赋值true,让画图时,画在图表的主图上(K线图就是主图,可以这么简单理解)。
  • varip beginPrice = 0: 用关键字varip声明了一个变量beginPrice初始赋值为0,这个值是用作网格的初始价格。
  • var spacing = input.float(-1, title="间距价格"): 设置一个策略参数,参数名字叫“间距价格”,就是每个网格点的间距,设置100就是价格每过100,交易一次。
  • var dir = input.string("long", title="方向", options = ["long", "short", "both"]): 设置了一个策略参数,名字叫“方向”,这个参数是一个带下拉框的选项,可以选择long,short,both。分别表示网格只做多、只做空、多空都做。
  • var amount = input.float(-1, title="下单量"): 设置一个参数,用来控制每次网格点交易时的交易量。
  • var numbers = input.int(-1, title="网格数量"): 网格点的数量,设置20就是一个方向20个网格点。
  • var profit = input.int(-1, title="盈利价差") / syminfo.mintick: 设置一个参数,控制每个网格点的持仓盈利到多少价差就平仓。

接下来,看代码:

if spacing == -1 and amount == -1 and numbers == -1 and profit == -1
    runtime.error("参数错误")

意思就是如果spacing、amount、numbers、profit这些参数任何没有设置的话,默认是-1,就让策略停止(没设置参数不能瞎跑~哈哈!)

Go on !

if not barstate.ishistory and beginPrice == 0 
    beginPrice := close 

这里的意思就是当策略处于实时K线阶段并且beginPrice == 0 时,去给beginPrice修改值,改为当前的最新价格。可以理解为策略正式运行时,初始当前价格为网格的初始价格。因为脚本是有历史K线BAR阶段的,策略在历史BAR阶段会执行一遍逻辑,历史BAR上去布置网格肯定是没什么意义。

什么是历史BAR阶段?

简单举个例子,比如当前时刻A,策略开始运行,策略拿到了一个有100个K线BAR的数据,随着时间肯定是100个BAR变成101个,102个… N个。从A时刻开始运行时,第101个BAR就是实时K线阶段了,这个时候是最新的实时数据了。那么从第1个BAR到第100个BAR,这些都是已经过去的历史行情了,但是策略也会在这些历史行情上运行一遍,所以这个阶段就是历史K线阶段。

barstate.ishistory这个是Pine语言的一个内置变量,如果当前的BAR是历史阶段的BAR,barstate.ishistory就为true,如果不是历史阶段的BAR就为false。所以 not barstate.ishistory 为true时,就是处于实时K线阶段了。

接下来,我们创建了一个函数

findTradeId(id) =>
    ret = "notFound"
    for i = 0 to strategy.opentrades - 1
        if strategy.opentrades.entry_id(i) == id 
            ret := strategy.opentrades.entry_id(i)
    ret 

这个函数的作用就是在当前已经开仓的所有订单中查找某个id是否存在,如果存在findTradeId函数调用时就返回存在的单子的ID(注意这个ID不是交易所的订单ID,是策略给订单起的名字或者理解为标签),如果不存在就返回字符串"notFound"。

接下来就开始网格布单了:

// 实时K线阶段
if not barstate.ishistory
    // 检索网格
    for i = 1 to numbers
        // 做多
        direction = dir == "both" ? "long" : dir 
        plot(beginPrice-i*spacing, direction+str.tostring(i), color.green)
        if direction == "long" and beginPrice-i*spacing > 0 and beginPrice-i*spacing < close and findTradeId(direction+str.tostring(i)) == "notFound"
            strategy.order(direction+str.tostring(i), strategy.long,  qty=amount, limit=beginPrice-i*spacing)
            strategy.exit("exit-"+direction+str.tostring(i), direction+str.tostring(i), qty_percent=100, profit=profit)
        // 做空
        direction := dir == "both" ? "short" : dir 
        plot(beginPrice+i*spacing, direction+str.tostring(i), color.red)
        if direction == "short" and beginPrice+i*spacing > close and findTradeId(direction+str.tostring(i)) == "notFound"
            strategy.order(direction+str.tostring(i), strategy.short, qty=amount, limit=beginPrice+i*spacing)
            strategy.exit("exit-"+direction+str.tostring(i), direction+str.tostring(i), qty_percent=100, profit=profit)

使用了for循环,根据numbers参数的数值,确定循环次数,即布置对应个数的订单。根据dir参数,设置direction。使用findTradeId函数查找当前网格位置的标签的订单是否已经开仓,只有没有开仓时才去下计划单(开仓了肯定就不能重复下了)。下单使用strategy.order函数指定limit参数做计划单。下计划单的同时下对应的平仓单。平仓单使用strategy.exit函数,指定profit参数,指定利润点数。

img

img

img

img

看收益曲线就知道,网格也是有风险的,并非包赢,只不过在一个大尺度上拉大网格相对风险小了一点而已。

那么,用这么易学易用的Pine语言如果还不会写策略的话,那我就…


Related

More

Stalker 这种胎教级别的教程能不能多来几个,最好专门来个pine的教程(知识付费)hhh 。梦总v587

artron 谢谢

bbbwwed2009 梦总V5

小小梦 有的,在B站已经做了一个系列的Pine教程了:https://www.bilibili.com/video/BV1sU4y1B71i/

小小梦 不客气,感谢支持FMZ。

小小梦 B站上有PINE语言入门视频也可以看。