mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-21 03:58:04 +00:00
Add TRIMA implementation and benchmarks; optimize WMA with SIMD
- Introduced `TrimaVector` class for multi-period Triangular Moving Average (TRIMA) calculations, optimized for SIMD. - Implemented last-value substitution for invalid inputs in TRIMA. - Added methods for calculating TRIMA for entire series and individual updates. - Enhanced `Wma` class with periodic resync to prevent floating-point drift and introduced SIMD optimizations for performance. - Updated benchmark suite to include TRIMA calculations alongside existing SMA, EMA, and WMA benchmarks.
This commit is contained in:
+121
-5
@@ -14,7 +14,7 @@ namespace QuanTAlib.Benchmarks;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static void Main()
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var config = ManualConfig.Create(DefaultConfig.Instance)
|
||||
.AddJob(Job.ShortRun
|
||||
@@ -24,12 +24,20 @@ public static class Program
|
||||
.AddColumn(StatisticColumn.StdDev)
|
||||
.HideColumns(Column.Job, Column.Error, Column.RatioSD);
|
||||
|
||||
BenchmarkRunner.Run<IndicatorBenchmarks>(config);
|
||||
if (args.Length == 0)
|
||||
{
|
||||
BenchmarkRunner.Run<IndicatorBenchmarks>(config);
|
||||
}
|
||||
else
|
||||
{
|
||||
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MemoryDiagnoser]
|
||||
[MarkdownExporter, HtmlExporter]
|
||||
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
|
||||
public class IndicatorBenchmarks
|
||||
{
|
||||
private const int BarCount = 200_000;
|
||||
@@ -52,6 +60,9 @@ public class IndicatorBenchmarks
|
||||
private double[][] _tulipWmaInputs = null!;
|
||||
private double[] _tulipWmaOptions = null!;
|
||||
private double[][] _tulipWmaOutputs = null!;
|
||||
private double[][] _tulipTrimaInputs = null!;
|
||||
private double[] _tulipTrimaOptions = null!;
|
||||
private double[][] _tulipTrimaOutputs = null!;
|
||||
|
||||
// Pre-allocated outputs for QuanTAlib Span API
|
||||
private double[] _quantalibOutput = null!;
|
||||
@@ -98,55 +109,160 @@ public class IndicatorBenchmarks
|
||||
_tulipWmaOptions = new double[] { Period };
|
||||
_tulipWmaOutputs = new[] { new double[BarCount - smaLookback] };
|
||||
|
||||
_tulipTrimaInputs = new[] { _closeValues };
|
||||
_tulipTrimaOptions = new double[] { Period };
|
||||
_tulipTrimaOutputs = new[] { new double[BarCount - smaLookback] };
|
||||
|
||||
// Pre-allocate QuanTAlib output
|
||||
_quantalibOutput = new double[BarCount];
|
||||
}
|
||||
|
||||
// ==================== SMA ====================
|
||||
[BenchmarkCategory("SMA")]
|
||||
[Benchmark(Description = "QuanTAlib SMA (Span)")]
|
||||
public void QuanTAlib_Sma_Span() => Sma.Calculate(_closeValues.AsSpan(), _quantalibOutput.AsSpan(), Period);
|
||||
|
||||
[BenchmarkCategory("SMA")]
|
||||
[Benchmark(Description = "QuanTAlib SMA (TSeries)")]
|
||||
public TSeries QuanTAlib_Sma_TSeries() => Sma.Calculate(_closeTseries, Period);
|
||||
|
||||
[BenchmarkCategory("SMA")]
|
||||
[Benchmark(Description = "QuanTAlib SMA (Streaming)")]
|
||||
public void QuanTAlib_Sma_Streaming()
|
||||
{
|
||||
var sma = new Sma(Period);
|
||||
for (int i = 0; i < _closeValues.Length; i++)
|
||||
{
|
||||
_quantalibOutput[i] = sma.Update(new TValue(_closeTseries.Times[i], _closeValues[i])).Value;
|
||||
}
|
||||
}
|
||||
|
||||
[BenchmarkCategory("SMA")]
|
||||
[Benchmark(Description = "Tulip SMA")]
|
||||
public void Tulip_Sma() => Tulip.Indicators.sma.Run(_tulipSmaInputs, _tulipSmaOptions, _tulipSmaOutputs);
|
||||
|
||||
[BenchmarkCategory("SMA")]
|
||||
[Benchmark(Description = "TALib SMA")]
|
||||
public Core.RetCode TALib_Sma() => TALib.Functions.Sma<double>(_closeValues, 0..^0, _talibOutput, out _, Period);
|
||||
|
||||
[BenchmarkCategory("SMA")]
|
||||
[Benchmark(Description = "Skender SMA")]
|
||||
public List<SmaResult> Skender_Sma() => _quotes.GetSma(Period).ToList();
|
||||
public double Skender_Sma()
|
||||
{
|
||||
double sum = 0;
|
||||
foreach (var r in _quotes.GetSma(Period))
|
||||
{
|
||||
sum += (double)(r.Sma ?? 0);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
// ==================== EMA ====================
|
||||
[BenchmarkCategory("EMA")]
|
||||
[Benchmark(Description = "QuanTAlib EMA (Span)")]
|
||||
public void QuanTAlib_Ema_Span() => Ema.Calculate(_closeValues.AsSpan(), _quantalibOutput.AsSpan(), Period);
|
||||
|
||||
[BenchmarkCategory("EMA")]
|
||||
[Benchmark(Description = "QuanTAlib EMA (TSeries)")]
|
||||
public TSeries QuanTAlib_Ema_TSeries() => Ema.Calculate(_closeTseries, Period);
|
||||
|
||||
[BenchmarkCategory("EMA")]
|
||||
[Benchmark(Description = "QuanTAlib EMA (Streaming)")]
|
||||
public void QuanTAlib_Ema_Streaming()
|
||||
{
|
||||
var ema = new Ema(Period);
|
||||
for (int i = 0; i < _closeValues.Length; i++)
|
||||
{
|
||||
_quantalibOutput[i] = ema.Update(new TValue(_closeTseries.Times[i], _closeValues[i])).Value;
|
||||
}
|
||||
}
|
||||
|
||||
[BenchmarkCategory("EMA")]
|
||||
[Benchmark(Description = "Tulip EMA")]
|
||||
public void Tulip_Ema() => Tulip.Indicators.ema.Run(_tulipEmaInputs, _tulipEmaOptions, _tulipEmaOutputs);
|
||||
|
||||
[BenchmarkCategory("EMA")]
|
||||
[Benchmark(Description = "TALib EMA")]
|
||||
public Core.RetCode TALib_Ema() => TALib.Functions.Ema<double>(_closeValues, 0..^0, _talibOutput, out _, Period);
|
||||
|
||||
[BenchmarkCategory("EMA")]
|
||||
[Benchmark(Description = "Skender EMA")]
|
||||
public List<EmaResult> Skender_Ema() => _quotes.GetEma(Period).ToList();
|
||||
public double Skender_Ema()
|
||||
{
|
||||
double sum = 0;
|
||||
foreach (var r in _quotes.GetEma(Period))
|
||||
{
|
||||
sum += (double)(r.Ema ?? 0);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
// ==================== WMA ====================
|
||||
[BenchmarkCategory("WMA")]
|
||||
[Benchmark(Description = "QuanTAlib WMA (Span)")]
|
||||
public void QuanTAlib_Wma_Span() => Wma.Calculate(_closeValues.AsSpan(), _quantalibOutput.AsSpan(), Period);
|
||||
|
||||
[BenchmarkCategory("WMA")]
|
||||
[Benchmark(Description = "QuanTAlib WMA (TSeries)")]
|
||||
public TSeries QuanTAlib_Wma_TSeries() => Wma.Calculate(_closeTseries, Period);
|
||||
|
||||
[BenchmarkCategory("WMA")]
|
||||
[Benchmark(Description = "QuanTAlib WMA (Streaming)")]
|
||||
public void QuanTAlib_Wma_Streaming()
|
||||
{
|
||||
var wma = new Wma(Period);
|
||||
for (int i = 0; i < _closeValues.Length; i++)
|
||||
{
|
||||
_quantalibOutput[i] = wma.Update(new TValue(_closeTseries.Times[i], _closeValues[i])).Value;
|
||||
}
|
||||
}
|
||||
|
||||
[BenchmarkCategory("WMA")]
|
||||
[Benchmark(Description = "Tulip WMA")]
|
||||
public void Tulip_Wma() => Tulip.Indicators.wma.Run(_tulipWmaInputs, _tulipWmaOptions, _tulipWmaOutputs);
|
||||
|
||||
[BenchmarkCategory("WMA")]
|
||||
[Benchmark(Description = "TALib WMA")]
|
||||
public Core.RetCode TALib_Wma() => TALib.Functions.Wma<double>(_closeValues, 0..^0, _talibOutput, out _, Period);
|
||||
|
||||
[BenchmarkCategory("WMA")]
|
||||
[Benchmark(Description = "Skender WMA")]
|
||||
public List<WmaResult> Skender_Wma() => _quotes.GetWma(Period).ToList();
|
||||
public double Skender_Wma()
|
||||
{
|
||||
double sum = 0;
|
||||
foreach (var r in _quotes.GetWma(Period))
|
||||
{
|
||||
sum += (double)(r.Wma ?? 0);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
// ==================== TRIMA ====================
|
||||
[BenchmarkCategory("TRIMA")]
|
||||
[Benchmark(Description = "QuanTAlib TRIMA (Span)")]
|
||||
public void QuanTAlib_Trima_Span() => Trima.Calculate(_closeValues.AsSpan(), _quantalibOutput.AsSpan(), Period);
|
||||
|
||||
[BenchmarkCategory("TRIMA")]
|
||||
[Benchmark(Description = "QuanTAlib TRIMA (TSeries)")]
|
||||
public TSeries QuanTAlib_Trima_TSeries() => Trima.Calculate(_closeTseries, Period);
|
||||
|
||||
[BenchmarkCategory("TRIMA")]
|
||||
[Benchmark(Description = "QuanTAlib TRIMA (Streaming)")]
|
||||
public void QuanTAlib_Trima_Streaming()
|
||||
{
|
||||
var trima = new Trima(Period);
|
||||
for (int i = 0; i < _closeValues.Length; i++)
|
||||
{
|
||||
_quantalibOutput[i] = trima.Update(new TValue(_closeTseries.Times[i], _closeValues[i])).Value;
|
||||
}
|
||||
}
|
||||
|
||||
[BenchmarkCategory("TRIMA")]
|
||||
[Benchmark(Description = "Tulip TRIMA")]
|
||||
public void Tulip_Trima() => Tulip.Indicators.trima.Run(_tulipTrimaInputs, _tulipTrimaOptions, _tulipTrimaOutputs);
|
||||
|
||||
[BenchmarkCategory("TRIMA")]
|
||||
[Benchmark(Description = "TALib TRIMA")]
|
||||
public Core.RetCode TALib_Trima() => TALib.Functions.Trima<double>(_closeValues, 0..^0, _talibOutput, out _, Period);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user