
Il s’agit d’une stratégie de négociation flexible basée sur le Keltner Channel. La stratégie prend en charge le trading bidirectionnel polyvalent en surveillant les hauts et les bas des prix qui traversent le canal. Le cœur de la stratégie est de construire un canal de prix en utilisant une moyenne mobile (MA) et d’ajuster dynamiquement la largeur du canal en combinaison avec l’amplitude d’onde réelle (ATR) afin de maintenir l’adaptabilité de la stratégie dans différents environnements de marché.
La stratégie repose principalement sur les principes suivants:
La stratégie est un système de négociation bien conçu et logiquement clair qui permet de saisir efficacement les opportunités du marché grâce à l’utilisation flexible des canaux Kettner et de divers indicateurs techniques. La stratégie est hautement personnalisable et adaptée aux traders ayant des préférences de risque différentes.
/*backtest
start: 2022-02-11 00:00:00
end: 2025-02-08 08:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/
//@version=6
strategy(title = "Jaakko's Keltner Strategy", overlay = true, initial_capital = 10000, default_qty_type = strategy.percent_of_equity, default_qty_value = 100)
// ──────────────────────────────────────────────────────────────────────────────
// ─── USER INPUTS ─────────────────────────────────────────────────────────────
// ──────────────────────────────────────────────────────────────────────────────
length = input.int(20, minval=1, title="Keltner MA Length")
mult = input.float(2.0, title="Multiplier")
src = input(close, title="Keltner Source")
useEma = input.bool(true, title="Use Exponential MA")
BandsStyle = input.string(title = "Bands Style", defval = "Average True Range", options = ["Average True Range", "True Range", "Range"])
atrLength = input.int(10, title="ATR Length")
// Choose which side(s) to trade
tradeMode = input.string(title = "Trade Mode", defval = "Long Only", options = ["Long Only", "Short Only", "Both"])
// ──────────────────────────────────────────────────────────────────────────────
// ─── KELTNER MA & BANDS ───────────────────────────────────────────────────────
// ──────────────────────────────────────────────────────────────────────────────
f_ma(source, length, emaMode) =>
maSma = ta.sma(source, length)
maEma = ta.ema(source, length)
emaMode ? maEma : maSma
ma = f_ma(src, length, useEma)
rangeMa = BandsStyle == "True Range" ? ta.tr(true) : BandsStyle == "Average True Range" ? ta.atr(atrLength) : ta.rma(high - low, length)
upper = ma + rangeMa * mult
lower = ma - rangeMa * mult
// ──────────────────────────────────────────────────────────────────────────────
// ─── CROSS CONDITIONS ─────────────────────────────────────────────────────────
// ──────────────────────────────────────────────────────────────────────────────
crossUpper = ta.crossover(src, upper) // potential long signal
crossLower = ta.crossunder(src, lower) // potential short signal
// ──────────────────────────────────────────────────────────────────────────────
// ─── PRICE LEVELS FOR STOP ENTRY (LONG) & STOP ENTRY (SHORT) ─────────────────
// ──────────────────────────────────────────────────────────────────────────────
bprice = 0.0
bprice := crossUpper ? high + syminfo.mintick : nz(bprice[1])
sprice = 0.0
sprice := crossLower ? low - syminfo.mintick : nz(sprice[1])
// ──────────────────────────────────────────────────────────────────────────────
// ─── BOOLEAN FLAGS FOR PENDING LONG/SHORT ─────────────────────────────────────
// ──────────────────────────────────────────────────────────────────────────────
crossBcond = false
crossBcond := crossUpper ? true : crossBcond[1]
crossScond = false
crossScond := crossLower ? true : crossScond[1]
// Cancel logic for unfilled orders (same as original)
cancelBcond = crossBcond and (src < ma or high >= bprice)
cancelScond = crossScond and (src > ma or low <= sprice)
// ──────────────────────────────────────────────────────────────────────────────
// ─── LONG SIDE ────────────────────────────────────────────────────────────────
// ──────────────────────────────────────────────────────────────────────────────
if (tradeMode == "Long Only" or tradeMode == "Both") // Only run if mode is long or both
// Cancel unfilled long if invalid
if cancelBcond
strategy.cancel("KltChLE")
// Place long entry
if crossUpper
strategy.entry("KltChLE", strategy.long, stop=bprice, comment="Long Entry")
// If we are also using “Both,” we rely on short side to flatten the long.
// But if “Long Only,” we can exit on crossLower or do nothing.
// Let’s do a "stop exit" if in "Long Only" (like the improved version).
if tradeMode == "Long Only"
// Cancel unfilled exit
if cancelScond
strategy.cancel("KltChLX")
// Place exit if crossLower
if crossLower
strategy.exit("KltChLX", from_entry="KltChLE", stop=sprice, comment="Long Exit")
// ──────────────────────────────────────────────────────────────────────────────
// ─── SHORT SIDE ───────────────────────────────────────────────────────────────
// ──────────────────────────────────────────────────────────────────────────────
if (tradeMode == "Short Only" or tradeMode == "Both") // Only run if mode is short or both
// Cancel unfilled short if invalid
if cancelScond
strategy.cancel("KltChSE")
// Place short entry
if crossLower
strategy.entry("KltChSE", strategy.short, stop=sprice, comment="Short Entry")
// If “Short Only,” we might do a symmetrical exit approach for crossUpper
// Or if "Both," going long automatically flattens the short in a no-hedge account.
// Let's replicate "stop exit" for short side if "Short Only" is chosen:
if tradeMode == "Short Only"
// Cancel unfilled exit
if cancelBcond
strategy.cancel("KltChSX")
// Place exit if crossUpper
if crossUpper
strategy.exit("KltChSX", from_entry="KltChSE", stop=bprice, comment="Short Exit")
// ──────────────────────────────────────────────────────────────────────────────
// ─── OPTIONAL VISUALS ─────────────────────────────────────────────────────────
// ──────────────────────────────────────────────────────────────────────────────
barcolor(strategy.position_size > 0 ? color.green : strategy.position_size < 0 ? color.red : na)
plotshape( strategy.position_size > 0 and strategy.position_size[1] <= 0, title = "BUY", text = '🚀', style = shape.labelup, location = location.belowbar, color = color.green, textcolor = color.white, size = size.small)
plotshape( strategy.position_size <= 0 and strategy.position_size[1] > 0, title = "SELL", text = '☄️', style = shape.labeldown, location = location.abovebar, color = color.red, textcolor = color.white, size = size.small)
plotshape(crossLower, style=shape.triangledown, color=color.red, location=location.abovebar, title="CrossLower Trigger")