Donchian Trend Following Strategy
The Donchian Trend Following strategy is developed based on the Donchian Channel principle described in the article "Black Box Trend Following – Lifting the Veil". This strategy uses the Donchian Channel to determine…

Overview
The Donchian Trend Following strategy is developed based on the Donchian Channel principle described in the article "Black Box Trend Following – Lifting the Veil". This strategy uses the Donchian Channel to determine the trend direction and establishes long or short positions when prices hit new highs or lows.
Strategy Logic
The strategy is based on the Donchian Channel indicator to judge the trend direction. The Donchian Channel consists of a longer period channel and a shorter period channel. When the price breaks through the longer period channel, it signals the start of a trend. When the price breaks through the shorter period channel, it signals the end of the trend.
Specifically, the longer period channel length is 50 days or 20 days, and the shorter period channel length is 50 days, 20 days or 10 days. If the price equals the highest price in 50 days, a long position is opened. If the price equals the lowest price in 50 days, a short position is opened. If the price equals the lowest price in 20 or 10 days, long positions are closed. If the price equals the highest price in 20 or 10 days, short positions are closed.
By combining two Donchian Channels of different periods, it can determine the direction to establish positions when a trend starts, and realize timely stop loss when the trend ends.
Advantage Analysis
The main advantages of this strategy are:
Strong ability to capture trends. It can track trends effectively by identifying the start and end of trends using Donchian Channel breakouts.
Proper risk control. It uses a moving stop loss to control single trade loss.
Flexible parameter adjustment. The combination of channel periods can be freely selected to adapt to different products and market environments.
Simple and clear trading logic. It is easy to understand and implement.
Risk Analysis
The risks of this strategy include:
Inability to adapt to range-bound markets. It will suffer consecutive small stop loss when the trend is unclear.
Breakout failure risk. Prices may pullback after breaching the channel, causing stop loss.
Period selection risk. Inappropriate channel period settings may lead to trading in noise.
Sharpe ratio decline risk. Increasing position size without adjusting stop loss may lead to declining Sharpe ratio.
The solutions are:
- Optimize parameters to select suitable channel period combinations.
- Adjust position size and stop loss properly to control risk.
- Use this strategy for products and markets with obvious trends.
Optimization Directions
The optimization directions for this strategy:
Adding filter conditions to avoid whipsaws, e.g. combining volume to judge true breakouts.
Optimizing channel period combination and position sizing to increase profit ratio. Adaptive stop loss can be introduced.
Trying breakpoint optimization to find optimal parameter sets.
Increasing machine learning algorithms for dynamic optimization and adjustment of parameters.
Conclusion
The Donchian Trend Following Strategy identifies the start and end of price trends using dual channels, and adopts trend following trading style with effective single trade loss control. This strategy has flexible parameter adjustment and easy implementation, making itself a very practical trend following strategy. But the insufficient profitability in range-bound markets and risks from parameter selection should be noted. Further optimizations can lead to better strategy performance.
Parameters
| Name | Default | What it controls |
|---|---|---|
| v_input_string_1 | 0 | Length: 50/50 | 50/20 | 20/10 | 20/20 | 100/100 |
| v_input_bool_1 | true | Permit long |
| v_input_bool_2 | true | Permit short |
| v_input_float_1 | 0.5 | Position Risk % |
| v_input_float_2 | 2 | ATR mult |
| v_input_int_1 | 20 | ATR Length |
| v_input_bool_3 | true | Close in end |
| v_input_bool_4 | false | Permit stop |
Source Pine Script
/*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
// =============================================================================Published by ChaoZhang on the FMZ strategy square · last modified 2024-01-31. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.
Build this with AgenKit
A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/Donchian-Trend-Following-Strategy.md- npx agenkit install engineering-kit in your project
- Paste the command into your agent and approve each phase
- Get tests, backtest, risk controls and a paper-trading switch
Understand it first
Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.
Related specs
- Breakout Strategy Based on Turtle TradingPine Script · Breakout · 1d
- Donchian Channel Breakout StrategyPine Script · Breakout · 4h
- Donchian Channel Trend Following StrategyPine Script · Breakout · 1h
- Donchian Channel and Larry Williams Large Trade Index StrategyPine Script · Breakout · 1m
- Momentum Breakout Trading Strategy 435896Pine Script · Breakout · 1d
- Multi Indicator Trend Breakout Strategy with Dynamic Stop LossPine Script · Breakout · 1d