回测结果与实盘出入场信号不符的解决方案咨询
问题描述
我在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
相关产品推荐
相关产品推荐

