mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-24 05:28:05 +00:00
normalization of methods
This commit is contained in:
@@ -28,11 +28,11 @@ public class BesselTests
|
||||
double[] output = new double[5];
|
||||
|
||||
var exLength = Assert.Throws<ArgumentException>(() =>
|
||||
Bessel.Calculate(source.AsSpan(), output.AsSpan(), 1));
|
||||
Bessel.Batch(source.AsSpan(), output.AsSpan(), 1));
|
||||
Assert.Equal("length", exLength.ParamName);
|
||||
|
||||
var exLengthZero = Assert.Throws<ArgumentException>(() =>
|
||||
Bessel.Calculate(source.AsSpan(), output.AsSpan(), 0));
|
||||
Bessel.Batch(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Equal("length", exLengthZero.ParamName);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class BesselTests
|
||||
double[] wrongSizeOutput = new double[3];
|
||||
|
||||
var ex = Assert.Throws<ArgumentException>(() =>
|
||||
Bessel.Calculate(source.AsSpan(), wrongSizeOutput.AsSpan(), 14));
|
||||
Bessel.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 14));
|
||||
Assert.Equal("output", ex.ParamName);
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ public class BesselTests
|
||||
|
||||
var tseriesResult = Bessel.Calculate(series, 14).Results;
|
||||
|
||||
Bessel.Calculate(source.AsSpan(), output.AsSpan(), 14);
|
||||
Bessel.Batch(source.AsSpan(), output.AsSpan(), 14);
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
@@ -276,7 +276,7 @@ public class BesselTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Bessel.Calculate(spanInput, spanOutput, length);
|
||||
Bessel.Batch(spanInput, spanOutput, length);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
|
||||
@@ -47,7 +47,7 @@ public sealed class BesselValidationTests : IDisposable
|
||||
// Same data via Span API
|
||||
var src = _testData.Data.Values.ToArray();
|
||||
var outSpan = new double[src.Length];
|
||||
Bessel.Calculate(src.AsSpan(), outSpan.AsSpan(), length);
|
||||
Bessel.Batch(src.AsSpan(), outSpan.AsSpan(), length);
|
||||
|
||||
// Verify last window for convergence and consistency
|
||||
ValidationHelper.VerifyData(qResult, outSpan, lookback: 0, skip: length, tolerance: ValidationHelper.DefaultTolerance);
|
||||
|
||||
@@ -481,12 +481,6 @@ public sealed class Bessel : AbstractBase
|
||||
/// <para><b>Complexity:</b> O(n) where n = source.Count</para>
|
||||
/// <para>The returned indicator maintains state and can continue processing new values.</para>
|
||||
/// </remarks>
|
||||
public static (TSeries Results, Bessel Indicator) Calculate(TSeries source, int length)
|
||||
{
|
||||
var bessel = new Bessel(length);
|
||||
TSeries results = bessel.Update(source);
|
||||
return (results, bessel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates filtered values for a span of doubles (stateless batch processing).
|
||||
@@ -504,7 +498,7 @@ public sealed class Bessel : AbstractBase
|
||||
/// <para>SIMD optimization is not applicable due to IIR recursive data dependency.</para>
|
||||
/// </remarks>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int length)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int length)
|
||||
{
|
||||
if (length < 2)
|
||||
{
|
||||
@@ -531,6 +525,12 @@ public sealed class Bessel : AbstractBase
|
||||
|
||||
CalculateCore(source, output, c1, c2, c3, length, ref state);
|
||||
}
|
||||
public static (TSeries Results, Bessel Indicator) Calculate(TSeries source, int length)
|
||||
{
|
||||
var bessel = new Bessel(length);
|
||||
TSeries results = bessel.Update(source);
|
||||
return (results, bessel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for reactive updates from subscribed publishers.
|
||||
@@ -567,4 +567,4 @@ public sealed class Bessel : AbstractBase
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@ public class BilateralTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Bilateral.Calculate(spanInput, spanOutput, period);
|
||||
Bilateral.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
|
||||
@@ -96,7 +96,7 @@ public sealed class BilateralValidationTests : IDisposable
|
||||
{
|
||||
// Calculate QuanTAlib Bilateral (Span API)
|
||||
double[] qOutput = new double[sourceData.Length];
|
||||
global::QuanTAlib.Bilateral.Calculate(sourceData.AsSpan(), qOutput.AsSpan(), period, sigmaSRatio, sigmaRMult);
|
||||
global::QuanTAlib.Bilateral.Batch(sourceData.AsSpan(), qOutput.AsSpan(), period, sigmaSRatio, sigmaRMult);
|
||||
|
||||
// Calculate Reference Bilateral
|
||||
var refResult = GetReferenceData(period, sigmaSRatio, sigmaRMult);
|
||||
|
||||
@@ -332,17 +332,11 @@ public sealed class Bilateral : AbstractBase
|
||||
/// <summary>
|
||||
/// Calculates bilateral filter values for a TSeries and returns both results and a primed indicator.
|
||||
/// </summary>
|
||||
public static (TSeries Results, Bilateral Indicator) Calculate(TSeries source, int period, double sigmaSRatio = 0.5, double sigmaRMult = 1.0)
|
||||
{
|
||||
var indicator = new Bilateral(period, sigmaSRatio, sigmaRMult);
|
||||
var results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates bilateral filter values using spans (zero allocation in hot path).
|
||||
/// </summary>
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> destination, int period, double sigmaSRatio = 0.5, double sigmaRMult = 1.0)
|
||||
private static void BatchCore(ReadOnlySpan<double> source, Span<double> destination, int period, double sigmaSRatio = 0.5, double sigmaRMult = 1.0)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
@@ -520,7 +514,13 @@ public sealed class Bilateral : AbstractBase
|
||||
/// </summary>
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> destination, int period, double sigmaSRatio = 0.5, double sigmaRMult = 1.0)
|
||||
{
|
||||
Calculate(source, destination, period, sigmaSRatio, sigmaRMult);
|
||||
BatchCore(source, destination, period, sigmaSRatio, sigmaRMult);
|
||||
}
|
||||
public static (TSeries Results, Bilateral Indicator) Calculate(TSeries source, int period, double sigmaSRatio = 0.5, double sigmaRMult = 1.0)
|
||||
{
|
||||
var indicator = new Bilateral(period, sigmaSRatio, sigmaRMult);
|
||||
var results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -534,4 +534,4 @@ public sealed class Bilateral : AbstractBase
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public class BpfTests
|
||||
|
||||
// 1. Span Mode
|
||||
double[] spanOutput = new double[series.Count];
|
||||
Bpf.Calculate(series.Values.ToArray(), spanOutput, lowerPeriod, upperPeriod);
|
||||
Bpf.Batch(series.Values.ToArray(), spanOutput, lowerPeriod, upperPeriod);
|
||||
|
||||
// 2. TSeries Batch Mode
|
||||
var bpfBatch = new Bpf(lowerPeriod, upperPeriod);
|
||||
@@ -111,7 +111,7 @@ public class BpfTests
|
||||
double[] input = Enumerable.Repeat(100.0, 500).ToArray();
|
||||
double[] output = new double[500];
|
||||
|
||||
Bpf.Calculate(input, output, 10, 20);
|
||||
Bpf.Batch(input, output, 10, 20);
|
||||
|
||||
// Last value should be close to 0
|
||||
Assert.True(Math.Abs(output[^1]) < 1e-6);
|
||||
|
||||
@@ -32,9 +32,9 @@ public class BpfValidationTests
|
||||
double[] out100 = new double[T];
|
||||
|
||||
// Instantiate BPF with LowerPeriod=40 (HP cutoff), UpperPeriod=10 (LP cutoff)
|
||||
Bpf.Calculate(sine5, out5, 40, 10);
|
||||
Bpf.Calculate(sine15, out15, 40, 10);
|
||||
Bpf.Calculate(sine100, out100, 40, 10);
|
||||
Bpf.Batch(sine5, out5, 40, 10);
|
||||
Bpf.Batch(sine15, out15, 40, 10);
|
||||
Bpf.Batch(sine100, out100, 40, 10);
|
||||
|
||||
// Analysis of results (last 100 samples to avoid warmup)
|
||||
double amp5 = GetAmplitude(out5);
|
||||
|
||||
+28
-14
@@ -115,7 +115,7 @@ public sealed class Bpf : AbstractBase
|
||||
double[] values = source.Values.ToArray();
|
||||
double[] results = new double[values.Length];
|
||||
|
||||
Calculate(values, results, LowerPeriod, UpperPeriod);
|
||||
Batch(values, results, LowerPeriod, UpperPeriod);
|
||||
|
||||
TSeries output = [];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
@@ -181,24 +181,15 @@ public sealed class Bpf : AbstractBase
|
||||
return Last;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
public static TSeries Batch(TSeries source, int lowerPeriod, int upperPeriod)
|
||||
{
|
||||
_state = default;
|
||||
_state.LastValid = double.NaN;
|
||||
_p_state = default;
|
||||
Last = default;
|
||||
var indicator = new Bpf(lowerPeriod, upperPeriod);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
foreach (double val in source)
|
||||
{
|
||||
Update(new TValue(DateTime.UtcNow, val), isNew: true);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int lowerPeriod, int upperPeriod)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int lowerPeriod, int upperPeriod)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
@@ -267,6 +258,29 @@ public sealed class Bpf : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_state = default;
|
||||
_state.LastValid = double.NaN;
|
||||
_p_state = default;
|
||||
Last = default;
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
foreach (double val in source)
|
||||
{
|
||||
Update(new TValue(DateTime.UtcNow, val), isNew: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Bpf Indicator) Calculate(TSeries source, int lowerPeriod, int upperPeriod)
|
||||
{
|
||||
var indicator = new Bpf(lowerPeriod, upperPeriod);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the source publisher if one was provided during construction.
|
||||
/// </summary>
|
||||
|
||||
@@ -21,7 +21,7 @@ public class ButterTests
|
||||
{
|
||||
var source = new double[10];
|
||||
var destination = new double[5];
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Butter.Calculate(source, destination, 5, double.NaN));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Butter.Batch(source, destination, 5, double.NaN));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -79,7 +79,7 @@ public class ButterTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Butter.Calculate(spanInput, spanOutput, period, double.NaN);
|
||||
Butter.Batch(spanInput, spanOutput, period, double.NaN);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public sealed class Butter : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
@@ -146,7 +147,7 @@ public sealed class Butter : AbstractBase
|
||||
{
|
||||
var result = new TSeries();
|
||||
Span<double> output = new double[source.Count];
|
||||
Calculate(source.Values, output, _period, double.NaN);
|
||||
Batch(source.Values, output, _period, double.NaN);
|
||||
|
||||
for (int i = 0; i < source.Count; i++)
|
||||
{
|
||||
@@ -168,7 +169,13 @@ public sealed class Butter : AbstractBase
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> destination, int period, double initialLast)
|
||||
public static TSeries Batch(TSeries source, int period)
|
||||
{
|
||||
var indicator = new Butter(period);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> destination, int period, double initialLast)
|
||||
{
|
||||
if (period < 2)
|
||||
{
|
||||
@@ -216,6 +223,12 @@ public sealed class Butter : AbstractBase
|
||||
destination[i] = y;
|
||||
}
|
||||
}
|
||||
public static (TSeries Results, Butter Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var indicator = new Butter(period);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the source publisher if one was provided during construction.
|
||||
@@ -228,4 +241,4 @@ public sealed class Butter : AbstractBase
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public class Cheby1Tests
|
||||
|
||||
// Span
|
||||
var spanResults = new double[count];
|
||||
Cheby1.Calculate(values, spanResults, period, 1.0);
|
||||
Cheby1.Batch(values, spanResults, period, 1.0);
|
||||
|
||||
// Compare
|
||||
for (int i = 0; i < count; i++)
|
||||
|
||||
@@ -118,7 +118,7 @@ public sealed class Cheby1 : AbstractBase
|
||||
double[] values = source.Values.ToArray();
|
||||
double[] results = new double[values.Length];
|
||||
|
||||
Calculate(values, results, Period, Ripple);
|
||||
Batch(values, results, Period, Ripple);
|
||||
|
||||
TSeries output = [];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
@@ -196,15 +196,15 @@ public sealed class Cheby1 : AbstractBase
|
||||
return Last;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
public static TSeries Batch(TSeries source, int period, double ripple = 1.0)
|
||||
{
|
||||
_state = default;
|
||||
_p_state = default;
|
||||
Last = default;
|
||||
var indicator = new Cheby1(period, ripple);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, double ripple = 1.0)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period, double ripple = 1.0)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
@@ -294,6 +294,20 @@ public sealed class Cheby1 : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_state = default;
|
||||
_p_state = default;
|
||||
Last = default;
|
||||
}
|
||||
|
||||
public static (TSeries Results, Cheby1 Indicator) Calculate(TSeries source, int period, double ripple = 1.0)
|
||||
{
|
||||
var indicator = new Cheby1(period, ripple);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the source publisher if one was provided during construction.
|
||||
/// </summary>
|
||||
|
||||
@@ -101,7 +101,7 @@ public class Cheby2Tests
|
||||
|
||||
// Span
|
||||
var spanResults = new double[count];
|
||||
Cheby2.Calculate(values, spanResults, period, 5.0);
|
||||
Cheby2.Batch(values, spanResults, period, 5.0);
|
||||
|
||||
// Compare
|
||||
for (int i = 0; i < count; i++)
|
||||
|
||||
@@ -136,7 +136,7 @@ public sealed class Cheby2 : AbstractBase
|
||||
double[] values = source.Values.ToArray();
|
||||
double[] results = new double[values.Length];
|
||||
|
||||
Calculate(values, results, Period, Attenuation);
|
||||
Batch(values, results, Period, Attenuation);
|
||||
|
||||
TSeries output = [];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
@@ -217,8 +217,14 @@ public sealed class Cheby2 : AbstractBase
|
||||
Last = default;
|
||||
}
|
||||
|
||||
public static TSeries Batch(TSeries source, int period, double attenuation = 5.0)
|
||||
{
|
||||
var indicator = new Cheby2(period, attenuation);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, double attenuation = 5.0)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period, double attenuation = 5.0)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
@@ -322,6 +328,12 @@ public sealed class Cheby2 : AbstractBase
|
||||
filt1 = filt;
|
||||
}
|
||||
}
|
||||
public static (TSeries Results, Cheby2 Indicator) Calculate(TSeries source, int period, double attenuation = 5.0)
|
||||
{
|
||||
var indicator = new Cheby2(period, attenuation);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the source publisher if one was provided during construction.
|
||||
@@ -334,4 +346,4 @@ public sealed class Cheby2 : AbstractBase
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public class EllipticTests
|
||||
// 3. Span
|
||||
double[] spanInput = series.Values.ToArray();
|
||||
double[] spanOutput = new double[spanInput.Length];
|
||||
Elliptic.Calculate(spanInput, spanOutput, period);
|
||||
Elliptic.Batch(spanInput, spanOutput, period);
|
||||
|
||||
// Assert
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
|
||||
@@ -201,6 +201,19 @@ public sealed class Elliptic : AbstractBase
|
||||
return Last;
|
||||
}
|
||||
|
||||
public static TSeries Batch(TSeries source, int period)
|
||||
{
|
||||
var indicator = new Elliptic(period);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
CalculateWithState(source, output, period, out _);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_state = default;
|
||||
@@ -209,10 +222,11 @@ public sealed class Elliptic : AbstractBase
|
||||
Last = default;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
public static (TSeries Results, Elliptic Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
CalculateWithState(source, output, period, out _);
|
||||
var indicator = new Elliptic(period);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@@ -61,7 +61,7 @@ public class GaussTests
|
||||
|
||||
// Static calculate comparison
|
||||
double[] output = new double[source.Count];
|
||||
Gauss.Calculate(source.Values, output, 1.0);
|
||||
Gauss.Batch(source.Values, output, 1.0);
|
||||
for (int i = 0; i < source.Count; i++)
|
||||
{
|
||||
Assert.Equal(seriesResult[i].Value, output[i], 1e-9);
|
||||
|
||||
@@ -163,7 +163,7 @@ public class GaussValidationTests : IDisposable
|
||||
foreach (var sigma in sigmas)
|
||||
{
|
||||
double[] output = new double[source.Length];
|
||||
Gauss.Calculate(source.AsSpan(), output.AsSpan(), sigma);
|
||||
Gauss.Batch(source.AsSpan(), output.AsSpan(), sigma);
|
||||
|
||||
var expected = CalculateExpectedGauss(source, sigma);
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ public sealed class Gauss : AbstractBase
|
||||
|
||||
// Calculate using static method for performance
|
||||
var resultValues = new double[source.Count];
|
||||
Calculate(source.Values, resultValues, _sigma);
|
||||
Batch(source.Values, resultValues, _sigma);
|
||||
|
||||
// Convert to TSeries
|
||||
var result = new TSeries();
|
||||
@@ -219,12 +219,10 @@ public sealed class Gauss : AbstractBase
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
public static TSeries Batch(TSeries source, double sigma = 1.0)
|
||||
{
|
||||
foreach (double value in source)
|
||||
{
|
||||
Update(new TValue(DateTime.MinValue, value), isNew: true);
|
||||
}
|
||||
var indicator = new Gauss(sigma);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -234,7 +232,7 @@ public sealed class Gauss : AbstractBase
|
||||
/// <param name="output">Output buffer (must be same length as source)</param>
|
||||
/// <param name="sigma">Standard deviation</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, double sigma)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, double sigma)
|
||||
{
|
||||
const int StackallocThreshold = 256;
|
||||
|
||||
@@ -337,6 +335,21 @@ public sealed class Gauss : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
foreach (double value in source)
|
||||
{
|
||||
Update(new TValue(DateTime.MinValue, value), isNew: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Gauss Indicator) Calculate(TSeries source, double sigma = 1.0)
|
||||
{
|
||||
var indicator = new Gauss(sigma);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the source publisher if one was provided during construction.
|
||||
/// </summary>
|
||||
|
||||
@@ -125,7 +125,7 @@ public class HannTests
|
||||
}
|
||||
|
||||
var tseriesResult = new Hann(length).Update(series);
|
||||
Hann.Calculate(input.AsSpan(), output.AsSpan(), length);
|
||||
Hann.Batch(input.AsSpan(), output.AsSpan(), length);
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
|
||||
@@ -136,7 +136,7 @@ public class HannValidationTests : IDisposable
|
||||
foreach (var len in lengths)
|
||||
{
|
||||
double[] output = new double[source.Length];
|
||||
Hann.Calculate(source.AsSpan(), output.AsSpan(), len);
|
||||
Hann.Batch(source.AsSpan(), output.AsSpan(), len);
|
||||
|
||||
var expected = CalculateExpectedHann(source, len);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace QuanTAlib;
|
||||
/// This filter provides strong smoothing properties but introduces lag, as the weights
|
||||
/// typically start and end at zero.
|
||||
/// </summary>
|
||||
[SkipLocalsInit]
|
||||
public sealed class Hann : AbstractBase
|
||||
{
|
||||
private readonly double[] _weights;
|
||||
@@ -156,7 +157,7 @@ public sealed class Hann : AbstractBase
|
||||
}
|
||||
|
||||
var resultValues = new double[source.Count];
|
||||
Calculate(source.Values, resultValues, Length);
|
||||
Batch(source.Values, resultValues, Length);
|
||||
|
||||
// Convert to TSeries
|
||||
var result = new TSeries();
|
||||
@@ -176,12 +177,10 @@ public sealed class Hann : AbstractBase
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
public static TSeries Batch(TSeries source, int length)
|
||||
{
|
||||
foreach (double value in source)
|
||||
{
|
||||
Update(new TValue(DateTime.MinValue, value), isNew: true);
|
||||
}
|
||||
var indicator = new Hann(length);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -191,7 +190,7 @@ public sealed class Hann : AbstractBase
|
||||
/// <param name="output">Output buffer (must be same length as source)</param>
|
||||
/// <param name="length">Lookback length</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int length)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int length)
|
||||
{
|
||||
if (length <= 1)
|
||||
{
|
||||
@@ -249,6 +248,21 @@ public sealed class Hann : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
foreach (double value in source)
|
||||
{
|
||||
Update(new TValue(DateTime.MinValue, value), isNew: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Hann Indicator) Calculate(TSeries source, int length)
|
||||
{
|
||||
var indicator = new Hann(length);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the source publisher if one was provided during construction.
|
||||
/// </summary>
|
||||
|
||||
@@ -68,7 +68,7 @@ public class HpTests
|
||||
var tseriesResult = hp.Update(source);
|
||||
var spanOutput = new double[source.Count];
|
||||
|
||||
Hp.Calculate(source.Values, spanOutput, 1600);
|
||||
Hp.Batch(source.Values, spanOutput, 1600);
|
||||
|
||||
for (int i = 0; i < source.Count; i++)
|
||||
{
|
||||
@@ -116,6 +116,6 @@ public class HpTests
|
||||
{
|
||||
double[] src = new double[10];
|
||||
double[] dst = new double[5];
|
||||
Assert.Throws<ArgumentException>(() => Hp.Calculate(src, dst, 1600));
|
||||
Assert.Throws<ArgumentException>(() => Hp.Batch(src, dst, 1600));
|
||||
}
|
||||
}
|
||||
|
||||
+15
-3
@@ -165,7 +165,7 @@ public sealed class Hp : AbstractBase
|
||||
}
|
||||
|
||||
var resultValues = new double[source.Count];
|
||||
Calculate(source.Values, resultValues, Lambda);
|
||||
Batch(source.Values, resultValues, Lambda);
|
||||
|
||||
var result = new TSeries();
|
||||
var times = source.Times;
|
||||
@@ -196,10 +196,16 @@ public sealed class Hp : AbstractBase
|
||||
return result;
|
||||
}
|
||||
|
||||
public static TSeries Batch(TSeries source, double lambda = 1600.0)
|
||||
{
|
||||
var indicator = new Hp(lambda);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static calculation of HP Filter on a span.
|
||||
/// </summary>
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, double lambda)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, double lambda)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
@@ -236,6 +242,12 @@ public sealed class Hp : AbstractBase
|
||||
prevTrend = val;
|
||||
}
|
||||
}
|
||||
public static (TSeries Results, Hp Indicator) Calculate(TSeries source, double lambda = 1600.0)
|
||||
{
|
||||
var indicator = new Hp(lambda);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the source publisher if one was provided during construction.
|
||||
@@ -248,4 +260,4 @@ public sealed class Hp : AbstractBase
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ public class HpfTests
|
||||
var tseriesResult = hpf.Update(source);
|
||||
var spanOutput = new double[source.Count];
|
||||
|
||||
Hpf.Calculate(source.Values, spanOutput, 40, out _);
|
||||
Hpf.Batch(source.Values, spanOutput, 40, out _);
|
||||
|
||||
for (int i = 0; i < source.Count; i++)
|
||||
{
|
||||
@@ -118,6 +118,6 @@ public class HpfTests
|
||||
{
|
||||
double[] src = new double[10];
|
||||
double[] dst = new double[5];
|
||||
Assert.Throws<ArgumentException>(() => Hpf.Calculate(src, dst, 40, out _));
|
||||
Assert.Throws<ArgumentException>(() => Hpf.Batch(src, dst, 40, out _));
|
||||
}
|
||||
}
|
||||
|
||||
+17
-4
@@ -181,7 +181,7 @@ public sealed class Hpf : AbstractBase
|
||||
|
||||
var output = new double[source.Count];
|
||||
|
||||
Calculate(source.Values, output, Length, out var endState);
|
||||
Batch(source.Values, output, Length, out var endState);
|
||||
|
||||
_state = new State
|
||||
{
|
||||
@@ -206,17 +206,22 @@ public sealed class Hpf : AbstractBase
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int length)
|
||||
public static TSeries Batch(TSeries source, int length = 40)
|
||||
{
|
||||
Calculate(source, output, length, out _);
|
||||
var indicator = new Hpf(length);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int length)
|
||||
{
|
||||
Batch(source, output, length, out _);
|
||||
}
|
||||
/// <summary>
|
||||
/// Batch HPF. Returns end state so callers can restore streaming state without replay.
|
||||
/// NaN/Inf => carry-forward last finite source.
|
||||
/// Outputs 0 for the first two finite samples, then runs the 2-pole recursion.
|
||||
/// </summary>
|
||||
public static void Calculate(
|
||||
public static void Batch(
|
||||
ReadOnlySpan<double> source,
|
||||
Span<double> output,
|
||||
int length,
|
||||
@@ -304,6 +309,14 @@ public sealed class Hpf : AbstractBase
|
||||
state = (hp1, hp2, src1, src2, samples, hasSrc);
|
||||
}
|
||||
|
||||
public static (TSeries Results, Hpf Indicator) Calculate(TSeries source, int length = 40)
|
||||
{
|
||||
var indicator = new Hpf(length);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the source publisher if one was provided during construction.
|
||||
/// </summary>
|
||||
|
||||
@@ -120,7 +120,7 @@ public class KalmanTests
|
||||
|
||||
// 2. Span Calculate
|
||||
var spanOutput = new double[series.Count];
|
||||
Kalman.Calculate(series.Values.ToArray(), spanOutput, 0.01, 0.1);
|
||||
Kalman.Batch(series.Values.ToArray(), spanOutput, 0.01, 0.1);
|
||||
|
||||
// 3. Streaming Update
|
||||
filter.Reset();
|
||||
|
||||
@@ -186,7 +186,7 @@ public sealed class Kalman : AbstractBase
|
||||
|
||||
var output = new double[source.Count];
|
||||
|
||||
Calculate(source.Values, output, ProcessNoise, MeasurementNoise,
|
||||
Batch(source.Values, output, ProcessNoise, MeasurementNoise,
|
||||
out double endX, out double endP, out int endSamples);
|
||||
|
||||
var result = new TSeries();
|
||||
@@ -203,19 +203,17 @@ public sealed class Kalman : AbstractBase
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
public static TSeries Batch(TSeries source, double q = 0.01, double r = 0.1)
|
||||
{
|
||||
foreach (double v in source)
|
||||
{
|
||||
Update(new TValue(DateTime.MinValue, v), isNew: true);
|
||||
}
|
||||
var indicator = new Kalman(q, r);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Batch KF. Returns final state so instance can restore without replay.
|
||||
/// NaN/Inf => prediction-only (hold x, p += q) once initialized.
|
||||
/// </summary>
|
||||
public static void Calculate(
|
||||
public static void Batch(
|
||||
ReadOnlySpan<double> source,
|
||||
Span<double> output,
|
||||
double q,
|
||||
@@ -278,15 +276,30 @@ public sealed class Kalman : AbstractBase
|
||||
|
||||
// Overload for Calculate without out params to maintain API compatibility if needed,
|
||||
// although the original Calculate signature was different anyway (returned void).
|
||||
// The previous implementation had: public static void Calculate(ReadOnlySpan<double> source, Span<double> output, double q, double r)
|
||||
// The previous implementation had: public static void Batch(ReadOnlySpan<double> source, Span<double> output, double q, double r)
|
||||
// We should keep this signature valid.
|
||||
|
||||
/// <summary>
|
||||
/// Static calculation of Kalman Filter on a span.
|
||||
/// </summary>
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, double q, double r)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, double q, double r)
|
||||
{
|
||||
Calculate(source, output, q, r, out _, out _, out _);
|
||||
Batch(source, output, q, r, out _, out _, out _);
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
foreach (double v in source)
|
||||
{
|
||||
Update(new TValue(DateTime.MinValue, v), isNew: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Kalman Indicator) Calculate(TSeries source, double q = 0.01, double r = 0.1)
|
||||
{
|
||||
var indicator = new Kalman(q, r);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -106,7 +106,7 @@ public sealed class LoessTests
|
||||
|
||||
// 2. Span Batch
|
||||
var resSpan = new double[data.Length];
|
||||
Loess.Calculate(data.AsSpan(), resSpan.AsSpan(), period);
|
||||
Loess.Batch(data.AsSpan(), resSpan.AsSpan(), period);
|
||||
|
||||
// 3. Streaming
|
||||
var loessStream = new Loess(period);
|
||||
|
||||
+47
-34
@@ -163,7 +163,7 @@ public sealed class Loess : AbstractBase
|
||||
|
||||
// Use static Calculate for performance on the whole series
|
||||
var resultValues = new double[source.Count];
|
||||
Calculate(source.Values, resultValues, Period);
|
||||
Batch(source.Values, resultValues, Period);
|
||||
|
||||
var result = new TSeries();
|
||||
var times = source.Times;
|
||||
@@ -200,6 +200,48 @@ public sealed class Loess : AbstractBase
|
||||
return result;
|
||||
}
|
||||
|
||||
public static TSeries Batch(TSeries source, int period)
|
||||
{
|
||||
var indicator = new Loess(period);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static stateless calculation optimized for SIMD.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output spans must be of equal length.", nameof(output));
|
||||
}
|
||||
|
||||
if (period < 3)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be at least 3.");
|
||||
}
|
||||
|
||||
int adjPeriod = (period & 1) == 0 ? period + 1 : period;
|
||||
|
||||
double[] kernel = new double[adjPeriod];
|
||||
GenerateKernelOldestFirst(adjPeriod, kernel);
|
||||
|
||||
ReadOnlySpan<double> kSpan = new ReadOnlySpan<double>(kernel);
|
||||
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
if (i < adjPeriod - 1)
|
||||
{
|
||||
output[i] = source[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
var window = source.Slice(i - adjPeriod + 1, adjPeriod);
|
||||
output[i] = DotProduct(window, kSpan);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
foreach (double value in source)
|
||||
@@ -310,40 +352,11 @@ public sealed class Loess : AbstractBase
|
||||
return sum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static stateless calculation optimized for SIMD.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
public static (TSeries Results, Loess Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output spans must be of equal length.", nameof(output));
|
||||
}
|
||||
|
||||
if (period < 3)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be at least 3.");
|
||||
}
|
||||
|
||||
int adjPeriod = (period & 1) == 0 ? period + 1 : period;
|
||||
|
||||
double[] kernel = new double[adjPeriod];
|
||||
GenerateKernelOldestFirst(adjPeriod, kernel);
|
||||
|
||||
ReadOnlySpan<double> kSpan = new ReadOnlySpan<double>(kernel);
|
||||
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
if (i < adjPeriod - 1)
|
||||
{
|
||||
output[i] = source[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
var window = source.Slice(i - adjPeriod + 1, adjPeriod);
|
||||
output[i] = DotProduct(window, kSpan);
|
||||
}
|
||||
var indicator = new Loess(period);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -37,12 +37,12 @@ public class NotchTests
|
||||
var data = _gbm.Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = data.Close;
|
||||
|
||||
// 1. Static Calculate (TSeries)
|
||||
var staticResult = Notch.Calculate(series, period, q);
|
||||
// 1. Static Batch(TSeries)
|
||||
var staticResult = Notch.Batch(series, period, q);
|
||||
|
||||
// 2. Static Calculate (Span)
|
||||
// 2. Static Batch(Span)
|
||||
double[] spanResult = new double[series.Count];
|
||||
Notch.Calculate(series.Values, spanResult.AsSpan(), period, q);
|
||||
Notch.Batch(series.Values, spanResult.AsSpan(), period, q);
|
||||
|
||||
// 3. Instance Update (TSeries)
|
||||
var instance = new Notch(period, q);
|
||||
|
||||
@@ -138,7 +138,7 @@ public sealed class Notch : AbstractBase
|
||||
ReadOnlySpan<double> srcSpan = source.Values;
|
||||
double[] outArray = new double[srcSpan.Length];
|
||||
|
||||
Calculate(srcSpan, outArray.AsSpan(), NotchFreq, Bandwidth);
|
||||
Batch(srcSpan, outArray.AsSpan(), NotchFreq, Bandwidth);
|
||||
|
||||
for (int i = 0; i < outArray.Length; i++)
|
||||
{
|
||||
@@ -159,7 +159,7 @@ public sealed class Notch : AbstractBase
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, double q = 1.0)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period, double q = 1.0)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
@@ -210,13 +210,13 @@ public sealed class Notch : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static TSeries Calculate(TSeries source, int period, double q = 1.0)
|
||||
public static TSeries Batch(TSeries source, int period, double q = 1.0)
|
||||
{
|
||||
var result = new TSeries();
|
||||
ReadOnlySpan<double> srcSpan = source.Values;
|
||||
double[] outArray = new double[srcSpan.Length];
|
||||
|
||||
Calculate(srcSpan, outArray.AsSpan(), period, q);
|
||||
Batch(srcSpan, outArray.AsSpan(), period, q);
|
||||
|
||||
for (int i = 0; i < outArray.Length; i++)
|
||||
{
|
||||
@@ -226,6 +226,13 @@ public sealed class Notch : AbstractBase
|
||||
return result;
|
||||
}
|
||||
|
||||
public static (TSeries Results, Notch Indicator) Calculate(TSeries source, int period, double q = 1.0)
|
||||
{
|
||||
var indicator = new Notch(period, q);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribes from the source publisher if one was provided during construction.
|
||||
/// </summary>
|
||||
|
||||
@@ -44,7 +44,7 @@ public class SgfTests
|
||||
// 3. Span Mode
|
||||
double[] spanInput = series.Values.ToArray();
|
||||
double[] spanOutput = new double[spanInput.Length];
|
||||
Sgf.Calculate(spanInput, spanOutput, period, polyOrder);
|
||||
Sgf.Batch(spanInput, spanOutput, period, polyOrder);
|
||||
|
||||
// Assert
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
|
||||
@@ -201,7 +201,7 @@ public class SgfValidationTests : IDisposable
|
||||
foreach (var (period, order) in scenarios)
|
||||
{
|
||||
double[] output = new double[source.Length];
|
||||
Sgf.Calculate(source.AsSpan(), output.AsSpan(), period, order);
|
||||
Sgf.Batch(source.AsSpan(), output.AsSpan(), period, order);
|
||||
|
||||
var expected = CalculateExpectedSgf(source, period, order);
|
||||
|
||||
|
||||
+20
-7
@@ -177,7 +177,7 @@ public sealed class Sgf : AbstractBase
|
||||
}
|
||||
|
||||
var resultValues = new double[source.Count];
|
||||
Calculate(source.Values, resultValues, _period, _polyOrder);
|
||||
Batch(source.Values, resultValues, _period, _polyOrder);
|
||||
|
||||
var result = new TSeries();
|
||||
var times = source.Times;
|
||||
@@ -197,16 +197,14 @@ public sealed class Sgf : AbstractBase
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
public static TSeries Batch(TSeries source, int period, int polyOrder = 2)
|
||||
{
|
||||
foreach (double value in source)
|
||||
{
|
||||
Update(new TValue(DateTime.MinValue, value), isNew: true);
|
||||
}
|
||||
var indicator = new Sgf(period, polyOrder);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, int polyOrder = 2)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period, int polyOrder = 2)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
@@ -262,6 +260,21 @@ public sealed class Sgf : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
foreach (double value in source)
|
||||
{
|
||||
Update(new TValue(DateTime.MinValue, value), isNew: true);
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Sgf Indicator) Calculate(TSeries source, int period, int polyOrder = 2)
|
||||
{
|
||||
var indicator = new Sgf(period, polyOrder);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void CalculateWeightsSpan(Span<double> weights, int period, int polyOrder)
|
||||
{
|
||||
|
||||
@@ -231,7 +231,7 @@ public class SsfTests
|
||||
var tseriesResult = Ssf.Calculate(series, 10).Results;
|
||||
|
||||
// Calculate with Span API
|
||||
Ssf.Calculate(source.AsSpan(), output.AsSpan(), 10);
|
||||
Ssf.Batch(source.AsSpan(), output.AsSpan(), 10);
|
||||
|
||||
// Compare results
|
||||
for (int i = 0; i < 100; i++)
|
||||
@@ -257,7 +257,7 @@ public class SsfTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Ssf.Calculate(spanInput, spanOutput, period);
|
||||
Ssf.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
|
||||
@@ -315,15 +315,8 @@ public sealed class Ssf : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Ssf Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var ssf = new Ssf(period);
|
||||
TSeries results = ssf.Update(source);
|
||||
return (results, ssf);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
@@ -353,6 +346,13 @@ public sealed class Ssf : AbstractBase
|
||||
CalculateCore(source, output, c1, c2, c3, period, ref state);
|
||||
}
|
||||
|
||||
public static (TSeries Results, Ssf Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var ssf = new Ssf(period);
|
||||
TSeries results = ssf.Update(source);
|
||||
return (results, ssf);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_state = State.New();
|
||||
@@ -371,4 +371,4 @@ public sealed class Ssf : AbstractBase
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,7 +317,7 @@ public class UsfTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Usf.Calculate(spanInput, spanOutput, period);
|
||||
Usf.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
@@ -370,11 +370,11 @@ public class UsfTests
|
||||
double[] wrongSizeOutput = new double[3];
|
||||
|
||||
// Period must be > 0
|
||||
Assert.Throws<ArgumentException>(() => Usf.Calculate(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Usf.Calculate(source.AsSpan(), output.AsSpan(), -1));
|
||||
Assert.Throws<ArgumentException>(() => Usf.Batch(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Usf.Batch(source.AsSpan(), output.AsSpan(), -1));
|
||||
|
||||
// Output must be same length as source
|
||||
Assert.Throws<ArgumentException>(() => Usf.Calculate(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
Assert.Throws<ArgumentException>(() => Usf.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -396,7 +396,7 @@ public class UsfTests
|
||||
var (tseriesResult, _) = Usf.Calculate(series, 10);
|
||||
|
||||
// Calculate with Span API
|
||||
Usf.Calculate(source.AsSpan(), output.AsSpan(), 10);
|
||||
Usf.Batch(source.AsSpan(), output.AsSpan(), 10);
|
||||
|
||||
// Compare results
|
||||
for (int i = 0; i < 100; i++)
|
||||
@@ -418,7 +418,7 @@ public class UsfTests
|
||||
}
|
||||
|
||||
// Warm up
|
||||
Usf.Calculate(source.AsSpan(), output.AsSpan(), 100);
|
||||
Usf.Batch(source.AsSpan(), output.AsSpan(), 100);
|
||||
|
||||
// This test verifies the method runs without throwing
|
||||
Assert.True(double.IsFinite(output[^1]));
|
||||
@@ -430,7 +430,7 @@ public class UsfTests
|
||||
double[] source = [100, 110, double.NaN, 120, 130];
|
||||
double[] output = new double[5];
|
||||
|
||||
Usf.Calculate(source.AsSpan(), output.AsSpan(), 3);
|
||||
Usf.Batch(source.AsSpan(), output.AsSpan(), 3);
|
||||
|
||||
// All outputs should be finite
|
||||
foreach (var val in output)
|
||||
|
||||
@@ -68,7 +68,7 @@ public sealed class UsfValidationTests : IDisposable
|
||||
// 3. Span Mode
|
||||
double[] sourceData = _testData.RawData.ToArray();
|
||||
double[] spanOutput = new double[sourceData.Length];
|
||||
Usf.Calculate(sourceData.AsSpan(), spanOutput.AsSpan(), period);
|
||||
Usf.Batch(sourceData.AsSpan(), spanOutput.AsSpan(), period);
|
||||
|
||||
// Compare batch vs streaming
|
||||
Assert.Equal(batchResult.Count, streamingResults.Count);
|
||||
|
||||
@@ -315,15 +315,8 @@ public sealed class Usf : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Usf Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var usf = new Usf(period);
|
||||
TSeries results = usf.Update(source);
|
||||
return (results, usf);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
@@ -353,6 +346,13 @@ public sealed class Usf : AbstractBase
|
||||
CalculateCore(source, output, c1, c2, c3, period, ref state);
|
||||
}
|
||||
|
||||
public static (TSeries Results, Usf Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var usf = new Usf(period);
|
||||
TSeries results = usf.Update(source);
|
||||
return (results, usf);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_state = State.New();
|
||||
@@ -371,4 +371,4 @@ public sealed class Usf : AbstractBase
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public class WienerTests
|
||||
// 3. Span Mode
|
||||
double[] spanInput = series.Values.ToArray();
|
||||
double[] spanOutput = new double[spanInput.Length];
|
||||
Wiener.Calculate(spanInput, spanOutput, period, smoothPeriod);
|
||||
Wiener.Batch(spanInput, spanOutput, period, smoothPeriod);
|
||||
|
||||
// Assert
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
|
||||
@@ -178,7 +178,7 @@ public class WienerValidationTests : IDisposable
|
||||
foreach (var (period, smooth) in scenarios)
|
||||
{
|
||||
double[] output = new double[source.Length];
|
||||
Wiener.Calculate(source.AsSpan(), output.AsSpan(), period, smooth);
|
||||
Wiener.Batch(source.AsSpan(), output.AsSpan(), period, smooth);
|
||||
|
||||
var expected = CalculateExpectedWiener(source, period, smooth);
|
||||
|
||||
|
||||
@@ -77,6 +77,26 @@ public sealed class Wiener : AbstractBase
|
||||
return result;
|
||||
}
|
||||
|
||||
public static TSeries Batch(TSeries source, int period, int smoothPeriod = 10)
|
||||
{
|
||||
var indicator = new Wiener(period, smoothPeriod);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> destination, int period, int smoothPeriod = 10)
|
||||
{
|
||||
if (destination.Length < source.Length)
|
||||
{
|
||||
throw new ArgumentException("Destination span is shorter than source span.", nameof(destination));
|
||||
}
|
||||
|
||||
var filter = new Wiener(period, smoothPeriod);
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
destination[i] = filter.Update(new TValue(0, source[i])).Value;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double Calc()
|
||||
{
|
||||
@@ -154,17 +174,10 @@ public sealed class Wiener : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> destination, int period, int smoothPeriod = 10)
|
||||
public static (TSeries Results, Wiener Indicator) Calculate(TSeries source, int period, int smoothPeriod = 10)
|
||||
{
|
||||
if (destination.Length < source.Length)
|
||||
{
|
||||
throw new ArgumentException("Destination span is shorter than source span.", nameof(destination));
|
||||
}
|
||||
|
||||
var filter = new Wiener(period, smoothPeriod);
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
destination[i] = filter.Update(new TValue(0, source[i])).Value;
|
||||
}
|
||||
var indicator = new Wiener(period, smoothPeriod);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user