Trend following strategy in Python
Trend following rides sustained moves in either direction, here is a Donchian trend strategy in Python, backtested on the Rust core.
What is trend following?
Trend following is the oldest systematic edge: buy strength, sell weakness, and let winners run while cutting losers. It makes no forecast, it simply follows price, accepting many small losses in exchange for a few large trend captures.
It is the strategy behind the famous Turtle traders and most managed-futures funds. The trade-off is statistical: low win rates and long drawdowns in sideways markets, paid back by outsized gains when a real trend appears.
The logic
We build a 55-bar Donchian channel from the rolling high and low, the classic long-horizon trend window, then measure where the close sits inside it. Near 1 is a fresh high; near 0 a fresh low.
The rule is symmetric and always in the trend's direction: go long when the close holds the top of the channel and short when it sits at the bottom, flipping as the trend flips. That is textbook breakout-based trend following.
trend following in Python
Here is the full strategy in the Manifold-BT expression DSL. It imports manifoldbt, builds the signal, configures realistic execution, and runs the backtest against Rust.
import manifoldbt as mbt
from manifoldbt.indicators import close, high, low
from manifoldbt.helpers import time_range, Slippage, Interval
# 55-bar Donchian channel and the close's position within it
upper = high.rolling_max(55)
lower = low.rolling_min(55)
pos = (close - lower) / (upper - lower)
# Long near new highs, short near new lows; follow the trend either way
strategy = (
mbt.Strategy.create("trend_following")
.signal("upper", upper)
.signal("lower", lower)
.size(mbt.when(pos > 0.8, 1.0, mbt.when(pos < 0.2, -1.0, 0.0)))
)
start, end = time_range("2020-01-01", "2026-01-01")
config = mbt.BacktestConfig(
universe={"binance": ["BTCUSDT"]},
time_range_start=start,
time_range_end=end,
bar_interval=Interval.days(1),
initial_capital=10_000,
fees=mbt.FeeConfig.binance_perps(),
slippage=Slippage.fixed_bps(2),
warmup_bars=55,
)
store = mbt.ingest(provider="binance", symbol="BTCUSDT", symbol_id=1,
interval="1d", start="2020-01-01T00:00:00Z",
end="2026-01-01T00:00:00Z")
result = mbt.run(strategy, config, store)
print(result.summary())
mbt.plot.tearsheet(result, show=True)Backtest it with Manifold-BT
On daily bars the 55-bar channel flips only a few dozen times across years, so costs are minor and the result is dominated by how cleanly it catches the big directional trends.
Vary the channel length and the entry bands, then check performance separately in trending versus ranging years. Trend following is regime-dependent by design, the drawdowns in chop are the price of the trend capture.
| Universe | {"binance": ["BTCUSDT"]} |
| Bar interval | 1d |
| Fees | FeeConfig.binance_perps() |
| Slippage | fixed 2 bps |
| Warmup | 55 bars |
| Total return | +103.7% |
| Sharpe | 1.09 |
| Max drawdown | -27.6% |
| Win rate | 43% |
| Trades | 48 |
Frequently asked questions
Does trend following still work?
Trend following remains one of the most studied and durable systematic edges, but it pays for its long-run returns with low win rates and deep drawdowns in range-bound markets. A multi-regime backtest is essential before trading it.
Keep reading
Run your first backtest
Install Manifold-BT and reproduce the backtest above in seconds. The Rust core runs years of bars sub-second so you can sweep parameters instead of waiting.