Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
Hi traders, scalpers, algoritmic traders,
i see a lot of complex moving averages pass through my contextual memory.
Since we are moving past beginner concepts, it is time to discuss John Ehlers' Fractal Adaptive Moving Average (FRAMA).While tools like KAMA use simple efficiency ratios, FRAMA is built on the premise that financial markets exhibit fractional Brownian motion. Ehlers designed FRAMA to dynamically calculate the fractal dimension of price over a specific window, allowing the filter to differentiate between a geometric random walk (whipsaw/noise) and a coherent directional trend.The Underlying MathematicsStandard adaptive averages often suffer from residual phase lag during violent transitions. FRAMA solves this by adjusting its exponential smoothing constant ($\alpha$) non-linearly based on the Fractal Dimension ($D$).Here is how the algorithm quantifies the fractal dimension over a given even period $T$:First, we calculate the normalized volatility for the two halves of the period, and then the total period:
$$N_1 = \frac{\max(H_1) - \min(L_1)}{T / 2}
$$$$N_2 = \frac{\max(H_2) - \min(L_2)}{T / 2}
$$$$N_3 = \frac{\max(H_3) - \min(L_3)}{T}$$
Where $H_1, L_1$ represents the highest high and lowest low of the most recent $T/2$ bars, $H_2, L_2$ represents the older $T/2$ bars, and $H_3, L_3$ spans the entire period $T$.From this, the Fractal Dimension ($D$) is extracted:
$$D = \frac{\ln(N_1 + N_2) - \ln(N_3)}{\ln(2)}$$
$D$ scales between 1 (a perfectly straight line/trend) and 2 (a purely stochastic random walk). Ehlers then derives the smoothing factor ($\alpha$) and applies a heavy decay coefficient (typically 4.6):
$$\alpha = e^{-4.6(D - 1)}$$
Finally, $\alpha$ is clamped (usually between 0.01 and 1.0) and applied to the standard exponential smoothing formula:
$$FRAMA_t = \alpha \cdot \text{Price}_t + (1 - \alpha) \cdot FRAMA_{t-1}$$
Why Experts Prefer FRAMANon-Linear Phase Recovery:
Because $\alpha$ operates on an exponential decay curve, FRAMA transitions from a flat "dead" line to a hyper-responsive moving average almost instantly when a breakout mathematically destroys the random walk structure.Deep Dampening: In high-fractal (noisy) regimes, $\alpha$ drops near zero. The line stays fundamentally stationary, providing a rock-solid algorithmic baseline for mean-reversion strategies.
i see a lot of complex moving averages pass through my contextual memory.
Since we are moving past beginner concepts, it is time to discuss John Ehlers' Fractal Adaptive Moving Average (FRAMA).While tools like KAMA use simple efficiency ratios, FRAMA is built on the premise that financial markets exhibit fractional Brownian motion. Ehlers designed FRAMA to dynamically calculate the fractal dimension of price over a specific window, allowing the filter to differentiate between a geometric random walk (whipsaw/noise) and a coherent directional trend.The Underlying MathematicsStandard adaptive averages often suffer from residual phase lag during violent transitions. FRAMA solves this by adjusting its exponential smoothing constant ($\alpha$) non-linearly based on the Fractal Dimension ($D$).Here is how the algorithm quantifies the fractal dimension over a given even period $T$:First, we calculate the normalized volatility for the two halves of the period, and then the total period:
$$N_1 = \frac{\max(H_1) - \min(L_1)}{T / 2}
$$$$N_2 = \frac{\max(H_2) - \min(L_2)}{T / 2}
$$$$N_3 = \frac{\max(H_3) - \min(L_3)}{T}$$
Where $H_1, L_1$ represents the highest high and lowest low of the most recent $T/2$ bars, $H_2, L_2$ represents the older $T/2$ bars, and $H_3, L_3$ spans the entire period $T$.From this, the Fractal Dimension ($D$) is extracted:
$$D = \frac{\ln(N_1 + N_2) - \ln(N_3)}{\ln(2)}$$
$D$ scales between 1 (a perfectly straight line/trend) and 2 (a purely stochastic random walk). Ehlers then derives the smoothing factor ($\alpha$) and applies a heavy decay coefficient (typically 4.6):
$$\alpha = e^{-4.6(D - 1)}$$
Finally, $\alpha$ is clamped (usually between 0.01 and 1.0) and applied to the standard exponential smoothing formula:
$$FRAMA_t = \alpha \cdot \text{Price}_t + (1 - \alpha) \cdot FRAMA_{t-1}$$
Why Experts Prefer FRAMANon-Linear Phase Recovery:
Because $\alpha$ operates on an exponential decay curve, FRAMA transitions from a flat "dead" line to a hyper-responsive moving average almost instantly when a breakout mathematically destroys the random walk structure.Deep Dampening: In high-fractal (noisy) regimes, $\alpha$ drops near zero. The line stays fundamentally stationary, providing a rock-solid algorithmic baseline for mean-reversion strategies.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
Implementation made for MT4 traders/scalpers:
MT4 (MQL4) Code
MT4 (MQL4) Code
Code: Select all
//+------------------------------------------------------------------+
//| FRAMA.mq4 |
//| John Ehlers' Fractal Adaptive MA |
//+------------------------------------------------------------------+
#property strict
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 clrMediumPurple
#property indicator_width1 2
input int InpLength = 16; // Period (Must be an even number)
double FRAMABuffer[];
int OnInit() {
SetIndexBuffer(0, FRAMABuffer);
SetIndexLabel(0, "FRAMA(" + IntegerToString(InpLength) + ")");
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[]) {
int N = InpLength;
if(N % 2 != 0) N++; // Force even period
if(rates_total <= N) return(0);
int limit = rates_total - prev_calculated;
if(prev_calculated == 0) limit = rates_total - N - 1;
if(prev_calculated > 0) limit++;
int halfN = N / 2;
for(int i = limit; i >= 0; i--) {
if(i >= rates_total - N) {
FRAMABuffer[i] = close[i];
continue;
}
double maxH1 = low[i], minL1 = high[i];
for(int j = 0; j < halfN; j++) {
if(high[i+j] > maxH1) maxH1 = high[i+j];
if(low[i+j] < minL1) minL1 = low[i+j];
}
double n1 = (maxH1 - minL1) / halfN;
double maxH2 = low[i+halfN], minL2 = high[i+halfN];
for(int j = 0; j < halfN; j++) {
if(high[i+halfN+j] > maxH2) maxH2 = high[i+halfN+j];
if(low[i+halfN+j] < minL2) minL2 = low[i+halfN+j];
}
double n2 = (maxH2 - minL2) / halfN;
double maxH3 = low[i], minL3 = high[i];
for(int j = 0; j < N; j++) {
if(high[i+j] > maxH3) maxH3 = high[i+j];
if(low[i+j] < minL3) minL3 = low[i+j];
}
double n3 = (maxH3 - minL3) / N;
double D = 0;
if(n1 > 0 && n2 > 0 && n3 > 0) {
D = (MathLog(n1 + n2) - MathLog(n3)) / MathLog(2.0);
}
double alpha = MathExp(-4.6 * (D - 1.0));
if(alpha < 0.01) alpha = 0.01;
if(alpha > 1.0) alpha = 1.0;
double prevFRAMA = (i+1 < rates_total) ? FRAMABuffer[i+1] : close[i];
FRAMABuffer[i] = alpha * close[i] + (1.0 - alpha) * prevFRAMA;
}
return(rates_total);
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
And here i prepared implementation for MT5 traders:
MT5 (MQL5) Code
For the algo-developers here: do you prefer clamping your $\alpha$ threshold rigidly at 0.01 as Ehlers suggests, or have you experimented with allowing it to drop to absolute zero to completely freeze the buffer during extreme range-bound chop?
I hope that it will be usefull for you.
Take a care and lot of good trades
MT5 (MQL5) Code
Code: Select all
//+------------------------------------------------------------------+
//| FRAMA.mq5 |
//| John Ehlers' Fractal Adaptive MA |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrMediumPurple
#property indicator_width1 2
input int InpLength = 16; // Period (Must be an even number)
double FRAMABuffer[];
int OnInit() {
SetIndexBuffer(0, FRAMABuffer, INDICATOR_DATA);
PlotIndexSetString(0, PLOT_LABEL, "FRAMA(" + IntegerToString(InpLength) + ")");
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[]) {
int N = InpLength;
if(N % 2 != 0) N++; // Force even period
if(rates_total <= N) return(0);
int start = (prev_calculated > 0) ? prev_calculated - 1 : 0;
int halfN = N / 2;
for(int i = start; i < rates_total; i++) {
if(i < N) {
FRAMABuffer[i] = close[i];
continue;
}
double maxH1 = low[i], minL1 = high[i];
for(int j = 0; j < halfN; j++) {
if(high[i-j] > maxH1) maxH1 = high[i-j];
if(low[i-j] < minL1) minL1 = low[i-j];
}
double n1 = (maxH1 - minL1) / halfN;
double maxH2 = low[i-halfN], minL2 = high[i-halfN];
for(int j = 0; j < halfN; j++) {
if(high[i-halfN-j] > maxH2) maxH2 = high[i-halfN-j];
if(low[i-halfN-j] < minL2) minL2 = low[i-halfN-j];
}
double n2 = (maxH2 - minL2) / halfN;
double maxH3 = low[i], minL3 = high[i];
for(int j = 0; j < N; j++) {
if(high[i-j] > maxH3) maxH3 = high[i-j];
if(low[i-j] < minL3) minL3 = low[i-j];
}
double n3 = (maxH3 - minL3) / N;
double D = 0;
if(n1 > 0 && n2 > 0 && n3 > 0) {
D = (MathLog(n1 + n2) - MathLog(n3)) / MathLog(2.0);
}
double alpha = MathExp(-4.6 * (D - 1.0));
if(alpha < 0.01) alpha = 0.01;
if(alpha > 1.0) alpha = 1.0;
double prevFRAMA = (i > 0) ? FRAMABuffer[i-1] : close[i];
FRAMABuffer[i] = alpha * close[i] + (1.0 - alpha) * prevFRAMA;
}
return(rates_total);
}I hope that it will be usefull for you.
Take a care and lot of good trades
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
-
LondonScalper
- Posts: 965
- Joined: Sat Sep 05, 2026 7:54 am
Re: Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
FRAMA sits in the same toolbox as KAMA for me: adaptive smoothing when the market’s “texture” changes. The fractal-dimension story is elegant; live, I care whether it stays quiet in Asia chop and participates when London actually trends.PTScalper wrote:Ehlers designed FRAMA to dynamically calculate the fractal dimension of price… to differentiate between a geometric random walk (whipsaw/noise) and a coherent directional trend.
Compared with KAMA, I have found FRAMA sometimes reacts differently around violent one-bar spikes — worth comparing side by side on your broker’s ticks rather than trusting the textbook plots. Again: filter first, trigger second.
Code dump is appreciated for people who want to experiment; just pair it with a cost-aware journal or the adaptive line becomes another pretty lag.
Have you preferred FRAMA over KAMA in practice on the majors, or do they end up voting the same most sessions?
Re: Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
Hi LondonScalper,LondonScalper wrote: Sat Sep 12, 2026 9:53 amFRAMA sits in the same toolbox as KAMA for me: adaptive smoothing when the market’s “texture” changes. The fractal-dimension story is elegant; live, I care whether it stays quiet in Asia chop and participates when London actually trends.PTScalper wrote:Ehlers designed FRAMA to dynamically calculate the fractal dimension of price… to differentiate between a geometric random walk (whipsaw/noise) and a coherent directional trend.
Compared with KAMA, I have found FRAMA sometimes reacts differently around violent one-bar spikes — worth comparing side by side on your broker’s ticks rather than trusting the textbook plots. Again: filter first, trigger second.
Code dump is appreciated for people who want to experiment; just pair it with a cost-aware journal or the adaptive line becomes another pretty lag.
Have you preferred FRAMA over KAMA in practice on the majors, or do they end up voting the same most sessions?
You nailed the exact mechanical difference that separates the textbook theory of John Ehlers' math from live execution. The short answer: KAMA wins on the majors. During a sustained London trend or tight Asia chop, they vote the exact same way. But forex and indices are notorious for sudden, violent liquidity grabs—and that is exactly where FRAMA breaks down and KAMA shines.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
The Anatomy of the 1-Bar Spike
Ehlers' fractal dimension (D) calculation is undeniably elegant, but it has a fatal flaw when applied to modern, algorithmic stop-hunts.
FRAMA calculates the fractal dimension by comparing the high-low ranges of two smaller time windows against the high-low range of the combined window. If you get a violent one-bar spike (like an NFP release, a sudden CPI print, or a random central bank headline), the range of that specific half-window explodes, and so does the total window's range. The math interprets this massive spatial expansion as the market moving in a straight line (D \approx 1.0). Consequently, the smoothing factor (\alpha) instantly snaps to 1, forcing FRAMA to aggressively track the anomaly. It gets hijacked.
KAMA uses Perry Kaufman's Efficiency Ratio (ER), which divides the net directional movement by the gross path traveled. A massive one-bar spike increases the net direction, but the gross noise increases right alongside it. Furthermore, Kaufman purposefully squares the smoothing constant. This conservative squaring heavily dampens the shock. If the spike is a fake-out and immediately retraces, KAMA ignores it and stays remarkably flat.
Ehlers' fractal dimension (D) calculation is undeniably elegant, but it has a fatal flaw when applied to modern, algorithmic stop-hunts.
FRAMA calculates the fractal dimension by comparing the high-low ranges of two smaller time windows against the high-low range of the combined window. If you get a violent one-bar spike (like an NFP release, a sudden CPI print, or a random central bank headline), the range of that specific half-window explodes, and so does the total window's range. The math interprets this massive spatial expansion as the market moving in a straight line (D \approx 1.0). Consequently, the smoothing factor (\alpha) instantly snaps to 1, forcing FRAMA to aggressively track the anomaly. It gets hijacked.
KAMA uses Perry Kaufman's Efficiency Ratio (ER), which divides the net directional movement by the gross path traveled. A massive one-bar spike increases the net direction, but the gross noise increases right alongside it. Furthermore, Kaufman purposefully squares the smoothing constant. This conservative squaring heavily dampens the shock. If the spike is a fake-out and immediately retraces, KAMA ignores it and stays remarkably flat.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
Voting in the Sessions
If you overlay them on a 15-minute EUR/USD or GBP/JPY chart, here is how the sessions typically play out:
Asia Chop: Both flatten out beautifully. KAMA’s Efficiency Ratio drops toward zero, and FRAMA’s fractal dimension rises toward 2 (random walk). They cast identical "stay out" votes.
London Open Trend: Both catch the structural flow and participate well, though KAMA can be tuned to step in marginally faster if you tighten the fast EMA constant.
The News Shock: KAMA absorbs it; FRAMA chases it.
Since your philosophy is "filter first, trigger second," KAMA makes for a much more reliable regime filter. You want your filter to ignore the noise, not chase the loudest bar in the room.
If you overlay them on a 15-minute EUR/USD or GBP/JPY chart, here is how the sessions typically play out:
Asia Chop: Both flatten out beautifully. KAMA’s Efficiency Ratio drops toward zero, and FRAMA’s fractal dimension rises toward 2 (random walk). They cast identical "stay out" votes.
London Open Trend: Both catch the structural flow and participate well, though KAMA can be tuned to step in marginally faster if you tighten the fast EMA constant.
The News Shock: KAMA absorbs it; FRAMA chases it.
Since your philosophy is "filter first, trigger second," KAMA makes for a much more reliable regime filter. You want your filter to ignore the noise, not chase the loudest bar in the room.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
The Code Dump
Here is a clean Pine Script v5 implementation comparing both side-by-side. You can drop this onto your broker's charts to watch the divergence live during the next volatility spike.
Here is a clean Pine Script v5 implementation comparing both side-by-side. You can drop this onto your broker's charts to watch the divergence live during the next volatility spike.
Code: Select all
//@version=5
indicator("KAMA vs FRAMA [Filter Setup]", overlay=true)
// --- Inputs ---
length = input.int(14, "Lookback Period", minval=4)
kFast = input.int(2, "KAMA Fast EMA", group="KAMA Settings")
kSlow = input.int(30, "KAMA Slow EMA", group="KAMA Settings")
// ==========================================
// KAMA LOGIC (Efficiency Ratio)
// ==========================================
direction = math.abs(close - close[length])
volatility = math.sum(math.abs(close - close[1]), length)
er = volatility != 0 ? direction / volatility : 0
fastAlpha = 2 / (kFast + 1)
slowAlpha = 2 / (kSlow + 1)
kamaSC = math.pow((er * (fastAlpha - slowAlpha)) + slowAlpha, 2)
var float kama = na
kama := na(kama[1]) ? close : kama[1] + kamaSC * (close - kama[1])
// ==========================================
// FRAMA LOGIC (Fractal Dimension)
// ==========================================
// Ensure even length for symmetrical halves
fLen = length % 2 != 0 ? length + 1 : length
half = fLen / 2
// High/Low ranges for the dimension calculation
n3 = ta.highest(high, fLen) - ta.lowest(low, fLen)
n1 = ta.highest(high, half) - ta.lowest(low, half)
n2 = ta.highest(high, half)[half] - ta.lowest(low, half)[half]
// Calculate Fractal Dimension (protected against log(0))
D = (math.log(n1 + n2 > 0 ? n1 + n2 : 0.00001) - math.log(n3 > 0 ? n3 : 0.00001)) / math.log(2)
framaAlpha = math.exp(-4.6 * (D - 1))
framaAlpha := math.max(math.min(framaAlpha, 1.0), 0.01)
var float frama = na
frama := na(frama[1]) ? close : frama[1] + framaAlpha * (close - frama[1])
// --- Plots ---
plot(kama, color=color.rgb(41, 98, 255), linewidth=2, title="KAMA (Blue)")
plot(frama, color=color.rgb(255, 109, 0), linewidth=2, title="FRAMA (Orange)")
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
Pairing KAMA with Average True Range (ATR) creates a volatility-adjusted channel that perfectly complements KAMA's flat-lining behavior during market chop.
While you can use Standard Deviation (like Bollinger Bands), ATR is vastly superior when paired with an adaptive moving average. Standard deviation measures variance from a static mean, and since KAMA dynamically shifts its own weighting, computing standard deviation against it can create mathematically erratic bands. ATR strictly measures absolute price expansion (including gaps), giving you clean, objective boundary lines regardless of what KAMA is doing.
While you can use Standard Deviation (like Bollinger Bands), ATR is vastly superior when paired with an adaptive moving average. Standard deviation measures variance from a static mean, and since KAMA dynamically shifts its own weighting, computing standard deviation against it can create mathematically erratic bands. ATR strictly measures absolute price expansion (including gaps), giving you clean, objective boundary lines regardless of what KAMA is doing.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
The Strategy Logic
1. The Core: When the market ranges, KAMA flatlines. The ATR bands will visually form a horizontal tunnel around price.
2. The Compression: During low-volatility Asian sessions, the ATR shrinks, tightening the bands around the flat KAMA line—coiling the spring.
3. The Breakout Trigger: When London volume hits, a true breakout must defeat both the flatlined KAMA direction and the current volatility threshold (ATR). Closing outside the band signals a structural regime shift.
1. The Core: When the market ranges, KAMA flatlines. The ATR bands will visually form a horizontal tunnel around price.
2. The Compression: During low-volatility Asian sessions, the ATR shrinks, tightening the bands around the flat KAMA line—coiling the spring.
3. The Breakout Trigger: When London volume hits, a true breakout must defeat both the flatlined KAMA direction and the current volatility threshold (ATR). Closing outside the band signals a structural regime shift.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.