refactor: Optimized: Precomputed DSP coefficients & hardware-pipelined math

This commit is contained in:
Toh4iem9
2026-09-28 08:20:50 +02:00
parent 5970a88626
commit 53bcd4064b
@@ -4,7 +4,7 @@
//| Copyright 2026, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, xxxxxxxx"
#property version "3.30" // Fixed: Preserved Persistent State on ArrayResize
#property version "3.40" // Optimized: Precomputed DSP coefficients & hardware-pipelined math
#ifndef EHLERS_SMOOTHER_CALCULATOR_MQH
#define EHLERS_SMOOTHER_CALCULATOR_MQH
@@ -28,6 +28,11 @@ protected:
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[];
@@ -39,6 +44,8 @@ protected:
const double &open[], const double &high[],
const double &low[], const double &close[]);
void PrecomputeCoefficients(void);
public:
CEhlersSmootherCalculator(void);
virtual ~CEhlersSmootherCalculator(void) {};
@@ -78,10 +85,13 @@ public:
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CEhlersSmootherCalculator::CEhlersSmootherCalculator(void) : m_period(20),
CEhlersSmootherCalculator::CEhlersSmootherCalculator(void) :
m_period(20),
m_type(SUPERSMOOTHER),
m_source_type(SOURCE_PRICE),
m_applied_price(PRICE_CLOSE_STD)
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);
@@ -90,6 +100,32 @@ CEhlersSmootherCalculator::CEhlersSmootherCalculator(void) : m_period(20),
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 |
//+------------------------------------------------------------------+
@@ -100,6 +136,8 @@ bool CEhlersSmootherCalculator::Init(const int period, const ENUM_SMOOTHER_TYPE
m_type = type;
m_source_type = source_type;
m_applied_price = price_source;
PrecomputeCoefficients();
return true;
}
@@ -152,13 +190,13 @@ bool CEhlersSmootherCalculator::PreparePriceSeries(const int rates_total, const
m_price[i] = m_ha_low[i];
break;
case PRICE_HA_MEDIAN:
m_price[i] = (m_ha_high[i] + m_ha_low[i]) / 2.0;
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]) / 4.0;
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:
@@ -190,13 +228,13 @@ bool CEhlersSmootherCalculator::PreparePriceSeries(const int rates_total, const
m_price[i] = low[i];
break;
case PRICE_MEDIAN_STD:
m_price[i] = (high[i] + low[i]) / 2.0;
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]) / 4.0;
m_price[i] = (high[i] + low[i] + 2.0 * close[i]) * 0.25;
break;
case PRICE_CLOSE_STD:
default:
@@ -215,7 +253,7 @@ bool CEhlersSmootherCalculator::PreparePriceSeries(const int rates_total, const
}
//+------------------------------------------------------------------+
//| Main Incremental Calculation Loop |
//| High-Performance Incremental Calculation (True O(1)) |
//+------------------------------------------------------------------+
void CEhlersSmootherCalculator::Calculate(const int rates_total, const int prev_calculated,
const double &open[], const double &high[],
@@ -225,7 +263,7 @@ void CEhlersSmootherCalculator::Calculate(const int rates_total, const int prev_
if(rates_total < 4)
return;
//--- Safe allocation of destination array without wiping history
// Safe array resizing
if(ArraySize(filter_buffer) != rates_total)
{
ArrayResize(filter_buffer, rates_total);
@@ -237,16 +275,9 @@ void CEhlersSmootherCalculator::Calculate(const int rates_total, const int prev_
if(!PreparePriceSeries(rates_total, start_index, open, high, low, close))
return;
//--- Calculate Analytical DSP Filter Coefficients
double a1 = MathExp(-M_SQRT2 * M_PI / (double)m_period);
double b1 = 2.0 * a1 * MathCos(M_SQRT2 * M_PI / (double)m_period);
double c2 = b1;
double c3 = -a1 * a1;
double c1 = (m_type == SUPERSMOOTHER) ? (1.0 - c2 - c3) : ((1.0 + c2 - c3) / 4.0);
int loop_start = MathMax(3, start_index);
// Initialization for the seed bars strictly on fresh calculation
// Deterministic seeding strictly on initial calculation
if(prev_calculated == 0)
{
filter_buffer[0] = m_price[0];
@@ -255,19 +286,26 @@ void CEhlersSmootherCalculator::Calculate(const int rates_total, const int prev_
loop_start = 3;
}
// Recursive 2-Pole Difference Equation
for(int i = loop_start; i < rates_total; i++)
// Recursive 2-Pole Difference Equation (Pipelined FMA Multiplication)
if(m_type == SUPERSMOOTHER)
{
double f1 = filter_buffer[i - 1];
double f2 = filter_buffer[i - 2];
double current_f;
if(m_type == SUPERSMOOTHER)
current_f = c1 * (m_price[i] + m_price[i - 1]) / 2.0 + c2 * f1 + c3 * f2;
else // ULTIMATESMOOTHER
current_f = (1.0 - c1) * m_price[i] + (2.0 * c1 - c2) * m_price[i - 1] - (c1 + c3) * m_price[i - 2] + c2 * f1 + c3 * f2;
filter_buffer[i] = current_f;
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];
}
}
}