OBV MACD指标转TradingView策略:信号滞后2K线修正求助
TradingView OBV MACD策略信号滞后对齐问题
问题描述
将Rafael Zioni开发的OBV MACD指标转换为TradingView v5交易策略后,回测时订单信号比指标的红蓝"+"标记滞后2根K线,入场出场时机过晚。该策略仅用于回测场景,需调整代码使订单与指标信号对齐,相关订单逻辑代码位于第235-249行。
当前实现代码
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // Modified from OBV MACD Indicator by Rafael Zioni // Converted indicator to version 5 //@version=5 // changed function to STRATEGY strategy("Test 1 OBV MACD Strategy", overlay=true, margin_long=100, margin_short=100) // MACD src1 = close window_len = 28 v_len = 14 price_spread = ta.stdev(high - low, window_len) v = ta.cum(math.sign(ta.change(src1)) * volume) smooth = ta.sma(v, v_len) v_spread = ta.stdev(v - smooth, window_len) shadow = (v - smooth) / v_spread * price_spread out = shadow > 0 ? high + shadow : low + shadow //plot(out, style=line,linewidth=3, color=color) len10 = input(1, title='OBV Length ') obvema = ta.ema(out, len10) // src = obvema type = input.string(defval='DZLEMA', title='MA Type', options=['TDEMA', 'TTEMA', 'TEMA', 'DEMA', 'EMA', 'AVG', 'THMA', 'ZLEMA', 'ZLDEMA', 'ZLTEMA', 'DZLEMA', 'TZLEMA', 'LLEMA', 'NMA']) showma = true len = input(9, title='MA Length ') showma1 = false len1 = 26 showma2 = false len2 = 52 nma(src, length1, length2) => lambda = length1 / length2 alpha = lambda * (length1 - 1) / (length1 - lambda) ma1 = ta.ema(src, length1) ma2 = ta.ema(ma1, length2) nma = (1 + alpha) * ma1 - alpha * ma2 nma dema(src, len) => ma1 = ta.ema(src, len) ma2 = ta.ema(ma1, len) 2 * ma1 - ma2 tema(src, len) => ma1 = ta.ema(src, len) ma2 = ta.ema(ma1, len) ma3 = ta.ema(ma2, len) 3 * (ma1 - ma2) + ma3 tdema(src, len) => ma1 = dema(src, len) ma2 = dema(ma1, len) ma3 = dema(ma2, len) 3 * (ma1 - ma2) + ma3 ttema(src, len) => ma1 = tema(src, len) ma2 = tema(ma1, len) ma3 = tema(ma2, len) 3 * (ma1 - ma2) + ma3 tnma(src, len) => ma1 = nma(src, len, 3) ma2 = nma(ma1, len, 3) ma3 = nma(ma2, len, 3) 3 * (ma1 - ma2) + ma3 // hma(src, len) => wma(2*wma(src, len/2)-wma(src, len), round(sqrt(len))) thma(src, len) => ma1 = ta.hma(src, len) ma2 = ta.hma(ma1, len) ma3 = ta.hma(ma2, len) 3 * (ma1 - ma2) + ma3 zlema(src, len) => lag = math.round((len - 1) / 2) zlsrc = src + src - src[lag] ta.ema(zlsrc, len) zldema(src, len) => lag = math.round((len - 1) / 2) zlsrc = src + src - src[lag] dema(zlsrc, len) zltema(src, len) => lag = math.round((len - 1) / 2) zlsrc = src + src - src[lag] tema(zlsrc, len) dzlema(src, len) => ma1 = zlema(src, len) ma2 = zlema(ma1, len) 2 * ma1 - ma2 tzlema(src, len) => ma1 = zlema(src, len) ma2 = zlema(ma1, len) ma3 = zlema(ma2, len) 3 * (ma1 - ma2) + ma3 llema(src, len) => srcnew = 0.25 * src + 0.5 * src[1] + 0.25 * src[2] ta.ema(srcnew, len) lltema(src, len) => srcnew = 0.25 * src + 0.5 * src[1] + 0.25 * src[2] tema(srcnew, len) myma(src, len) => if type == 'EMA' ta.ema(src, len) else if type == 'DEMA' dema(src, len) else if type == 'TEMA' tema(src, len) else if type == 'TDEMA' tdema(src, len) else if type == 'TTEMA' ttema(src, len) else if type == 'THMA' thma(src, len) else if type == 'ZLEMA' zlema(src, len) else if type == 'ZLDEMA' zldema(src, len) else if type == 'ZLTEMA' zltema(src, len) else if type == 'DZLEMA' dzlema(src, len) else if type == 'TZLEMA' tzlema(src, len) else if type == 'LLEMA' llema(src, len) else if type == 'NMA' nma(src, len, len1) else math.avg(ttema(src, len), tdema(src, len)) ma = showma ? myma(src, len) : na slow_length = input(title='MACD Slow Length', defval=26) //signal_length = input(title="MACD Signal Smoothing", type=input.integer, minval = 1, maxval = 50, defval = 9) src12 = close ////PLOTS THE 0 LINE plot(0, linewidth=3, color=color.new(color.black, 0)) // Calculating MACD slow_ma = ta.ema(src12, slow_length) macd = ma - slow_ma //signal_length=input(9) //signal = ema(macd, signal_length) //plot(signal,linewidth=2) src5 = macd len5 = input(2) offset = 0 calcSlope(src5, len5) => sumX = 0.0 sumY = 0.0 sumXSqr = 0.0 sumXY = 0.0 for i = 1 to len5 by 1 val = src5[len5 - i] per = i + 1.0 sumX += per sumY += val sumXSqr += per * per sumXY += val * per sumXY slope = (len5 * sumXY - sumX * sumY) / (len5 * sumXSqr - sumX * sumX) average = sumY / len5 intercept = average - slope * sumX / len5 + slope [slope, average, intercept] var float tmp = na [s, a5, i] = calcSlope(src5, len5) tt1 = i + s * (len5 - offset) ////script based on alex grover from https://www.tradingview.com/script/KzTi6CZP-T-Channels/ p = 1 src15 = tt1 b5 = 0. dev5 = 0. oc = 0 n5 = ta.cum(1) - 1 a15 = ta.cum(math.abs(src15 - nz(b5[1], src15))) / n5 * p b5 := src15 > nz(b5[1], src15) + a15 ? src15 : src15 < nz(b5[1], src15) - a15 ? src15 : nz(b5[1], src15) //---- dev5 := ta.change(b5) ? a15 : nz(dev5[1], a15) //---- oc := ta.change(b5) > 0 ? 1 : ta.change(b5) < 0 ? -1 : nz(oc[1]) //---- cs = oc == 1 ? color.blue : color.red //// THIS PLOTS THE LINES //change(oc)>0 plot(b5, color=cs, linewidth=4, transp=50) down = ta.change(oc) < 0 up = ta.change(oc) > 0 showsignal = input(true) //// THIS PLOTS THE "+" SIGNS plot(showsignal and up ? tt1 : na, style=plot.style_cross, color=color.new(color.blue, 0), linewidth=4, offset=-1) plot(showsignal and down ? tt1 : na, style=plot.style_cross, color=color.new(color.red, 0), linewidth=4, offset=-1) // I want a buy and sell order on the above signals // Submit entry orders if up // strategy.entry("BUY", strategy.long, 1000 ) // enter long by market if current open great then previous high // Submit exit orders if down strategy.close_all() //hist = macd - signal //barColor =hist >= 0 and hist> signal ? color.teal : hist > 0 and hist < signal ? color.lime : hist < 0 and hist < signal ? color.red : color.orange //plot(hist, color=barColor, style=plot.style_histogram, linewidth=3) upper = tt1 lower = tt1 // DIVS code piv = input(true, 'Hide pivots?') shrt = false xbars = input.int(50, 'period', minval=1) hb = math.abs(ta.highestbars(upper, xbars)) lb = math.abs(ta.lowestbars(lower, xbars)) max = float(na) max_upper = float(na) min = float(na) min_lower = float(na) pivoth = bool(na) pivotl = bool(na) max := hb == 0 ? close : na(max[1]) ? close : max[1] max_upper := hb == 0 ? upper : na(max_upper[1]) ? upper : max_upper[1] min := lb == 0 ? close : na(min[1]) ? close : min[1] min_lower := lb == 0 ? lower : na(min_lower[1]) ? lower : min_lower[1] if close > max max := close max if upper > max_upper max_upper := upper max_upper if close < min_lower min_lower := lower min_lower if lower < min_lower min_lower := lower min_lower pivoth := max_upper == max_upper[2] and max_upper[2] != max_upper[3] ? true : na pivotl := min_lower == min_lower[2] and min_lower[2] != min_lower[3] ? true : na // we are not plotting anything in the strategy so these are commented out //plotshape(piv ? na : shrt ? na : pivoth ? max_upper + 2 : na, location=location.absolute, style=shape.labeldown, color=color.new(color.red, 0), size=size.tiny, text='Pivot', textcolor=color.new(color.white, 0), offset=0) //plotshape(piv ? na : shrt ? na : pivotl ? min_lower - 2 : na, location=location.absolute, style=shape.labelup, color=color.new(color.blue, 0), size=size.tiny, text='Pivot', textcolor=color.new(color.white, 0), offset=0)
问题原因分析
信号滞后的核心原因是:
- 指标的红蓝十字标记使用了
offset=-1,将当前K线计算出的信号绘制到前一根K线上,造成视觉上信号提前的效果。 - 交易策略默认在信号触发的K线收盘后下单,下一根K线开盘时执行入场/平仓,导致实际订单执行比视觉上的十字标记晚2根K线(十字在K线n-1,订单在K线n+1执行)。
解决方案
方法一:调整信号显示位置,对齐策略逻辑
将十字标记的offset=-1移除,让信号绘制在实际计算的K线上,这样订单在下一根K线执行的逻辑和视觉显示一致,不会有滞后感。
修改后的信号绘制代码:
//// THIS PLOTS THE "+" SIGNS plot(showsignal and up ? tt1 : na, style=plot.style_cross, color=color.new(color.blue, 0), linewidth=4) plot(showsignal and down ? tt1 : na, style=plot.style_cross, color=color.new(color.red, 0), linewidth=4)
方法二:调整订单执行时机,对齐视觉信号
如果需要保留十字标记的offset=-1显示效果,可以修改订单逻辑,让回测订单在信号对应的K线成交:
修改后的订单逻辑代码:
// Submit entry orders if up // 在前一根K线收盘价限价入场,对齐十字标记的显示位置 strategy.entry("BUY", strategy.long, 1000, limit=close[1]) // Submit exit orders if down // 在前一根K线收盘价限价平仓,对齐十字标记的显示位置 strategy.close_all(limit=close[1])
或者使用当前K线收盘价成交,让订单执行和信号计算的K线一致:
// Submit entry orders if up // 在当前K线收盘价成交,对应十字标记的前一根K线的下一根,视觉对齐 strategy.entry("BUY", strategy.long, 1000, price=close, when=barstate.isconfirmed) // Submit exit orders if down strategy.close_all(price=close, when=barstate.isconfirmed)
验证说明
两种方法都能解决视觉上的信号滞后问题:
- 方法一更符合实盘交易逻辑,信号显示在实际可执行订单的前一根K线。
- 方法二适合回测场景下的视觉对齐,让订单成交位置和十字标记的显示位置匹配。
内容的提问来源于stack exchange,提问作者Adam B
相关产品推荐
相关产品推荐

