基于凯特纳通道的灵活多空交易策略

ATR EMA SMA MA TR
创建日期: 2025-02-10 15:07:12 最后修改: 2025-02-10 15:07:12
复制: 3 点击次数: 113
1
关注
1181
关注者

基于凯特纳通道的灵活多空交易策略

概述

这是一个基于凯特纳通道(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")
相关推荐
更多内容