After extensive research and development, I’m releasing the Adaptive Composite Moving Average (ACMA), a moving average designed to address the limitations of traditional MAs by combining multiple proven adaptive techniques into a single, configurable indicator.
The goal was to create an MA that reduces whipsaws in ranging markets, responds quickly in trending markets, adapts automatically to changing conditions, and gives traders control over behavior through intuitive parameters.
Theoretical Foundations
ACMA builds upon established concepts from technical analysis literature:
What Makes ACMA Original
While individual components are well-established, ACMA introduces several original elements:
Parameters
Weighting Modes Explained
Recommended Settings
// =============================================
// ADAPTIVE COMPOSITE MOVING AVERAGE (ACMA)
// =============================================
// https://www.prorealcode.com
// Sharing ProRealTime Knowledge
// Version: 1.0
// Author: Nicolas
// Platform: ProRealTime
//
// =============================================
// OVERVIEW
// =============================================
// The ACMA is a moving average that combines multiple proven adaptive techniques
// into a unified, user-configurable indicator. It addresses common MA problems:
//
// • Whipsaws in ranging markets
// • Lag in trending markets
// • Static parameters that don’t adapt to changing conditions
//
// The indicator dynamically adjusts its behavior based on market regime,
// offering four distinct weighting modes to suit different trading styles.
//
// =============================================
// THEORETICAL FOUNDATIONS
// =============================================
// ACMA builds upon several well-established concepts:
//
// 1. EFFICIENCY RATIO (Perry Kaufman, 1995)
// Originally developed for KAMA (Kaufman Adaptive Moving Average)
// ER = Direction / Noise = |Close – Close[n]| / Σ|Close[i] – Close[i+1]|
// Range: 0 (choppy) to 1 (trending)
// Reference: “Trading Systems and Methods” by Perry Kaufman
//
// 2. DISTANCE-BASED WEIGHTING (Gaussian/ALMA concepts)
// Prices closer to the mean receive higher weights
// Similar to Arnaud Legoux Moving Average (ALMA) kernel approach
// Reference: “ALMA” by Arnaud Legoux and Dimitrios Kouzis-Loukas
//
// 3. RECENCY WEIGHTING (Classic WMA/EMA)
// Recent prices receive higher weights than older prices
// Foundation of Weighted Moving Average (WMA) and EMA
// Reference: Standard technical analysis literature
//
// 4. VOLATILITY REGIME ADAPTATION (VIDYA concepts)
// Adapts smoothing based on volatility conditions
// Similar to Tushar Chande’s VIDYA (Variable Index Dynamic Average)
// Reference: “The New Technical Trader” by Tushar Chande
//
// =============================================
// WHAT MAKES ACMA ORIGINAL
// =============================================
// While individual components are established, ACMA’s originality lies in:
//
// 1. COMPOSITE WEIGHTING ARCHITECTURE
// Four selectable weighting modes in a single indicator, allowing traders
// to switch strategies without changing indicators
//
// 2. BLENDED STABILITY FILTER
// Unlike binary hold/update logic, ACMA uses proportional blending:
// – When price and MA diverge with small magnitude, output blends smoothly
// – Eliminates “stair-step” artifacts common in stability filters
// – Formula: output = previous + (current – previous) × blendFactor
//
// 3. REGIME-AWARE MODE (Mode 4)
// Dynamically combines distance and recency weighting based on ER:
// – High ER (trending): Favors recency → faster response
// – Low ER (ranging): Favors mean proximity → noise reduction
// This dual-adaptive approach is not found in standard MAs
//
// 4. UNIFIED SENSITIVITY CONTROL
// Single parameter controls weighting aggressiveness across all modes,
// providing consistent behavior adjustment
//
// =============================================
// PARAMETERS
// =============================================
// LENGTH : Lookback period for calculations (default: 20)
// WEIGHTMODE : Weighting algorithm selection (1-4)
// 1 = Distance-based (prices near SMA weighted higher)
// 2 = Recency-based (recent prices weighted higher)
// 3 = Combined (distance × recency)
// 4 = Regime Adaptive (ER-driven blend)
// SENSITIVITY : Weighting aggressiveness, 1.0-5.0 (default: 2.0)
// Higher = more aggressive weighting
// STABILITYON : Enable stability filter, 0 or 1 (default: 1)
// Reduces whipsaws when price/MA diverge
//
// =============================================
// RECOMMENDED SETTINGS BY TRADING STYLE
// =============================================
// Scalping : LENGTH=10, WEIGHTMODE=2, SENSITIVITY=3.0, STABILITYON=0
// Day Trading : LENGTH=20, WEIGHTMODE=4, SENSITIVITY=2.0, STABILITYON=1
// Swing Trading : LENGTH=30, WEIGHTMODE=3, SENSITIVITY=1.5, STABILITYON=1
// Position : LENGTH=50, WEIGHTMODE=1, SENSITIVITY=1.0, STABILITYON=1
//
// =============================================
// === PARAMETERS ===
LENGTH = 20
WEIGHTMODE = 4
SENSITIVITY = 2.0
STABILITYON = 1
// === CORE CALCULATIONS ===
sma = average[LENGTH](close)
stdev = std[LENGTH](close)
IF stdev = 0 THEN
stdev = 0.0001
ENDIF
// === EFFICIENCY RATIO ===
// Measures trend strength: 0 = choppy/ranging, 1 = strong trend
// Formula from Kaufman’s Adaptive Moving Average (KAMA)
direction = ABS(close – close[LENGTH])
noise = 0
FOR i = 0 TO LENGTH – 2
noise = noise + ABS(close[i] – close[i + 1])
NEXT
IF noise > 0 THEN
efficiencyRatio = direction / noise
ELSE
efficiencyRatio = 0.5
ENDIF
// === WEIGHTED MOVING AVERAGE CALCULATION ===
sumWeights = 0
weightedSum = 0
FOR i = 0 TO LENGTH – 1
currentPrice = close[i]
// Calculate normalized distance from mean
distance = ABS(currentPrice – sma) / stdev
IF distance < 0.1 THEN
distance = 0.1
ENDIF
// === WEIGHTING MODE SELECTION ===
IF WEIGHTMODE = 1 THEN
// ─────────────────────────────────────────────────
// MODE 1: DISTANCE-BASED
// Concept: Prices closer to SMA are more “reliable”
// Effect: Smooths outliers, stable in ranges
// ─────────────────────────────────────────────────
weight = POW(1 / distance, SENSITIVITY)
ELSIF WEIGHTMODE = 2 THEN
// ─────────────────────────────────────────────────
// MODE 2: RECENCY-BASED
// Concept: Recent prices are more relevant
// Effect: Reactive, follows price closely
// ─────────────────────────────────────────────────
weight = POW(LENGTH – i, SENSITIVITY)
ELSIF WEIGHTMODE = 3 THEN
// ─────────────────────────────────────────────────
// MODE 3: COMBINED
// Concept: Balance proximity and recency
// Effect: Moderate reactivity with noise filtering
// ─────────────────────────────────────────────────
distWeight = POW(1 / distance, SENSITIVITY / 2)
recWeight = POW(LENGTH – i, SENSITIVITY / 2)
weight = distWeight * recWeight
ELSE
// ─────────────────────────────────────────────────
// MODE 4: REGIME ADAPTIVE (Recommended)
// Concept: Let market conditions drive the blend
// – Trending (high ER): Favor recency
// – Ranging (low ER): Favor mean proximity
// Effect: Self-adjusting to market regime
// ─────────────────────────────────────────────────
trendWeight = POW(LENGTH – i, SENSITIVITY * efficiencyRatio)
meanWeight = POW(1 / distance, SENSITIVITY * (1 – efficiencyRatio))
weight = trendWeight * meanWeight
ENDIF
weightedSum = weightedSum + (currentPrice * weight)
sumWeights = sumWeights + weight
NEXT
IF sumWeights > 0 THEN
acmaRaw = weightedSum / sumWeights
ELSE
acmaRaw = sma
ENDIF
// === STABILITY FILTER ===
// Reduces whipsaws when price and MA move in opposite directions
// Uses blended transition instead of hard switching to avoid stair-steps
IF STABILITYON = 1 THEN
priceChange = close – close[1]
acmaChange = acmaRaw – acmaRaw[1]
// Adaptive threshold: tighter in trends, looser in ranges
stabilityThreshold = 0.1 + (0.3 * (1 – efficiencyRatio))
IF acmaRaw[1] <> 0 THEN
relativeChange = ABS(acmaChange / acmaRaw[1])
ELSE
relativeChange = 0
ENDIF
// Detect divergence: price and MA moving opposite directions
isDiverging = (priceChange > 0 AND acmaChange < 0) OR (priceChange < 0 AND acmaChange > 0)
IF isDiverging AND relativeChange < stabilityThreshold THEN
// Smooth blend instead of hard hold
blendFactor = relativeChange / stabilityThreshold
acma = acmaRaw[1] + (acmaRaw – acmaRaw[1]) * blendFactor
ELSE
acma = acmaRaw
ENDIF
ELSE
acma = acmaRaw
ENDIF
// === OUTPUT ===
RETURN acma AS “ACMA”
Feedback Welcome
This is version 1.0. I’m actively developing this indicator and welcome feedback from the community. If you have suggestions or find issues, please comment below.