TSI CCI Hull Moving Average Trading Strategy

Author: ChaoZhang, Date: 2023-09-18 17:17:52
Tags:

Overview

This strategy combines the TSI, CCI indicators and Hull Moving Average to determine and trade trends. TSI and CCI identify price waves while Hull MA confirms trend direction. Profit targets are set when long/short signals occur for profitable exits.

Strategy Logic

The TSI curve and signal line are calculated. Long signal when curve crosses above line, short on downward crossover. CCI indicates overbought/oversold levels. Price crossing above Hull MA suggests bull market, and below for bear market. Long/short trades are taken when TSI, CCI and Hull MA breakout conditions align. Profit targets are set to exit positions when reached.

Advantages

  • TSI strongly identifies trend direction
  • CCI effectively detects overbought/oversold
  • Hull MA filters false breakouts improving signals
  • Profit targets allow exiting at peak profitability
  • Combining multiple indicators improves robustness

Risks

  • Lag exists in TSI, CCI and other indicators
  • Hull MA cannot perfectly determine turning points
  • Exact price reversal timing cannot be accurately determined
  • Poor profit target setting risks missing profit potential

Risks can be reduced by tuning indicators, optimizing profit algorithms etc.

Enhancements

  • Test TSI/CCI combinations to improve sensitivity
  • Consider dynamic/trailing profit targets
  • Add other indicators to determine reversals
  • Test across different products to improve robustness

Conclusion

This multiple indicator strategy with profit targeting shows good backtest results. Further refinements like parameter optimization can make it a stable quant trading system.


/*backtest
start: 2023-08-18 00:00:00
end: 2023-09-17 00:00:00
period: 3h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

//@version=4

strategy(title="TSI CCI Hull", shorttitle="TSICCIHULL", default_qty_type=strategy.percent_of_equity, default_qty_value=100, calc_on_order_fills= false, calc_on_every_tick=true, pyramiding=0, commission_type=strategy.commission.percent, commission_value=0.018)
long = input(title="Long Length", type=input.integer, defval=50)
short = input(title="Short Length", type=input.integer, defval=50)
signal = input(title="Signal Length", type=input.integer, defval=25)
price=input(title="Source",type=input.source,defval=close)
Period=input(26, minval=1)
lineupper = input(title="Upper Line", type=input.integer, defval=100)
linelower = input(title="Lower Line", type=input.integer, defval=-100)
p=price
length= Period
double_smooth(src, long, short) =>
    fist_smooth = ema(src, long)
    ema(fist_smooth, short)
pc = change(price)
double_smoothed_pc = double_smooth(pc, long, short)
double_smoothed_abs_pc = double_smooth(abs(pc), long, short)
tsi_value = 100 * (double_smoothed_pc / double_smoothed_abs_pc)
keh = tsi_value*5 > linelower ? color.red : color.lime
teh = ema(tsi_value*5, signal*5) > lineupper ? color.red : color.lime
meh = ema(tsi_value*5, signal*5) > tsi_value*5 ? color.red : color.lime
i1=plot(tsi_value*5, title="TSI Value", color=color.black, linewidth=1,transp=100)
i2=plot(ema(tsi_value*5, signal*5), title="TSI Signal", color=color.black, linewidth=1,transp=100)
fill(i1,i2,color=meh,transp=85)
plot(cross(tsi_value*5, ema(tsi_value*5, signal*5)) ? tsi_value*5 : na, style=plot.style_circles, color=color.black, linewidth=10)
plot(cross(tsi_value*5, ema(tsi_value*5, signal*5)) ? tsi_value*5 : na, style=plot.style_circles, color=color.white, linewidth=8,transp=0)
plot(cross(tsi_value*5, ema(tsi_value*5, signal*5)) ? tsi_value*5 : na, style=plot.style_circles, color=meh, linewidth=5)
n2ma = 2 * wma(p, round(length / 2))
nma = wma(p, length)
diff = n2ma - nma
sqn = round(sqrt(length))
n1 = wma(diff, sqn)
cci = (p - n1) / (0.015 * dev(p, length))
c = cci > 0 ? color.lime : color.red
c1 = cci > 20 ? color.lime : color.silver
c2 = cci < -20 ? color.red : color.silver
cc=plot(cci, color=c, title="CCI Line", linewidth=2)
cc2=plot(cci[1], color=color.gray, linewidth=1,transp=100)
fill(cc,cc2,color=c,transp=85)
plot(cross(20, cci) ? 20 : na, style=plot.style_cross,title="CCI cross UP",  color=c1, linewidth=2,transp=100,offset=-2)
plot(cross(-20, cci) ? -20 : na, style=plot.style_cross,title="CCI cross down",  color=c2, linewidth=2,transp=100,offset=-2)

TSI1=ema(tsi_value*5, signal*5)
TSI2=ema(tsi_value*5, signal*5)[2]

hullma_smoothed = wma(2*wma(n1, Period/2)-wma(n1, Period), round(sqrt(Period)))
//plot(hullma_smoothed*200)

// Make input options that configure backtest date range
startDate = input(title="Start Date", type=input.integer,
     defval=1, minval=1, maxval=31)
startMonth = input(title="Start Month", type=input.integer,
     defval=1, minval=1, maxval=12)
startYear = input(title="Start Year", type=input.integer,
     defval=2018, minval=1800, maxval=2100)

endDate = input(title="End Date", type=input.integer,
     defval=1, minval=1, maxval=31)
endMonth = input(title="End Month", type=input.integer,
     defval=7, minval=1, maxval=12)
endYear = input(title="End Year", type=input.integer,
     defval=9999, minval=1800, maxval=2100)
     
// Look if the close time of the current bar
// falls inside the date range
inDateRange = (time >= timestamp(syminfo.timezone, startYear,
         startMonth, startDate, 0, 0)) and
     (time < timestamp(syminfo.timezone, endYear, endMonth, endDate, 0, 0))
     
LongProfitPercent=input(0.5)
ShortProfitPercent=input(0.5)
LP=(LongProfitPercent/100)+1
SP=(ShortProfitPercent/100)+1

LongProfitSource=input(title="profit long source",type=input.source,defval=close)
ShortProfitSource=input(title="profit short source",type=input.source,defval=close)

longCondition = TSI1>TSI2 and hullma_smoothed<price and cci>0
shortCondition = TSI1<TSI2 and hullma_smoothed>price and cci<0

if (longCondition and cci>cci[1] and cci > 0 and n1>n1[1] and inDateRange)
    strategy.entry("buy", strategy.long)
strategy.close("buy", when = shortCondition and cci<cci[1] and cci < 0 and n1<n1[1] or LongProfitSource>strategy.position_avg_price*LP and inDateRange)
if (shortCondition and cci<cci[1] and cci < 0 and n1<n1[1] and inDateRange)
    strategy.entry("sell", strategy.short)
strategy.close("sell", when = longCondition and cci>cci[1] and cci > 0 and n1>n1[1] or ShortProfitSource<strategy.position_avg_price/SP and inDateRange)

More