one of the biggest dilemmas in technical analysis is choosing a moving average length: short periods generate excessive false signals in choppy markets, while long periods lag too far behind when a breakout happens.Developed by Perry Kaufman in 1995, Kaufman’s Adaptive Moving Average (KAMA) solves this problem dynamically. Instead of sticking to a fixed speed, KAMA continuously tracks market noise and automatically speeds up during strong trends and slows down during consolidation.How KAMA Adapts to VolatilityKAMA uses an Efficiency Ratio ($ER$) that measures the ratio of directional price change relative to total market volatility over a set period $n$ (default is 10):
$$ER_t = \frac{\vert{}\text{Price}_t - \text{Price}_{t-n}\vert{}}{\sum_{i=0}^{n-1} \vert{}\text{Price}_{t-i} - \text{Price}_{t-i-1}\vert{}}$$
Trending Market ($ER \approx 1$): Price moves directly in one direction with minimal pullbacks. KAMA speeds up and acts like a fast 2-period EMA.Noisy / Choppy Market ($ER \approx 0$): Price whipsaws sideways with little net progress. KAMA slows down and acts like a slow 30-period EMA.The calculated $ER$ is then converted into a dynamic Smoothing Constant ($SC$):
$$SC_t = \left[ ER_t \cdot \left( \frac{2}{\text{Fast}+1} - \frac{2}{\text{Slow}+1} \right) + \frac{2}{\text{Slow}+1} \right]^2
$$$$KAMA_t = KAMA_{t-1} + SC_t \cdot (\text{Price}_t - KAMA_{t-1})$$
Practical Ways to Trade KAMA
Trend Slope Filter: When KAMA has a clear upward slope, favor long trades; when sloping downward, favor shorts.
The Flat-Line Pause: When KAMA turns completely horizontal, the market is ranging. Use this as a direct signal to avoid breakout entries.
Dynamic Trailing Stop: Use the KAMA line as a trailing stop-loss level that tightens automatically when the trend accelerates and widens during pullbacks.
MT4 (MQL4) Implementation
Code: Select all
//+------------------------------------------------------------------+
//| KAMA.mq4 |
//| Kaufman Adaptive Moving Average for MetaTrader|
//+------------------------------------------------------------------+
#property copyright "Forum Community"
#property link ""
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 clrDarkOrange
#property indicator_width1 2
input int InpPeriodER = 10; // Efficiency Ratio Period
input int InpFastPeriod = 2; // Fast EMA Period
input int InpSlowPeriod = 30; // Slow EMA Period
double KAMABuffer[];
int OnInit() {
SetIndexBuffer(0, KAMABuffer);
SetIndexStyle(0, DRAW_LINE);
SetIndexLabel(0, "KAMA(" + IntegerToString(InpPeriodER) + ")");
return(INIT_SUCCEEDED);
}
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[]) {
if(rates_total <= InpPeriodER) return(0);
int limit = rates_total - prev_calculated;
if(prev_calculated == 0) limit = rates_total - InpPeriodER - 1;
if(prev_calculated > 0) limit++;
double fastSC = 2.0 / (InpFastPeriod + 1.0);
double slowSC = 2.0 / (InpSlowPeriod + 1.0);
for(int i = limit; i >= 0; i--) {
if(i >= rates_total - InpPeriodER) {
KAMABuffer[i] = close[i];
continue;
}
double change = MathAbs(close[i] - close[i + InpPeriodER]);
double volatility = 0.0;
for(int j = 0; j < InpPeriodER; j++) {
volatility += MathAbs(close[i + j] - close[i + j + 1]);
}
double er = (volatility > 0.0) ? (change / volatility) : 0.0;
double sc = MathPow(er * (fastSC - slowSC) + slowSC, 2);
double prevKAMA = (i + 1 < rates_total) ? KAMABuffer[i + 1] : close[i];
KAMABuffer[i] = prevKAMA + sc * (close[i] - prevKAMA);
}
return(rates_total);
}