Trend Filter Quantitative Strategy Based on Keltner Channels and CCI Indicator

Author: ChaoZhang, Date: 2024-02-27 15:47:20



This strategy combines Keltner Channels, CCI indicator and RSI indicator with trading volume conditions to create a relatively complete trend filtering quantitative trading strategy. It generates buy and sell signals when prices break through key areas, indicators give trading signals, and large trading volumes appear. At the same time, moving averages are used for trend judgment to avoid trading without a clear trend.

Strategy Logic

The strategy makes trading decisions mainly based on the following indicators and conditions:

  1. Keltner Channels: Calculate upper and lower bands based on typical price and ATR over a period to determine if price is within the channel.

  2. CCI Indicator: Used to determine whether price is overbought or oversold.

  3. RSI Indicator: Assists in judging overbought/oversold levels.

  4. Trading Volume: Requires breakout of certain moving average value.

  5. Trend Filter with MAs: Use SMA, EMA etc. to determine overall trend direction.

With trend direction condition met, buy and sell signals are generated when price breaks Keltner Channel bands, CCI and RSI give signals, and trading volume surges.


The strategy combines multiple indicators and conditions to filter uncertain signals and make decisions more reliable:

  1. Trend filter avoids unclear volatile markets.

  2. Keltner Channels identify key breakout levels.

  3. CCI and RSI signals are relatively accurate.

  4. Volume surge helps prevent some false breakouts.


Main risks:

  1. Improper trend judgment may miss stronger trends. Test different MA parameters.

  2. Wrong indicator parameters may cause missed or false signals. Optimize parameters.

  3. Ineffective volume magnification leaves certain false breakout risks. Test different multipliers.

Optimization Directions

Potential optimization directions:

  1. Test different MA types and lengths for better trend filter.

  2. Optimize parameters of Keltner Channels, CCI, RSI for more accurate signals.

  3. Test different volume multipliers to find optimal level.

  4. Consider adding stop loss to limit max loss per trade.


Overall, this strategy combines Keltner Channels, CCI, RSI indicators and trading volume conditions to create a relatively complete trend filtering quantitative trading strategy. It has advantages like avoiding unclear volatile markets, identifying key breakouts, getting relatively accurate overbought/oversold signals, and preventing some false breakouts. Risks exist in aspects like improper parameter settings and ineffective volume magnification. Further optimizations can be done on the trend filtering method, indicator parameters, volume multiplier, and adding stop loss mechanisms.

start: 2024-01-27 00:00:00
end: 2024-02-26 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]

strategy("Custom Keltner CCI Strategy with Trend Filter", overlay=true )
// Input Parameters for allowing long and short trades
allowLong = input(true, title="Allow Long Trades")
allowShort = input(true, title="Allow Short Trades")
// Trend Filter Inputs
maType = input(title="MA Type", options=["OFF", "SMA", "EMA", "SMMA", "CMA", "TMA"], defval="OFF")
trendFilterMethod = input(title="Trend Filter Method", options=["OFF", "Normal", "Reversed"], defval="OFF")
maLength = input(14, title="MA Length")
// Other Input Parameters
lengthKC = input(30, title="Keltner Channels Length")
multKC = input(0.7, title="Keltner Channels Multiplier")
lengthCCI = input(5, title="CCI Length")
overboughtCCI = input(75, title="CCI Overbought Level")
oversoldCCI = input(-75, title="CCI Oversold Level")
rsiPeriod = input(30, title="RSI Period")
rsiOverbought = input(60, title="RSI Overbought Level")
rsiOversold = input(60, title="RSI Oversold Level")
volumeMultiplier = input(0, title="Volume Multiplier", type=input.float, step=0.1, minval=0)
// Define Moving Averages
var float maValue = na
if (maType == "SMA")
    maValue := sma(close, maLength)
else if (maType == "EMA")
    maValue := ema(close, maLength)
else if (maType == "SMMA")
    maValue := na(maValue[1]) ? sma(close, maLength) : (maValue[1] * (maLength - 1) + close) / maLength
else if (maType == "CMA")
    maValue := na(maValue[1]) ? sma(close, maLength) : (sma(close, maLength) + (sma(close, maLength) - maValue[1])) / 2
else if (maType == "TMA")
    maValue := sma(sma(close, round(maLength/2)), round(maLength/2)+1)
// Entry Conditions with Trend Filter
longCondition = allowLong and (trendFilterMethod == "OFF" or (trendFilterMethod == "Normal" and close > maValue) or (trendFilterMethod == "Reversed" and close < maValue))
shortCondition = allowShort and (trendFilterMethod == "OFF" or (trendFilterMethod == "Normal" and close < maValue) or (trendFilterMethod == "Reversed" and close > maValue))
// Keltner Channels
typicalPrice = hlc3
middleLine = sma(typicalPrice, lengthKC)
range = multKC * atr(lengthKC)
upperChannel = middleLine + range
lowerChannel = middleLine - range
// CCI
cci = cci(close, lengthCCI)
// RSI
rsi = rsi(close, rsiPeriod)
// Volume
volCondition = volume > sma(volume, 50) * volumeMultiplier
// Combined Entry Conditions with Trend Filter
longCondition := longCondition and cci < oversoldCCI and low < lowerChannel and rsi < rsiOversold and volCondition
shortCondition := shortCondition and cci > overboughtCCI and high > upperChannel and rsi > rsiOverbought and volCondition
// Execute orders at the open of the new bar after conditions are met
if (longCondition)
    strategy.entry("Long", strategy.long)
if (shortCondition)
    strategy.entry("Short", strategy.short)
// Exit Conditions
strategy.close("Long", when = cci > 0)
strategy.close("Short", when = cci < 0)
// Plotting
plot(upperChannel,, linewidth=1)
plot(lowerChannel,, linewidth=1)
hline(overboughtCCI, "Overbought",
hline(oversoldCCI, "Oversold",