mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-26 06:18:05 +00:00
Add Span API for SMA, EMA, and WMA with zero-allocation performance improvements
- Implemented zero-allocation methods for SMA, EMA, and WMA calculations using ReadOnlySpan and Span. - Added unit tests for Span API to validate input, match TSeries calculations, handle NaN values, and ensure zero allocation. - Enhanced documentation to include usage examples for the new Span API. - Introduced performance benchmarks comparing the new Span API against existing TSeries implementations and other libraries.
This commit is contained in:
@@ -186,6 +186,61 @@ public class Ema
|
||||
return ema.Update(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates EMA in-place using period, writing results to pre-allocated output span.
|
||||
/// Zero-allocation method for maximum performance.
|
||||
/// Alpha = 2 / (period + 1)
|
||||
/// </summary>
|
||||
/// <param name="source">Input values</param>
|
||||
/// <param name="output">Output span (must be same length as source)</param>
|
||||
/// <param name="period">EMA period (must be > 0)</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
if (period <= 0)
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
|
||||
double alpha = 2.0 / (period + 1);
|
||||
Calculate(source, output, alpha);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates EMA in-place using alpha, writing results to pre-allocated output span.
|
||||
/// Zero-allocation method for maximum performance.
|
||||
/// </summary>
|
||||
/// <param name="source">Input values</param>
|
||||
/// <param name="output">Output span (must be same length as source)</param>
|
||||
/// <param name="alpha">Smoothing factor (0 < alpha <= 1)</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, double alpha)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
throw new ArgumentException("Source and output must have the same length");
|
||||
if (alpha <= 0 || alpha > 1)
|
||||
throw new ArgumentException("Alpha must be between 0 and 1", nameof(alpha));
|
||||
|
||||
int len = source.Length;
|
||||
double ema = 0;
|
||||
double e = 1.0;
|
||||
double lastValid = 0;
|
||||
double oneMinusAlpha = 1.0 - alpha;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
double val = source[i];
|
||||
if (!double.IsFinite(val))
|
||||
val = lastValid;
|
||||
else
|
||||
lastValid = val;
|
||||
|
||||
ema += alpha * (val - ema);
|
||||
e *= oneMinusAlpha;
|
||||
|
||||
// Bias correction until warmed up
|
||||
output[i] = e > 1e-10 ? ema / (1.0 - e) : ema;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the EMA state.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user