This script implements a sophisticated technical indicator known as the Jurik Moving Average (JMA). Unlike standard moving averages that often suffer from significant "lag" (delaying the visual representation of price action) or "overshoot" (creating jagged, erratic lines), this logic is engineered to provide a smooth, responsive curve that stays as close to the price movement as possible while filtering out market noise.
Core Logic Overview
The script operates by processing historical price data through several distinct mathematical layers. Instead of simply averaging the last few prices, it uses a multi-stage calculation to determine the current "smooth" value:
1. Dynamic Parameter Initialization Upon loading, the script establishes its core mathematical constants based on user inputs (Length and Phase). It calculates specific multipliers involving logarithms and square roots. These variables dictate how aggressively the indicator reacts to new price changes versus how much weight it gives to historical data.
2. Price Deviation Tracking The script maintains a series of internal "buffers" (temporary memory) to keep track of recent price trends. It identifies the difference between the current price and previous price points to calculate "absolute values." By measuring these deviations, the indicator can sense the momentum of the market—determining if a move is an impulsive breakout or a minor fluctuation.
3. Signal Filtering & Smoothing (The "Big Cycle") This is the most complex part of the logic. The script employs several specialized mechanisms:
- Logarithmic Scaling: It uses power functions to adjust how much weight a specific price change carries. If a price move is very large, the indicator adjusts its sensitivity to ensure the line doesn't "snap" violently or lag too far behind.
- Adaptive Filtering: The logic continuously updates internal variables (
paramAandparamB) that act like "shadow" prices. These shadows follow the actual price but are smoothed out, allowing the script to compare current reality against a smoothed historical baseline. - Recursive Processing: The final output is refined through a recursive calculation where each new point depends on both the current price and the previously calculated value of the indicator itself.
4. Phase and Smoothing Adjustments The "Phase" input allows the user to fine-tune the "tightness" of the line. The logic applies this phase to the final calculation, essentially allowing you to choose between a very fast, reactive line (high sensitivity) or a slow, sweeping curve (high stability).
Summary for the User
In non-technical terms, this script acts like a smart filter. It analyzes where the price is going by looking at how it moved in the past, calculates the "momentum" of that move using complex math to ignore "fake" noise, and then draws a smooth line on your chart. The goal is to provide you with a visual representation of the trend that responds quickly to real changes while remaining steady enough to be readable during volatile trading periods.
//+------------------------------------------------------------------+
//| JMA.mq4 |
//| Weld, Jurik Research |
//| http://weld.torguem.net |
//+------------------------------------------------------------------+
#property copyright "Weld"
#property link "http://weld.torguem.net"
//I'm just intresting in 11 JMA and 8 JMA on 15M charts. No trading
//activity just alert.
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 Aqua
//---- input parameters
extern int Length = 14;
extern int Phase = 0;
//---- buffers
double JMAValueBuffer [];
double fC0Buffer [];
double fA8Buffer [];
double fC8Buffer [];
//---- temporary buffers
double list[128], ring1[128], ring2[11], buffer[62];
//---- bool flag
bool initFlag;
//---- integer vars
int limitValue, startValue, loopParam, loopCriteria;
int cycleLimit, highLimit, counterA, counterB;
//---- double vars
double cycleDelta, lowDValue, highDValue, absValue, paramA, paramB;
double phaseParam, logParam, JMAValue, series, sValue, sqrtParam,
lengthDivider;
//---- temporary int variables
int s58, s60, s40, s38, s68;
//+------------------------------------------------------------------+
//| JMA initFlagization function |
//+------------------------------------------------------------------+
int init()
{
double lengthParam;
//---- 3 additional buffers are used for counting.
IndicatorBuffers(4);
//---- drawing settings
SetIndexStyle (0, DRAW_LINE);
SetIndexDrawBegin(0, 30);
//---- 4 indicator buffers mapping
SetIndexBuffer (0, JMAValueBuffer);
SetIndexBuffer (1, fC0Buffer);
SetIndexBuffer (2, fA8Buffer);
SetIndexBuffer (3, fC8Buffer);
//---- initialize one buffer (neccessary)
ArrayInitialize (ring2, 0);
ArrayInitialize (ring1, 0);
ArrayInitialize (buffer, 0);
//---- name for DataWindow and indicator subwindow label
IndicatorShortName ("JMAValue(" + Length + "," + Phase + ")");
SetIndexLabel (0, "JMAValue");
//---- initial part
limitValue = 63;
startValue = 64;
//----
for (int i = 0; i <= limitValue; i++) list [i] = -1000000;
for (i = startValue; i <= 127; i++) list [i] = 1000000;
//----
initFlag = true;
if (Length < 1.0000000002) lengthParam = 0.0000000001;
else lengthParam = (Length - 1) / 2.0;
//----
if (Phase < -100) phaseParam = 0.5;
else if (Phase > 100) phaseParam = 2.5;
else phaseParam = Phase / 100.0 + 1.5;
//----
logParam = MathLog (MathSqrt (lengthParam)) / MathLog (2.0);
//----
if (logParam + 2.0 < 0) logParam = 0;
else logParam = logParam + 2.0;
//----
sqrtParam = MathSqrt(lengthParam) * logParam;
lengthParam = lengthParam * 0.9;
lengthDivider = lengthParam / (lengthParam + 2.0);
//----
return;
}
//+------------------------------------------------------------------+
//| JMA iteration function |
//+------------------------------------------------------------------+
int start()
{
//---- get already counted bars
int counted_bars = IndicatorCounted();
//---- check for possible errors
if (counted_bars < 0) return (-1);
int limit = Bars - counted_bars - 1;
//---- main cycle
for (int shift = limit; shift >= 0; shift--) {
series = Close [shift];
if (loopParam < 61) {
loopParam++;
buffer [loopParam] = series;
}
if (loopParam > 30) {
if (initFlag) {
initFlag = false;
int diffFlag = 0;
for (int i = 1; i <= 29; i++) {
if (buffer [i + 1] != buffer [i]) diffFlag = 1;
}
highLimit = diffFlag * 30;
if (highLimit == 0) paramB = series;
else paramB = buffer[1];
paramA = paramB;
if (highLimit > 29) highLimit = 29;
} else
highLimit = 0;
//---- big cycle
for (i = highLimit; i >= 0; i--) {
if (i == 0) sValue = series; else sValue =
buffer [31 - i];
if (MathAbs (sValue - paramA) > MathAbs
(sValue - paramB)) absValue = MathAbs(sValue - paramA); else absValue
= MathAbs(sValue - paramB);
double dValue = absValue +
0.0000000001; //1.0e-10;
if (counterA <= 1) counterA = 127; else
counterA--;
if (counterB <= 1) counterB = 10; else
counterB--;
if (cycleLimit < 128) cycleLimit++;
cycleDelta += (dValue - ring2 [counterB]);
ring2 [counterB] = dValue;
if (cycleLimit > 10) highDValue =
cycleDelta / 10.0; else highDValue = cycleDelta / cycleLimit;
if (cycleLimit > 127) {
dValue = ring1 [counterA];
ring1 [counterA] = highDValue;
s68 = 64; s58 = s68;
while (s68 > 1) {
if (list [s58] < dValue) {
s68 = s68 / 2.0;
s58 += s68;
} else
if (list [s58] <= dValue)
{
s68 = 1;
} else {
s68 = s68 / 2.0;
s58 -= s68;
}
}
} else {
ring1 [counterA] = highDValue;
if ((limitValue + startValue) > 127) {
startValue--;
s58 = startValue;
} else {
limitValue++;
s58 = limitValue;
}
if (limitValue > 96) s38 = 96; else s38
= limitValue;
if (startValue < 32) s40 = 32; else s40
= startValue;
}
//----
s68 = 64;
s60 = s68;
while (s68 > 1) {
if (list [s60] >= highDValue) {
if (list [s60 - 1] <= highDValue) {
s68 = 1;
}
else {
s68 = s68 / 2.0;
s60 -= s68;
}
}
else {
s68 = s68 / 2.0;
s60 += s68;
}
if ((s60 == 127) && (highDValue > list[127])) s60 = 128;
}
if (cycleLimit > 127) {
if (s58 >= s60) {
if (((s38 + 1) > s60) && ((s40 - 1) < s60))
lowDValue += highDValue;
else if ((s40 > s60) && ((s40 - 1) < s58))
lowDValue += list [s40 - 1];
}
else if (s40 >= s60) {
if (((s38 + 1) < s60) && ((s38 + 1) > s58))
lowDValue += list[s38 + 1];
}
else if ((s38 + 2) > s60)
lowDValue += highDValue;
else if (((s38 + 1) < s60) && ((s38 + 1) > s58))
lowDValue += list[s38 + 1];
if (s58 > s60) {
if (((s40 - 1) < s58) && ((s38 + 1) > s58))
lowDValue -= list [s58];
else if ((s38 < s58) && ((s38 + 1) > s60))
lowDValue -= list[s38];
}
else {
if (((s38 + 1) > s58) && ((s40 - 1) < s58))
lowDValue -= list [s58];
else if ((s40 > s58) && (s40 < s60))
lowDValue -= list [s40];
}
}
if (s58 <= s60) {
if (s58 >= s60) list[s60] = highDValue; else {
for (int j = s58 + 1; j <= (s60 - 1); j++) {
list [j - 1] = list[j];
}
list [s60 - 1] = highDValue;
}
} else {
for (j = s58 - 1; j >= s60; j--) {
list [j + 1] = list [j];
}
list [s60] = highDValue;
}
if (cycleLimit <= 127) {
lowDValue = 0;
for (j = s40; j <= s38; j++) {
lowDValue += list[j];
}
}
//----
if ((loopCriteria + 1) > 31) loopCriteria = 31; else loopCriteria++;
double JMATempValue, sqrtDivider = sqrtParam / (sqrtParam + 1.0);
if (loopCriteria <= 30) {
if (sValue - paramA > 0) paramA = sValue; else paramA = sValue - (sValue - paramA) * sqrtDivider;
if (sValue - paramB < 0) paramB = sValue; else paramB = sValue - (sValue - paramB) * sqrtDivider;
JMATempValue = series;
if (loopCriteria == 30) {
fC0Buffer [shift] = series;
int intPart;
if (MathCeil(sqrtParam) >= 1) intPart = MathCeil(sqrtParam); else intPart = 1;
int leftInt = IntPortion (intPart);
if (MathFloor(sqrtParam) >= 1) intPart = MathFloor(sqrtParam); else intPart = 1;
int rightPart = IntPortion (intPart);
if (leftInt == rightPart) dValue = 1.0;
else
dValue = (sqrtParam - rightPart) / (leftInt - rightPart);
if (rightPart <= 29) int upShift = rightPart; else upShift = 29;
if (leftInt <= 29) int dnShift = leftInt; else dnShift = 29;
fA8Buffer [shift] = (series - buffer [loopParam - upShift]) * (1 - dValue) / rightPart + (series - buffer[loopParam - dnShift]) * dValue / leftInt;
}
} else {
double powerValue, squareValue;
dValue = lowDValue / (s38 - s40 + 1);
if (0.5 <= logParam - 2.0) powerValue = logParam - 2.0;
else powerValue = 0.5;
if (logParam >= MathPow(absValue/dValue, powerValue)) dValue = MathPow (absValue/dValue, powerValue); else dValue = logParam;
if (dValue < 1) dValue = 1;
powerValue = MathPow (sqrtDivider, MathSqrt (dValue));
if (sValue - paramA > 0) paramA = sValue; else paramA = sValue - (sValue - paramA) * powerValue;
if (sValue - paramB < 0) paramB = sValue; else paramB = sValue - (sValue - paramB) * powerValue;
}
}
// ---- end of big cycle
if (loopCriteria > 30) {
JMATempValue = JMAValueBuffer [shift
+ 1];
powerValue = MathPow (lengthDivider, dValue);
squareValue = MathPow (powerValue, 2);
fC0Buffer [shift] = (1 - powerValue) * series + powerValue * fC0Buffer [shift + 1];
fC8Buffer [shift] = (series - fC0Buffer [shift]) * (1 - lengthDivider) + lengthDivider * fC8Buffer [shift + 1];
fA8Buffer [shift] = (phaseParam * fC8Buffer [shift] + fC0Buffer [shift] - JMATempValue) *
(powerValue * (-2.0) + squareValue + 1) + squareValue * fA8Buffer [shift + 1];
JMATempValue += fA8Buffer [shift];
}
JMAValue = JMATempValue;
}
if (loopParam <= 30) JMAValue = 0;
JMAValueBuffer [shift] = JMAValue;
//---- End of main cycle
}
return;
}
//+------------------------------------------------------------------+
int IntPortion (double param) {
if (param > 0) return (MathFloor (param));
if (param < 0) return (MathCeil (param));
return (0.0);
}
//+------------------------------------------------------------------+
Comments