Skip to content
Open RaamTrade

MarketMeanness — Market Meanness Index

How often price reverses back toward the window’s mean. Near 75% is what a random walk produces; lower readings mean a real trend is present. Counter-intuitively, high is bad for trend rules.

Signature

MarketMeanness(period, on=<source>)
Argument Type Required Notes
period int Param or literal Yes Window. Default 60, bounds 3–400.
on= symbol, OHLCV field, or indicator No Source. Defaults to the trade symbol’s close.

Formula

Count the bars that moved down while above the window’s mean, plus those that moved up while below it, as a percentage of the window:

$$ \text{MMI} = 100,\frac{#{x_{i+1} < x_i \wedge x_i > \bar{x}} + #{x_{i+1} > x_i \wedge x_i < \bar{x}}}{n-1} $$

Warmup

period bars; 75 (the random-walk value) until then.

Reading the value

mmi.value
mmi < 70

Example

class MeannessGate(Strategy):
"""Trend rules only when the market is less mean-reverting than random."""
trade = Symbol()
sizing = FixedCapital()
def execute(self):
mmi = MarketMeanness(60, on=self.trade)
sma = SMA(50, on=self.trade)
if mmi.value < 70 and self.trade.close > sma.value and self.trade.is_flat:
self.buy(self.trade, reason=f"MMI {mmi.value:.0f}")
if mmi.value > 78 and self.trade.is_long:
self.sell(self.trade, self.trade.position, reason="Market gone mean")

Notes

  • The Market Meanness Index as popularised by the Financial Hacker blog, using the window mean as the median proxy — RaamCode has no sorted(), so a true rolling median is not available. On a roughly symmetric window the two agree closely; on a strongly skewed one the mean sits away from the median and the reading shifts. Stated because it is a deviation from the original definition.
  • Proven by an assert-via-trade run on SPY daily (stayed inside 0–100 on every bar; zero alarm trades).