McGinley Dynamic

Haiyue
12min

I. What is McGinley Dynamic

The McGinley Dynamic (MD) is an adaptive moving average that automatically adjusts its tracking speed based on market velocity. It was invented in 1990 by American technical analyst John R. McGinley and published in the Journal of Technical Analysis.

Historical Background

Through extensive research on moving averages, McGinley identified a core problem: no matter which fixed-period moving average you choose, it is always too fast or too slow under certain market conditions. Market speed (volatility and trend strength) is dynamic, but traditional moving average parameters are fixed.

To address this, McGinley designed a moving average that can “sense” market speed and adjust automatically. When the market moves rapidly, MD accelerates tracking; when the market moves slowly or consolidates, MD decelerates to avoid false signals. This adaptive behavior is achieved through the (P/MD)4(P/MD)^4 term in the formula.

Indicator Classification

  • Type: Overlay indicator, plotted on the price chart
  • Category: Moving Averages, adaptive subcategory
  • Default Parameters: Period n=14n = 14, adjustment coefficient k=0.6k = 0.6, data source is closing price (Close)
McGinley’s Description

McGinley himself described his indicator as “a smoothing mechanism that happens to track prices far more closely than any moving average.” He argued that MD should not be viewed as a moving average, but rather as a “price smoothing tool.”


II. Mathematical Principles and Calculation

Core Formula

The recursive formula for McGinley Dynamic is:

MDt=MDt1+PtMDt1kn(PtMDt1)4MD_t = MD_{t-1} + \frac{P_t - MD_{t-1}}{k \cdot n \cdot \left(\frac{P_t}{MD_{t-1}}\right)^4}

Where:

  • PtP_t: Price at period tt (closing price)
  • MDt1MD_{t-1}: McGinley Dynamic value from the previous period
  • nn: Period parameter (default 14)
  • kk: Adjustment constant (default 0.6, as recommended by McGinley in his paper)

The initial value is typically set to the first price: MD0=P0MD_0 = P_0.

Adaptive Mechanism Explained

The key to the formula lies in the denominator’s (PtMDt1)4\left(\frac{P_t}{MD_{t-1}}\right)^4 term. Let us analyze its behavior:

When price rises rapidly (PtMDt1P_t \gg MD_{t-1}):

  • Pt/MDt1>1P_t / MD_{t-1} > 1, and this ratio raised to the 4th power is large
  • The denominator kn(Pt/MDt1)4k \cdot n \cdot (P_t/MD_{t-1})^4 increases
  • The update step PtMDt1denominator\frac{P_t - MD_{t-1}}{\text{denominator}} decreases relatively
  • But since the numerator (PtMDt1)(P_t - MD_{t-1}) is also large, the net effect is that MD accelerates upward

When price drops rapidly (PtMDt1P_t \ll MD_{t-1}):

  • Pt/MDt1<1P_t / MD_{t-1} < 1, and this ratio raised to the 4th power is small
  • The denominator kn(Pt/MDt1)4k \cdot n \cdot (P_t/MD_{t-1})^4 decreases
  • The update step PtMDt1denominator\frac{P_t - MD_{t-1}}{\text{denominator}} increases relatively
  • The negative numerator combined with the small denominator causes MD to drop rapidly

When price is near MD (PtMDt1P_t \approx MD_{t-1}):

  • Pt/MDt11P_t / MD_{t-1} \approx 1, the 4th power term is approximately 1
  • The denominator is approximately knk \cdot n
  • Behavior resembles an EMA with period knk \cdot n
Why the 4th Power

McGinley determined through experimentation that the 4th power provides the optimal adaptive effect. Lower powers (such as squared) provide insufficient adjustment, while higher powers (such as 6th power) overreact. The 4th power is the empirically selected optimum.

Role of Parameter kk

k=0.6k = 0.6 is McGinley’s recommended value:

  • Smaller kk -> MD responds faster
  • Larger kk -> MD responds slower
  • k=0.6k = 0.6 provides a good balance in most market environments

Comparison with EMA

Writing EMA in a similar recursive form:

EMAt=EMAt1+PtEMAt1n+12EMA_t = EMA_{t-1} + \frac{P_t - EMA_{t-1}}{\frac{n+1}{2}}

Compare with MD:

MDt=MDt1+PtMDt1kn(PtMDt1)4MD_t = MD_{t-1} + \frac{P_t - MD_{t-1}}{k \cdot n \cdot \left(\frac{P_t}{MD_{t-1}}\right)^4}

The difference is that MD’s “effective period” kn(Pt/MDt1)4k \cdot n \cdot (P_t/MD_{t-1})^4 is dynamic, while EMA’s effective period n+12\frac{n+1}{2} is fixed.


III. Python Implementation

import numpy as np
import pandas as pd

def mcginley_dynamic(close: pd.Series, period: int = 14,
                     k: float = 0.6) -> pd.Series:
    """
    Calculate the McGinley Dynamic indicator

    Parameters
    ----------
    close : pd.Series
        Closing price series
    period : int
        Period parameter, default is 14
    k : float
        Adjustment coefficient, default is 0.6

    Returns
    -------
    pd.Series
        McGinley Dynamic value series
    """
    values = close.values.astype(float)
    n = len(values)
    result = np.full(n, np.nan)

    # Initial value
    result[0] = values[0]

    for i in range(1, n):
        md_prev = result[i - 1]
        p = values[i]

        if md_prev == 0 or np.isnan(md_prev):
            result[i] = p
            continue

        ratio = p / md_prev
        denominator = k * period * (ratio ** 4)

        if denominator == 0:
            result[i] = p
        else:
            result[i] = md_prev + (p - md_prev) / denominator

    return pd.Series(result, index=close.index, name=f"MD_{period}")


def mcginley_dynamic_numpy(close: np.ndarray, period: int = 14,
                           k: float = 0.6) -> np.ndarray:
    """
    McGinley Dynamic implementation using numpy (low-level version)
    """
    n = len(close)
    result = np.full(n, np.nan)
    result[0] = close[0]

    for i in range(1, n):
        md = result[i - 1]
        p = close[i]

        if md <= 0 or np.isnan(md):
            result[i] = p
            continue

        ratio = p / md
        denom = k * period * (ratio ** 4)
        result[i] = md + (p - md) / max(denom, 1e-10)

    return result


# ========== Usage Example ==========
if __name__ == "__main__":
    np.random.seed(42)
    dates = pd.date_range("2024-01-01", periods=150, freq="D")

    # Construct a price series with trends and consolidation
    trend = np.concatenate([
        np.linspace(0, 10, 50),    # Uptrend
        np.linspace(10, 8, 30),     # Slow pullback
        np.random.randn(30) * 0.3,  # Consolidation
        np.linspace(0, -8, 40),     # Downtrend
    ])
    price = 100 + trend + np.random.randn(150) * 0.5

    df = pd.DataFrame({
        "date": dates,
        "open":  price + np.random.randn(150) * 0.2,
        "high":  price + np.abs(np.random.randn(150) * 0.5),
        "low":   price - np.abs(np.random.randn(150) * 0.5),
        "close": price,
        "volume": np.random.randint(1000, 10000, size=150),
    })
    df.set_index("date", inplace=True)

    # MD compared with EMA/SMA
    df["SMA_14"]  = df["close"].rolling(14).mean()
    df["EMA_14"]  = df["close"].ewm(span=14, adjust=False).mean()
    df["MD_14"]   = mcginley_dynamic(df["close"], period=14, k=0.6)

    print("=== SMA vs EMA vs McGinley Dynamic Comparison ===")
    print(df[["close", "SMA_14", "EMA_14", "MD_14"]].tail(15))

    # Compare different k values
    df["MD_k04"] = mcginley_dynamic(df["close"], 14, k=0.4)
    df["MD_k06"] = mcginley_dynamic(df["close"], 14, k=0.6)
    df["MD_k10"] = mcginley_dynamic(df["close"], 14, k=1.0)

    print("\n=== McGinley Dynamic with Different k Values ===")
    print(df[["close", "MD_k04", "MD_k06", "MD_k10"]].tail(10))

    # Trend signal
    df["md_signal"] = np.where(df["close"] > df["MD_14"], "LONG", "SHORT")
    print("\n=== MD Trend Signal ===")
    print(df[["close", "MD_14", "md_signal"]].tail(10))

IV. Problems the Indicator Solves

1. Fundamental Flaw of Fixed-Period Moving Averages

The core dilemma faced by traditional moving averages:

  • Short period -> Performs well in trends, but generates many false signals during consolidation
  • Long period -> Fewer false signals during consolidation, but lags significantly in trends

MD simultaneously addresses both problems through its adaptive mechanism: automatically accelerating in trends (effectively shorter period) and automatically decelerating during consolidation (effectively longer period).

2. Eliminating Whipsaws

In ranging markets, price frequently crosses moving averages, creating whipsaw signals. Since MD automatically slows down during consolidation and stays close to price, it dramatically reduces these false crossovers.

3. Adaptive Trailing Stop

MD serves as an excellent trailing stop-loss line:

  • Closely follows price during trending moves
  • Remains stable during price consolidation
  • No manual parameter adjustment needed

4. Trend Filter

Using MD as a trend filter:

  • Price above MD -> Long only
  • Price below MD -> Short only

Due to MD’s adaptive nature, this filtering is more reliable than a fixed EMA.

Unique Advantage

MD’s greatest advantage is “set it and forget it.” Traditional moving averages often require period parameter adjustments across different market environments, whereas MD’s adaptive mechanism automatically handles this adjustment.


V. Advantages, Disadvantages, and Use Cases

Advantages

AdvantageDescription
Adaptive speedAutomatically adjusts tracking speed based on market conditions
Reduced whipsawsProduces very few false crossover signals in ranging markets
Tight price trackingFollows current price more closely than same-period EMA
Robust parametersDefault parameters perform well across most markets without frequent tuning

Disadvantages

DisadvantageDescription
AsymmetryDue to the nonlinear (P/MD)4(P/MD)^4 term, behavior during up and down moves is not perfectly symmetric
Non-standardNot built into all trading platforms
Opaque theoryThe 4th power and k=0.6k=0.6 choices are based more on empirical testing than rigorous derivation
Hard to combineUnlike EMA, not easily used as a building component for other indicators

Use Cases

  • Trend following: As a primary trend identification tool
  • Trailing stop-loss: As an adaptive stop-loss reference line
  • Multi-market strategies: A single set of parameters works across multiple markets, reducing optimization effort
  • Medium to long-term trading: Performs best on daily and higher timeframes

Comparison with Other Adaptive Moving Averages

FeatureMDKAMAFRAMA
Adaptation basisPrice/MA ratioEfficiency ratioFractal dimension
Formula complexitySimpleMediumHigh
Number of parameters2 (n, k)3 (n, fast, slow)2 (n, batch)
SmoothnessHighHighMedium
PopularityMediumHigherLow
Notes
  1. MD’s denominator contains (P/MD)4(P/MD)^4. When price approaches zero or MD approaches zero, numerical instability may occur. Add protective logic when handling low-priced stocks or cryptocurrencies.
  2. MD is not suitable for smoothing oscillator-type indicators (such as RSI), because its adaptive mechanism is designed for trending prices.
  3. The choice of initial value (MD0=P0MD_0 = P_0) affects the first few periods of calculation, but the impact vanishes after sufficient data.