Skip to content

Momentum rotation ​

How do you backtest "each week, hold the coins that rose most" without picking the coins with hindsight? Take the list from request.universe, score every member with request.each, pick with cs.top, and trade it on a book. Each piece reads only what was known at the bar's close.

ameno
indicator("Momentum rotation")
int top = input.int(20, "Universe size", minval = 2)
int hold = input.int(5, "Coins held", minval = 1)
int lookback = input.int(30, "Momentum, days", minval = 2)

// The top coins by rank on each date, stablecoins left out
array<string> members = request.universe(top,
  exclude_tags = "stablecoin")
// Each member's return over the lookback, on its own bars
array<float> mom = request.each(members, close / close[lookback] - 1)
array<bool> chosen = cs.top(mom, hold)
array<float> weights = array.map(chosen, on => on ? 1.0 / hold : 0.0)

book.config(fee_bps = 10, slippage_bps = 5, initial_capital = 100000)
if timeframe.change("1W")
    book.target(members, weights, reason = "weekly momentum")
plot(book.equity(), "Equity")
plot(book.turnover(), "Turnover", display = display.data_window)

Run it on a daily chart of a coin that trades every day, such as CRYPTO:BTCUSD, so the book has a bar on every day its coins trade. request.universe, request.each, cs.top and the book.* calls are all AmenoScript additions, so the script is AmenoScript and runs leak-safe. It is a teaching example, not a recommendation.

How it works ​

  • request.universe(top, exclude_tags = "stablecoin") is the 20 best-ranked coins on CoinMarketCap that aren't stablecoins, as they stood on each date. A coin that later failed is in the list while it ranked.
  • request.each(members, close / close[lookback] - 1) runs the expression on each member's own daily bars. close[lookback] is that coin's own past, not a position in the list.
  • cs.top(mom, hold) is true on the five largest returns. A coin with too little history reads na and is never picked.
  • array.map(chosen, on => ...) turns the picks into weights: a fifth each, 0 for the rest.
  • book.target(members, weights) states the whole book. Coins not picked go to zero, and the fills come at the next open, paying the fee, the slippage and an impact charge for the share of volume each takes.
  • timeframe.change("1W") is true on the first bar of each week, so the book trades once a week and drifts in between.

Traps ​

  • New members arrive on their way up. A coin usually joins the list through the move that lifted its rank, so momentum tends to pick it at the top of that move. How long a member has been in shows how to keep a coin out until it has stayed in the list a while.
  • Volume. This book states initial_capital, so each fill is capped at 1% of the coin's dollar volume, and each fill pays an impact charge of impact_bps, 100 by default, times the square root of its share of volume. Set volume_cap and impact_bps in book.config to change them.
  • CMC: prices are research prices. The run says so: nobody could trade at a ranking site's daily close.

See also ​

AmenoScript is compatible with Pine Script v6. Pine Script is a trademark of TradingView, Inc., which has no part in AmenoScript.