mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-25 22:08:05 +00:00
feat: Add Cumulative Moving Average (CMA) implementation with detailed documentation
- Introduced Cma class for calculating the Cumulative Moving Average using Welford's algorithm with FMA for precision. - Added methods for batch processing and streaming updates. - Implemented a comprehensive markdown documentation for CMA, covering its mathematical foundation, performance profile, and use cases. - Enhanced existing trend indicators (Bessel, Butter, Htit, Jma, Mama, Ssf, Vidya) with FMA for improved numerical stability and precision. - Updated Adosc to utilize a single-pass algorithm for performance optimization. - Fixed date initialization in benchmarks to ensure UTC consistency.
This commit is contained in:
@@ -74,4 +74,4 @@ Trend indicators are the bread and butter of technical analysis—and often just
|
||||
| YZVAMA | Yang-Zhang Volatility Adjusted MA | |
|
||||
| ZLDEMA | Zero-Lag Double Exponential MA | |
|
||||
| ZLEMA | Zero-Lag Exponential MA | |
|
||||
| ZLTEMA | Zero-Lag Triple Exponential MA | |
|
||||
| ZLTEMA | Zero-Lag Triple Exponential MA | |
|
||||
@@ -135,7 +135,8 @@ public sealed class Bessel : AbstractBase, IDisposable
|
||||
|
||||
double filt = _state.Count < 3
|
||||
? val
|
||||
: (_c1 * val) + (_c2 * _state.F1) + (_c3 * _state.F2);
|
||||
: Math.FusedMultiplyAdd(_c3, _state.F2,
|
||||
Math.FusedMultiplyAdd(_c2, _state.F1, _c1 * val));
|
||||
|
||||
_state.F2 = _state.F1;
|
||||
_state.F1 = filt;
|
||||
@@ -184,7 +185,8 @@ public sealed class Bessel : AbstractBase, IDisposable
|
||||
|
||||
double filt = _state.Count < 3
|
||||
? val
|
||||
: (_c1 * val) + (_c2 * _state.F1) + (_c3 * _state.F2);
|
||||
: Math.FusedMultiplyAdd(_c3, _state.F2,
|
||||
Math.FusedMultiplyAdd(_c2, _state.F1, _c1 * val));
|
||||
|
||||
_state.F2 = _state.F1;
|
||||
_state.F1 = filt;
|
||||
@@ -276,7 +278,8 @@ public sealed class Bessel : AbstractBase, IDisposable
|
||||
|
||||
double filt = state.Count < 3
|
||||
? val
|
||||
: (c1 * val) + (c2 * state.F1) + (c3 * state.F2);
|
||||
: Math.FusedMultiplyAdd(c3, state.F2,
|
||||
Math.FusedMultiplyAdd(c2, state.F1, c1 * val));
|
||||
|
||||
state.F2 = state.F1;
|
||||
state.F1 = filt;
|
||||
|
||||
@@ -113,9 +113,14 @@ public sealed class Butter : AbstractBase
|
||||
}
|
||||
|
||||
double x = input.Value;
|
||||
// IIR: y = (b0*x + b1*x1 + b2*x2 - a1*y1 - a2*y2) * invA0
|
||||
// Using chained FMA for precision
|
||||
double y = _state.Count < 2
|
||||
? x
|
||||
: (_b0 * x + _b1 * _state.X1 + _b2 * _state.X2 - _a1 * _state.Y1 - _a2 * _state.Y2) * _invA0;
|
||||
: Math.FusedMultiplyAdd(-_a2, _state.Y2,
|
||||
Math.FusedMultiplyAdd(-_a1, _state.Y1,
|
||||
Math.FusedMultiplyAdd(_b2, _state.X2,
|
||||
Math.FusedMultiplyAdd(_b1, _state.X1, _b0 * x)))) * _invA0;
|
||||
|
||||
// Update state
|
||||
_state.X2 = _state.X1;
|
||||
@@ -185,9 +190,14 @@ public sealed class Butter : AbstractBase
|
||||
destination[i] = i > 0 ? destination[i - 1] : initialLast;
|
||||
continue;
|
||||
}
|
||||
// IIR: y = (b0*x + b1*x1 + b2*x2 - a1*y1 - a2*y2) * invA0
|
||||
// Using chained FMA for precision
|
||||
double y = i < 2
|
||||
? x
|
||||
: (b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2) * invA0;
|
||||
: Math.FusedMultiplyAdd(-a2, y2,
|
||||
Math.FusedMultiplyAdd(-a1, y1,
|
||||
Math.FusedMultiplyAdd(b2, x2,
|
||||
Math.FusedMultiplyAdd(b1, x1, b0 * x)))) * invA0;
|
||||
|
||||
x2 = x1;
|
||||
x1 = x;
|
||||
|
||||
+19
-19
@@ -161,17 +161,17 @@ public sealed class Htit : AbstractBase
|
||||
double i2_val = i1 - jQ;
|
||||
double q2_val = q1 + jI;
|
||||
|
||||
// Smooth i2, q2
|
||||
_state.I2 = 0.2 * i2_val + 0.8 * _p_state.I2;
|
||||
_state.Q2 = 0.2 * q2_val + 0.8 * _p_state.Q2;
|
||||
// Smooth i2, q2 (using FMA for precision)
|
||||
_state.I2 = Math.FusedMultiplyAdd(0.2, i2_val, 0.8 * _p_state.I2);
|
||||
_state.Q2 = Math.FusedMultiplyAdd(0.2, q2_val, 0.8 * _p_state.Q2);
|
||||
|
||||
// 6. Homodyne Discriminator
|
||||
double re_val = (_state.I2 * _p_state.I2) + (_state.Q2 * _p_state.Q2);
|
||||
double im_val = (_state.I2 * _p_state.Q2) - (_state.Q2 * _p_state.I2);
|
||||
double re_val = Math.FusedMultiplyAdd(_state.I2, _p_state.I2, _state.Q2 * _p_state.Q2);
|
||||
double im_val = Math.FusedMultiplyAdd(_state.I2, _p_state.Q2, -_state.Q2 * _p_state.I2);
|
||||
|
||||
// Smooth re, im
|
||||
_state.Re = 0.2 * re_val + 0.8 * _p_state.Re;
|
||||
_state.Im = 0.2 * im_val + 0.8 * _p_state.Im;
|
||||
// Smooth re, im (using FMA)
|
||||
_state.Re = Math.FusedMultiplyAdd(0.2, re_val, 0.8 * _p_state.Re);
|
||||
_state.Im = Math.FusedMultiplyAdd(0.2, im_val, 0.8 * _p_state.Im);
|
||||
|
||||
// 7. Calculate Period
|
||||
double angle = Math.Atan2(_state.Im, _state.Re);
|
||||
@@ -190,9 +190,9 @@ public sealed class Htit : AbstractBase
|
||||
if (period < 6) period = 6;
|
||||
if (period > 50) period = 50;
|
||||
|
||||
// Smooth the period
|
||||
_state.Period = 0.2 * period + 0.8 * prevPeriod;
|
||||
_state.SmoothPeriod = 0.33 * _state.Period + 0.67 * _p_state.SmoothPeriod;
|
||||
// Smooth the period (using FMA)
|
||||
_state.Period = Math.FusedMultiplyAdd(0.2, period, 0.8 * prevPeriod);
|
||||
_state.SmoothPeriod = Math.FusedMultiplyAdd(0.33, _state.Period, 0.67 * _p_state.SmoothPeriod);
|
||||
|
||||
// 8. Instantaneous Trend
|
||||
int dcPeriods = (int)(double.IsNaN(_state.SmoothPeriod) ? 0 : _state.SmoothPeriod + 0.5);
|
||||
@@ -376,15 +376,15 @@ public sealed class Htit : AbstractBase
|
||||
double i2_val = i1 - jQ;
|
||||
double q2_val = q1 + jI;
|
||||
|
||||
i2 = 0.2 * i2_val + 0.8 * p_i2;
|
||||
q2 = 0.2 * q2_val + 0.8 * p_q2;
|
||||
i2 = Math.FusedMultiplyAdd(0.2, i2_val, 0.8 * p_i2);
|
||||
q2 = Math.FusedMultiplyAdd(0.2, q2_val, 0.8 * p_q2);
|
||||
|
||||
// 6. Homodyne Discriminator
|
||||
double re_val = (i2 * p_i2) + (q2 * p_q2);
|
||||
double im_val = (i2 * p_q2) - (q2 * p_i2);
|
||||
double re_val = Math.FusedMultiplyAdd(i2, p_i2, q2 * p_q2);
|
||||
double im_val = Math.FusedMultiplyAdd(i2, p_q2, -q2 * p_i2);
|
||||
|
||||
re = 0.2 * re_val + 0.8 * p_re;
|
||||
im = 0.2 * im_val + 0.8 * p_im;
|
||||
re = Math.FusedMultiplyAdd(0.2, re_val, 0.8 * p_re);
|
||||
im = Math.FusedMultiplyAdd(0.2, im_val, 0.8 * p_im);
|
||||
|
||||
// 7. Calculate Period
|
||||
double angle = Math.Atan2(im, re);
|
||||
@@ -402,8 +402,8 @@ public sealed class Htit : AbstractBase
|
||||
if (newPeriod < 6) newPeriod = 6;
|
||||
if (newPeriod > 50) newPeriod = 50;
|
||||
|
||||
period = 0.2 * newPeriod + 0.8 * p_period;
|
||||
smoothPeriod = 0.33 * period + 0.67 * p_smoothPeriod;
|
||||
period = Math.FusedMultiplyAdd(0.2, newPeriod, 0.8 * p_period);
|
||||
smoothPeriod = Math.FusedMultiplyAdd(0.33, period, 0.67 * p_smoothPeriod);
|
||||
|
||||
// 8. Instantaneous Trend
|
||||
double safeSmooth = double.IsNaN(smoothPeriod) ? 0 : smoothPeriod;
|
||||
|
||||
@@ -216,13 +216,17 @@ public sealed class Jma : AbstractBase
|
||||
prevJma = value;
|
||||
|
||||
double alpha = Math.Exp(_logLengthDivider * d);
|
||||
double decay = 1.0 - alpha;
|
||||
double alpha2 = alpha * alpha;
|
||||
|
||||
double c0 = (1.0 - alpha) * value + alpha * _state.LastC0;
|
||||
double c8 = (value - c0) * (1.0 - _lengthDivider) + _lengthDivider * _state.LastC8;
|
||||
double a8 = (_phaseParam * c8 + c0 - prevJma) *
|
||||
(alpha * (-2.0) + alpha2 + 1.0) +
|
||||
alpha2 * _state.LastA8;
|
||||
// EMA smoothing: c0 = decay * value + alpha * LastC0
|
||||
double c0 = Math.FusedMultiplyAdd(_state.LastC0, alpha, decay * value);
|
||||
// EMA smoothing: c8 = (value - c0) * (1 - lengthDivider) + lengthDivider * LastC8
|
||||
double lengthDecay = 1.0 - _lengthDivider;
|
||||
double c8 = Math.FusedMultiplyAdd(_state.LastC8, _lengthDivider, lengthDecay * (value - c0));
|
||||
// IIR filter: a8 = (phase * c8 + c0 - prevJma) * coef + alpha2 * LastA8
|
||||
double coef = Math.FusedMultiplyAdd(alpha, -2.0, alpha2 + 1.0);
|
||||
double a8 = Math.FusedMultiplyAdd(_state.LastA8, alpha2, Math.FusedMultiplyAdd(_phaseParam, c8, c0 - prevJma) * coef);
|
||||
|
||||
double jma = prevJma + a8;
|
||||
|
||||
|
||||
+20
-20
@@ -170,17 +170,17 @@ public sealed class Mama : AbstractBase
|
||||
double i2_val = i1 - jQ;
|
||||
double q2_val = q1 + jI;
|
||||
|
||||
// Smooth i2, q2
|
||||
_state.I2 = SmoothCoef * i2_val + SmoothPrev * _p_state.I2;
|
||||
_state.Q2 = SmoothCoef * q2_val + SmoothPrev * _p_state.Q2;
|
||||
// Smooth i2, q2 (using FMA for precision)
|
||||
_state.I2 = Math.FusedMultiplyAdd(SmoothCoef, i2_val, SmoothPrev * _p_state.I2);
|
||||
_state.Q2 = Math.FusedMultiplyAdd(SmoothCoef, q2_val, SmoothPrev * _p_state.Q2);
|
||||
|
||||
// Homodyne discriminator
|
||||
double re_val = (_state.I2 * _p_state.I2) + (_state.Q2 * _p_state.Q2);
|
||||
double im_val = (_state.I2 * _p_state.Q2) - (_state.Q2 * _p_state.I2);
|
||||
double re_val = Math.FusedMultiplyAdd(_state.I2, _p_state.I2, _state.Q2 * _p_state.Q2);
|
||||
double im_val = Math.FusedMultiplyAdd(_state.I2, _p_state.Q2, -_state.Q2 * _p_state.I2);
|
||||
|
||||
// Smooth re, im
|
||||
_state.Re = SmoothCoef * re_val + SmoothPrev * _p_state.Re;
|
||||
_state.Im = SmoothCoef * im_val + SmoothPrev * _p_state.Im;
|
||||
// Smooth re, im (using FMA)
|
||||
_state.Re = Math.FusedMultiplyAdd(SmoothCoef, re_val, SmoothPrev * _p_state.Re);
|
||||
_state.Im = Math.FusedMultiplyAdd(SmoothCoef, im_val, SmoothPrev * _p_state.Im);
|
||||
|
||||
// Calculate Period
|
||||
double angle = Math.Atan2(_state.Im, _state.Re);
|
||||
@@ -198,8 +198,8 @@ public sealed class Mama : AbstractBase
|
||||
if (period < MinPeriod) period = MinPeriod;
|
||||
if (period > MaxPeriod) period = MaxPeriod;
|
||||
|
||||
// Smooth Period
|
||||
_state.Period = SmoothCoef * period + SmoothPrev * _p_state.Period;
|
||||
// Smooth Period (using FMA)
|
||||
_state.Period = Math.FusedMultiplyAdd(SmoothCoef, period, SmoothPrev * _p_state.Period);
|
||||
|
||||
// Phase calculation
|
||||
_state.Phase = Math.Atan2(q1, i1);
|
||||
@@ -380,17 +380,17 @@ public sealed class Mama : AbstractBase
|
||||
double i2_val = i1 - jQ;
|
||||
double q2_val = q1 + jI;
|
||||
|
||||
// Smooth i2, q2
|
||||
i2 = SmoothCoef * i2_val + SmoothPrev * p_i2;
|
||||
q2 = SmoothCoef * q2_val + SmoothPrev * p_q2;
|
||||
// Smooth i2, q2 (using FMA for precision)
|
||||
i2 = Math.FusedMultiplyAdd(SmoothCoef, i2_val, SmoothPrev * p_i2);
|
||||
q2 = Math.FusedMultiplyAdd(SmoothCoef, q2_val, SmoothPrev * p_q2);
|
||||
|
||||
// Homodyne discriminator
|
||||
double re_val = (i2 * p_i2) + (q2 * p_q2);
|
||||
double im_val = (i2 * p_q2) - (q2 * p_i2);
|
||||
double re_val = Math.FusedMultiplyAdd(i2, p_i2, q2 * p_q2);
|
||||
double im_val = Math.FusedMultiplyAdd(i2, p_q2, -q2 * p_i2);
|
||||
|
||||
// Smooth re, im
|
||||
re = SmoothCoef * re_val + SmoothPrev * p_re;
|
||||
im = SmoothCoef * im_val + SmoothPrev * p_im;
|
||||
// Smooth re, im (using FMA)
|
||||
re = Math.FusedMultiplyAdd(SmoothCoef, re_val, SmoothPrev * p_re);
|
||||
im = Math.FusedMultiplyAdd(SmoothCoef, im_val, SmoothPrev * p_im);
|
||||
|
||||
// Calculate Period
|
||||
double angle = Math.Atan2(im, re);
|
||||
@@ -408,8 +408,8 @@ public sealed class Mama : AbstractBase
|
||||
if (newPeriod < MinPeriod) newPeriod = MinPeriod;
|
||||
if (newPeriod > MaxPeriod) newPeriod = MaxPeriod;
|
||||
|
||||
// Smooth Period
|
||||
period = SmoothCoef * newPeriod + SmoothPrev * p_period;
|
||||
// Smooth Period (using FMA)
|
||||
period = Math.FusedMultiplyAdd(SmoothCoef, newPeriod, SmoothPrev * p_period);
|
||||
|
||||
// Phase calculation
|
||||
double phase = Math.Atan2(q1, i1);
|
||||
|
||||
@@ -127,7 +127,8 @@ public sealed class Ssf : AbstractBase
|
||||
|
||||
double ssf = (_state.Count < 4)
|
||||
? val
|
||||
: (_c1 * (val + _state.PrevInput) * 0.5) + (_c2 * _state.Ssf1) + (_c3 * _state.Ssf2);
|
||||
: Math.FusedMultiplyAdd(_c3, _state.Ssf2,
|
||||
Math.FusedMultiplyAdd(_c2, _state.Ssf1, _c1 * (val + _state.PrevInput) * 0.5));
|
||||
|
||||
_state.Ssf2 = _state.Ssf1;
|
||||
_state.Ssf1 = ssf;
|
||||
@@ -177,7 +178,8 @@ public sealed class Ssf : AbstractBase
|
||||
|
||||
double ssf = (_state.Count < 4)
|
||||
? val
|
||||
: (_c1 * (val + _state.PrevInput) * 0.5) + (_c2 * _state.Ssf1) + (_c3 * _state.Ssf2);
|
||||
: Math.FusedMultiplyAdd(_c3, _state.Ssf2,
|
||||
Math.FusedMultiplyAdd(_c2, _state.Ssf1, _c1 * (val + _state.PrevInput) * 0.5));
|
||||
|
||||
_state.Ssf2 = _state.Ssf1;
|
||||
_state.Ssf1 = ssf;
|
||||
@@ -268,7 +270,8 @@ public sealed class Ssf : AbstractBase
|
||||
|
||||
double ssf = (state.Count < 4)
|
||||
? val
|
||||
: (c1 * (val + state.PrevInput) * 0.5) + (c2 * state.Ssf1) + (c3 * state.Ssf2);
|
||||
: Math.FusedMultiplyAdd(c3, state.Ssf2,
|
||||
Math.FusedMultiplyAdd(c2, state.Ssf1, c1 * (val + state.PrevInput) * 0.5));
|
||||
|
||||
state.Ssf2 = state.Ssf1;
|
||||
state.Ssf1 = ssf;
|
||||
|
||||
@@ -131,7 +131,8 @@ public sealed class Vidya : AbstractBase, IDisposable
|
||||
}
|
||||
|
||||
double dynamicAlpha = _alpha * vi;
|
||||
_state.CurrentVidya = dynamicAlpha * price + (1.0 - dynamicAlpha) * _state.LastVidya;
|
||||
double dynamicDecay = 1.0 - dynamicAlpha;
|
||||
_state.CurrentVidya = Math.FusedMultiplyAdd(_state.LastVidya, dynamicDecay, dynamicAlpha * price);
|
||||
_state.CurrentClose = price;
|
||||
|
||||
Last = new TValue(input.Time, _state.CurrentVidya);
|
||||
@@ -239,7 +240,8 @@ public sealed class Vidya : AbstractBase, IDisposable
|
||||
}
|
||||
|
||||
double dynamicAlpha = _alpha * vi;
|
||||
double currentVidya = dynamicAlpha * price + (1.0 - dynamicAlpha) * lastVidya;
|
||||
double dynamicDecay = 1.0 - dynamicAlpha;
|
||||
double currentVidya = Math.FusedMultiplyAdd(lastVidya, dynamicDecay, dynamicAlpha * price);
|
||||
|
||||
_state.CurrentVidya = currentVidya;
|
||||
_state.CurrentClose = price;
|
||||
@@ -337,7 +339,8 @@ public sealed class Vidya : AbstractBase, IDisposable
|
||||
}
|
||||
|
||||
double dynamicAlpha = alpha * vi;
|
||||
double currentVidya = dynamicAlpha * price + (1.0 - dynamicAlpha) * lastVidya;
|
||||
double dynamicDecay = 1.0 - dynamicAlpha;
|
||||
double currentVidya = Math.FusedMultiplyAdd(lastVidya, dynamicDecay, dynamicAlpha * price);
|
||||
|
||||
output[i] = currentVidya;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user