Momentum strategy in Python

Momentum buys what is already going up, here is a Python momentum strategy, backtested with real costs on the Rust engine.

What is momentum?

Momentum is the empirical tendency for assets that have risen to keep rising over the near term. It is one of the most robust effects across markets and timeframes, the mirror image of mean reversion, and the two often complement each other in a portfolio.

A momentum strategy holds strength and avoids weakness. The trade-off is whipsaw: in choppy, directionless markets momentum buys the top and sells the bottom, so position sizing and trend filters matter.

The logic

We define momentum as the percentage gap between price and its 30-bar average. When price trades more than 5% above its recent base, the trend is strong enough to ride; otherwise we stand aside.

The position is long-only here: full size when momentum is strong and positive, flat otherwise. A wide stop-loss lets winners run while still capping catastrophic reversals.

momentum 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.

momentum.py
import manifoldbt as mbt
from manifoldbt.indicators import close
from manifoldbt.helpers import time_range, Slippage, Interval

# 30-bar momentum: price relative to its recent average
momentum = close / close.rolling_mean(30) - mbt.lit(1.0)

# Long while momentum is strong and positive
strategy = (
    mbt.Strategy.create("momentum")
    .signal("mom", momentum)
    .size(mbt.when(momentum > 0.05, 1.0, 0.0))
    .stop_loss(pct=8.0)
)

start, end = time_range("2021-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=30,
)
store = mbt.ingest(provider="binance", symbol="BTCUSDT", symbol_id=1,
                   interval="1d", start="2021-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

Daily BTC bars keep the trade count low and the signal slow, which suits momentum, fewer, longer holds mean fewer fees and less whipsaw than an intraday version.

Sweep the lookback and the entry threshold to map the plateau where the edge is stable. Momentum is regime-dependent, so also check performance separately in trending versus ranging years before committing.

Backtest configuration
Universe{"binance": ["BTCUSDT"]}
Bar interval1d
FeesFeeConfig.binance_perps()
Slippagefixed 2 bps
Warmup30 bars
Sample tearsheet (illustrative, not a forecast)
Total return+91.4%
Sharpe1.05
Max drawdown-29.3%
Win rate44%
Trades63

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.

$pip install manifoldbt