
이것은 Keltner Channel에 기반한 유연한 거래 전략이다. 이 전략은 다공간 양방향 거래를 지원하며, 가격 돌파 채널의 오르락 내리락을 모니터링하여 거래를 한다. 전략의 핵심은 이동 평균 (MA) 을 사용하여 가격 채널을 구축하고 실제 파도 (ATR) 와 결합하여 채널 폭을 동적으로 조정하여 다양한 시장 환경에서 전략의 적응성을 유지한다.
이 전략은 다음과 같은 핵심 원칙에 기초하고 있습니다.
이 전략은 완벽하게 설계된, 논리적으로 명확한 거래 시스템으로, 케이터 통로와 다양한 기술 지표를 유연하게 사용하여 시장 기회를 효과적으로 포착합니다. 전략은 사용자 정의가 강하여 다양한 위험 선호를 가진 거래자의 사용에 적합합니다. 지속적인 최적화 및 개선으로, 이 전략은 다양한 시장 환경에서 안정적인 성능을 유지할 것으로 예상됩니다.
/*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")