# D04-F02-A05 — McClellan Volume Oscillator: Compare fast and slow exponential states of daily up-volume minus down-volume

> **Domain:** D04 — Market Breadth and Internals  
> **Family:** D04-F02 — McClellan Family  
> **Status:** Video-ready  
> **Last reviewed:** 2026-07-25

## Executive summary

McClellan Volume Oscillator transforms ordered daily breadth in three explicit stages:

\[
v_t=UV_t-DV_t
\]

\[
F_t=0.10v_t+0.90F_{t-1},\qquad
S_t=0.05v_t+0.95S_{t-1}
\]

\[
MVO_t=F^{(v)}_t-S^{(v)}_t
\]

The 10% and 5% trends correspond to conventional EMA labels 19 and 39. This package uses simple-average seeds at observations 19 and 39, emits its first oscillator at observation 39, preserves recursive state without rounding, and resolves source revisions at an explicit causal cutoff.

## Problem and financial relevance

Daily breadth can be noisy and its scale can change. This topic asks whether recent raw up-down volume is running above or below its slower baseline. It measures a defined transformation and state path; it does not prove prediction, market causation, or profitability.

## Learning objectives

The reader can:

- calculate the exact input transformation and explain its units;
- reproduce the 10%/5% dual-EMA warm-up;
- distinguish current input sign from oscillator sign;
- apply the documented cumulative seed when present;
- reject missing, provisional, cancelled, conflicting, or incompatible evidence;
- recompute a corrected suffix without leaking a future revision;
- distinguish this topic from the other five McClellan-family variants.

## Prerequisites

- Net Advances
- Exponential Moving Average
- Traditional McClellan Oscillator
- Ordered time series, revisions, and simple averages

## Scope and variant contract

The canonical package uses raw reported share volume in one declared unit and adjustment basis. It does not ratio-adjust daily volume or mix venues, currencies, or units.

Excluded: alternative EMA lengths, first-value seeding, silent interpolation, vendor-specific thresholds, divergence signals, ratio-adjusted volume unless named, and any performance promise.

| Nearby variant | Different input or state | Why it is not interchangeable |
|---|---|---|
| Traditional issue-count oscillator | Raw A-D | Magnitude changes with the issue population. |
| Ratio-adjusted issue oscillator | 1000(A-D)/(A+D) | Normalization changes units and zero-denominator behavior. |
| Traditional Summation Index | Cumulative raw-count oscillator | Adds a path-dependent state and seed. |
| RASI | Cumulative ratio-adjusted oscillator | Uses normalized input and a zero neutral seed. |
| Volume Oscillator | Up Volume minus Down Volume | Measures allocated share volume, not equal-vote issue counts. |
| Volume Summation Index | Cumulative volume oscillator | Adds volume units, cumulative state, and a zero seed. |

## Input data contract

Each record contains a unique `session_sequence`, `session_date`, `effective_at`, `available_at`, non-negative `revision`, `event_type`, `source_evidence_state`, stable series identity, and the required breadth fields. Revisions after the caller's cutoff are ignored. Retained revisions must start at zero, remain contiguous, and have one unique head.

For volume topics, `up_volume`, `down_volume`, and `unchanged_volume` are non-negative shares and must sum exactly to positive `total_volume`. `volume_unit` and `volume_adjustment_basis` must remain constant. A venue, unit, or adjustment-basis change creates a new series.

A validated zero signed input is a real observation; a missing session is not silently replaced with zero.

## Parameters and initialization

| Parameter | Canonical value | Meaning |
|---|---:|---|
| Fast smoothing constant | 0.10 | 10% Trend, conventional EMA-19 |
| Slow smoothing constant | 0.05 | 5% Trend, conventional EMA-39 |
| Fast seed | SMA of observations 1–19 | First fast state |
| Slow seed | SMA of observations 1–39 | First slow state |
| First oscillator | Observation 39 | Both states exist |
| Cumulative starting level | not applicable | State immediately before the first ready oscillator |
| Rounding | Display only | Recursive state remains full precision |

This topic emits the oscillator only. Cumulative state belongs to the neighboring Summation Index topic.

## Output contract

Every resolved record produces `input_value`, `fast_ema`, `slow_ema`, `oscillator`, optional `index_value`, `status`, observation number, and session date. Status is `warming_fast`, `warming_slow`, or `ready`.

The enclosing result is `resolved`, `incomplete`, `ambiguous`, or `unsupported`. Non-resolved results expose no ordinary points, preventing a partial path from looking authoritative.

## Mathematical formulation and intuition

The exponential update is:

\[
EMA_t=EMA_{t-1}+\alpha(z_t-EMA_{t-1})
\]

The fast state closes 10% of its forecast error each observation; the slow state closes 5%. Their difference is positive when recent breadth is above its slower state. Older data never drops out exactly.

Corrections propagate through both EMA states and every later oscillator because exponential memory never becomes exactly zero.

## Algorithm

1. Apply the explicit `available_at <= cutoff` rule.
2. Resolve one contiguous revision head for every session sequence.
3. Reject cancellation, provisional evidence, duplicate revisions, gaps, or series-identity breaks atomically.
4. Transform each resolved record using the topic-specific formula.
5. Seed the fast EMA with observations 1–19 and the slow EMA with observations 1–39.
6. Emit the oscillator from observation 39.
7. Keep cumulative state out of this topic.
8. Preserve unrounded state and provenance.

## Pseudocode

```text
resolve revisions available at cutoff
if evidence is incomplete, ambiguous, cancelled, or incompatible:
    return atomic non-resolved result with no points

transform each accepted record into z
fast = SMA(z[1..19]) at observation 19
slow = SMA(z[1..39]) at observation 39
for every later observation:
    fast = fast + 0.10 * (z - fast)
    slow = slow + 0.05 * (z - slow)
oscillator = fast - slow
emit oscillator without cumulative index state
```

## Synthetic worked example

The fixture contains 60 deterministic weekday sessions. The first 39 are balanced, so both EMA seeds and the first oscillator are zero.

| Point | Input | Fast EMA | Slow EMA | Oscillator | Index |
|---|---:|---:|---:|---:|---:|
| First ready, observation 39 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | — |
| Final, observation 60 | -900000.000000 | 102760.965516 | 94102.471414 | 8658.494101 | — |

Exact inputs and expected values are in [the shared fixture](./datasets/shared-fixture.json). The values are synthetic teaching inputs and author-derived calculations, not market observations.

## Choose this variant when

Choose this oscillator when allocated share volume—not equal-vote issue counts—is the intended breadth unit.

One high-volume issue can dominate the daily input; this is a feature of the volume unit, not a bug to hide. The family decision is therefore not “which line looks better?” It is “which observation unit and which state contract answer the question?”

| Decision | This topic | Choose a sibling when… |
|---|---|---|
| Daily unit | reported shares | the required observation unit differs |
| Output memory | dual EMA oscillator only | the task needs a cumulative path |
| Source-scale behavior | Doubling every allocated volume doubles the raw volume input, both EMA states, and the oscillator. | a different scale policy is required |
| Invalid evidence | The volume partition is empty or does not reconcile, so the input is not a valid zero. | the source cannot satisfy this contract |

## Nonzero guided-lab path

The implementation fixture remains deliberately simple for exact cross-language checks. The guided lab uses a separate 60-session synthetic teaching path so that the first ready screen is informative rather than empty.

| Observation | Regime | Input | Fast | Slow | Oscillator | Index |
|---:|---|---:|---:|---:|---:|---:|
| 39 | reversal | 800,000 | 36,478.0323 | 225,641 | -189,163 | — |
| 45 | stress and recovery | -800,000 | 345,441 | 369,243 | -23,802.2724 | — |
| 60 | stress and recovery | 200,000 | 124,050 | 191,039 | -66,989.0970 | — |

At observation 45, changing the source scale from 1× to 2× changes the oscillator from -23,802.2724 to -47,604.5449. Doubling every allocated volume doubles the raw volume input, both EMA states, and the oscillator.

The records and independently derived checkpoints are stored in [playground-scenarios.json](./datasets/playground-scenarios.json). They are synthetic and are not used as empirical market evidence.

## Implementation and tests

- [Python reference](./implementations/python/)
- [TypeScript reference](./implementations/typescript/)
- [Python tests](./tests/)
- [Fixture methodology](./datasets/README.md)
- [Guided-lab teaching scenarios](./datasets/playground-scenarios.json)
- [Guided-lab teaching scenarios](./datasets/playground-scenarios.json)
- [Guided-lab teaching scenarios](./datasets/playground-scenarios.json)
- [Guided-lab teaching scenarios](./datasets/playground-scenarios.json)
- [Guided-lab teaching scenarios](./datasets/playground-scenarios.json)
- [Guided-lab teaching scenarios](./datasets/playground-scenarios.json)
- [Independent worked arithmetic](./examples/worked-example.json)

Tests cover warm-up, exact fixture parity, scaling, cumulative recurrence, zero or invalid partitions, missing sequences, duplicate revisions, future-revision cutoff behavior, and series identity breaks.

## Source-role boundary

The first-party methodology supports the transform and sequence of operations; the SMA initialization, software statuses, synthetic path, and source-scale lab are package choices. No interpretive threshold from a methodology article is treated as universal evidence.

## Historical-example decision: deferred

A named historical example could improve context, but a reproducible case needs the exact venue universe, final issue or volume allocation, revisions, source timestamp, volume adjustment basis, cumulative calibration history, and redistribution permission. The first-party methodology does not supply all of those fields as one reusable dataset. This package therefore uses a visibly synthetic fixture and does not invent a historical value.

## Edge cases and limitations

- A missing observation is not a zero input.
- SMA and first-value EMA seeds differ early in a series.
- Changing universe or volume methodology requires a series break.
- Cumulative levels depend on the stated starting constant.
- A corrected historical input changes the entire dependent suffix.
- Raw volume and raw issue-count magnitudes are not stable across all eras.
- Ratio adjustment improves issue-count scale comparability but does not make different universes equivalent.
- Formula correctness is not evidence of a profitable signal.

## Visual explanations

- [Calculation flow](./visuals/mermaid/calculation-flow.md)
- [Family transform](./visuals/static/family-transform.svg)
- [Worked state path](./visuals/static/worked-state.svg)
- [Guided playground](./visuals/animated/playground.html)
- [Visual plan](./visuals/VISUAL-PLAN.md)
- [Visual QA record](./visuals/QA.md)

## Related catalog topics

- Net Advances supplies issue-count breadth.
- EMA supplies the recursive smoothing contract.
- Traditional McClellan Oscillator is the family foundation.
- McClellan Volume Summation Index is the next handoff.

## References

See [REFERENCES.md](./REFERENCES.md). Sourced formulas, author choices, synthetic inputs, and derived arithmetic are kept separate.
