forked from airat77786/MQL5Book
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);
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
|