OBV MACD Indicator 策略

点击:763 | 发布时间:2025-01-03 01:27:22

感谢原作者的慷慨奉献,本策略来源于指标地址为https://tw.tradingview.com/script/jXvqrU4q-OBV-MACD-Indicator/

策略回测效果(时间周期:2H):

使用参数配置如下:

运行效果:

蓝色开多,红色开空


BCH :800%


BNB:487%


ETH: 180%



策略源代码如下:

//@version=5

// study("OBV MACD Indicator",overlay=false)
// 由RafaelZioni提供 https://tw.tradingview.com/script/jXvqrU4q-OBV-MACD-Indicator/
// upgrader @ktrader2100 date 2025/01/02
strategy(title="OBV-STG", overlay=false, currency="USD", pyramiding=0,
 default_qty_type = strategy.cash, calc_on_every_tick = false, initial_capital=1000)
// 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="DEMA", 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
	
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) => ta.wma(2*ta.wma(src, len/2)-ta.wma(src, len), math.round(math.sqrt(len)))

thma(src, len) => 
    ma1 = hma(src, len)
    ma2 = hma(ma1, len)
    ma3 = 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.int(title="MACD Slow Length", defval=26)
//signal_length = input(title="MACD Signal Smoothing", type=input.integer, minval = 1, maxval = 50, defval = 9)
src12=close
plot(0,linewidth=3,color=color.black)
// 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
        val = src5[len5-i]
        per = i + 1.0
        sumX := sumX + per
        sumY := sumY + val
        sumXSqr := sumXSqr + per * per
        sumXY := sumXY + val * per
        
        
    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
//change(oc)>0
plot(b5,color=cs,linewidth=4,transp=50)
//

down = ta.change(oc)<0 
up = ta.change(oc)>0
showsignal=input(true)
plot(showsignal and up  ?tt1 :na, style=plot.style_cross, color=color.blue, linewidth=4, transp=0,offset=-1)
plot(showsignal and down ?tt1 :na, style=plot.style_cross, color=color.red, linewidth=4, transp=0,offset=-1)

//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

plotshape(piv ? na : shrt ? na : pivoth ? max_upper + 2 : na, location=location.absolute, style=shape.labeldown, color=color.red, size=size.tiny, text="Pivot", textcolor=color.white, transp=0, offset=0)
plotshape(piv ? na : shrt ? na : pivotl ? min_lower - 2 : na, location=location.absolute, style=shape.labelup, color=color.blue, size=size.tiny, text="Pivot", textcolor=color.white, transp=0, offset=0)




//-----------------------------------------------------------------------------}
//KT CODE START...
//-----------------------------------------------------------------------------{
version = '1.1'
symbol_name = syminfo.tickerid
ASSETS = strategy.initial_capital
INIT_USDT_UNIT = ASSETS

// KT INIT START...
PLR = input.float(0, 'PLR', minval=0, step=0.1, group='Trade Opts')
PROFIT = input.float(1, 'PROFIT', minval=0, step=0.1, group='Trade Opts')
showprofit = input.bool(false, "showprofit", group='Trade Opts')

//-----------------------------------------------------------------------------}
//UDT's
//-----------------------------------------------------------------------------{
type ktraderes
    float profit
    float profit_p
    string opt
    string ptime
    float amount
    float usdt_amount
    float open_price
    float pprice
    string debuginfo
    float stoplose
    float takeprofit

type ktorder
    string type
    int time
    string timedate
    string ptime
    float pt
    float currency
    float price
    float pprice
    float profit
    string msg
    float stoplose
    float takeprofit

//-----------------------------------------------------------------------------}
//Variables
//-----------------------------------------------------------------------------{
varip orders = array.new()

//-----------------------------------------------------------------------------}
//Methods - functions
//-
getTradeResult(string ptime, float price) =>
    orderlen = orders.size()
    float Ebamount = 0
    float Esamount = 0
    float Ebcurrency = 0
    float Escurrency = 0
    Eshow = ''
    tradeopt = 'none'
    float amount = 0
    float open_price = 0
    float profit = 0
    float profit_p = 0

    if orderlen > 0
        for i = 0 to orderlen -1
            dfrow = orders.get(i)
            if dfrow.type == 'buy'
                Ebamount := Ebamount + float(dfrow.currency) / float(dfrow.price)
                Ebcurrency := Ebcurrency + float(dfrow.currency)
            else
                Esamount := Esamount + float(dfrow.currency) / float(dfrow.price)
                Escurrency := Escurrency + float(dfrow.currency)

        if Ebamount > 0 and Esamount > 0
            if Ebamount > Esamount
                tradeopt := 'buy'
                amount := Ebamount - Esamount
            else
                tradeopt := 'sell'
                amount := Esamount - Ebamount
        else
            if Ebamount > 0
                tradeopt := 'buy'
                amount := Ebamount
            else if Esamount > 0
                tradeopt := 'sell'
                amount := Esamount

        if tradeopt != 'none'
            if tradeopt == 'buy'
                profit := amount * price - (Ebcurrency - Escurrency)
            else
                profit := (Escurrency - Ebcurrency) - (amount * price)

    // if profit != 0
    if tradeopt == 'buy'
        profit_p := profit / (Ebcurrency - Escurrency) * 100
    else if tradeopt == 'sell'
        profit_p := profit / (Escurrency - Ebcurrency) * 100

    debuginfo = "Escurrency: " + str.tostring(Escurrency) + "Ebcurrency: " + str.tostring(Ebcurrency)

    float stoplose = 0
    float takeprofit = 0
    if orderlen == 1
        stoplose := orders.get(0).stoplose
        takeprofit := orders.get(0).takeprofit

    res = ktraderes.new(debuginfo = debuginfo, profit = profit, profit_p = profit_p, pprice = price,
     amount = amount, opt = tradeopt, open_price = open_price, ptime = ptime, stoplose = stoplose, takeprofit = takeprofit)
    res


transTimeToTimedate(int transtime, int ghour)=>
    needtranstime = transtime
    if transtime == 0
        needtranstime := time

    gmt = ghour > 0 ? 'GMT+'+str.tostring(ghour) : 'GMT'
    timedate = str.format_time(int(needtranstime), "yyyy-MM-dd HH:mm:ss", gmt)
    timedate
nowstr = transTimeToTimedate(0, 8)

addOrder(optype, currency, price, addtime, timedate, msg="减仓", stoplose=0, takeprofit=0)=>
    tdres = getTradeResult(nowstr, close)
    canopt = true
    if tdres.opt != 'none'
        if tdres.opt == optype
            canopt := false
    if canopt == true
        // label.new(bar_index + 10, 1, optype, size = size.small, color = color.rgb(116, 8, 8))
        neworder = ktorder.new(type = optype, currency = currency, price = price, time = addtime, timedate = timedate, msg = msg, stoplose=stoplose, takeprofit=takeprofit)
        if optype == 'buy'
            strategy.order("buy", strategy.long, qty = currency / price, comment = msg)
        else if optype == 'sell'
            strategy.order("sell", strategy.short,  qty = currency / price, comment = msg)
        orders.push(neworder)

pushmsg(string tradecon, float tradeconprice, string side, float price, float currency, float stoplose = 0 , float takeprofit = 0) =>
    msgstring = '{"tradecon": "'+tradecon+'", "tradeconprice": "'+str.tostring(tradeconprice)+'", "side": "'+side+'", "price": "'+str.tostring(price)+'", "currency": "'+str.tostring(currency)+'", "stoplose": "'+str.tostring(stoplose)+'", "takeprofit": "'+str.tostring(takeprofit)+'" , "symbol_name": "'+symbol_name+'"}'
    alert(message = msgstring, freq = alert.freq_once_per_bar)
    ''

print_profit(atprice) =>
    if showprofit == true
        orderl = orders.size()
        int i = 0
        showtext = ""
        showcolumns = array.from("type", "price", "currency", "stop", "take","time", "msg")
        showtext := array.join(showcolumns, ",") + "n"
        ktres = getTradeResult(transTimeToTimedate(0, 8), close)
        while i < orders.size()
            corder = orders.get(i)

            jarr = array.from(corder.type, str.tostring(corder.price), str.tostring(corder.currency), str.tostring(corder.stoplose), str.tostring(corder.takeprofit), corder.timedate, corder.msg)
            rowstr = jarr.join(',')

            if str.length(showtext) > 1000
                showtext += "n..."
                // last line dump
                lastlinenum = orders.size() - 1
                corder := orders.get(orders.size() - 1)
                jarr := array.from(corder.type, str.tostring(corder.price), str.tostring(corder.currency), str.tostring(corder.stoplose), str.tostring(corder.takeprofit), corder.timedate, corder.msg)
                rowstr := jarr.join(',')
                showtext += ("n" + str.tostring(lastlinenum) + '#' + rowstr)
                break
            showtext += ("n" + str.tostring(i) + '#' + rowstr)
            i += 1
            ''
        resshowstr = "n res:---" + ktres.opt + "n profit:" + str.tostring(ktres.profit) + "n amount:" +
     str.tostring(ktres.amount) + "n pprice:" + str.tostring(ktres.pprice)+
     "n profit_p: " + str.tostring(ktres.profit_p)
        label.new(bar_index, atprice, showtext + resshowstr, size = size.small, color = color.white)
    ''

//-----------------------------------------------------------------------------}
// KT Calculating
//-----------------------------------------------------------------------------{

tradeopt = 'none'
float stoplose = 0
float takeprofit = 0
float mvatr = close * PROFIT / 100

tdres = getTradeResult(nowstr, close)

plot(showsignal and up  ?tt1 :na, style=plot.style_cross, color=color.blue, linewidth=4, transp=0,offset=-1)
plot(showsignal and down ?tt1 :na, style=plot.style_cross, color=color.red, linewidth=4, transp=0,offset=-1)


buycon = up ? tt1 : na
sellcon = down ? tt1 : na

// plot(state , title = 'state')
// plot(buycon ? 1 : 0 , title = 'buycon')
// plot(sellcon ? 1 : 0 , title = 'sellcon')

if buycon
    //label.new(bar_index, state, tdres.opt, size = size.small, color = color.rgb(158, 56, 56))
    tradeopt := 'buy'
    if tdres.opt == 'sell'
        strategy.close_all()
        orders := array.new(0)
        tdres := getTradeResult(nowstr, close)

    //label.new(bar_index + 5, state, tdres.opt, size = size.small, color = #b10d0d)

    if tdres.opt == 'none'
        if PLR > 0 and PROFIT > 0
            float mvatr2 = close + (close * PROFIT / 100)
            stoplose := close - mvatr / PLR
            takeprofit := close + mvatr
            //label.new(bar_index, takeprofit, 'probuy:' + str.tostring(mvatr), size = size.small, color = #b10d0d,force_overlay = true)
        else
            stoplose := 0
            takeprofit := 0

    addOrder(optype = tradeopt, currency = INIT_USDT_UNIT, price = close, addtime=time,
         timedate = nowstr, msg='开多',
         stoplose = stoplose, takeprofit = takeprofit)

    pushmsg(tradecon='<',
                 tradeconprice=close,
                 side='buy',
                 price=close,
                 currency=1,
                 stoplose = stoplose,
                 takeprofit = takeprofit)

if sellcon
    tradeopt := 'sell'
    if tdres.opt == 'buy'
        strategy.close_all()
        orders := array.new(0)
        tdres := getTradeResult(nowstr, close)

    if tdres.opt == 'none'
        if PLR > 0 and PROFIT > 0
            stoplose := close + mvatr / PLR
            takeprofit := close - mvatr
        else
            stoplose := 0
            takeprofit := 0

        addOrder(optype = tradeopt, currency = INIT_USDT_UNIT, price = close,
             addtime=time, timedate = nowstr, msg='开空',
             stoplose = stoplose, takeprofit = takeprofit)

        pushmsg(tradecon='>',
                 tradeconprice=close,
                 side='sell',
                 price=close,
                 currency=1,
                 stoplose = stoplose,
                 takeprofit = takeprofit)

if PLR > 0
    if tdres.opt != 'none'

        if tdres.profit > 0
            if tdres.opt == 'buy'
                if close >= tdres.takeprofit
                    strategy.close_all(comment = '多单止盈')
                    orders := array.new(0)

            else if tdres.opt == 'sell'
                if close <= tdres.takeprofit
                    strategy.close_all(comment = '空单止盈')
                    orders := array.new(0)
        else if tdres.profit < 0

            if tdres.opt == 'buy'
                if close <= tdres.stoplose
                    strategy.close_all(comment = '多单止损')
                    orders := array.new(0)

            else if tdres.opt == 'sell'
                if close >= tdres.stoplose
                    strategy.close_all(comment = '空单止损')
                    orders := array.new(0)

if barstate.islastconfirmedhistory
    print_profit(0)