You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

回测结果与实盘出入场信号不符的解决方案咨询

问题描述

我在Pine Script策略中使用了calc_on_every_tick、calc_on_order_fills和use_bar_magnifier函数,策略通过SL(止损)或TP(止盈)离场,且TP采用TSL(跟踪止盈)。但运行策略时,回测结果与交易所实盘交易差异极大,请问如何让回测避免重绘,准确显示K线内部真实的出入场信号?

策略代码
//@version=5
strategy("ХХХ", overlay=true)

from_day = input.int(defval=1, title='From Day', minval=1)
from_month = input.int(defval=3, title='From Month', minval=1)
from_year = input.int(defval=2018, title='From Year', minval=1970)

to_day = input.int(defval=1, title='To Day', minval=1)
to_month = input.int(defval=1, title='To Month', minval=1)
to_year = input.int(defval=2100, title='To Year', minval=1970)

time_cond = time > timestamp(from_year, from_month, from_day, 00, 00) and time < timestamp(to_year, to_month, to_day, 23, 59)

ma_type = input.string('EMA', 'MA Type', ['ALMA','HMA','SMA','SWMA','VWMA','WMA','ZLEMA','EMA'], group = 'Setup')
ma_period = input.int(9, 'MA Period (Length)', 1, group='Setup')

alma_offset = input.float(0.85, 'ALMA Shift', 0, 1, 0.05, group = 'Setup (ALMA)')
alma_sigma = input.int(6, 'ALMA Deviation', 1, step = 1, group = 'Setup (ALMA)')

show_line_1(x) =>
    input.bool(true, 'Show Close line', group = 'On/Off') ? x : na
show_line_2(x) =>
    input.bool(false, 'Show High/Low lines', group = 'On/Off') ? x : na
show_fill(x) =>
    input.bool(true, 'Show fill', group = 'On/Off') ? x : na

//╠════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
//║ Calculations :
f(x) =>
    switch ma_type
        'ALMA' => ta.alma(x, ma_period, alma_offset, alma_sigma)
        'HMA' => ta.hma(x, ma_period)
        'SMA' => ta.sma(x, ma_period)
        'SWMA' => ta.swma(x)
        'VWMA' => ta.vwma(x, ma_period)
        'WMA' => ta.vwma(x, ma_period)
        'ZLEMA' => ta.ema(x + x - x[math.floor((ma_period - 1) / 2)], ma_period)
        => ta.ema(x, ma_period)

ma_heikinashi_open = f(request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, open))
ma_heikinashi_close = f(request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, close))
ma_heikinashi_high = f(request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, high))
ma_heikinashi_low = f(request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, low))

trend = 100 * (ma_heikinashi_close - ma_heikinashi_open) / (ma_heikinashi_high - ma_heikinashi_low)
//╠════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
//║ Colors :
color_positive = input.color(color.new(#26A69A, 0), 'Positive color (Bullish)', group = 'Colors')
color_negative = input.color(color.new(#EF5350, 0), 'Negative color (Bearish)', group = 'Colors')
color_neutral = input.color(color.new(#808080, 0), 'Neutral color', group = 'Colors')

color_trend = trend > 0 ? color_positive : color_negative
//╠════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
//║ Plot :
plot_open = plot(ma_heikinashi_open, 'Open line', na)
plot_close = plot(ma_heikinashi_close, 'Close line', show_line_1(color_trend), 2)
plot_high = plot(ma_heikinashi_high, 'High line', show_line_2(color_neutral))
plot_low = plot(ma_heikinashi_low, 'Low line', show_line_2(color_neutral))

plot_highest = plot(math.max(ma_heikinashi_open, ma_heikinashi_close),'Highest Body line', na)
plot_lowest = plot(math.min(ma_heikinashi_open, ma_heikinashi_close),'Lowest Body line', na)
//╠════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
//║ Fill :
fill(plot_open, plot_close, color.new(color_trend,50), 'Open/Close Cloud')
fill(plot_high, plot_highest, show_fill(color.new(color_neutral,87.5)), title = 'High Cloud')
fill(plot_lowest, plot_low, show_fill(color.new(color_neutral,87.5)), title = 'Low Cloud')
//╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

// Look for moving average crosses
crossAbove = trend>0
crossUnder = trend<0

if crossAbove and time_cond and strategy.position_size == 0
    color.new(color.green, 80)
    strategy.entry("long", strategy.long , comment = "LONG"  )
    
else if crossUnder and time_cond and strategy.position_size == 0
    color.new(color.red, 80)
    strategy.entry("short", strategy.short , comment = "SORT"  )   

stoploss = input.float( 2 , "Stop Loss percentage" , minval= 0 )
takeprofit = input.float( 2 , "Take Profit percentage" , minval= 0 )
trailingstop = input(true , "Trailing Stop")
trail_p = input.float( 0.05 , "Trailing Stop percentage (of entry price)" , minval= 0 )

av_p = strategy.position_avg_price

if trailingstop 
    
    if strategy.position_size > 0 and strategy.position_size[1] <= 0
        mes_pa = "   start trail: "+str.tostring(   av_p + (av_p *  takeprofit/100)    ) +"\n trail offset: "+ str.tostring( av_p *(trail_p/100)   ) +"\n SL: "+ str.tostring( av_p - (av_p *  stoploss/100)    ) 
        label.new(bar_index, low - low *(trail_p/100) , mes_pa , style = label.style_label_up , color = color.white ,textcolor = color.black, size = size.normal)

    else if  strategy.position_size < 0 and strategy.position_size[1] >= 0
        mes_pa = "   start trail: "+str.tostring(   av_p - (av_p *  takeprofit/100)    ) +"\n trail offset: "+ str.tostring(   av_p *(trail_p/100)     )  +"\n SL: "+ str.tostring( av_p + (av_p *  stoploss/100)    ) 
        label.new(bar_index, high + high *(trail_p/100) , mes_pa , style = label.style_label_down  , color = color.white ,textcolor = color.black, size = size.normal)

    strategy.exit("exit_buy", "long" ,  when= strategy.position_size > 0  ,  stop  =  av_p - (av_p *  stoploss/100)  ,  trail_price = av_p + (av_p *  takeprofit/100) , trail_offset = av_p *(trail_p/100) *1/syminfo.mintick  , comment = "EXIT LONG1" ) 
    strategy.exit("exit_sell", "short" ,when= strategy.position_size < 0,  stop  =  av_p + (av_p *  stoploss/100),  trail_price = av_p - (av_p *  takeprofit/100) , trail_offset = av_p *(trail_p/100) *1/syminfo.mintick  , comment = "EXIT SHORT1") 
    
else
    strategy.exit("exit_buy", "long" ,  when= strategy.position_size > 0 ,  stop  =  av_p - (av_p *  stoploss/100)  ,  limit  =  av_p + (av_p *  takeprofit/100) , comment = "EXIT LONG2" ) 
    strategy.exit("exit_sell", "short" ,when= strategy.position_size < 0 ,  stop  =  av_p + (av_p *  stoploss/100)   ,  limit  =  av_p - (av_p *  takeprofit/100) , comment = "EXIT SHORT2") 
解决方案

1. 修复策略初始化参数

你提到的calc_on_every_tick等参数未在策略声明中启用,需要显式添加,确保回测逐tick计算并处理订单成交:

strategy("ХХХ", overlay=true, calc_on_every_tick=true, calc_on_order_fills=true, use_bar_magnifier=true)

2. 消除Heikin-Ashi数据的重绘

Heikin-Ashi的实时K线数据会随行情更新而变化,导致指标重绘。必须获取已闭合的Heikin-Ashi K线计算指标,修改request.security调用:

// 获取已闭合的Heikin-Ashi数据
ha_open = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, open, barmerge.gaps_on, barmerge.lookahead_off)
ha_close = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, close, barmerge.gaps_on, barmerge.lookahead_off)
ha_high = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, high, barmerge.gaps_on, barmerge.lookahead_off)
ha_low = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, low, barmerge.gaps_on, barmerge.lookahead_off)

// 用已闭合数据计算均线
ma_heikinashi_open = f(ha_open)
ma_heikinashi_close = f(ha_close)
ma_heikinashi_high = f(ha_high)
ma_heikinashi_low = f(ha_low)

barmerge.lookahead_off参数会强制使用已闭合的历史K线数据,彻底避免重绘。

3. 修正入场信号逻辑

当前trend>0/trend<0会在K线过程中反复触发,改为交叉确认逻辑,只在趋势方向切换时入场:

crossAbove = ta.crossover(trend, 0)  // 趋势从负转正时做多
crossUnder = ta.crossunder(trend, 0) // 趋势从正转负时做空

4. 修复跟踪止盈参数计算

trail_offset的计算逻辑错误,无需对syminfo.mintick取倒数,改用百分比跟踪更简洁:

// 做多跟踪止盈
strategy.exit("exit_buy", "long", 
    stop=av_p - (av_p * stoploss/100), 
    trail_price=av_p + (av_p * takeprofit/100), 
    trail_percent=trail_p, 
    comment="EXIT LONG1"
)

// 做空跟踪止盈
strategy.exit("exit_sell", "short", 
    stop=av_p + (av_p * stoploss/100), 
    trail_price=av_p - (av_p * takeprofit/100), 
    trail_percent=trail_p, 
    comment="EXIT SHORT1"
)

5. 回测准确性验证

完成修改后,开启策略设置中的"每根K线仅一次信号"选项,避免同一K线内重复触发入场;同时选择"真实成交量"模式,模拟实盘滑点和成交延迟,进一步缩小回测与实盘的差异。

内容的提问来源于stack exchange,提问作者Barmoley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 10:52:33