Keltner 채널을 기반으로 한 유연한 롱 및 숏 트레이딩 전략

ATR EMA SMA MA TR
생성 날짜: 2025-02-10 15:07:12 마지막으로 수정됨: 2025-02-10 15:07:12
복사: 4 클릭수: 448
avatar of ChaoZhang ChaoZhang
1
집중하다
1617
수행원

Keltner 채널을 기반으로 한 유연한 롱 및 숏 트레이딩 전략

개요

이것은 Keltner Channel에 기반한 유연한 거래 전략이다. 이 전략은 다공간 양방향 거래를 지원하며, 가격 돌파 채널의 오르락 내리락을 모니터링하여 거래를 한다. 전략의 핵심은 이동 평균 (MA) 을 사용하여 가격 채널을 구축하고 실제 파도 (ATR) 와 결합하여 채널 폭을 동적으로 조정하여 다양한 시장 환경에서 전략의 적응성을 유지한다.

전략 원칙

이 전략은 다음과 같은 핵심 원칙에 기초하고 있습니다.

  1. EMA 또는 SMA를 통해 가격의 중심 추세를 계산하여 통로 중간 궤도를 형성합니다
  2. ATR, TR 또는 Range를 사용하여 변동률을 계산하여 통로를 구축합니다
  3. 가격이 상승할 때 다중 신호를, 하락할 때 하위 신호를 트리거
  4. 입출출을 위한 단일한 스톱로스 임의 메커니즘을 적용하여 거래 실행의 신뢰성을 높여주기
  5. 유연한 거래 방식을 지원합니다. 단장, 단장 또는 양방향 거래

전략적 이점

  1. 적응성 - ATR을 통해 채널 폭을 동적으로 조정하여 다양한 시장 변동 환경에 적응할 수 있도록합니다.
  2. 리스크 관리가 완벽하다 - 단일한 스톱 로드 메커니즘을 사용하여 거래를하고, 위험을 효과적으로 제어합니다.
  3. 운영의 유연성 - 시장 특성과 거래 선호도에 따라 조정할 수 있는 여러 거래 모드를 지원합니다.
  4. 유효성 검증 - 특히 변동성이 높은 시장에서 암호화폐와 주식 시장에서 잘 수행
  5. 투명성 - 거래 신호와 포지션 상태를 직관적으로 표시합니다.

전략적 위험

  1. 흔들림 시장 위험 - 가로판 흔들림 시장에서 빈번한 가짜 브레이크 신호가 발생할 수 있습니다.
  2. 슬라이드 포인트 위험 - 유동성이 부족한 시장에서 스톱 손실 위탁은 큰 슬라이드 포인트에 직면 할 수 있습니다.
  3. 트렌드 리버스 리스크 - 트렌드가 급격히 변할 경우 더 큰 손실을 입을 수 있습니다.
  4. 매개 변수 민감성 - 채널 매개 변수의 선택이 전략 성능에 중요한 영향을 미칩니다.

전략 최적화 방향

  1. 트렌드 필터를 도입 - 트렌드 판단 지표를 추가하여 가짜 브레이크 신호를 줄이십시오.
  2. 동적 매개 변수 최적화 - 시장의 변동에 따라 동적으로 채널 매개 변수를 조정
  3. 손해 방지 기능 개선 - 모바일 손해 방지 기능을 추가하여 수익을 더 잘 보호합니다.
  4. 트랜지먼트 확인이 증가 - 트랜지먼트 지표를 결합하여 신호 신뢰성을 향상시킵니다.
  5. 포지션 관리를 최적화 - 더 나은 위험을 제어하기 위해 동적 포지션 관리를 도입

요약하다

이 전략은 완벽하게 설계된, 논리적으로 명확한 거래 시스템으로, 케이터 통로와 다양한 기술 지표를 유연하게 사용하여 시장 기회를 효과적으로 포착합니다. 전략은 사용자 정의가 강하여 다양한 위험 선호를 가진 거래자의 사용에 적합합니다. 지속적인 최적화 및 개선으로, 이 전략은 다양한 시장 환경에서 안정적인 성능을 유지할 것으로 예상됩니다.

전략 소스 코드
/*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")