0bps

Strategy vault / Breakout

trend following strategy based on Donchian Channels

The Puling strategy is a trend following strategy based on Donchian Channels. It uses fast and slow Donchian Channels to identify the trend direction and enter on pullbacks. The advantages of this strategy are that…

Author
ChaoZhang
Complexity
Intermediate · 102 lines
Parameters
15
Backtest bar
2h · intraday
Tested on
BTC/USDT · futures

IMG

Overview

The Puling strategy is a trend following strategy based on Donchian Channels. It uses fast and slow Donchian Channels to identify the trend direction and enter on pullbacks. The advantages of this strategy are that it can track trends automatically and cut losses in time when the trend changes. But it also has the risks of drawdown and stop loss being too close.

Strategy Logic

The strategy first defines the fast channel period as 20 bars, and the slow channel period as 50 bars. The fast channel is used to set the stop loss price, while the slow channel is used to determine the trend direction and entry timing.

First, the highest high and lowest low of the fast channel are calculated, and the midpoint is taken as the stop loss line. At the same time, the highest high and lowest low of the slow channel are calculated, and the channel top and bottom are used as the entry lines.

When the price breaks through the top of the slow channel, go long. When the price breaks through the bottom of the slow channel, go short. After entering the position, set the stop loss at the midpoint of the fast channel.

So the slow channel determines the major trend direction, while the fast channel tracks minor breakouts within a small range to determine the stop loss point. When the major trend reverses, the price will first break through the stop loss line of the fast channel to realize the stop loss.

Advantages

  • Automatically track trends and cut losses in time. The double channel structure can automatically track trends and quickly cut losses when trends reverse.

  • Enter on pullbacks, with some trend filtering effect. Only taking entries when price breaks through channel boundaries can filter out some false breakouts without real trend.

  • Controllable risk. The close stop loss distance can control single loss.

Risks

  • Larger drawdowns. Trend following strategies can have relatively large drawdowns which requires psychological preparation.

  • Stop loss too close. The fast channel period is short so the stop loss is close, prone to being stopped out. We can appropriately relax the fast channel period.

  • Too many trades. The double channel structure can generate excessive entries, requiring reasonable position sizing.

Optimization Directions

  • Add entry filters. We can add volatility etc. in entry conditions to filter breakouts without enough trend strength.

  • Optimize channel period parameters. We can find the optimal channel parameter combinations systematically.

  • Combining multiple timeframes. Determine the major trend on higher timeframes and trade on lower timeframes.

  • Dynamic stop loss distance. Adjust stop distance dynamically based on market volatility.

Summary

The Puling strategy is a standard trend following strategy overall. It uses price channels to determine trend direction and sets stop loss to control risks. The strategy has some advantages but also the problems of drawdown and stop loss being too close. We can optimize it through adjusting channel parameters, adding filters etc. But we should note trend following strategies require strong psychology to endure drawdowns.

Parameters

NameDefaultWhat it controls
v_input_1trueLong
v_input_2trueShort
v_input_32Risk size, %
v_input_420Fast channel (for stop-loss)
v_input_550Slow channel (for entries)
v_input_6trueShow offset
v_input_7trueShow lines
v_input_8trueShow label (drawdown)
v_input_9trueShow background
v_input_101900From Year
v_input_112100To Year
v_input_12trueFrom Month
v_input_1312To Month
v_input_14trueFrom day
v_input_1531To day

Source Pine Script

/*backtest
start: 2023-09-30 00:00:00
end: 2023-10-30 00:00:00
period: 2h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

//Noro
//2020

//@version=4
strategy("Noro's RiskTurtle Strategy", shorttitle = "RiskTurtle str", overlay = true, default_qty_type = strategy.percent_of_equity, initial_capital = 100, default_qty_value = 100, commission_value = 0.1)

//Settings
needlong  = input(true, defval = true, title = "Long")
needshort = input(true, defval = true, title = "Short")
risk      = input(2, minval = 0.1, maxval = 99, title = "Risk size, %")
fast      = input(20, minval = 1, title = "Fast channel (for stop-loss)")
slow      = input(50, minval = 1, title = "Slow channel (for entries)")
showof    = input(true, defval = true, title = "Show offset")
showll    = input(true, defval = true, title = "Show lines")
showdd    = input(true, defval = true, title = "Show label (drawdown)")
showbg    = input(true, defval = true, title = "Show background")
fromyear  = input(1900, defval = 1900, minval = 1900, maxval = 2100, title = "From Year")
toyear    = input(2100, defval = 2100, minval = 1900, maxval = 2100, title = "To Year")
frommonth = input(01, defval = 01, minval = 01, maxval = 12, title = "From Month")
tomonth   = input(12, defval = 12, minval = 01, maxval = 12, title = "To Month")
fromday   = input(01, defval = 01, minval = 01, maxval = 31, title = "From day")
today     = input(31, defval = 31, minval = 01, maxval = 31, title = "To day")

//Donchian price channel fast
hf = highest(high, fast)
lf = lowest(low, fast)
center = (hf + lf) / 2

//Donchian price chennal slow
hs = highest(high, slow)
ls = lowest(low, slow)

//Lines
colorpc = showll ? color.blue : na
colorsl = showll ? color.red : na
offset = showof ? 1 : 0
plot(hs, offset = offset, color = colorpc, title = "Slow channel high")
plot(ls, offset = offset, color = colorpc, title = "Slow channel low")
plot(center, offset = offset, color = colorsl, title = "Fast channel stop-loss")

//Background
size = strategy.position_size
colorbg = showbg == false ? na : size > 0 ? color.lime : size < 0 ? color.red : na
bgcolor(colorbg, transp = 70)

//Var
loss = 0.0
maxloss = 0.0
equity = 0.0
truetime = true

//Lot size
risksize = -1 * risk
risklong = ((center / hs) - 1) * 100
coeflong = abs(risksize / risklong)
lotlong = (strategy.equity / close) * coeflong
riskshort = ((center / ls) - 1) * 100
coefshort = abs(risksize / riskshort)
lotshort = (strategy.equity / close) * coefshort

//Orders
strategy.entry("Long", strategy.long, lotlong, stop = hs, when = needlong and strategy.position_size == 0 and hs > 0 and truetime)
strategy.entry("Short", strategy.short, lotshort, stop = ls, when = needshort and strategy.position_size == 0 and ls > 0 and truetime)
strategy.exit("LongExit", "Long", stop = center, when = needlong and strategy.position_size > 0)
strategy.exit("Short", stop = center, when = needshort and strategy.position_size < 0)
if time > timestamp(toyear, tomonth, today, 23, 59)
    strategy.close_all()
    strategy.cancel("Long")
    strategy.cancel("Short")
    
if showdd

    //Drawdown
    max = 0.0
    max := max(strategy.equity, nz(max[1]))
    dd = (strategy.equity / max - 1) * 100
    min = 100.0
    min := min(dd, nz(min[1]))
    
    //Max loss size
    equity := strategy.position_size == 0 ? strategy.equity : equity[1]
    loss := equity < equity[1] ? ((equity / equity[1]) - 1) * 100 : 0
    maxloss := min(nz(maxloss[1]), loss)
    
    //Label
    min := round(min * 100) / 100
    maxloss := round(maxloss * 100) / 100
    labeltext = "Drawdown: " + tostring(min) + "%" + "\nMax.loss " + tostring(maxloss) + "%"
    var label la = na
    label.delete(la)
    tc = min > -100 ? color.white : color.red
    osx = timenow + round(change(time)*10)
    osy = highest(100)
    // la := label.new(x = osx, y = osy, text = labeltext, xloc = xloc.bar_time, yloc = yloc.price, color = color.black, style = label.style_labelup, textcolor = tc)

Published by ChaoZhang on the FMZ strategy square · last modified 2023-10-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.

in Claude Code, Cursor, Codex & 4 more
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/trend-following-strategy-based-on-Donchian-Channels.md
  1. npx agenkit install engineering-kit in your project
  2. Paste the command into your agent and approve each phase
  3. 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

esc

Try , , or .

↑↓ move↵ open