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.
Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
Advanced Algorithmic Filtering: The Fractal Adaptive Moving Average (FRAMA) + MQL Code
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.