D07-F01-A09 / Complete engineering topic

MESA Adaptive Moving Average (MAMA): Formula, FAMA, Code, and Interactive Guide

A reproducible MAMA tutorial covering Hilbert phase, adaptive alpha, warm-up, Python and TypeScript, edge cases, and a 144-point laboratory.

D07 · TECHNICAL INDICATORS
D07-F01-A09Canonical / Tested / Open
D07 / D07-F01

The useful question behind the MESA Adaptive Moving Average (MAMA) is simple: can one exponential moving average respond quickly at selected moments without remaining noisy all the time?

John Ehlers' MAMA answers by changing the EMA coefficient with the measured rate of phase change. Its output often looks like a ratchet: a quick move toward price, followed by a slower hold. A second line—FAMA, the Following Adaptive Moving Average—uses half the current coefficient and therefore follows more slowly.

This guide shows the complete causal path, the exact 32-observation TA-Lib warm-up, and the implementation choices that commonly make two MAMA charts disagree. The examples are deterministic synthetic data. They teach the calculation; they do not advertise a trading return.

The result before the machinery

The canonical lab contains 144 observations across five regimes. Price is gray, MAMA purple, FAMA green, and alpha amber. Notice that MAMA does not simply choose a new fixed window. Its speed can change every observation.

Price, MAMA, FAMA, and alpha across the canonical synthetic scenario

At index 95, delta phase is 62.506244°. The raw coefficient 0.5 / 62.506244 is below the 0.05 slow limit, so alpha is clamped to 0.05. At index 96, phase snaps across the one-argument arctangent boundary. The signed phase difference is floored to one degree, alpha becomes 0.5, and MAMA moves halfway from its prior value toward current price.

IndexPriceDelta phaseAlphaMAMAFAMA
95107.74003062.5062440.050000107.235009107.083257
96107.7800301.0000000.500000107.507520107.189323

These values are author-derived from synthetic input and independently checked against TA-Lib 0.7.1.

What MAMA computes—and what its name can obscure

MAMA belongs to Ehlers' MESA body of work, but the published indicator does not run a maximum-entropy spectral analysis on each bar. It uses Hilbert-transform states to estimate phase and a homodyne discriminator to maintain a supporting period state. The rate of phase change selects alpha.

The complete MAMA signal pipeline

The price path and phase path meet at MAMA:

Rendering system map…

Period corrects the Hilbert FIR on the next observation. Alpha is derived from the current Q1/I1 phase difference, not directly from period.

Input contract: the first source of disagreement

The original EasyLanguage listing defaults to midpoint price, (high + low) / 2. TA-Lib accepts one generic real series. Both are legitimate, but they are not interchangeable.

For a reproducible calculation:

  • use finite float64 values in oldest-to-newest order;
  • declare close, hl2, hlc3, or another source before calling MAMA;
  • keep one corporate-action adjustment basis throughout;
  • reject null, NaN, infinite, duplicate, and out-of-order observations;
  • remember that “cycle length” is measured in observations, not clock time; and
  • use 0.01 <= slowLimit <= fastLimit <= 0.99.

TA-Lib validates the limits independently. This tutorial adds slow <= fast because a lower limit above an upper limit has no useful adaptive meaning.

Step 1: smooth price

Let P_t be the declared source:

Plain text
Smooth_t = (4P_t + 3P_(t-1) + 2P_(t-2) + P_(t-3)) / 10

This four-observation weighted average reduces the fastest variation before the phase machinery sees it.

Step 2: form the Hilbert states

For a processed sequence x, define:

Plain text
H_t(x) = [0.0962x_t + 0.5769x_(t-2)
          - 0.5769x_(t-4) - 0.0962x_(t-6)]
         × (0.075Period_(t-1) + 0.54)

Then:

Plain text
Detrender_t = H_t(Smooth)
Q1_t = H_t(Detrender)
I1_t = Detrender_(t-3)
jI_t = H_t(I1)
jQ_t = H_t(Q1)

I2_t = 0.2(I1_t - jQ_t) + 0.8I2_(t-1)
Q2_t = 0.2(Q1_t + jI_t) + 0.8Q2_(t-1)

I1 and Q1 are the InPhase and Quadrature components used for phase. I2/Q2 feed the homodyne period estimator.

Step 3: understand the phase snap

Canonical MAMA uses one-argument atan(Q1/I1), whose result spans roughly -90°..+90°. It does not use atan2.

How the signed phase wrap selects the fast limit

The update is:

Plain text
Phase_t = degrees(atan(Q1_t / I1_t))       if I1_t != 0; otherwise 0
DeltaPhase_t = max(1, Phase_(t-1) - Phase_t)

That signed subtraction is load-bearing. When phase snaps from negative to positive, prior - current is negative. The one-degree floor turns that event into a fast attack. Replacing it with absolute phase distance, 360-degree normalization, or atan2 creates a different indicator.

Step 4: turn phase rate into alpha

With defaults:

Plain text
alpha_t = max(0.05, 0.50 / DeltaPhase_t)

MAMA alpha as a function of delta phase

At one degree, alpha is 0.5. At ten degrees, 0.5 / 10 = 0.05. Above ten degrees, the slow floor holds.

To build intuition, a fixed EMA coefficient has equivalent span N = 2/alpha - 1. MAMA's default bounds therefore correspond to spans from 3 to 39. FAMA uses half alpha, corresponding to 7 through 79. These numbers are intuition aids, not literal MAMA windows.

Step 5: update MAMA and FAMA

Plain text
MAMA_t = alpha_t P_t + (1 - alpha_t) MAMA_(t-1)
FAMA_t = (alpha_t/2) MAMA_t + (1 - alpha_t/2) FAMA_(t-1)

MAMA applies alpha to current price. FAMA applies half-alpha to the current MAMA. That is why FAMA steps at the same moments but travels less.

Step 6: update the supporting period

Plain text
Re_t = 0.2(I2_t I2_(t-1) + Q2_t Q2_(t-1)) + 0.8Re_(t-1)
Im_t = 0.2(I2_t Q2_(t-1) - Q2_t I2_(t-1)) + 0.8Im_(t-1)

When both values are nonzero, raw period is 360 / degrees(atan(Im/Re)). It is clamped between 0.67 and 1.5 times the prior period, bounded to 6..50, and smoothed with 0.2 current plus 0.8 prior. This period affects the Hilbert correction on the next observation.

The 32-observation warm-up is not optional

TA-Lib uses a fixed lookback of 32 plus any separately configured unstable period. This package uses zero additional unstable period.

MAMA warm-up and aligned output timeline

  • indices 0..11 prewarm the four-bar price smoother;
  • indices 12..31 update zero-seeded Hilbert, period, MAMA, and FAMA state;
  • index 32 is the first public MAMA/FAMA output.

The first public value is reproducible, but “valid for parity” does not mean “free from initialization influence.” FAMA's half-alpha can retain substantial zero-seed bias. Give the algorithm longer prehistory, display the warm-up honestly, and replay from the beginning before slicing a later range.

A readable implementation strategy

TA-Lib's generated C uses separate odd/even circular buffers for speed. A readable port can preserve the same transitions with one small Hilbert-stage object containing:

  • a three-slot buffer for each parity;
  • the previous input for each parity;
  • the previous 0.5769-weighted input; and
  • a shared circular index.

The package provides complete Python and TypeScript versions. Both return aligned diagnostics, so a learner can inspect phase, delta phase, alpha, period, MAMA, and FAMA—not only the final two lines.

Explore four long scenarios

Open the guided MAMA phase-rate laboratory. Each scenario has 144 observations:

  1. Canonical regime shift: long cycle, rising trend, short cycle, quiet shelf, and decline.
  2. Clean 20-bar cycle: a control for phase progression and wrap events.
  3. Step and trend: a level shock followed by changing slopes.
  4. Flat zero energy: a constant series followed by epsilon-scale motion, exposing division guards and scale sensitivity.

Choose a scenario, change the two limits, step one observation, and compare the chart, active rule, exact diagnostics, guidance, and audit table.

Edge cases that deserve explicit tests

Edge caseCorrect treatment
fewer than 33 valuesno public MAMA/FAMA output
flat seriesphase zero when I1 is zero; remain finite
NaN or infinityreject; otherwise recursive NaN contamination persists
slow above fastreject in this package
newest-first inputreverse before calculation
arbitrary mid-series restartreplay sufficient prehistory
unadjusted splitdo not confuse mechanical jump with ordinary phase event
irregular observation intervalsdisclose; the method counts observations

MAMA versus KAMA

Both are causal EMA-shaped adaptive smoothers, but their control signals and compatibility risks are different.

Decision pointKAMAMAMA
Adaptive questionHow direct was recent travel?How quickly did measured phase change?
Control signaldirectionless ERsigned delta phase
ParametersE, fast period, slow periodfast alpha limit, slow alpha limit
First public output hereindex Eindex 32
Inspectpath, ER, alpha, SCphase, delta phase, alpha, MAMA/FAMA gap
Main mismatch causesseed, zero path, period conventionsource price, phase convention, seed, warm-up
Data trapbasis jumps look efficientgaps/noise destabilize phase

KAMA adapts from path efficiency while MAMA adapts from phase rate

Choose KAMA when path efficiency is the useful diagnostic. Choose MAMA when phase-rate response is the useful diagnostic. The same “fast” and “slow” language does not make them interchangeable, and neither choice proves a useful trading signal.

Does a MAMA/FAMA crossover predict a trend?

A cross says that the faster adaptive line moved from one side of its slower follower to the other. It is a precisely testable state transition. It is not, by itself, proof of forecast skill or profitability.

Ehlers reported a 100-stock long-only test for 1998–2001, but the published account lacks the complete stock universe, point-in-time membership, data vendor, adjustment basis, initialization history, execution clock, dividends, slippage, and full cost model. This tutorial therefore defers a historical performance example. A future case must supply those items before it can support more than “the author reported this test.”

What to remember

  • MAMA is an adaptive EMA driven by signed Hilbert phase change.
  • Preserve one-argument atan, the signed difference, and the one-degree floor.
  • Fast and slow limits are coefficients, not fixed periods.
  • FAMA uses half alpha on current MAMA.
  • The TA-Lib-compatible first output is index 32, but seed influence can remain.
  • Input source, ordering, adjustment, and missing-data policy are part of the algorithm contract.
  • Matching the formula does not establish a profitable strategy.

Next, compare this phase-driven adaptation with Kaufman Adaptive Moving Average, whose coefficient responds to directional efficiency instead.

Primary and authoritative sources

MAMA state flow

Purpose: separate the phase-rate path that chooses alpha from the price path that MAMA actually smooths.

Rendering system map…

Takeaway: dominant period corrects the Hilbert transform on the next observation; alpha is selected from the current Q1/I1 phase difference.

References9 primary sources and evidence notes

Expand the source trail, evidence role, and limitations behind the engineering choices.

All web sources were accessed on 2026-07-26. Original publications establish authorship and intent; the pinned maintained implementation resolves exact initialization, update order, and compatibility behavior.

R1 — MESA Adaptive Moving Averages

  • Organization or authors: John F. Ehlers; Technical Analysis of Stocks & Commodities
  • Source type: Original article excerpt
  • Publication or effective date: September 2001
  • Version: September 2001 web excerpt
  • URL or DOI: https://traders.com/documentation/feedbk_docs/2001/09/Abstracts_new/Ehlers/ehlers.html
  • Accessed: 2026-07-26
  • Jurisdiction and applicability: Not jurisdiction-specific; historical technical-analysis publication
  • Evidence role: Sourced fact
  • Supports: Ehlers authorship, publication date, Hilbert phase-rate adaptation, the fast-attack/slow-decay objective, and the EMA foundation.
  • Limitations: The excerpt omits the complete code and most empirical detail. It cannot resolve TA-Lib initialization or reproduce the reported test.
  • Publication decision: Cite and paraphrase the public excerpt; do not reproduce copyrighted article text.

R2 — MAMA: The Mother of Adaptive Moving Averages

  • Organization or authors: John F. Ehlers
  • Source type: Author manuscript/article mirror
  • Publication or effective date: 2001
  • Version: PDF mirror inspected 2026-07-26
  • URL or DOI: https://c.mql5.com/forextsd/forum/157/mesa_adaptive_moving_average_mama.pdf
  • Accessed: 2026-07-26
  • Jurisdiction and applicability: Not jurisdiction-specific; original indicator description hosted by a third party
  • Evidence role: Sourced fact and reported empirical claim
  • Supports: EasyLanguage listing; midpoint-price default; Hilbert coefficients; period, phase, alpha, MAMA, and FAMA equations; the author's description of a historical 100-stock test.
  • Limitations: The host is not the publisher. The test lacks the exact universe, point-in-time membership, provider, adjustment basis, initialization history, execution clock, dividends, slippage, and full cost model required for reproduction.
  • Publication decision: Summarize the equations and evidence limitation. Link the source; do not republish the listing or present its test as verified profitability.

R3 — Pinned TA-Lib MAMA source of truth

  • Organization or authors: TA-Lib project
  • Source type: Maintained BSD-licensed reference implementation
  • Publication or effective date: Repository state dated 18 July 2026
  • Version: Commit 65093f3dc37a62e176c38329b550d5aab5775133
  • URL or DOI: https://github.com/TA-Lib/ta-lib/blob/65093f3dc37a62e176c38329b550d5aab5775133/ta_codegen/input/mama/mama.c
  • Accessed: 2026-07-26
  • Jurisdiction and applicability: Open-source software; canonical compatibility target for this package
  • Evidence role: Sourced implementation fact
  • Supports: Fixed 32-observation lookback; 12-observation compatibility prewarm; zero initialization; Hilbert coefficients; one-argument atan; signed phase difference; update order; period clamps; output indexing.
  • Limitations: Optimized generated state layout is harder to teach directly. It establishes TA-Lib behavior, not a universal definition for every MAMA port.
  • Publication decision: Describe behavior and link the pinned source; do not copy maintained implementation code into the article.

R4 — Official TA-Lib MAMA function page

  • Organization or authors: TA-Lib project
  • Source type: Official maintained function documentation
  • Publication or effective date: Last updated 15 July 2026
  • Version: Function documentation viewed 2026-07-26
  • URL or DOI: https://ta-lib.org/functions/mama
  • Accessed: 2026-07-26
  • Jurisdiction and applicability: Open-source software documentation; current TA-Lib public contract
  • Evidence role: Sourced fact
  • Supports: MAMA and FAMA output meanings; phase, signed delta-phase, alpha, MAMA, and FAMA summary formulas; parameter roles; maintained native source links.
  • Limitations: The summary omits the full Hilbert chain, lookback derivation, zero seed, and update ordering. Its bullish/bearish crossover labels describe a convention, not independently validated predictive performance.
  • Publication decision: Cite the formula summary and parameter roles. Preserve the package's explicit no-prediction boundary.

R5 — TA-Lib MAMA metadata

  • Organization or authors: TA-Lib project
  • Source type: Maintained official function metadata
  • Publication or effective date: Repository main inspected 2026-07-26
  • Version: Current repository metadata on access date
  • URL or DOI: https://github.com/TA-Lib/ta-lib/blob/main/ta_codegen/input/mama/mama.yaml
  • Accessed: 2026-07-26
  • Jurisdiction and applicability: Open-source software metadata; public parameter contract
  • Evidence role: Sourced implementation fact
  • Supports: Input/output names, defaults, independent accepted parameter ranges [0.01, 0.99], and the unstable-period flag.
  • Limitations: Independent numeric validation does not express the package's stricter semantic choice slow_limit <= fast_limit.
  • Publication decision: Cite parameter metadata and label the package's ordered limits as an implementation choice.

R6 — Rocket Science for Traders

  • Organization or authors: John F. Ehlers; John Wiley & Sons
  • Source type: Original book bibliographic record
  • Publication or effective date: 2001
  • Version: ISBN 9780471405672
  • URL or DOI: https://books.google.com/books/about/Rocket_Science_for_Traders.html?id=_KjOT1b9bfUC
  • Accessed: 2026-07-26
  • Jurisdiction and applicability: Not jurisdiction-specific; book provenance
  • Evidence role: Sourced fact
  • Supports: MAMA chapter provenance and the broader Hilbert/digital-signal- processing context.
  • Limitations: The available preview is limited. Exact package arithmetic is checked against R2 and R3 instead.
  • Publication decision: Cite bibliographic metadata only.

R7 — TA-Lib API: unstable periods

  • Organization or authors: TA-Lib project
  • Source type: Official maintained API documentation
  • Publication or effective date: Current documentation viewed 2026-07-26
  • Version: TA-Lib C/C++ API documentation
  • URL or DOI: https://ta-lib.org/api/?h=unstable
  • Accessed: 2026-07-26
  • Jurisdiction and applicability: Open-source software API documentation
  • Evidence role: Sourced implementation fact
  • Supports: Recursive functions can depend on the starting point and TA-Lib can strip an additional configured unstable period.
  • Limitations: General API documentation does not establish MAMA-specific arithmetic or a universally sufficient convergence period.
  • Publication decision: Use only to explain the additional unstable-period option and initialization sensitivity.

R8 — QuantConnect MESA Adaptive Moving Average documentation

  • Organization or authors: QuantConnect
  • Source type: Official maintained platform documentation
  • Publication or effective date: Current documentation viewed 2026-07-26
  • Version: LEAN supported-indicator documentation
  • URL or DOI: https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/mesa-adaptive-moving-average
  • Accessed: 2026-07-26
  • Jurisdiction and applicability: Platform documentation; not jurisdiction-specific
  • Evidence role: Sourced platform observation
  • Supports: A maintained public MAMA indicator surface, automatic/manual update workflows, readiness, and named MAMA/FAMA outputs.
  • Limitations: Platform documentation does not replace pinned source for exact coefficient, seed, phase, or output-alignment parity.
  • Publication decision: Use as portability context only; do not assert numerical equivalence without matching the complete contract.

R9 — Canonical package calculations

  • Organization or authors: The Fintech Builder topic package
  • Source type: Author-derived calculation from synthetic teaching input
  • Publication or effective date: 2026-07-26
  • Version: datasets/mama-fixtures.json
  • URL or DOI: Local package artifact
  • Accessed: 2026-07-26
  • Jurisdiction and applicability: Deterministic educational fixture; no market jurisdiction
  • Evidence role: Author-derived calculation and synthetic teaching input
  • Supports: The four 144-observation traces, worked checkpoints, equivalent-EMA spans, MAMA/FAMA gap diagnostics, and cross-language parity.
  • Limitations: Synthetic output proves definition conformance, not market association, prediction, or profitability.
  • Publication decision: Public CC0-style teaching values may be displayed when clearly labeled synthetic and author-derived.

Source and derived boundary

The 144-observation datasets are author-generated synthetic teaching data. Expected arrays are author-derived with the package implementation and were independently checked against TA-Lib Python 0.7.1/core MAMA. No provider market observations, named securities, private evidence, or redistributed historical prices appear in this package.

mama.ts
export const MAMA_LOOKBACK = 32;
export const MAMA_PROCESSING_START = 12;

export type MamaResult = Record<
  | "smooth" | "detrender" | "i1" | "q1" | "i2" | "q2"
  | "re" | "im" | "period" | "phase" | "deltaPhase" | "alpha"
  | "mama" | "fama",
  Array<number | null>
>;

class HilbertStage {
  private buffers = [[[0, 0, 0], [0, 0, 0]]][0];
  private previousInput = [0, 0];
  private previousWeighted = [0, 0];

  step(value: number, parity: number, index: number, factor: number): number {
    const weightedNow = 0.0962 * value;
    let output = -this.buffers[parity][index];
    this.buffers[parity][index] = weightedNow;
    output += weightedNow;
    output -= this.previousWeighted[parity];
    this.previousWeighted[parity] = 0.5769 * this.previousInput[parity];
    output += this.previousWeighted[parity];
    this.previousInput[parity] = value;
    return output * factor;
  }
}

export function mama(
  values: readonly number[],
  fastLimit = 0.5,
  slowLimit = 0.05,
): MamaResult {
  if (!(0.01 <= slowLimit && slowLimit <= fastLimit && fastLimit <= 0.99)) {
    throw new RangeError("limits must satisfy 0.01 <= slowLimit <= fastLimit <= 0.99");
  }
  if (values.some((value) => !Number.isFinite(value))) {
    throw new TypeError("values must contain only finite numbers");
  }

  const names: Array<keyof MamaResult> = [
    "smooth", "detrender", "i1", "q1", "i2", "q2", "re", "im",
    "period", "phase", "deltaPhase", "alpha", "mama", "fama",
  ];
  const result = Object.fromEntries(
    names.map((name) => [name, Array<number | null>(values.length).fill(null)]),
  ) as MamaResult;
  if (values.length <= MAMA_PROCESSING_START) return result;

  const smooth = Array<number>(values.length).fill(0);
  for (let t = 3; t < values.length; t += 1) {
    smooth[t] = (
      4 * values[t] + 3 * values[t - 1] + 2 * values[t - 2] + values[t - 3]
    ) / 10;
    result.smooth[t] = smooth[t];
  }

  const detrenderStage = new HilbertStage();
  const q1Stage = new HilbertStage();
  const jiStage = new HilbertStage();
  const jqStage = new HilbertStage();
  const i1Prev2 = [0, 0];
  const i1Prev3 = [0, 0];
  let hilbertIndex = 0;
  let period = 0, previousI2 = 0, previousQ2 = 0, re = 0, im = 0;
  let mamaValue = 0, famaValue = 0, previousPhase = 0;

  for (let t = MAMA_PROCESSING_START; t < values.length; t += 1) {
    const parity = t % 2;
    const other = 1 - parity;
    const factor = 0.075 * period + 0.54;
    const detrender = detrenderStage.step(smooth[t], parity, hilbertIndex, factor);
    const q1 = q1Stage.step(detrender, parity, hilbertIndex, factor);
    const i1 = i1Prev3[parity];
    const ji = jiStage.step(i1, parity, hilbertIndex, factor);
    const jq = jqStage.step(q1, parity, hilbertIndex, factor);
    const q2 = 0.2 * (q1 + ji) + 0.8 * previousQ2;
    const i2 = 0.2 * (i1 - jq) + 0.8 * previousI2;
    i1Prev3[other] = i1Prev2[other];
    i1Prev2[other] = detrender;
    if (parity === 0) hilbertIndex = (hilbertIndex + 1) % 3;

    const phase = i1 !== 0 ? Math.atan(q1 / i1) * 180 / Math.PI : 0;
    const deltaPhase = Math.max(1, previousPhase - phase);
    previousPhase = phase;
    const alpha = deltaPhase > 1
      ? Math.max(slowLimit, fastLimit / deltaPhase)
      : fastLimit;
    mamaValue = alpha * values[t] + (1 - alpha) * mamaValue;
    const famaAlpha = 0.5 * alpha;
    famaValue = famaAlpha * mamaValue + (1 - famaAlpha) * famaValue;

    re = 0.2 * (i2 * previousI2 + q2 * previousQ2) + 0.8 * re;
    im = 0.2 * (i2 * previousQ2 - q2 * previousI2) + 0.8 * im;
    previousI2 = i2;
    previousQ2 = q2;
    const oldPeriod = period;
    if (im !== 0 && re !== 0) period = 360 / (Math.atan(im / re) * 180 / Math.PI);
    period = Math.min(period, 1.5 * oldPeriod);
    period = Math.max(period, 0.67 * oldPeriod);
    period = Math.min(50, Math.max(6, period));
    period = 0.2 * period + 0.8 * oldPeriod;

    result.detrender[t] = detrender; result.i1[t] = i1; result.q1[t] = q1;
    result.i2[t] = i2; result.q2[t] = q2; result.re[t] = re; result.im[t] = im;
    result.period[t] = period; result.phase[t] = phase;
    result.deltaPhase[t] = deltaPhase; result.alpha[t] = alpha;
    if (t >= MAMA_LOOKBACK) {
      result.mama[t] = mamaValue;
      result.fama[t] = famaValue;
    }
  }
  return result;
}
Full-height labmama playgroundOpen full screen