上一篇我们领略了calc_on_order_fills
的强大威力,它能让策略在订单成交后即时反应,触发连锁下单。但很多开发者在实际使用中,会遇到一个极其困惑的现象:我明明在代码里写了 calc_on_order_fills=true
,为什么我的策略还是老样子,一点即时反应都没有?
如果你也遇到了这个问题,请放心,这不是 Bug。这背后隐藏着一个关于信号有效期的精妙逻辑陷阱。今天,学长就带你彻底把它弄明白。
一个失灵的策略
我们来看一个交易吞没形态的策略。逻辑很简单:
- 当出现一根高于EMA的吞没时,做多。
- 当出现一根低于EMA的吞没时,做空。
- 我们设置
pyramiding=10
,希望在信号出现后,利用calc_on_order_fills
的连锁反应快速建仓。
策略代码如下:
//@version=6
strategy(title="吞没 - 示例策略", overlay=true,pyramiding=10, calc_on_order_fills=false)
priceData = input.source(hl2, title="价格数据")
emaLen = input.int(12, title="EMA")
smoothLen = input.int(3, title="EMA 平滑", minval=1)
//计算
emaValue = ta.ema(priceData[10], emaLen)
emaSmooth = ta.ema(emaValue, smoothLen)
plot(emaSmooth, color=#F4A460, linewidth=2)
//交易
outsideBar = high > high[1] and low < low[1]
enterLong = priceData > emaSmooth and outsideBar
enterShort = priceData < emaSmooth and outsideBar
bgcolor(outsideBar ? color.new(color.teal,85) : na)
//订单
if enterLong
strategy.entry("Long Entry", strategy.long)
if enterShort
strategy.entry("Short Entry", strategy.short)
这是 calc_on_order_fills=false
在默认状态下的策略代码,现在我们打开那个即时反应的开关:
strategy(title="吞没 - 示例策略", overlay=true,pyramiding=10, calc_on_order_fills=true)
保存代码,策略重新加载后,大家会发现,和开启前一模一样,毫无变化! 预想中的连锁下单没有出现,calc_on_order_fills
好像失灵了。
信号过期问题
为什么会这样?要理解原因,我们必须把订单的生成和执行过程在时间轴上拆解开:
- 信号K线(假设是第10根K线)收盘时:此时
outsideBar
条件为true
(因为它就是一根吞没),策略计算后,enterLong
条件满足,于是生成了一个多头订单。 - 成交K线(第11根K线)开盘时:第10根K线生成的订单,在此时被成交了。由于我们开启了
calc_on_order_fills
,订单成交的瞬间,立刻触发了一次在第11根K线内部的额外计算。 - 额外计算的瞬间(仍在第11根K线上):策略重新检查开仓条件
enterLong
,此时代码检查的是当前这根K线(第11根)是不是吞没。然而第11根K线大概率不是一根吞没,所以outsideBar
条件为false
。因此enterLong
条件也为false
,策略认为无事可做,不会生成新的订单。
结论就是: 触发第一笔订单的那个关键信号(outsideBar
),在第二笔订单想要生成时,已经过期失效了,连锁反应的第一环就断了,后续的下单自然无从谈起。
要解决这个问题,我们就必须让我们的交易信号活得久一点,至少能持续到下一根K线,我们需要修改开仓条件,告诉策略:“如果当前这根K线是信号K线,或者上一根K线是信号K线,我都认!”
这个逻辑在PineScript中实现起来非常简单,只需一个小小的改动:
修改前的条件:
enterLong = priceData > emaSmooth and outsideBar
enterShort = priceData < emaSmooth and outsideBar
修改后的条件:
// 让信号有效期延长一根K线
enterLong = priceData > emaSmooth and (outsideBar or outsideBar[1])
enterShort = priceData < emaSmooth and (outsideBar or outsideBar[1])
outsideBar[1]
就是获取上一根K线的 outsideBar
状态,通过 or
操作符,我们成功地将信号的有效期延长了一根K线。
应用修改后的完整代码:
//@version=6
strategy(title="吞没 - 示例策略", overlay=true,
pyramiding=10, calc_on_order_fills=true)
priceData = input.source(hl2, title="价格数据")
emaLen = input.int(12, title="EMA 长度")
smoothLen = input.int(3, title="EMA 平滑", minval=1)
emaValue = ta.ema(priceData[10], emaLen)
emaSmooth = ta.ema(emaValue, smoothLen)
plot(emaSmooth, color=#F4A460, linewidth=2)
outsideBar = high > high[1] and low < low[1]
enterLong = priceData > emaSmooth and (outsideBar or outsideBar[1])
enterShort = priceData < emaSmooth and (outsideBar or outsideBar[1])
bgcolor(outsideBar ? color.new(color.teal, 85) : na)
if enterLong
strategy.entry("Long Entry", strategy.long)
if enterShort
strategy.entry("Short Entry", strategy.short)
总结
calc_on_order_fills
并非一个即插即用的魔术开关,它更像是一个催化剂。它只提供了在订单成交后立即进行额外计算的机会,但你的策略代码必须有能力抓住这个机会。
- 功能失灵的根本原因: 触发初始订单的信号,在订单成交后的额外计算中已经过期失效。
- 解决方案:让信号的有效期能够至少延续到订单成交的K线上(通常是信号K线的下一根),比如使用
condition or condition[1]
的逻辑。
理解了信号有效期这个概念,你就能真正驾驭 calc_on_order_fills
,让你的策略在需要时,爆发出惊人的执行效率。