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

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)

问题原因分析

信号滞后的核心原因是:

  1. 指标的红蓝十字标记使用了offset=-1,将当前K线计算出的信号绘制到前一根K线上,造成视觉上信号提前的效果。
  2. 交易策略默认在信号触发的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 23:33:30