多尼昌趋势跟踪策略是根据文章“Black Box Trend Following – Lifting the Veil”中描述的多尼昌通道原理开发的趋势跟踪策略。该策略利用多尼昌通道判断价格趋势,根据价格创新高或创新低来建仓做多或做空。
策略基于多尼昌通道指标判断趋势方向。多尼昌通道由一个较长周期的通道和一个较短周期的通道组成。当价格突破较长周期的通道时,判断为趋势开始;当价格突破较短周期的通道时,判断为趋势结束。
具体来说,较长周期通道长度为50天或20天,较短周期通道长度为50天、20天或10天。如果价格等于50天内的最高价,则开多单;如果价格等于50天内的最低价,则开空单。如果价格等于20天或10天内的最低价,则平掉多单;如果价格等于20天或10天内的最高价,则平掉空单。
这样,通过两个不同周期多尼昌通道的组合,可以在趋势开始时确定方向建仓,在趋势结束时及时止损离场。
该策略主要有以下优势:
捕捉趋势的能力强。通过突破多尼昌通道判断趋势开始和结束,可以有效跟踪趋势。
风险控制到位。采用移动止损来控制单笔损失。
参数调整灵活。可以自由选择通道的周期组合,适应不同品种和市场环境。
简单明确的交易逻辑。容易理解和实施。
该策略也存在以下风险:
无法适应震荡市。当趋势不明显时,将出现多次小幅度的调整,带来止损损失。
突破失败风险。价格突破通道后可能再次回调,造成止损。
周期选择风险。如果通道周期设定不当,将导致trading in noise。
夏普比率下降风险。如果加大仓位而不调整止损幅度,将面临夏普比率下降的风险。
对应解决方法: 1. 优化参数,选择合适的通道周期组合。 2. 适当调整仓位和止损幅度,控制风险。 3. 在趋势明显的品种和市场使用该策略。
该策略可以从以下几个方向进行优化:
增加过滤条件,避免whipsaws。例如结合量能指标等判断真实突破。
优化通道周期的组合和仓位控制,提高盈亏比。可以引入自适应止损机制。
尝试breakpoint优化,找到最佳的参数组合。
增加机器学习算法,实现参数的动态优化和调整。
多尼昌趋势跟踪策略通过双通道判断价格趋势开始和结束,采取趋势跟踪的交易方式,有效控制单笔损失。该策略参数调整灵活,易于实施,是一个非常实用的趋势跟踪策略。但也需要注意在震荡行情下的盈利能力不足以及参数选择带来的风险。通过进一步优化,可以获得更好的策略效果。
/*backtest start: 2023-12-01 00:00:00 end: 2023-12-31 23:59:59 period: 1h basePeriod: 15m exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}] */ //@version=5 strategy(title="Donchian", overlay=true, pyramiding=0, initial_capital=1000, commission_type=strategy.commission.cash_per_order, commission_value=2, slippage=2) // ============================================================================= // VARIABLES // ============================================================================= donch_string = input.string(title="Length", options = ['20/10','50/20', '50/50', '20/20', '100/100'], defval='50/50') permit_long = input.bool(title = 'Permit long', defval = true) permit_short = input.bool(title = 'Permit short', defval = true) risk_percent = input.float(title="Position Risk %", defval=0.5, step=0.25) stopOffset = input.float(title="ATR mult", defval=2.0, step=0.5) atrLen = input.int(title="ATR Length", defval=20) close_in_end = input.bool(title = 'Close in end', defval = true) permit_stop = input.bool(title = 'Permit stop', defval = false) // ============================================================================= // CALCULATIONS // ============================================================================= donch_len_big = donch_string == '50/20' ? 50 : donch_string == '50/50' ? 50 : donch_string == '20/20' ? 20 : donch_string == '20/10' ? 20 : donch_string == '100/100' ? 100 : na donch_len_small = donch_string == '50/20' ? 20 : donch_string == '50/50' ? 50 : donch_string == '20/20' ? 20 : donch_string == '20/10' ? 10 : donch_string == '100/100' ? 100 : na big_maxclose = ta.highest(close, donch_len_big) big_minclose = ta.lowest(close, donch_len_big) small_maxclose = ta.highest(close, donch_len_small) small_minclose = ta.lowest(close, donch_len_small) atrValue = ta.atr(atrLen)[1] tradeWindow = true // ============================================================================= // NOTOPEN QTY // ============================================================================= risk_usd = (risk_percent / 100) * strategy.equity atr_currency = (atrValue * syminfo.pointvalue) notopen_qty = risk_usd / (stopOffset * atr_currency) // ============================================================================= // LONG STOP // ============================================================================= long_stop_price = 0.0 long_stop_price := strategy.position_size > 0 and na(long_stop_price[1]) ? strategy.position_avg_price - stopOffset * atrValue : strategy.position_size > 0 and strategy.openprofit > risk_usd ? strategy.position_avg_price: strategy.position_size > 0 ? long_stop_price[1] : na // ============================================================================= // SHORT STOP // ============================================================================= short_stop_price = 0.0 short_stop_price := strategy.position_size < 0 and na(short_stop_price[1]) ? strategy.position_avg_price + stopOffset * atrValue : strategy.position_size < 0 and strategy.openprofit > risk_usd ? strategy.position_avg_price : strategy.position_size < 0 ? short_stop_price[1] : na // ============================================================================= // PLOT VERTICAL COLOR BAR // ============================================================================= cross_up = strategy.position_size <= 0 and close == big_maxclose and close >= syminfo.mintick and tradeWindow and permit_long cross_dn = strategy.position_size >= 0 and close == big_minclose and close >= syminfo.mintick and tradeWindow and permit_short bg_color = cross_up ? color.green : cross_dn ? color.red : na bg_color := color.new(bg_color, 70) bgcolor(bg_color) // ============================================================================= // PLOT DONCHIAN LINES // ============================================================================= s1 = cross_up ? na : cross_dn ? na : strategy.position_size != 0 ? strategy.position_avg_price : na s2 = cross_up ? na : cross_dn ? na : strategy.position_size > 0 ? small_minclose : strategy.position_size < 0 ? small_maxclose : na s3 = cross_up ? na : cross_dn ? na : not permit_stop ? na : strategy.position_size > 0 ? long_stop_price : strategy.position_size < 0 ? short_stop_price : na plot(series=big_maxclose, style=plot.style_linebr, color=color.black, linewidth=1, title="Donch Big Maxclose Black") plot(series=big_minclose, style=plot.style_linebr, color=color.black, linewidth=1, title="Donch Big Minclose Black") plot(series=s1, style=plot.style_linebr, color=color.yellow, linewidth=2, title="Entry Yellow") plot(series=s2, style=plot.style_linebr, color=color.red, linewidth=1, title="Donch Small Red") plot(series=s3, style=plot.style_linebr, color=color.fuchsia, linewidth=2, title="Stop Fuchsia") // ============================================================================= // ENTRY ORDERS // ============================================================================= if strategy.position_size <= 0 and close == big_maxclose and close >= syminfo.mintick and tradeWindow and permit_long strategy.entry("Long", strategy.long, qty=notopen_qty) if strategy.position_size >= 0 and close == big_minclose and close >= syminfo.mintick and tradeWindow and permit_short strategy.entry("Short", strategy.short, qty=notopen_qty) // ============================================================================= // EXIT ORDERS // ============================================================================= if strategy.position_size > 0 and permit_stop strategy.exit(id="Stop", from_entry="Long", stop=long_stop_price) if strategy.position_size < 0 and permit_stop strategy.exit(id="Stop", from_entry="Short", stop=short_stop_price) // ========== if strategy.position_size > 0 and close == small_minclose and not barstate.islast strategy.close(id="Long", comment='Donch') if strategy.position_size < 0 and close == small_maxclose and not barstate.islast strategy.close(id="Short", comment='Donch') // ========== if close_in_end if not tradeWindow strategy.close_all(comment='Close in end') // ============================================================================= // END // =============================================================================