156 lines
5.6 KiB
MQL5
156 lines
5.6 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| IndTripleEMA.mq5 |
|
|
//| Copyright 2021, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "2021, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#property description "Triple Exponential Moving Average"
|
|
|
|
// indicator settings
|
|
#property indicator_chart_window
|
|
#property indicator_buffers 4
|
|
#property indicator_plots 1
|
|
|
|
// drawing settings
|
|
#property indicator_type1 DRAW_LINE
|
|
#property indicator_color1 Orange
|
|
#property indicator_width1 1
|
|
#property indicator_label1 "EMA³"
|
|
#property indicator_applied_price PRICE_CLOSE
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| How to handle 'begin' parameter in OnCalculate function |
|
|
//+------------------------------------------------------------------+
|
|
enum BEGIN_POLICY
|
|
{
|
|
STRICT, // strict
|
|
CUSTOM, // custom
|
|
NONE, // no
|
|
};
|
|
|
|
// inputs
|
|
input int InpPeriodEMA = 14; // EMA period:
|
|
input BEGIN_POLICY InpHandleBegin = STRICT; // Handle 'begin' parameter:
|
|
|
|
// indicator buffers
|
|
double TemaBuffer[];
|
|
double Ema[];
|
|
double EmaOfEma[];
|
|
double EmaOfEmaOfEma[];
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| EMA internal settings |
|
|
//+------------------------------------------------------------------+
|
|
// 'Offset' is reserved to define indeterminate region of warming up,
|
|
// where available number of elements is less than smoothing 'period'.
|
|
// But it's not actually needed for EMA, because unlike other MAs
|
|
// EMA's 'period' is not used directly to process 'period' elements,
|
|
// but affects inertial part of accumulation, which implicitly involves
|
|
// much more previous data trails (over 'period' elements).
|
|
// For triple EMA it's sometimes set to '3 * InpPeriodEMA - 3'.
|
|
// In this source code, being a part of the book, Offset = 0,
|
|
// which makes it easier to analize where source data begins.
|
|
const int Offset = 0;
|
|
const double K = 2.0 / (InpPeriodEMA + 1);
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator initialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnInit()
|
|
{
|
|
// indicator setting
|
|
const string caption = StringFormat("EMA³(%d)%s", InpPeriodEMA,
|
|
StringSubstr(EnumToString(InpHandleBegin), 0, 1));
|
|
IndicatorSetString(INDICATOR_SHORTNAME, caption);
|
|
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
|
|
|
|
// indicator buffers mapping
|
|
SetIndexBuffer(0, TemaBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(1, Ema, INDICATOR_CALCULATIONS);
|
|
SetIndexBuffer(2, EmaOfEma, INDICATOR_CALCULATIONS);
|
|
SetIndexBuffer(3, EmaOfEmaOfEma, INDICATOR_CALCULATIONS);
|
|
|
|
// plot setup
|
|
PlotIndexSetString(0, PLOT_LABEL, caption);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Triple Exponential Moving Average |
|
|
//+------------------------------------------------------------------+
|
|
int OnCalculate(const int rates_total,
|
|
const int prev_calculated,
|
|
const int begin,
|
|
const double &price[])
|
|
{
|
|
if(rates_total < Offset) return 0;
|
|
|
|
const int _begin = InpHandleBegin == STRICT ? (begin < rates_total ? begin : fmax(rates_total - 1, 0)) : 0;
|
|
|
|
// fresh start or refresh
|
|
if(prev_calculated == 0)
|
|
{
|
|
Print("begin=", _begin, " ", EnumToString(InpHandleBegin));
|
|
|
|
// we can adjust plot settings dynamically
|
|
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, _begin + Offset);
|
|
|
|
// prepare arrays
|
|
ArrayInitialize(Ema, EMPTY_VALUE);
|
|
ArrayInitialize(EmaOfEma, EMPTY_VALUE);
|
|
ArrayInitialize(EmaOfEmaOfEma, EMPTY_VALUE);
|
|
ArrayInitialize(TemaBuffer, EMPTY_VALUE);
|
|
Ema[_begin] = EmaOfEma[_begin] = EmaOfEmaOfEma[_begin] = price[_begin];
|
|
}
|
|
|
|
// main loop with respect to _begin
|
|
for(int i = fmax(prev_calculated - 1, _begin);
|
|
i < rates_total && !IsStopped(); i++)
|
|
{
|
|
EMA(price, Ema, i, _begin);
|
|
EMA(Ema, EmaOfEma, i, _begin);
|
|
EMA(EmaOfEma, EmaOfEmaOfEma, i, _begin);
|
|
|
|
if(InpHandleBegin == CUSTOM) // empty data guard
|
|
{
|
|
if(Ema[i] == EMPTY_VALUE
|
|
|| EmaOfEma[i] == EMPTY_VALUE
|
|
|| EmaOfEmaOfEma[i] == EMPTY_VALUE)
|
|
continue;
|
|
}
|
|
TemaBuffer[i] = 3 * Ema[i] - 3 * EmaOfEma[i] + EmaOfEmaOfEma[i];
|
|
}
|
|
return rates_total;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Exponential Moving Average |
|
|
//| Precondition: pos is in bounds of both arrays |
|
|
//+------------------------------------------------------------------+
|
|
void EMA(const double &source[], double &result[], const int pos, const int begin = 0)
|
|
{
|
|
if(InpHandleBegin == CUSTOM) // empty data guard
|
|
{
|
|
if(source[pos] == EMPTY_VALUE || !MathIsValidNumber(source[pos]))
|
|
{
|
|
result[pos] = EMPTY_VALUE;
|
|
return;
|
|
}
|
|
else
|
|
if(pos > 0 && result[pos - 1] == EMPTY_VALUE)
|
|
{
|
|
result[pos] = source[pos];
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(pos <= begin)
|
|
{
|
|
result[pos] = source[pos];
|
|
}
|
|
else
|
|
{
|
|
result[pos] = source[pos] * K + result[pos - 1] * (1 - K);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|