The logic within this script is designed to create a sophisticated technical indicator that smooths out price data while attempting to minimize "lag"—the delay between a price movement occurring and the indicator reflecting that change.
At its core, the logic functions through several distinct mathematical stages:
Dynamic Adaptation
Instead of using a standard moving average (which treats all past data points equally), this script calculates how much weight to give to recent price changes versus older data. It uses "Phase" and "Length" inputs to determine how aggressively the indicator should react. By calculating various powers and roots based on these inputs, it creates a curve that can be adjusted to be more sensitive or more stable depending on the market conditions.
Signal Filtering
The script employs a unique mechanism to identify and process price momentum. It tracks the difference between current prices and historical values, identifying whether the "swing" in price is increasing or decreasing. This allows the logic to "anticipate" some of the curvature of the trend rather than just trailing behind it.
Complex Smoothing Layers
The indicator does not produce its final line in a single step. It passes the data through multiple layers of processing:
- Initial Filtering: The raw price data is stored in a memory buffer to establish a baseline.
- Momentum Analysis: It calculates the "velocity" of price movement.
- Weighted Smoothing: It applies specific weights to the filtered results to ensure the line remains fluid and doesn't create erratic spikes or "noise."
- Phase Shifting: It uses a secondary calculation to adjust the position of the indicator, aiming to keep it as close to the current price action as possible without being overwhelmed by minor fluctuations.
Final Output
The final result is a smoothed line that represents a balanced middle ground between two extremes:
- A standard moving average (which is very smooth but very slow/laggy).
- A raw price line (which is very fast but very noisy/erratic).
By using this multi-layered mathematical approach, the script provides a visual representation of the trend that aims to be "smart"—reacting quickly to significant shifts in direction while ignoring the minor "jitter" of the market.
//+------------------------------------------------------------------+
//| JMA.mq4 |
//| Weld, Jurik Research |
//| http://weld.torguem.net |
//+------------------------------------------------------------------+
#property copyright "Weld"
#property link "http://weld.torguem.net"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 Aqua
//---- input parameters
extern int Length = 14;
extern int Phase = 0;
//---- buffers
double JMABuffer [];
double fC0Buffer [];
double fA8Buffer [];
double fC8Buffer [];
double list[128], ring1[128], ring2[11], buffer[62];
bool initial;
int s28, s30, s48, s68, s50, s70, fF0, s58, s60, s40, s38, v5, v6, fD8, fE8, fE0;
double vv, v1, v2, v3, v4, s8, s10, s18, s20, f8, f10, f18, f20, f28, f30, f38, f40, f48, f50, f58, f60, f68,
f70, f78, f80, f88, f90, f98, fA0, fB0, fB8, fC0, fD0, fF8, JMA, series;
//+------------------------------------------------------------------+
//| JMA initialization function |
//+------------------------------------------------------------------+
int init()
{
int i;
//---- 2 additional buffers are used for counting.
IndicatorBuffers(4);
//---- drawing settings
SetIndexStyle (0, DRAW_LINE, STYLE_SOLID, 2);
SetIndexDrawBegin(0, 30);
//---- 2 indicator buffers mapping
SetIndexBuffer (0, JMABuffer);
SetIndexBuffer (1, fC0Buffer);
SetIndexBuffer (2, fA8Buffer);
SetIndexBuffer (3, fC8Buffer);
//---- name for DataWindow and indicator subwindow label
IndicatorShortName ("JMA(" + Length + "," + Phase + ")");
SetIndexLabel (0, "JMA");
//---- initial part
s28 = 63; s30 = 64;
for (i = 0; i <= s28; i++) list [i] = -1000000;
for (i = s30; i <= 127; i++) list [i] = 1000000;
initial = true;
if (Length < 1.0000000002) f80 = 0.0000000001;
else f80 = (Length - 1) / 2.0;
if (Phase < -100) f10 = 0.5;
else if (Phase > 100) f10 = 2.5;
else f10 = Phase / 100.0 + 1.5;
v1 = MathLog (MathSqrt (f80));
v2 = v1;
if (v1 / MathLog(2.0) + 2.0 < 0) v3 = 0;
else v3 = v2 / MathLog (2.0) + 2.0;
f98 = v3;
if (0.5 <= f98 - 2.0) f88 = f98 - 2.0;
else f88 = 0.5;
f78 = MathSqrt(f80) * f98;
f90 = f78 / (f78 + 1.0);
f80 = f80 * 0.9;
f50 = f80 / (f80 + 2.0);
return;
}
//+------------------------------------------------------------------+
//| JMA iteration function |
//+------------------------------------------------------------------+
int start()
{
int ii, jj, shift, limit;
int counted_bars = IndicatorCounted();
//---- check for possible errors
if (counted_bars < 0) return (-1);
limit = Bars - counted_bars - 1;
//----
for (shift = limit; shift >= 0; shift--) {
series = Close [shift];
if (fF0 < 61) {
fF0++;
buffer [fF0] = series;
}
if (fF0 > 30) {
if (initial) {
initial = false;
v5 = 0;
for (ii = 1; ii <= 29; ii++) {
if (buffer [ii+1] != buffer[ii]) v5 = 1;
}
fD8 = v5 * 30;
if (fD8 == 0) f38 = series;
else f38 = buffer[1];
f18 = f38;
if (fD8 > 29) fD8 = 29;
} else
fD8 = 0;
//---- big cycle
for (ii = fD8; ii >= 0; ii--) {
if (ii == 0) f8 = series; else f8 = buffer [31 - ii];
f28 = f8 - f18;
f48 = f8 - f38;
if (MathAbs (f28) > MathAbs (f48)) v2 = MathAbs(f28); else v2 = MathAbs(f48);
fA0 = v2;
vv = fA0 + 0.0000000001; //1.0e-10;
if (s48 <= 1) s48 = 127; else s48--;
if (s50 <= 1) s50 = 10; else s50--;
if (s70 < 128) s70++;
s8 += (vv - ring2 [s50]);
ring2 [s50] = vv;
if (s70 > 10) s20 = s8 / 10.0; else s20 = s8 / s70;
if (s70 > 127) {
s10 = ring1 [s48];
ring1 [s48] = s20;
s68 = 64; s58 = s68;
while (s68 > 1) {
if (list [s58] < s10) {
s68 = s68 / 2.0;
s58 += s68;
} else
if (list [s58] <= s10) {
s68 = 1;
} else {
s68 = s68 / 2.0;
s58 -= s68;
}
}
} else {
ring1 [s48] = s20;
if ((s28 + s30) > 127) {
s30--;
s58 = s30;
} else {
s28++;
s58 = s28;
}
if (s28 > 96) s38 = 96; else s38 = s28;
if (s30 < 32) s40 = 32; else s40 = s30;
}
s68 = 64;
s60 = s68;
while (s68 > 1) {
if (list [s60] >= s20) {
if (list [s60 - 1] <= s20) {
s68 = 1;
}
else {
s68 = s68 / 2.0;
s60 -= s68;
}
}
else {
s68 = s68 / 2.0;
s60 += s68;
}
if ((s60 == 127) && (s20 > list[127])) s60 = 128;
}
if (s70 > 127) {
if (s58 >= s60) {
if (((s38 + 1) > s60) && ((s40 - 1) < s60))
s18 += s20;
else if ((s40 > s60) && ((s40 - 1) < s58))
s18 += list [s40 - 1];
}
else if (s40 >= s60) {
if (((s38 + 1) < s60) && ((s38 + 1) > s58))
s18 += list[s38 + 1];
}
else if ((s38 + 2) > s60)
s18 += s20;
else if (((s38 + 1) < s60) && ((s38 + 1) > s58))
s18 += list[s38 + 1];
if (s58 > s60) {
if (((s40 - 1) < s58) && ((s38 + 1) > s58))
s18 -= list [s58];
else if ((s38 < s58) && ((s38 + 1) > s60))
s18 -= list [s38];
}
else {
if (((s38 + 1) > s58) && ((s40 - 1) < s58))
s18 -= list [s58];
else if ((s40 > s58) && (s40 < s60))
s18 -= list [s40];
}
}
if (s58 <= s60) {
if (s58 >= s60) list[s60] = s20; else {
for (jj = s58 + 1; jj <= (s60 - 1); jj++) {
list [jj - 1] = list[jj];
}
list [s60 - 1] = s20;
}
} else {
for (jj = s58 - 1; jj >= s60; jj--) {
list [jj + 1] = list [jj];
}
list[s60] = s20;
}
if (s70 <= 127) {
s18 = 0;
for (jj = s40; jj <= s38; jj++) {
s18 += list[jj];
}
}
f60 = s18 / (s38 - s40 + 1);
//----
if ((fF8 + 1) > 31) fF8 = 31; else fF8++;
if (fF8 <= 30) {
if (f28 > 0) f18 = f8; else f18 = f8 - f28 * f90;
if (f48 < 0) f38 = f8; else f38 = f8 - f48 * f90;
fB8 = series;
if (fF8 == 30) {
fC0Buffer [shift] = series;
if (MathCeil(f78) >= 1) v4 = MathCeil(f78); else v4 = 1;
fE8 = IntPortion (v4);
if (MathFloor(f78) >= 1) v2 = MathFloor(f78); else v2 = 1;
fE0 = IntPortion (v2);
if (fE8 == fE0) f68 = 1;
else {
v4 = fE8 - fE0;
f68 = (f78 - fE0) / v4;
}
if (fE0 <= 29) v5 = fE0; else v5 = 29;
if (fE8 <= 29) v6 = fE8; else v6 = 29;
fA8Buffer [shift] = (series - buffer[fF0 - v5]) * (1 - f68) / fE0 + (series - buffer[fF0 - v6]) * f68 / fE8;
}
}
//----
else {
if (f98 >= MathPow(fA0/f60, f88)) v1 = MathPow (fA0/f60, f88); else v1 = f98;
if (v1 < 1) v2 = 1;
else {
if (f98 >= MathPow (fA0/f60, f88)) v3 = MathPow (fA0/f60, f88); else v3 = f98;
v2 = v3;
}
f58 = v2;
f70 = MathPow (f90, MathSqrt(f58));
if (f28 > 0) f18 = f8; else f18 = f8 - f28 * f70;
if (f48 < 0) f38 = f8; else f38 = f8 - f48 * f70;
}
}
// ---- end of big cycle
if (fF8 > 30) {
fB8 = JMABuffer [shift + 1];
f30 = MathPow (f50, f58);
fC0 = (1 - f30) * series + f30 * fC0Buffer [shift + 1];
fC8Buffer [shift] = (series - fC0) * (1 - f50) + f50 * fC8Buffer [shift + 1];
fD0 = f10 * fC8Buffer [shift] + fC0;
f20 = f30 * (-2.0);
f40 = f30 * f30;
fB0 = f20 + f40 + 1;
fA8Buffer [shift] = (fD0 - fB8) * fB0 + f40 * fA8Buffer [shift + 1];
fB8 += fA8Buffer [shift];
fC0Buffer [shift] = fC0;
}
JMA = fB8;
}
if (fF0 <= 30) JMA = 0;
JMABuffer [shift] = JMA;
//---- End of main cycle "for"
}
return;
}
//+------------------------------------------------------------------+
int IntPortion (double param) {
if (param > 0) return (MathFloor (param));
if (param < 0) return (MathCeil (param));
return (0.0);
}
//+------------------------------------------------------------------+
Comments