This indicator is a collection of moving averages.
1. MVA - Simple Moving Average
MVA[i]=(Price[i]+Price[i-1]+…+Price[i-N])/N, where N-period
2. EMA - Exponential Moving Average
EMA[i]=EMA[i-1]+2*(Price[i]-EMA[i-1])/(1+N)
3. Wilder - Wilder Exponential Moving Average
Wilder[i]=Wilder[i-1]+(Price[i]-Wilder[i-1])/N
4. LWMA - Linear Weighted Moving Average
LWMA[i]=Sum/Weight, where
Sum=Price[i]*N+Price[i-1]*(N-1)+…+Price[i-N+1]*(1),
Weight=N+(N-1)+…+1=N*(N+1)/2.
5. SineWMA - Sine Weighted Moving Average
SineWMA[i]=Sum/Weight, where
Sum=Price[i-N+1]*sin(PI*(N)/(N+1))+Price[i-N+2]*sin(PI*(N-1)/(N+1))+…+Price[i]*sin(PI*1/(N+1))
Weight= sin(PI*(N)/(N+1))+ sin(PI*(N-1)/(N+1))+…+ sin(PI*1/(N+1)).
6. TriMA - Triangular Moving Average
TriMA[i]=(MVA(i,len)+MVA(i-1,len)+…+MVA(i-len+1,len))/len, where
MVA(i,N) – Simple Moving Average,
len=(N+1)/2.
7. LSMA - Least Square Moving Average
LSMA[i]=Sum/(N*(N+1)), where
Sum=(1-(N+1)/3)*Price[i-N+1]+(2-(N+1)/3)*Price[i-N+2]+…+(N-(N+1)/3)*Price[1].
8. SMMA - Smoothed Moving Average
SMMA[i]=(Sum-SMMA[i-1]+Price[i])/N, where
Sum=Price[i-1]+Price[i-2]+…+Price[i-N].
9. HMA - Hull Moving Average by Alan Hull
HMA[i]=LWMA(i,len,(2*LWMA(i,N/2,Price)-LWMA(i,N,Price))), where
len=Sqrt(N),
LWMA(i,N,Price) - Linear Weighted Moving Average
10. ZeroLagEMA - Zero-Lag Exponential Moving Average
ZeroLagEMA[i]=Alpha*(2*Price[i]-Price[i-lag])+(1-Alpha)* ZeroLagEMA[i-1], where
Alpha=2/(N+1),
Lag=(N-1)/2.
11. DEMA - Double Exponential Moving Average by Patrick Mulloy
DEMA[i]=2*D1[i]-D2[i], where
D1[i]=D1[i-1]+2*(Price[i]-D1[i-1])/(1+N),
D2[i]=D2[i-1]+2*(D1[i]-D2[i-1])/(1+N).
12. T3 - T3 by T.Tillson
T3[i]=DEMA(i,DEMA2), where
DEMA2[i]=DEMA(i,DEMA1),
DEMA1[i]=DEMA(i,Price),
DEMA - Double Exponential Moving Average
13. ITrend - Instantaneous Trendline by J.Ehlers
ITrend[i]=(Price[i]+2*Price[i-1]+Price[i-2])/4 for i<=7,
ITrend[i]=(Alpha-0.25*Alpha*Alpha)*Price[i]+0.5*Alpha*Alpha*Price[i-1]-(Alpha-0.75*Alpha*Alpha)*Price[i-2]+2*(1-Alpha)*ITrend[i-1]-(1-Alpha)*(1-Alpha)*ITrend[i-2] for i>7, where
Alpha=2/(N+1)
14. Median - Moving Median
Set of the prices (Price[i], Price[i-1], …, Price[i-N]) is sorted (on increase or decrease) and take value from of the set (N/2).
15. GeoMean - Geometric Mean
GeoMean[i]=Price[i]^(1/N)*Price[i-1]^(1/N)*…*Price[i-N+1]^(1/N).
16. REMA - Regularized EMA by Chris Satchwell
REMA[i]=(REMA[i-1]*(1+2*Lambda)+Alpha*(Price[i]-REMA[i-1])-Lambda*REMA[i-2])/(1+Lambda), where
Alpha=2/(N+1),
Lambda=0.5.
17. ILRS - Integral of Linear Regression Slope
ILRS[i]=(N*Sum1-Sum*Sumy)/(Sum*Sum-N*Sum2)+MVA(I,N), where
Sum=N*(N-1)*0.5,
Sum2=N*(N-1)*(2*N-1)/6,
Sum1=1*Price[i-1]+2*Price[i-2]+…+(N-1)*Price[i-N+1],
Sumy=Price[i]+Price[i-1]+…+Price[i-N+1],
MVA(i,N) – Simple Moving Average.
18. IE/2 - Combination of LSMA and ILRS
IE/2=(ILRS+LSMA)/2
19. TriMAgen - Triangular Moving Average generalized by J.Ehlers
TriMAgen[i]=Sum/(len+1), where
Sum=MVA(i,len)+MVA(i-1,len)+…+MVA(i-len,len), where
MVA(i,N) – Simple Moving Average,
len=(N+1)/2.
20. JSmooth - Smoothing by Mark Jurik
JSmooth[i]=J5[i], where
J5[i]=J5[i-1]+J4[i],
J4[i]=(J3[i]-J5[i-1])*(1-Alpha)*(1-Alpha)+J4[i-1]*Alpha*Alpha,
J3[i]=J1[i]+J2[i],
J2[i]=(Price[i]-J1[i])*(1-Alpha)+J2[i-1]*Alpha,
J1[i]=Price[i]*(1-Alpha)+J1[i-1]*Alpha,
Alpha=0.45*N/(0.45*(N-1)+2).
- Code: Select all
function Init()
indicator:name("Averages indicator");
indicator:description("Averages indicator");
indicator:requiredSource(core.Tick);
indicator:type(core.Indicator);
indicator.parameters:addGroup("Calculation");
indicator.parameters:addString("Method", "Method", "", "MVA");
indicator.parameters:addStringAlternative("Method", "MVA", "", "MVA");
indicator.parameters:addStringAlternative("Method", "EMA", "", "EMA");
indicator.parameters:addStringAlternative("Method", "Wilder", "", "Wilder");
indicator.parameters:addStringAlternative("Method", "LWMA", "", "LWMA");
indicator.parameters:addStringAlternative("Method", "SineWMA", "", "SineWMA");
indicator.parameters:addStringAlternative("Method", "TriMA", "", "TriMA");
indicator.parameters:addStringAlternative("Method", "LSMA", "", "LSMA");
indicator.parameters:addStringAlternative("Method", "SMMA", "", "SMMA");
indicator.parameters:addStringAlternative("Method", "HMA", "", "HMA");
indicator.parameters:addStringAlternative("Method", "ZeroLagEMA", "", "ZeroLagEMA");
indicator.parameters:addStringAlternative("Method", "DEMA", "", "DEMA");
indicator.parameters:addStringAlternative("Method", "T3", "", "T3");
indicator.parameters:addStringAlternative("Method", "ITrend", "", "ITrend");
indicator.parameters:addStringAlternative("Method", "Median", "", "Median");
indicator.parameters:addStringAlternative("Method", "GeoMean", "", "GeoMean");
indicator.parameters:addStringAlternative("Method", "REMA", "", "REMA");
indicator.parameters:addStringAlternative("Method", "ILRS", "", "ILRS");
indicator.parameters:addStringAlternative("Method", "IE/2", "", "IE/2");
indicator.parameters:addStringAlternative("Method", "TriMAgen", "", "TriMAgen");
indicator.parameters:addStringAlternative("Method", "JSmooth", "", "JSmooth");
indicator.parameters:addInteger("Period", "Period", "", 20);
indicator.parameters:addBoolean("ColorMode", "ColorMode", "", true);
indicator.parameters:addGroup("Style");
indicator.parameters:addColor("MainClr", "Main color", "Main color", core.rgb(0, 255, 0));
indicator.parameters:addColor("UPclr", "UP color", "UP color", core.rgb(255, 0, 0));
indicator.parameters:addColor("DNclr", "DN color", "DN color", core.rgb(0, 0, 255));
indicator.parameters:addInteger("widthLinReg", "Line width", "Line width", 1, 1, 5);
indicator.parameters:addInteger("styleLinReg", "Line style", "Line style", core.LINE_SOLID);
indicator.parameters:setFlag("styleLinReg", core.FLAG_LINE_STYLE);
end
local first;
local MainBuff = nil;
local ColorMode, UPclr, DNclr;
local updateParams;
local UpdateFunction;
local name;
function Prepare(onlyName)
source = instance.source;
local Method = instance.parameters.Method;
if Method == "IE/2" then
Method = "IE_2";
end
Period = instance.parameters.Period;
ColorMode = instance.parameters.ColorMode;
if _G[Method .. "Init"] == nil or _G[Method .. "Update"] == nil then
assert(false, "The method " .. Method .. " is unknown");
end
name = profile:id() .. "(" .. source:name() .. "," .. instance.parameters.Method .. "," .. Period .. ")";
instance:name(name);
if onlyName then
return ;
end
ColorMode = instance.parameters.ColorMode;
UPclr = instance.parameters.UPclr;
DNclr = instance.parameters.DNclr;
updateParams = _G[Method .. "Init"](source, Period);
UpdateFunction = _G[Method .. "Update"];
MainBuff = instance:addStream("MainBuff", core.Line, name .. ".MA", "MA", instance.parameters.MainClr, updateParams.first);
MainBuff:setWidth(instance.parameters.widthLinReg);
MainBuff:setStyle(instance.parameters.styleLinReg);
first = updateParams.first;
updateParams.buffer = MainBuff;
end
function Update(period, mode)
if period >= first then
UpdateFunction(updateParams, period, mode);
if ColorMode then
if MainBuff[period] > MainBuff[period - 1] then
MainBuff:setColor(period, UPclr);
elseif MainBuff[period] < MainBuff[period - 1] then
MainBuff:setColor(period, DNclr);
end
end
end
end
-- =============================================================================
-- Implementations
-- =============================================================================
--
-- Simple moving average
--
function MVAInit(source, n)
local p = {};
p.first = source:first() + n - 1;
p.n = n;
p.offset = n - 1;
p.source = source;
return p;
end
function MVAUpdate(params, period, mode)
params.buffer[period] = mathex.avg(params.source, period - params.offset, period);
end
--
-- Exponential moving average
--
function EMAInit(source, n)
local p = {};
p.first = source:first();
p.k = 2.0 / (n + 1.0);
p.source = source;
return p;
end
function EMAUpdate(params, period, mode)
if period == params.first then
params.buffer[period] = params.source[period];
else
params.buffer[period] = (1 - params.k) * params.buffer[period - 1] + params.k * params.source[period];
end
end
--
-- Linear-weighted moving average
--
function LWMAInit(source, n)
local p = {};
p.first = source:first() + n - 1;
p.n = n;
p.offset = n - 1;
p.source = source;
return p;
end
function LWMAUpdate(params, period, mode)
params.buffer[period] = mathex.lwma(params.source, period - params.offset, period);
end
--
-- Wilders smooting average
--
function WilderInit(source, n)
local p = {};
p.n = n;
p.n1 = 2 * n - 1;
p.k = 2.0 / (p.n1 + 1.0);
p.first = source:first() + p.n1 - 1;
p.source = source;
return p;
end
function WilderUpdate(params, period, mode)
if period == params.first then
params.buffer[period] = mathex.avg(source, period - params.n + 1, period);
else
params.buffer[period] = ((params.source[period] - params.buffer[period - 1]) * params.k) + params.buffer[period - 1];
end
end
--
-- SMMA (smoothed moving average)
--
function SMMAInit(source, n)
local p = {};
p.first = source:first() + n - 1;
p.n = n;
p.source = source;
return p;
end
function SMMAUpdate(params, period, mode)
if period == params.first then
params.buffer[period] = mathex.avg(params.source, period - params.n + 1, period);
else
params.buffer[period] = (params.buffer[period - 1] * (params.n - 1) + params.source[period]) / params.n;
end
end
--
-- GeoMean
--
function GeoMeanInit(source, n)
local p = {};
p.first = source:first() + n - 1;
p.n = n;
p.exp = 1 / n;
p.offset = n - 1;
p.source = source;
return p;
end
function GeoMeanUpdate(params, period, mode)
local i, s, src;
s = 1;
src = params.source;
for i = period - params.offset, period, 1 do
s = s * src[i];
end
params.buffer[period] = math.pow(s, params.exp);
end
--
-- SineWMA: Sine weighted moving average
--
function SineWMAInit(source, n)
local p = {};
p.source = source;
p.n = n;
p.offset = n - 1;
p.sine = {};
p.first = source:first() + n - 1;
local i, w;
w = 0;
for i = 1, n, 1 do
p.sine[i] = math.sin(math.pi * (n - i + 1) / (n + 1));
w = w + p.sine[i];
end
p.weight = w;
p.alwaysZero = (w == 0);
return p;
end
function SineWMAUpdate(params, period, mode)
local sum = 0;
if not params.alwaysZero then
local src = params.source;
local sine = params.sine;
local n = params.n;
local p = period - n;
for i = 1, n, 1 do
sum = sum + src[p + i] * sine[i];
end
sum = sum / params.weight;
end
params.buffer[period] = sum;
end
--
-- TriMA: Triangular Moving Average
--
function TriMAInit(source, n)
local p = {};
p.source = source;
p.n = n;
p.len = math.ceil((n + 1) / 2);
p.first1 = source:first() + p.len - 1;
p.mabuffer = instance:addInternalStream(p.first1, 0);
p.first = p.first1 + p.len - 1;
p.offset = p.len - 1;
return p;
end
function TriMAUpdate(params, period, mode)
local off = params.offset;
if period == params.first then
-- fill sma's before the first value
local i;
for i = params.first1, params.first, 1 do
params.mabuffer[i] = mathex.avg(params.source, i - off, i);
end
else
params.mabuffer[period] = mathex.avg(params.source, period - off, period);
end
params.buffer[period] = mathex.avg(params.mabuffer, period - off, period);
end
--
-- LSMA: Least Square Moving Average (or EPMA, Linear Regression Line)
--
function LSMAInit(source, n)
local p = {};
p.source = source;
p.n = n;
p.offset = p.n - 1;
p.first = source:first() + n - 1;
return p;
end
function LSMAUpdate(params, period, mode)
params.buffer[period] = mathex.lreg(params.source, period - params.offset, period);
end
--
-- HMA: Hull Moving Average by Alan Hull
--
function HMAInit(source, n)
assert(n >= 4, "n must be at least 4");
local p = {};
p.source = source;
p.n = n;
p.len = n;
p.halflen = math.max(math.floor(p.len / 2), 1);
p.first1 = source:first() + p.halflen - 1;
p.lwma1 = instance:addInternalStream(p.first1, 0);
p.first2 = source:first() + p.len - 1;
p.lwma2 = instance:addInternalStream(p.first2, 0);
p.first3 = math.max(p.first1, p.first2);
p.tmp = instance:addInternalStream(p.first3, 0);
p.len1 = math.max(math.floor(math.sqrt(n)), 1) - 1;
p.first = p.first3 + p.len1 - 1;
return p;
end
function HMAUpdate(params, period, mode)
if period == params.first then
local i;
local src = params.source;
for i = params.first1, period, 1 do
params.lwma1[i] = mathex.lwma(params.source, i - params.halflen + 1, i);
end
for i = params.first2, period, 1 do
params.lwma2[i] = mathex.lwma(params.source, i - params.len + 1, i);
end
for i = params.first3, period, 1 do
params.tmp[i] = 2 * params.lwma1[i] - params.lwma2[i];
end
else
params.lwma1[period] = mathex.lwma(params.source, period - params.halflen + 1, period);
params.lwma2[period] = mathex.lwma(params.source, period - params.len + 1, period);
params.tmp[period] = 2 * params.lwma1[period] - params.lwma2[period];
end
params.buffer[period] = mathex.lwma(params.tmp, period - params.len1 + 1, period);
end
--
-- Zero-lag EMA
--
function ZeroLagEMAInit(source, n)
local p = {};
p.alpha = 2.0 / (n + 1.0);
p.lag = math.ceil((n - 1) / 2);
p.first = source:first() + p.lag;
p.source = source;
return p;
end
function ZeroLagEMAUpdate(params, period, mode)
if period == params.first then
params.buffer[period] = params.source[period];
else
params.buffer[period] = params.alpha * (2 * params.source[period] - params.source[period - params.lag]) +
(1 - params.alpha) * params.buffer[period - 1];
end
end
--
-- DEMA: Double Exponential Moving Average (DEMA)
-- DEMA(n) = 2 * EMA(n) - EMA(EMA(n), n)
--
function DEMAInit(source, n)
local p = {};
p.first = source:first();
p.k = 2.0 / (n + 1.0);
p.ema = instance:addInternalStream(p.first, 0);
p.ema2 = instance:addInternalStream(p.first, 0);
p.source = source;
return p;
end
function DEMAUpdate(params, period, mode)
if period == params.first then
params.ema[period] = params.source[period];
params.ema2[period] = params.source[period];
params.buffer[period] = params.source[period];
else
local ema, ema2, k, k1;
ema = params.ema;
ema2 = params.ema2;
k = params.k;
k1 = 1 - params.k;
ema[period] = k1 * ema[period - 1] + k * params.source[period];
ema2[period] = k1 * ema2[period - 1] + k * ema[period];
params.buffer[period] = 2 * ema[period] - ema2[period];
end
end
--
-- T3: T3 by T.Tillson
-- T3 = DEMA(DEMA(DEMA)))
--
function T3Init(source, n)
local p = {};
p.dema1 = DEMAInit(source, n);
p.dema1.buffer = instance:addInternalStream(p.dema1.first, 0);
p.dema2 = DEMAInit(p.dema1.buffer, n);
p.dema2.buffer = instance:addInternalStream(p.dema2.first, 0);
p.dema3 = DEMAInit(p.dema2.buffer, n);
p.dema3.buffer = nil;
p.first = p.dema3.first;
return p;
end
function T3Update(params, period, mode)
if params.dema3.buffer == nil then
params.dema3.buffer = params.buffer;
end
DEMAUpdate(params.dema1, period, mode);
DEMAUpdate(params.dema2, period, mode);
DEMAUpdate(params.dema3, period, mode);
end
--
-- ITrend
--
function ITrendInit(source, n)
local p = {}, alpha;
p.first = source:first() + 2;
p.first7 = p.first + 7;
alpha = 2.0 / (n + 1.0);
p.k = alpha;
p.k1 = (alpha - alpha * alpha / 4);
p.k2 = 0.5 * alpha * alpha;
p.k3 = (alpha - 0.75 * alpha * alpha);
p.k4 = 2 * (1 - alpha);
p.k5 = (1 - alpha) * (1 - alpha);
p.source = source;
return p;
end
function ITrendUpdate(params, period, mode)
local src = params.source;
if period <= params.first7 then
params.buffer[period] = (src[period] + 2 * src[period - 1] + src[period - 2]) / 4;
else
params.buffer[period] = params.k1 * src[period] + params.k2 * src[period - 1] - params.k3 * src[period - 2] +
params.k4 * params.buffer[period - 1] - params.k5 * params.buffer[period - 2];
end
end
--
-- Median: the floating median
--
function MedianInit(source, n)
local p = {};
p.source = source;
p.first = source:first() + n - 1;
p.middle = math.ceil((n - 1) / 2);
if p.middle * 2 == (n - 1) then
p.even = true;
else
p.even = false;
end
p.array = {};
p.n = n;
local i = 1, n, 1 do
p.array[i] = 0;
end
return p;
end
function MedianUpdate(params, period, mode)
local i, arr, n, src;
arr = params.array;
n = params.n;
src = params.source;
for i = 1, n, 1 do
arr[i] = src[period - n + i];
end
table.sort(arr);
if params.even then
params.buffer[period] = arr[params.middle];
else
params.buffer[period] = (arr[params.middle] + arr[params.middle + 1]) / 2;
end
end
--
-- REMA - Regularized moving average
-- Rp + alpha*(close - Rp) + lambda*(Rp + (Rp-Rpp))
-- REMA = ------------------------------------------------
-- 1 + lambda
-- Lamda is 0.5
--
function REMAInit(source, n)
local p = {};
p.first = source:first();
p.first3 = source:first() + 2;
p.k = 2.0 / (n + 1.0);
p.source = source;
return p;
end
function REMAUpdate(params, period, mode)
if period <= params.first3 then
params.buffer[period] = params.source[period];
else
local rp = params.buffer[period - 1];
local rpp = params.buffer[period - 2];
params.buffer[period] = (params.k * params.source[period] + (1 - params.k) * rp + 0.5 * (2 * rp - rpp)) / 1.5;
end
end
--
-- ILRS: Integral of Linear Regression Slope
-- ILRS = LINEARREGSLOPE(PRICE, PERIOD) + AVERAGE(PRICE, PERIOD);
--
function ILRSInit(source, n)
local p = {};
p.source = source;
p.n = n;
p.offset = p.n - 1;
p.first = source:first() + n - 1;
return p;
end
function ILRSUpdate(params, period, mode)
local from = period - params.offset;
params.buffer[period] = mathex.lregSlope(params.source, from, period) + mathex.avg(params.source, from, period);
end
--
-- IE/2:
-- IE/2 = (ILRS + LSMA) / 2
--
function IE_2Init(source, n)
local p = {};
p.source = source;
p.n = n;
p.offset = p.n - 1;
p.first = source:first() + n - 1;
return p;
end
function IE_2Update(params, period, mode)
local from = period - params.offset;
params.buffer[period] = (mathex.lregSlope(params.source, from, period) + mathex.avg(params.source, from, period) + mathex.lreg(params.source, from, period)) / 2;
end
--
-- TriMA: Triangular Moving Average generalized
--
function TriMAgenInit(source, n)
local p = {};
p.source = source;
p.n = n;
p.len = math.floor((n + 1) / 2);
p.len2 = math.ceil((n + 1) / 2);
p.first1 = source:first() + p.len - 1;
p.mabuffer = instance:addInternalStream(p.first1, 0);
p.first = p.first1 + p.len2 - 1;
p.offset = p.len - 1;
p.offset2 = p.len2 - 1;
return p;
end
function TriMAgenUpdate(params, period, mode)
local off = params.offset;
if period == params.first then
-- fill sma's before the first value
local i;
for i = params.first1, params.first, 1 do
params.mabuffer[i] = mathex.avg(params.source, i - off, i);
end
else
params.mabuffer[period] = mathex.avg(params.source, period - off, period);
end
params.buffer[period] = mathex.avg(params.mabuffer, period - params.offset2, period);
end
--
-- JSmooth
--
--
function JSmoothInit(source, n)
local p = {};
p.first = source:first();
p.first3 = source:first() + 3;
p.alpha = 0.45 * (n - 1) / (0.45 * (n - 1) + 2);
p.alpha1 = 1 - p.alpha;
p.alpha1_2 = math.pow((1 - p.alpha), 2);
p.alpha_2 = math.pow(p.alpha, 2)
p.a1 = instance:addInternalStream(source:first(), 0);
p.a2 = instance:addInternalStream(source:first(), 0);
p.a3 = instance:addInternalStream(source:first(), 0);
p.a4 = instance:addInternalStream(source:first(), 0);
p.source = source;
return p;
end
function JSmoothUpdate(params, period, mode)
if period < params.first3 then
params.a1[period] = params.source[period];
params.a2[period] = 0;
params.a3[period] = params.source[period];
params.a4[period] = 0;
params.buffer[period] = params.source[period];
else
local price = params.source[period];
params.a1[period] = params.alpha1 * price + params.alpha * params.a1[period - 1];
params.a2[period] = (price - params.a1[period]) * params.alpha1 + params.alpha * params.a2[period - 1];
params.a3[period] = params.a1[period] + params.a2[period];
params.a4[period] = (params.a3[period] - params.buffer[period - 1]) * params.alpha1_2 + params.alpha_2 * params.a4[period - 1];
params.buffer[period] = params.buffer[period - 1] + params.a4[period];
end
end
This indicator will provides Audio / Email Alerts on Averages slope change.
Dec 14, 2015: Compatibility issue Fixed. _Alert helper is not longer needed.
If you want to use updated version of this indicator,
please make sure to use TS Version 01.14.101415. or higher.
Averages Update.
In addition to improving performance,
Some algorithms are changed, or corrected.
Fully compatible with the old version.
Please note that some of methods are faster than another. To compare please use the chart below. The hight bar means the faster method (the data is for the optimized version and indicore 1.0):
Just to reference here is the comparison of performance of old and new version of the indicator. The higher bar means better performance improvements:
To read how such improvements was made please refer this article:
viewtopic.php?f=28&t=3785&start=0
p.s. The original indicator indicator, as far as I see is made on the base of MA_AllAverages indicator by Copyright © 2007-08, TrendLaboratory:
http://desynced.no-ip.org/fx/eas/mq4script-2829.php
Old version. For the new version see above.
MT4/MQ4 version.
viewtopic.php?f=38&t=66535