感谢原作者的慷慨奉献,本策略来源于指标地址为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)