策略效果:

BTC 策略曲线
少有对BTC 交易赢利曲线平滑的策略

上源码:
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © ktrader2100
//@version=5
// use 4h
strategy(title="Hull Squeeze Mix", overlay=true, currency="USD", pyramiding=0,
default_qty_type=strategy.cash, calc_on_every_tick=true, initial_capital=1000)
v = 1.1
// Hull Suite by InSilico
//INPUT
src = input(close, title="Source")
modeSwitch = input.string("Hma", title="Hull Variation", options=["Hma", "Thma", "Ehma"])
length = input(110, title="Length(180-200 for floating S/R , 55 for swing entry)")
lengthMult = input(1.0, title="Length multiplier (Used to view higher timeframes with straight band)")
useHtf = input(false, title="Show Hull MA from X timeframe? (good for scalping)")
htf = input.timeframe("240", title="Higher timeframe")
switchColor = input(true, "Color Hull according to trend?")
candleCol = input(false,title="Color candles based on Hull's Trend?")
visualSwitch = input(true, title="Show as a Band?")
thicknesSwitch = input(1, title="Line Thickness")
transpSwitch = input.int(40, title="Band Transparency",step=5)
//FUNCTIONS
//HMA
HMA(_src, _length) => ta.wma(2 * ta.wma(_src, _length / 2) - ta.wma(_src, _length), math.round(math.sqrt(_length)))
//EHMA
EHMA(_src, _length) => ta.ema(2 * ta.ema(_src, _length / 2) - ta.ema(_src, _length), math.round(math.sqrt(_length)))
//THMA
THMA(_src, _length) => ta.wma(ta.wma(_src,_length / 3) * 3 - ta.wma(_src, _length / 2) - ta.wma(_src, _length), _length)
//SWITCH
Mode(modeSwitch, src, len) =>
modeSwitch == "Hma" ? HMA(src, len) :
modeSwitch == "Ehma" ? EHMA(src, len) :
modeSwitch == "Thma" ? THMA(src, len/2) : na
//OUT
_hull = Mode(modeSwitch, src, int(length * lengthMult))
HULL = useHtf ? request.security(syminfo.ticker, htf, _hull) : _hull
MHULL = HULL[0]
SHULL = HULL[2]
//COLOR
hullColor = switchColor ? (HULL > HULL[2] ? #00ff00 : #ff0000) : #ff9800
//PLOT
///< Frame
Fi1 = plot(MHULL, title="MHULL", color=hullColor, linewidth=thicknesSwitch)
Fi2 = plot(visualSwitch ? SHULL : na, title="SHULL", color=hullColor, linewidth=thicknesSwitch)
alertcondition(ta.crossover(MHULL, SHULL), title="Hull trending up.", message="Hull trending up.")
alertcondition(ta.crossover(SHULL, MHULL), title="Hull trending down.", message="Hull trending down.")
///< Ending Filler
fill(Fi1, Fi2, title="Band Filler", color=hullColor)
///BARCOLOR
barcolor(color = candleCol ? (switchColor ? hullColor : na) : na)
// Squeeze Momentum Indicator
// 输入参数
length2 = input.int(20, title="BB Length")
mult = input.float(5, title="BB MultFactor")
lengthKC = input.int(21, title="KC Length")
multKC = input.float(1.5, title="KC MultFactor")
useTrueRange = input.bool(true, title="Use TrueRange (KC)")
// 计算布林带 (BB)
source = close
basis = ta.sma(source, length2)
dev = multKC * ta.stdev(source, length2)
upperBB = basis + dev
lowerBB = basis - dev
// 计算肯特纳通道 (KC)
ma = ta.sma(source, lengthKC)
range1 = useTrueRange ? ta.tr : (high - low)
rangema = ta.sma(range1, lengthKC)
upperKC = ma + rangema * multKC
lowerKC = ma - rangema * multKC
// 确定挤压状态
sqzOn = (lowerBB > lowerKC) and (upperBB < upperKC)
sqzOff = (lowerBB < lowerKC) and (upperBB > upperKC)
noSqz = not sqzOn and not sqzOff
// 计算动量
val = ta.linreg(source - math.avg(math.avg(ta.highest(high, lengthKC), ta.lowest(low, lengthKC)), ta.sma(close, lengthKC)),
lengthKC, 0)
// 设置柱状图颜色
bcolor = val > 0 ? (val > nz(val[1]) ? color.lime : color.green)
: (val < nz(val[1]) ? color.red : color.maroon)
// 设置零线颜色
scolor = noSqz ? color.blue : sqzOn ? color.black : color.gray
// 绘制图表
plot(val, color=bcolor, style=plot.style_histogram, linewidth=4)
// plot(0, color=scolor, style=plot.style_cross, linewidth=2)
// KT STGS
symbol_name = syminfo.tickerid
// kt stgs start ...
var LQ_RATIO = 0.6
INIT_USDT_UNIT = strategy.initial_capital
PLR = input.float(0, 'PLR', minval=0, step=0.1, group='KT Trade Opts')
showprofit = input.bool(false, "showprofit", group='KT Trade Opts')
check100xlist = array.new_string(0, '')
if barstate.isfirst
check100xlist.push('BTCUSDT')
check100xlist.push('ETHUSDT')
check100xlist.push('SOLUSDT')
for i = 0 to array.size(check100xlist) - 1
strValue = array.get(check100xlist, i)
if str.contains(symbol_name, strValue)
LQ_RATIO := 0.6
break
LQ_RATIO := 0.8
// plot(LQ_RATIO, title = 'LQ_RATIO')
//-----------------------------------------------------------------------------}
//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<ktorder>()
//Methods - functions
//-
method n(float piv) => bool out = not na(piv)
pushmsg(string tradecon, float tradeconprice, string side, float price, float currency, float stoplose = 0 , float takeprofit = 0) =>
// strtpl = '{"tradecon": ">", "tradeconprice": "92946", "side": "sell", "price": "92946", "currency": "5000", "stoplose": "0", "takeprofit": "0"}'
// strtpl = "{"tradecon": {0}, "tradeconprice": "{1}", "side": "{2}", "price": "{3}", "currency": "{4}", "stoplose": "{5}", "takeprofit": "{6}"}'
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+'"}'
// msgstring = str.format(strtpl, tradecon)
alert(message = msgstring, freq = alert.freq_once_per_bar)
''
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, time, 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
neworder = ktorder.new(type = optype, currency = currency, price = price, time = time, 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)
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)
''
// todo starts ....
buycon = false
sellcon = false
tdres = getTradeResult(nowstr, close)
pmsg = false
tradeopt = 'none'
float stoplose = 0
float takeprofit = 0
// todo cac...
// alertcondition(ta.crossover(MHULL, SHULL), title="Hull trending up.", message="Hull trending up.")
// alertcondition(ta.crossover(SHULL, MHULL), title="Hull trending down.", message="Hull trending down.")
// bcolor = val > 0 ? (val > nz(val[1]) ? color.lime : color.green)
// : (val < nz(val[1]) ? color.red : color.maroon)
if ta.crossover(MHULL, SHULL) and val > nz(val[1])
buycon := true
if ta.crossover(SHULL, MHULL) and val < nz(val[1])
sellcon := true
// todo cac end...
if buycon
tradeopt := 'buy'
if tdres.opt == 'sell'
strategy.close_all()
orders := array.new<ktorder>(0)
tdres := getTradeResult(nowstr, close)
if tdres.opt == 'none'
if PLR > 0
mv = close * LQ_RATIO / 100
stoplose := close - mv
takeprofit := close + mv * PLR
else
stoplose := 0
takeprofit := 0
currency = INIT_USDT_UNIT
mcurrency = 1
msg='开多'
addOrder(optype = tradeopt, currency = currency, price = close, time=time,
timedate = nowstr, msg=msg,
stoplose = stoplose, takeprofit = takeprofit)
pushmsg(tradecon='<',
tradeconprice=close,
side='buy',
price=close,
currency=mcurrency,
stoplose = 0,
takeprofit = 0)
if sellcon
tradeopt := 'sell'
if tdres.opt == 'buy'
strategy.close_all()
orders := array.new<ktorder>(0)
tdres := getTradeResult(nowstr, close)
if tdres.opt == 'none'
if PLR > 0
mv = close * LQ_RATIO / 100
stoplose := close + mv
takeprofit := close - mv * PLR
else
stoplose := 0
takeprofit := 0
currency = INIT_USDT_UNIT
mcurrency = 1
msg='开空'
addOrder(optype = tradeopt, currency = currency, price = close,
time=time, timedate = nowstr, msg=msg,
stoplose = stoplose, takeprofit = takeprofit)
pushmsg(tradecon='>',
tradeconprice=close,
side='sell',
price=close,
currency=mcurrency,
stoplose = 0,
takeprofit = 0)
// plot(orders.size(), 'ordersize')
// plot(close, title = "tickprice")
if barstate.islastconfirmedhistory
print_profit(0)