MQL5Book/Indicators/p5/IndTripleEMA.mq5
super.admin 1c8e83ce31 convert
2025-05-30 16:09:41 +02:00

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);
}
}
//+------------------------------------------------------------------+