ROC — Rate of Change
Percentage price change over the last N bars. The normalized cousin of Momentum. Single value.
Signature
ROC(period, on=<source>)| Argument | Type | Required | Notes |
|---|---|---|---|
period |
int Param or literal | Yes | Lookback. Default 14, bounds 1–200. |
on= |
symbol, OHLCV field, or indicator | No | Source. Defaults to the trade symbol’s close. |
Formula
$$ \text{ROC} = \frac{\text{close}t - \text{close}{t-N}}{\text{close}_{t-N}} \times 100 $$
Warmup
period + 1 bars (the window must hold both endpoints). Reads 0 until then.
Reading the value
roc = ROC(14, on=self.trade)
roc.value # percentage, e.g. 3.2 = +3.2% over 14 barsroc.crossed_above(0) # momentum turning positiveExample
class RocMomentum(Strategy): """Enter on strong momentum, exit when it fades.""" period = Param(14, min=5, max=50) threshold = Param(5, min=1, max=20) trade = Symbol() sizing = FixedCapital()
def execute(self): roc = ROC(self.period, on=self.trade) self.stop_loss(3.0)
if roc.crossed_above(self.threshold) and self.trade.is_flat: self.buy(self.trade, reason=f"ROC {roc.value:.1f}%")
if roc.crossed_below(0) and self.trade.is_long: self.sell(self.trade, self.trade.position, reason="Momentum faded")Notes
- ROC is percentage-based, so thresholds transfer across instruments — unlike
Momentum, whose absolute value scales with price. - ROC oscillates around zero with no bounds — a ±100 clamp does not exist (a stock that doubles reads +100).
- Zero-line crosses are the coarse regime signal; threshold crosses (as in the example) trade the acceleration.