Files
mql5/Include/MyIncludes/Ehlers_Smoother_Calculator.mqh
T

326 lines
12 KiB
Plaintext

//+------------------------------------------------------------------+
//| Ehlers_Smoother_Calculator.mqh |
//| Engine for John Ehlers' SuperSmoother & UltimateSmoother |
//| Copyright 2026, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, xxxxxxxx"
#property version "3.40" // Optimized: Precomputed DSP coefficients & hardware-pipelined math
#ifndef EHLERS_SMOOTHER_CALCULATOR_MQH
#define EHLERS_SMOOTHER_CALCULATOR_MQH
#include <MyIncludes\HeikinAshi_Tools.mqh>
#ifndef ENUM_SMOOTHER_DEFINITIONS_DEFINED
#define ENUM_SMOOTHER_DEFINITIONS_DEFINED
enum ENUM_SMOOTHER_TYPE { SUPERSMOOTHER, ULTIMATESMOOTHER };
enum ENUM_INPUT_SOURCE { SOURCE_PRICE, SOURCE_MOMENTUM };
#endif
//+==================================================================+
//| CLASS 1: CEhlersSmootherCalculator |
//+==================================================================+
class CEhlersSmootherCalculator
{
protected:
int m_period;
ENUM_SMOOTHER_TYPE m_type;
ENUM_INPUT_SOURCE m_source_type;
ENUM_APPLIED_PRICE_HA_ALL m_applied_price;
//--- Precomputed Analytical DSP Filter Coefficients (Calculated ONCE in Init)
double m_c1, m_c2, m_c3;
double m_c1_half; // Pre-factored c1 * 0.5 for SuperSmoother
double m_u0, m_u1, m_u2; // Pre-factored constants for UltimateSmoother
//--- Persistent State Buffers
double m_price[];
double m_ha_open[], m_ha_high[], m_ha_low[], m_ha_close[];
//--- Embedded Composition Engine
CHeikinAshi_Calculator m_ha_engine;
virtual bool PreparePriceSeries(const int rates_total, const int start_index,
const double &open[], const double &high[],
const double &low[], const double &close[]);
void PrecomputeCoefficients(void);
public:
CEhlersSmootherCalculator(void);
virtual ~CEhlersSmootherCalculator(void) {};
//--- Enhanced Pro Init (4 Parameters)
bool Init(const int period, const ENUM_SMOOTHER_TYPE type,
const ENUM_INPUT_SOURCE source_type, const ENUM_APPLIED_PRICE_HA_ALL price_source);
//--- Legacy Compatible Init (3 Parameters)
bool Init(const int period, const ENUM_SMOOTHER_TYPE type, const ENUM_INPUT_SOURCE source_type)
{
return Init(period, type, source_type, PRICE_CLOSE_STD);
}
//--- Modern Unified Calculation Method
void Calculate(const int rates_total, const int prev_calculated,
const double &open[], const double &high[],
const double &low[], const double &close[],
double &filter_buffer[]);
//--- Legacy Overload (With explicit price_type parameter)
void Calculate(const int rates_total, const int prev_calculated,
const ENUM_APPLIED_PRICE price_type,
const double &open[], const double &high[],
const double &low[], const double &close[],
double &filter_buffer[])
{
if(m_applied_price >= PRICE_CLOSE_STD)
m_applied_price = (ENUM_APPLIED_PRICE_HA_ALL)price_type;
Calculate(rates_total, prev_calculated, open, high, low, close, filter_buffer);
}
int GetPeriod(void) const { return m_period; }
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CEhlersSmootherCalculator::CEhlersSmootherCalculator(void) :
m_period(20),
m_type(SUPERSMOOTHER),
m_source_type(SOURCE_PRICE),
m_applied_price(PRICE_CLOSE_STD),
m_c1(0.0), m_c2(0.0), m_c3(0.0),
m_c1_half(0.0), m_u0(0.0), m_u1(0.0), m_u2(0.0)
{
ArraySetAsSeries(m_price, false);
ArraySetAsSeries(m_ha_open, false);
ArraySetAsSeries(m_ha_high, false);
ArraySetAsSeries(m_ha_low, false);
ArraySetAsSeries(m_ha_close, false);
}
//+------------------------------------------------------------------+
//| Precompute Analytical DSP Filter Coefficients (Run Once in Init) |
//+------------------------------------------------------------------+
void CEhlersSmootherCalculator::PrecomputeCoefficients(void)
{
double omega = M_SQRT2 * M_PI / (double)m_period;
double a1 = MathExp(-omega);
double b1 = 2.0 * a1 * MathCos(omega);
m_c2 = b1;
m_c3 = -a1 * a1;
if(m_type == SUPERSMOOTHER)
{
m_c1 = 1.0 - m_c2 - m_c3;
m_c1_half = m_c1 * 0.5; // Factorized for fast FMA multiplication
}
else // ULTIMATESMOOTHER
{
m_c1 = (1.0 + m_c2 - m_c3) * 0.25;
m_u0 = 1.0 - m_c1;
m_u1 = 2.0 * m_c1 - m_c2;
m_u2 = -(m_c1 + m_c3);
}
}
//+------------------------------------------------------------------+
//| Initialization |
//+------------------------------------------------------------------+
bool CEhlersSmootherCalculator::Init(const int period, const ENUM_SMOOTHER_TYPE type,
const ENUM_INPUT_SOURCE source_type, const ENUM_APPLIED_PRICE_HA_ALL price_source)
{
m_period = (period < 2) ? 2 : period;
m_type = type;
m_source_type = source_type;
m_applied_price = price_source;
PrecomputeCoefficients();
return true;
}
//+------------------------------------------------------------------+
//| Prepare Price Series (Standard / Heikin Ashi / Momentum) |
//+------------------------------------------------------------------+
bool CEhlersSmootherCalculator::PreparePriceSeries(const int rates_total, const int start_index,
const double &open[], const double &high[],
const double &low[], const double &close[])
{
if(ArraySize(m_price) != rates_total)
{
ArrayResize(m_price, rates_total);
ArraySetAsSeries(m_price, false);
}
bool is_heikin_ashi = (m_applied_price <= PRICE_HA_CLOSE);
if(is_heikin_ashi)
{
if(ArraySize(m_ha_open) != rates_total)
{
ArrayResize(m_ha_open, rates_total);
ArrayResize(m_ha_high, rates_total);
ArrayResize(m_ha_low, rates_total);
ArrayResize(m_ha_close, rates_total);
ArraySetAsSeries(m_ha_open, false);
ArraySetAsSeries(m_ha_high, false);
ArraySetAsSeries(m_ha_low, false);
ArraySetAsSeries(m_ha_close, false);
}
m_ha_engine.Calculate(rates_total, start_index, open, high, low, close,
m_ha_open, m_ha_high, m_ha_low, m_ha_close);
for(int i = start_index; i < rates_total; i++)
{
if(m_source_type == SOURCE_PRICE)
{
switch(m_applied_price)
{
case PRICE_HA_OPEN:
m_price[i] = m_ha_open[i];
break;
case PRICE_HA_HIGH:
m_price[i] = m_ha_high[i];
break;
case PRICE_HA_LOW:
m_price[i] = m_ha_low[i];
break;
case PRICE_HA_MEDIAN:
m_price[i] = (m_ha_high[i] + m_ha_low[i]) * 0.5;
break;
case PRICE_HA_TYPICAL:
m_price[i] = (m_ha_high[i] + m_ha_low[i] + m_ha_close[i]) / 3.0;
break;
case PRICE_HA_WEIGHTED:
m_price[i] = (m_ha_high[i] + m_ha_low[i] + 2.0 * m_ha_close[i]) * 0.25;
break;
case PRICE_HA_CLOSE:
default:
m_price[i] = m_ha_close[i];
break;
}
}
else // SOURCE_MOMENTUM
{
m_price[i] = m_ha_close[i] - m_ha_open[i];
}
}
}
else
{
for(int i = start_index; i < rates_total; i++)
{
if(m_source_type == SOURCE_PRICE)
{
switch(m_applied_price)
{
case PRICE_OPEN_STD:
m_price[i] = open[i];
break;
case PRICE_HIGH_STD:
m_price[i] = high[i];
break;
case PRICE_LOW_STD:
m_price[i] = low[i];
break;
case PRICE_MEDIAN_STD:
m_price[i] = (high[i] + low[i]) * 0.5;
break;
case PRICE_TYPICAL_STD:
m_price[i] = (high[i] + low[i] + close[i]) / 3.0;
break;
case PRICE_WEIGHTED_STD:
m_price[i] = (high[i] + low[i] + 2.0 * close[i]) * 0.25;
break;
case PRICE_CLOSE_STD:
default:
m_price[i] = close[i];
break;
}
}
else // SOURCE_MOMENTUM
{
m_price[i] = close[i] - open[i];
}
}
}
return true;
}
//+------------------------------------------------------------------+
//| High-Performance Incremental Calculation (True O(1)) |
//+------------------------------------------------------------------+
void CEhlersSmootherCalculator::Calculate(const int rates_total, const int prev_calculated,
const double &open[], const double &high[],
const double &low[], const double &close[],
double &filter_buffer[])
{
if(rates_total < 4)
return;
// Safe array resizing
if(ArraySize(filter_buffer) != rates_total)
{
ArrayResize(filter_buffer, rates_total);
ArraySetAsSeries(filter_buffer, false);
}
int start_index = (prev_calculated == 0) ? 0 : (prev_calculated - 1);
if(!PreparePriceSeries(rates_total, start_index, open, high, low, close))
return;
int loop_start = MathMax(3, start_index);
// Deterministic seeding strictly on initial calculation
if(prev_calculated == 0)
{
filter_buffer[0] = m_price[0];
filter_buffer[1] = m_price[1];
filter_buffer[2] = m_price[2];
loop_start = 3;
}
// Recursive 2-Pole Difference Equation (Pipelined FMA Multiplication)
if(m_type == SUPERSMOOTHER)
{
for(int i = loop_start; i < rates_total; i++)
{
filter_buffer[i] = m_c1_half * (m_price[i] + m_price[i - 1]) +
m_c2 * filter_buffer[i - 1] +
m_c3 * filter_buffer[i - 2];
}
}
else // ULTIMATESMOOTHER
{
for(int i = loop_start; i < rates_total; i++)
{
filter_buffer[i] = m_u0 * m_price[i] +
m_u1 * m_price[i - 1] +
m_u2 * m_price[i - 2] +
m_c2 * filter_buffer[i - 1] +
m_c3 * filter_buffer[i - 2];
}
}
}
//+==================================================================+
//| CLASS 2: CEhlersSmootherCalculator_HA (Legacy Wrapp) |
//+==================================================================+
class CEhlersSmootherCalculator_HA : public CEhlersSmootherCalculator
{
public:
CEhlersSmootherCalculator_HA(void)
{
m_applied_price = PRICE_HA_CLOSE;
}
};
#endif // EHLERS_SMOOTHER_CALCULATOR_MQH
//+------------------------------------------------------------------+