Hull Squeeze Mix

点击:559 | 发布时间:2025-03-28 11:14:54

策略效果:



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)