mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-22 20:48:04 +00:00
normalization of methods
This commit is contained in:
@@ -115,7 +115,7 @@ public class AlmaTests
|
||||
double[] input = series.Values.ToArray();
|
||||
double[] output = new double[input.Length];
|
||||
|
||||
Alma.Calculate(input.AsSpan(), output.AsSpan(), 10);
|
||||
Alma.Batch(input.AsSpan(), output.AsSpan(), 10);
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
@@ -284,7 +284,7 @@ public class AlmaTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Alma.Calculate(spanInput, spanOutput, period);
|
||||
Alma.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
@@ -317,12 +317,12 @@ public class AlmaTests
|
||||
double[] output = new double[5];
|
||||
double[] wrongSizeOutput = new double[3];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => Alma.Calculate(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Alma.Calculate(source.AsSpan(), output.AsSpan(), 3, sigma: 0));
|
||||
Assert.Throws<ArgumentException>(() => Alma.Calculate(source.AsSpan(), output.AsSpan(), 3, sigma: -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Alma.Calculate(source.AsSpan(), output.AsSpan(), 3, offset: -0.1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Alma.Calculate(source.AsSpan(), output.AsSpan(), 3, offset: 1.1));
|
||||
Assert.Throws<ArgumentException>(() => Alma.Calculate(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
Assert.Throws<ArgumentException>(() => Alma.Batch(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Alma.Batch(source.AsSpan(), output.AsSpan(), 3, sigma: 0));
|
||||
Assert.Throws<ArgumentException>(() => Alma.Batch(source.AsSpan(), output.AsSpan(), 3, sigma: -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Alma.Batch(source.AsSpan(), output.AsSpan(), 3, offset: -0.1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Alma.Batch(source.AsSpan(), output.AsSpan(), 3, offset: 1.1));
|
||||
Assert.Throws<ArgumentException>(() => Alma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -331,7 +331,7 @@ public class AlmaTests
|
||||
double[] source = [100, 110, double.NaN, 120, 130];
|
||||
double[] output = new double[5];
|
||||
|
||||
Alma.Calculate(source.AsSpan(), output.AsSpan(), 3);
|
||||
Alma.Batch(source.AsSpan(), output.AsSpan(), 3);
|
||||
|
||||
foreach (var val in output)
|
||||
{
|
||||
|
||||
@@ -102,7 +102,7 @@ public sealed class AlmaValidationTests : IDisposable
|
||||
{
|
||||
// Calculate QuanTAlib ALMA (Span API)
|
||||
double[] qOutput = new double[sourceData.Length];
|
||||
global::QuanTAlib.Alma.Calculate(sourceData, qOutput.AsSpan(), period, offset, sigma);
|
||||
global::QuanTAlib.Alma.Batch(sourceData, qOutput.AsSpan(), period, offset, sigma);
|
||||
|
||||
// Calculate Skender ALMA
|
||||
var sResult = _testData.SkenderQuotes.GetAlma(period, offset, sigma).ToList();
|
||||
|
||||
@@ -26,6 +26,7 @@ public sealed class Alma : AbstractBase
|
||||
private readonly ITValuePublisher? _source;
|
||||
private readonly TValuePublishedHandler? _pubHandler;
|
||||
private bool _isNew = true;
|
||||
private bool _disposed;
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private record struct State(double LastValidValue, bool IsInitialized);
|
||||
@@ -83,9 +84,13 @@ public sealed class Alma : AbstractBase
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
if (!_disposed)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -181,7 +186,7 @@ public sealed class Alma : AbstractBase
|
||||
var tSpan = CollectionsMarshal.AsSpan(t);
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
Calculate(source.Values, vSpan, _period, _offset, _sigma);
|
||||
Batch(source.Values, vSpan, _period, _offset, _sigma);
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
// Restore state
|
||||
@@ -305,7 +310,7 @@ public sealed class Alma : AbstractBase
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, double offset = 0.85, double sigma = 6.0)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period, double offset = 0.85, double sigma = 6.0)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
@@ -433,6 +438,13 @@ public sealed class Alma : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Alma Indicator) Calculate(TSeries source, int period, double offset = 0.85, double sigma = 6.0)
|
||||
{
|
||||
var indicator = new Alma(period, offset, sigma);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_buffer.Clear();
|
||||
@@ -440,4 +452,4 @@ public sealed class Alma : AbstractBase
|
||||
_p_state = _state;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class BlmaTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Blma.Calculate(spanInput, spanOutput, period);
|
||||
Blma.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
|
||||
+11
-11
@@ -151,7 +151,7 @@ public sealed class Blma : AbstractBase
|
||||
|
||||
try
|
||||
{
|
||||
Calculate(source.Values, output, _period);
|
||||
BatchCore(source.Values, output, _period);
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
@@ -243,17 +243,11 @@ public sealed class Blma : AbstractBase
|
||||
/// <summary>
|
||||
/// Calculates BLMA values for a TSeries and returns both results and a primed indicator.
|
||||
/// </summary>
|
||||
public static (TSeries Results, Blma Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var indicator = new Blma(period);
|
||||
var results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates BLMA values using spans (high-performance batch API).
|
||||
/// Core batch calculation of BLMA values using spans (high-performance batch API).
|
||||
/// </summary>
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> destination, int period)
|
||||
private static void BatchCore(ReadOnlySpan<double> source, Span<double> destination, int period)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
@@ -377,6 +371,12 @@ public sealed class Blma : AbstractBase
|
||||
/// </summary>
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> destination, int period)
|
||||
{
|
||||
Calculate(source, destination, period);
|
||||
BatchCore(source, destination, period);
|
||||
}
|
||||
}
|
||||
public static (TSeries Results, Blma Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var indicator = new Blma(period);
|
||||
var results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
}
|
||||
@@ -152,7 +152,7 @@ public class BwmaTests
|
||||
}
|
||||
|
||||
var spanResults = new double[series.Count];
|
||||
Bwma.Calculate(series.Values, spanResults, 10);
|
||||
Bwma.Batch(series.Values, spanResults, 10);
|
||||
|
||||
for (int i = 0; i < spanResults.Length; i++)
|
||||
{
|
||||
@@ -175,7 +175,7 @@ public class BwmaTests
|
||||
}
|
||||
|
||||
var spanResults = new double[series.Count];
|
||||
Bwma.Calculate(series.Values, spanResults, 10, 2);
|
||||
Bwma.Batch(series.Values, spanResults, 10, 2);
|
||||
|
||||
for (int i = 0; i < spanResults.Length; i++)
|
||||
{
|
||||
|
||||
@@ -60,7 +60,7 @@ public sealed class BwmaValidationTests : IDisposable
|
||||
// 3. Span API
|
||||
ReadOnlySpan<double> sourceData = _testData.RawData.Span;
|
||||
double[] spanOutput = new double[sourceData.Length];
|
||||
Bwma.Calculate(sourceData, spanOutput.AsSpan(), period, order);
|
||||
Bwma.Batch(sourceData, spanOutput.AsSpan(), period, order);
|
||||
|
||||
// Verify streaming vs batch
|
||||
Assert.Equal(streamingResults.Count, batchResults.Count);
|
||||
@@ -257,7 +257,7 @@ public sealed class BwmaValidationTests : IDisposable
|
||||
|
||||
// Span
|
||||
double[] spanOutput = new double[dataWithNaN.Count];
|
||||
Bwma.Calculate(dataWithNaN.Values, spanOutput.AsSpan(), period);
|
||||
Bwma.Batch(dataWithNaN.Values, spanOutput.AsSpan(), period);
|
||||
|
||||
// Verify all produce same results
|
||||
for (int i = 0; i < streamingResults.Count; i++)
|
||||
|
||||
@@ -26,6 +26,7 @@ public sealed class Bwma : AbstractBase
|
||||
private readonly ITValuePublisher? _source;
|
||||
private readonly TValuePublishedHandler? _pubHandler;
|
||||
private bool _isNew = true;
|
||||
private bool _disposed;
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private record struct State
|
||||
@@ -81,9 +82,13 @@ public sealed class Bwma : AbstractBase
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
if (!_disposed)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -199,7 +204,7 @@ public sealed class Bwma : AbstractBase
|
||||
var tSpan = CollectionsMarshal.AsSpan(t);
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
Calculate(source.Values, vSpan, _period, _order);
|
||||
Batch(source.Values, vSpan, _period, _order);
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
_buffer.Clear();
|
||||
@@ -338,7 +343,7 @@ public sealed class Bwma : AbstractBase
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, int order = 0)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period, int order = 0)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
@@ -477,6 +482,13 @@ public sealed class Bwma : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Bwma Indicator) Calculate(TSeries source, int period, int order = 0)
|
||||
{
|
||||
var indicator = new Bwma(period, order);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_buffer.Clear();
|
||||
@@ -484,4 +496,4 @@ public sealed class Bwma : AbstractBase
|
||||
_p_state = _state;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public sealed class Conv : AbstractBase
|
||||
private readonly ITValuePublisher? _source;
|
||||
private readonly TValuePublishedHandler? _subHandler;
|
||||
private bool _isNew = true;
|
||||
private bool _disposed;
|
||||
|
||||
private record struct State(double LastValidValue);
|
||||
private State _state;
|
||||
@@ -56,9 +57,13 @@ public sealed class Conv : AbstractBase
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_source != null && _subHandler != null)
|
||||
if (!_disposed)
|
||||
{
|
||||
_source.Pub -= _subHandler;
|
||||
if (disposing && _source != null && _subHandler != null)
|
||||
{
|
||||
_source.Pub -= _subHandler;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -276,6 +281,13 @@ public sealed class Conv : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Conv Indicator) Calculate(TSeries source, double[] kernel)
|
||||
{
|
||||
var indicator = new Conv(kernel);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_buffer.Clear();
|
||||
@@ -283,4 +295,4 @@ public sealed class Conv : AbstractBase
|
||||
_p_state.LastValidValue = double.NaN;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,8 +150,8 @@ public class DwmaTests
|
||||
double[] output = new double[5];
|
||||
double[] wrongSizeOutput = new double[3];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => Dwma.Calculate(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Dwma.Calculate(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
Assert.Throws<ArgumentException>(() => Dwma.Batch(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Dwma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -160,7 +160,7 @@ public class DwmaTests
|
||||
double[] source = [100, 110, double.NaN, 120, 130];
|
||||
double[] output = new double[5];
|
||||
|
||||
Dwma.Calculate(source.AsSpan(), output.AsSpan(), 3);
|
||||
Dwma.Batch(source.AsSpan(), output.AsSpan(), 3);
|
||||
|
||||
foreach (var val in output)
|
||||
{
|
||||
@@ -185,7 +185,7 @@ public class DwmaTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Dwma.Calculate(spanInput, spanOutput, period);
|
||||
Dwma.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
|
||||
@@ -22,6 +22,7 @@ public sealed class Dwma : AbstractBase
|
||||
private readonly Wma _wma2;
|
||||
private readonly ITValuePublisher? _source;
|
||||
private readonly TValuePublishedHandler? _handler;
|
||||
private bool _disposed;
|
||||
private int _sampleCount;
|
||||
|
||||
public override bool IsHot => _sampleCount >= WarmupPeriod;
|
||||
@@ -53,9 +54,13 @@ public sealed class Dwma : AbstractBase
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _source != null && _handler != null)
|
||||
if (!_disposed)
|
||||
{
|
||||
_source.Pub -= _handler;
|
||||
if (disposing && _source != null && _handler != null)
|
||||
{
|
||||
_source.Pub -= _handler;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -91,7 +96,7 @@ public sealed class Dwma : AbstractBase
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
source.Times.CopyTo(tSpan);
|
||||
Calculate(source.Values, vSpan, _period);
|
||||
Batch(source.Values, vSpan, _period);
|
||||
|
||||
Reset();
|
||||
|
||||
@@ -129,7 +134,7 @@ public sealed class Dwma : AbstractBase
|
||||
}
|
||||
|
||||
[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)
|
||||
{
|
||||
@@ -166,6 +171,13 @@ public sealed class Dwma : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Dwma Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var indicator = new Dwma(period);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_wma1.Reset();
|
||||
@@ -173,4 +185,4 @@ public sealed class Dwma : AbstractBase
|
||||
_sampleCount = 0;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public class GwmaTests
|
||||
double[] input = series.Values.ToArray();
|
||||
double[] output = new double[input.Length];
|
||||
|
||||
Gwma.Calculate(input.AsSpan(), output.AsSpan(), 10);
|
||||
Gwma.Batch(input.AsSpan(), output.AsSpan(), 10);
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
@@ -283,7 +283,7 @@ public class GwmaTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Gwma.Calculate(spanInput, spanOutput, period);
|
||||
Gwma.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
@@ -316,11 +316,11 @@ public class GwmaTests
|
||||
double[] output = new double[5];
|
||||
double[] wrongSizeOutput = new double[3];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => Gwma.Calculate(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Gwma.Calculate(source.AsSpan(), output.AsSpan(), 3, sigma: 0));
|
||||
Assert.Throws<ArgumentException>(() => Gwma.Calculate(source.AsSpan(), output.AsSpan(), 3, sigma: -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Gwma.Calculate(source.AsSpan(), output.AsSpan(), 3, sigma: 1.1));
|
||||
Assert.Throws<ArgumentException>(() => Gwma.Calculate(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
Assert.Throws<ArgumentException>(() => Gwma.Batch(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Gwma.Batch(source.AsSpan(), output.AsSpan(), 3, sigma: 0));
|
||||
Assert.Throws<ArgumentException>(() => Gwma.Batch(source.AsSpan(), output.AsSpan(), 3, sigma: -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Gwma.Batch(source.AsSpan(), output.AsSpan(), 3, sigma: 1.1));
|
||||
Assert.Throws<ArgumentException>(() => Gwma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -329,7 +329,7 @@ public class GwmaTests
|
||||
double[] source = [100, 110, double.NaN, 120, 130];
|
||||
double[] output = new double[5];
|
||||
|
||||
Gwma.Calculate(source.AsSpan(), output.AsSpan(), 3);
|
||||
Gwma.Batch(source.AsSpan(), output.AsSpan(), 3);
|
||||
|
||||
foreach (var val in output)
|
||||
{
|
||||
|
||||
@@ -83,7 +83,7 @@ public sealed class GwmaValidationTests : IDisposable
|
||||
{
|
||||
// Calculate QuanTAlib GWMA (Span API)
|
||||
double[] spanOutput = new double[sourceData.Length];
|
||||
Gwma.Calculate(sourceData, spanOutput.AsSpan(), period, sigma);
|
||||
Gwma.Batch(sourceData, spanOutput.AsSpan(), period, sigma);
|
||||
|
||||
// Calculate QuanTAlib GWMA (batch TSeries)
|
||||
var gwmaBatch = new Gwma(period, sigma);
|
||||
|
||||
@@ -25,6 +25,7 @@ public sealed class Gwma : AbstractBase
|
||||
private readonly ITValuePublisher? _source;
|
||||
private readonly TValuePublishedHandler? _pubHandler;
|
||||
private bool _isNew = true;
|
||||
private bool _disposed;
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private record struct State
|
||||
@@ -84,9 +85,13 @@ public sealed class Gwma : AbstractBase
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
if (!_disposed)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -178,7 +183,7 @@ public sealed class Gwma : AbstractBase
|
||||
var tSpan = CollectionsMarshal.AsSpan(t);
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
Calculate(source.Values, vSpan, _period, _sigma);
|
||||
Batch(source.Values, vSpan, _period, _sigma);
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
// Restore internal state to match the streaming path:
|
||||
@@ -288,7 +293,7 @@ public sealed class Gwma : AbstractBase
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, double sigma = 0.4)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period, double sigma = 0.4)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
@@ -438,6 +443,13 @@ public sealed class Gwma : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Gwma Indicator) Calculate(TSeries source, int period, double sigma = 0.4)
|
||||
{
|
||||
var indicator = new Gwma(period, sigma);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_buffer.Clear();
|
||||
@@ -445,4 +457,4 @@ public sealed class Gwma : AbstractBase
|
||||
_p_state = _state;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ public class HammaTests
|
||||
double[] input = series.Values.ToArray();
|
||||
double[] output = new double[input.Length];
|
||||
|
||||
Hamma.Calculate(input.AsSpan(), output.AsSpan(), 10);
|
||||
Hamma.Batch(input.AsSpan(), output.AsSpan(), 10);
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
@@ -277,7 +277,7 @@ public class HammaTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Hamma.Calculate(spanInput, spanOutput, period);
|
||||
Hamma.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
@@ -310,8 +310,8 @@ public class HammaTests
|
||||
double[] output = new double[5];
|
||||
double[] wrongSizeOutput = new double[3];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => Hamma.Calculate(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Hamma.Calculate(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
Assert.Throws<ArgumentException>(() => Hamma.Batch(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Hamma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -320,7 +320,7 @@ public class HammaTests
|
||||
double[] source = [100, 110, double.NaN, 120, 130];
|
||||
double[] output = new double[5];
|
||||
|
||||
Hamma.Calculate(source.AsSpan(), output.AsSpan(), 3);
|
||||
Hamma.Batch(source.AsSpan(), output.AsSpan(), 3);
|
||||
|
||||
foreach (var val in output)
|
||||
{
|
||||
|
||||
@@ -75,7 +75,7 @@ public sealed class HammaValidationTests : IDisposable
|
||||
{
|
||||
// Calculate QuanTAlib HAMMA (Span API)
|
||||
double[] spanOutput = new double[sourceData.Length];
|
||||
Hamma.Calculate(sourceData, spanOutput.AsSpan(), period);
|
||||
Hamma.Batch(sourceData, spanOutput.AsSpan(), period);
|
||||
|
||||
// Calculate QuanTAlib HAMMA (batch TSeries)
|
||||
var hammaBatch = new Hamma(period);
|
||||
|
||||
@@ -24,6 +24,7 @@ public sealed class Hamma : AbstractBase
|
||||
private readonly ITValuePublisher? _source;
|
||||
private readonly TValuePublishedHandler? _pubHandler;
|
||||
private bool _isNew = true;
|
||||
private bool _disposed;
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private record struct State
|
||||
@@ -78,9 +79,13 @@ public sealed class Hamma : AbstractBase
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
if (!_disposed)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -183,7 +188,7 @@ public sealed class Hamma : AbstractBase
|
||||
var tSpan = CollectionsMarshal.AsSpan(t);
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
Calculate(source.Values, vSpan, _period);
|
||||
Batch(source.Values, vSpan, _period);
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
// Restore state
|
||||
@@ -271,7 +276,7 @@ public sealed class Hamma : AbstractBase
|
||||
/// <param name="period">Lookback period for the Hamming window (default: 10)</param>
|
||||
/// <exception cref="ArgumentException">Thrown when output length doesn't match source length.</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period = 10)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period = 10)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
@@ -389,6 +394,13 @@ public sealed class Hamma : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Hamma Indicator) Calculate(TSeries source, int period = 10)
|
||||
{
|
||||
var indicator = new Hamma(period);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_buffer.Clear();
|
||||
@@ -397,4 +409,4 @@ public sealed class Hamma : AbstractBase
|
||||
_p_state = _state;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ public class HanmaTests
|
||||
double[] input = series.Values.ToArray();
|
||||
double[] output = new double[input.Length];
|
||||
|
||||
Hanma.Calculate(input.AsSpan(), output.AsSpan(), 10);
|
||||
Hanma.Batch(input.AsSpan(), output.AsSpan(), 10);
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
@@ -292,7 +292,7 @@ public class HanmaTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Hanma.Calculate(spanInput, spanOutput, period);
|
||||
Hanma.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
@@ -325,8 +325,8 @@ public class HanmaTests
|
||||
double[] output = new double[5];
|
||||
double[] wrongSizeOutput = new double[3];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => Hanma.Calculate(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Hanma.Calculate(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
Assert.Throws<ArgumentException>(() => Hanma.Batch(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Hanma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -335,7 +335,7 @@ public class HanmaTests
|
||||
double[] source = [100, 110, double.NaN, 120, 130];
|
||||
double[] output = new double[5];
|
||||
|
||||
Hanma.Calculate(source.AsSpan(), output.AsSpan(), 3);
|
||||
Hanma.Batch(source.AsSpan(), output.AsSpan(), 3);
|
||||
|
||||
foreach (var val in output)
|
||||
{
|
||||
|
||||
@@ -104,7 +104,7 @@ public class HanmaValidationTests
|
||||
// Span
|
||||
double[] input = series.Values.ToArray();
|
||||
double[] spanOutput = new double[input.Length];
|
||||
Hanma.Calculate(input.AsSpan(), spanOutput.AsSpan(), period);
|
||||
Hanma.Batch(input.AsSpan(), spanOutput.AsSpan(), period);
|
||||
|
||||
// All should match
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
|
||||
@@ -24,6 +24,7 @@ public sealed class Hanma : AbstractBase
|
||||
private readonly ITValuePublisher? _source;
|
||||
private readonly TValuePublishedHandler? _pubHandler;
|
||||
private bool _isNew = true;
|
||||
private bool _disposed;
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private record struct State(double LastValidValue, bool IsInitialized);
|
||||
@@ -56,7 +57,7 @@ public sealed class Hanma : AbstractBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Creates HANMA chained to a source publisher for event-based updates.
|
||||
/// </summary>
|
||||
/// <param name="source">Data source for event-based updates</param>
|
||||
/// <param name="period">Lookback period for the Hanning window (default: 10)</param>
|
||||
@@ -73,9 +74,13 @@ public sealed class Hanma : AbstractBase
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
if (!_disposed)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -177,7 +182,7 @@ public sealed class Hanma : AbstractBase
|
||||
var tSpan = CollectionsMarshal.AsSpan(t);
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
Calculate(source.Values, vSpan, _period);
|
||||
Batch(source.Values, vSpan, _period);
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
// Restore state
|
||||
@@ -277,7 +282,7 @@ public sealed class Hanma : AbstractBase
|
||||
/// <param name="period">Lookback period for the Hanning window (default: 10)</param>
|
||||
/// <exception cref="ArgumentException">Thrown when output length doesn't match source length.</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period = 10)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period = 10)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
@@ -425,6 +430,13 @@ public sealed class Hanma : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Hanma Indicator) Calculate(TSeries source, int period = 10)
|
||||
{
|
||||
var indicator = new Hanma(period);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_buffer.Clear();
|
||||
@@ -432,4 +444,4 @@ public sealed class Hanma : AbstractBase
|
||||
_p_state = _state;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public class HmaTests
|
||||
double[] input = series.Values.ToArray();
|
||||
double[] output = new double[input.Length];
|
||||
|
||||
Hma.Calculate(input.AsSpan(), output.AsSpan(), 14);
|
||||
Hma.Batch(input.AsSpan(), output.AsSpan(), 14);
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
@@ -219,8 +219,8 @@ public class HmaTests
|
||||
double[] output = new double[5];
|
||||
double[] wrongSizeOutput = new double[3];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => Hma.Calculate(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Hma.Calculate(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
Assert.Throws<ArgumentException>(() => Hma.Batch(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Hma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -229,7 +229,7 @@ public class HmaTests
|
||||
double[] source = [100, 110, double.NaN, 120, 130];
|
||||
double[] output = new double[5];
|
||||
|
||||
Hma.Calculate(source.AsSpan(), output.AsSpan(), 3);
|
||||
Hma.Batch(source.AsSpan(), output.AsSpan(), 3);
|
||||
|
||||
foreach (var val in output)
|
||||
{
|
||||
@@ -254,7 +254,7 @@ public class HmaTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Hma.Calculate(spanInput, spanOutput, period);
|
||||
Hma.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
|
||||
@@ -140,7 +140,7 @@ public sealed class HmaValidationTests : IDisposable
|
||||
{
|
||||
// Calculate QuanTAlib HMA (Span API)
|
||||
double[] qOutput = new double[sourceData.Length];
|
||||
global::QuanTAlib.Hma.Calculate(sourceData.AsSpan(), qOutput.AsSpan(), period);
|
||||
global::QuanTAlib.Hma.Batch(sourceData.AsSpan(), qOutput.AsSpan(), period);
|
||||
|
||||
// Calculate Skender HMA
|
||||
var sResult = _testData.SkenderQuotes.GetHma(period).ToList();
|
||||
|
||||
@@ -94,7 +94,7 @@ public sealed class Hma : AbstractBase
|
||||
var tSpan = CollectionsMarshal.AsSpan(t);
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
Calculate(source.Values, vSpan, _period);
|
||||
Batch(source.Values, vSpan, _period);
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
// Restore state for streaming
|
||||
@@ -144,14 +144,14 @@ public sealed class Hma : AbstractBase
|
||||
CollectionsMarshal.SetCount(t, len);
|
||||
CollectionsMarshal.SetCount(v, len);
|
||||
|
||||
Calculate(source.Values, CollectionsMarshal.AsSpan(v), period);
|
||||
Batch(source.Values, CollectionsMarshal.AsSpan(v), period);
|
||||
source.Times.CopyTo(CollectionsMarshal.AsSpan(t));
|
||||
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
[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 (source.Length != output.Length)
|
||||
{
|
||||
@@ -204,6 +204,13 @@ public sealed class Hma : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Hma Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var indicator = new Hma(period);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void CalculateIntermediate(ReadOnlySpan<double> halfWma, ReadOnlySpan<double> fullWma, Span<double> output)
|
||||
{
|
||||
@@ -262,4 +269,4 @@ public sealed class Hma : AbstractBase
|
||||
_sampleCount = 0;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ public class HwmaTests
|
||||
double[] input = series.Values.ToArray();
|
||||
double[] output = new double[input.Length];
|
||||
|
||||
Hwma.Calculate(input.AsSpan(), output.AsSpan(), 10);
|
||||
Hwma.Batch(input.AsSpan(), output.AsSpan(), 10);
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
@@ -292,7 +292,7 @@ public class HwmaTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Hwma.Calculate(spanInput, spanOutput, period);
|
||||
Hwma.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
@@ -325,8 +325,8 @@ public class HwmaTests
|
||||
double[] output = new double[5];
|
||||
double[] wrongSizeOutput = new double[3];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => Hwma.Calculate(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Hwma.Calculate(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
Assert.Throws<ArgumentException>(() => Hwma.Batch(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Hwma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -335,7 +335,7 @@ public class HwmaTests
|
||||
double[] source = [100, 110, double.NaN, 120, 130];
|
||||
double[] output = new double[5];
|
||||
|
||||
Hwma.Calculate(source.AsSpan(), output.AsSpan(), 3);
|
||||
Hwma.Batch(source.AsSpan(), output.AsSpan(), 3);
|
||||
|
||||
foreach (var val in output)
|
||||
{
|
||||
|
||||
@@ -99,7 +99,7 @@ public class HwmaValidationTests
|
||||
// Span
|
||||
double[] input = series.Values.ToArray();
|
||||
double[] spanOutput = new double[input.Length];
|
||||
Hwma.Calculate(input.AsSpan(), spanOutput.AsSpan(), period);
|
||||
Hwma.Batch(input.AsSpan(), spanOutput.AsSpan(), period);
|
||||
|
||||
// All should match
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
|
||||
@@ -26,6 +26,7 @@ public sealed class Hwma : AbstractBase
|
||||
private readonly ITValuePublisher? _source;
|
||||
private readonly TValuePublishedHandler? _pubHandler;
|
||||
private bool _isNew = true;
|
||||
private bool _disposed;
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private record struct State(
|
||||
@@ -118,9 +119,13 @@ public sealed class Hwma : AbstractBase
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
if (!_disposed)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -263,7 +268,7 @@ public sealed class Hwma : AbstractBase
|
||||
/// <param name="period">Period for smoothing factors (default: 10)</param>
|
||||
/// <exception cref="ArgumentException">Thrown when output length doesn't match source length.</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period = 10)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period = 10)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
@@ -342,10 +347,17 @@ public sealed class Hwma : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Hwma Indicator) Calculate(TSeries source, int period = 10)
|
||||
{
|
||||
var indicator = new Hwma(period);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_state = new State(double.NaN, 0, 0, double.NaN, IsInitialized: false);
|
||||
_p_state = _state;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ public class LsmaTests
|
||||
values[i] = bar.Close;
|
||||
}
|
||||
|
||||
Lsma.Calculate(values, output, period);
|
||||
Lsma.Batch(values, output, period);
|
||||
|
||||
var lsma = new Lsma(period);
|
||||
for (int i = 0; i < count; i++)
|
||||
|
||||
@@ -67,7 +67,7 @@ public class LsmaValidationTests
|
||||
{
|
||||
// Calculate QuanTAlib LSMA (Span API)
|
||||
double[] qOutput = new double[_testData.RawData.Length];
|
||||
global::QuanTAlib.Lsma.Calculate(_testData.RawData.Span, qOutput.AsSpan(), period);
|
||||
global::QuanTAlib.Lsma.Batch(_testData.RawData.Span, qOutput.AsSpan(), period);
|
||||
|
||||
// Calculate Skender EPMA
|
||||
var sResult = _testData.SkenderQuotes.GetEpma(period).ToList();
|
||||
|
||||
@@ -221,7 +221,7 @@ public sealed class Lsma : AbstractBase
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
double initialLastValid = _state.LastValidValue;
|
||||
Calculate(source.Values, vSpan, _period, _offset, initialLastValid);
|
||||
Batch(source.Values, vSpan, _period, _offset, initialLastValid);
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
// Restore state
|
||||
@@ -282,7 +282,7 @@ public sealed class Lsma : AbstractBase
|
||||
/// Zero-allocation method for maximum performance.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, int offset = 0, double initialLastValid = double.NaN)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period, int offset = 0, double initialLastValid = double.NaN)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
@@ -395,6 +395,13 @@ public sealed class Lsma : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Lsma Indicator) Calculate(TSeries source, int period, int offset = 0)
|
||||
{
|
||||
var indicator = new Lsma(period, offset);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the LSMA state.
|
||||
/// </summary>
|
||||
@@ -422,4 +429,4 @@ public sealed class Lsma : AbstractBase
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,11 +300,11 @@ public class PwmaTests
|
||||
double[] wrongSizeOutput = new double[3];
|
||||
|
||||
// Period must be > 0
|
||||
Assert.Throws<ArgumentException>(() => Pwma.Calculate(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Pwma.Calculate(source.AsSpan(), output.AsSpan(), -1));
|
||||
Assert.Throws<ArgumentException>(() => Pwma.Batch(source.AsSpan(), output.AsSpan(), 0));
|
||||
Assert.Throws<ArgumentException>(() => Pwma.Batch(source.AsSpan(), output.AsSpan(), -1));
|
||||
|
||||
// Output must be same length as source
|
||||
Assert.Throws<ArgumentException>(() => Pwma.Calculate(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
Assert.Throws<ArgumentException>(() => Pwma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -326,7 +326,7 @@ public class PwmaTests
|
||||
var tseriesResult = Pwma.Batch(series, 10);
|
||||
|
||||
// Calculate with Span API
|
||||
Pwma.Calculate(source.AsSpan(), output.AsSpan(), 10);
|
||||
Pwma.Batch(source.AsSpan(), output.AsSpan(), 10);
|
||||
|
||||
// Compare results
|
||||
for (int i = 0; i < 100; i++)
|
||||
@@ -341,7 +341,7 @@ public class PwmaTests
|
||||
double[] source = [10, 20, 30];
|
||||
double[] output = new double[3];
|
||||
|
||||
Pwma.Calculate(source.AsSpan(), output.AsSpan(), 3);
|
||||
Pwma.Batch(source.AsSpan(), output.AsSpan(), 3);
|
||||
|
||||
// PWMA(3) warmup:
|
||||
// i=0: 10 (1^2*10 / 1^2) = 10
|
||||
@@ -369,7 +369,7 @@ public class PwmaTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Pwma.Calculate(spanInput, spanOutput, period);
|
||||
Pwma.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
|
||||
@@ -175,7 +175,7 @@ public sealed class Pwma : AbstractBase
|
||||
var tSpan = CollectionsMarshal.AsSpan(t);
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
Calculate(source.Values, vSpan, _period);
|
||||
Batch(source.Values, vSpan, _period);
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
// Restore state
|
||||
@@ -234,7 +234,7 @@ public sealed class Pwma : AbstractBase
|
||||
}
|
||||
|
||||
[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 (source.Length != output.Length)
|
||||
{
|
||||
@@ -255,6 +255,13 @@ public sealed class Pwma : AbstractBase
|
||||
CalculateScalarCore(source, output, period);
|
||||
}
|
||||
|
||||
public static (TSeries Results, Pwma Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var indicator = new Pwma(period);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void CalculateScalarCore(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
@@ -356,4 +363,4 @@ public sealed class Pwma : AbstractBase
|
||||
_p_state = default;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ public class SgmaTests
|
||||
double[] input = series.Values.ToArray();
|
||||
double[] output = new double[input.Length];
|
||||
|
||||
Sgma.Calculate(input.AsSpan(), output.AsSpan(), 9);
|
||||
Sgma.Batch(input.AsSpan(), output.AsSpan(), 9);
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
@@ -268,7 +268,7 @@ public class SgmaTests
|
||||
var tValues = series.Values.ToArray();
|
||||
var spanInput = new ReadOnlySpan<double>(tValues);
|
||||
var spanOutput = new double[tValues.Length];
|
||||
Sgma.Calculate(spanInput, spanOutput, period);
|
||||
Sgma.Batch(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
var streamingInd = new Sgma(period);
|
||||
@@ -298,9 +298,9 @@ public class SgmaTests
|
||||
double[] output = new double[5];
|
||||
double[] wrongSizeOutput = new double[3];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => Sgma.Calculate(source.AsSpan(), output.AsSpan(), 2));
|
||||
Assert.Throws<ArgumentException>(() => Sgma.Calculate(source.AsSpan(), output.AsSpan(), 5, 5));
|
||||
Assert.Throws<ArgumentException>(() => Sgma.Calculate(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
Assert.Throws<ArgumentException>(() => Sgma.Batch(source.AsSpan(), output.AsSpan(), 2));
|
||||
Assert.Throws<ArgumentException>(() => Sgma.Batch(source.AsSpan(), output.AsSpan(), 5, 5));
|
||||
Assert.Throws<ArgumentException>(() => Sgma.Batch(source.AsSpan(), wrongSizeOutput.AsSpan(), 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -309,7 +309,7 @@ public class SgmaTests
|
||||
double[] source = [100, 110, double.NaN, 120, 130];
|
||||
double[] output = new double[5];
|
||||
|
||||
Sgma.Calculate(source.AsSpan(), output.AsSpan(), 3);
|
||||
Sgma.Batch(source.AsSpan(), output.AsSpan(), 3);
|
||||
|
||||
foreach (var val in output)
|
||||
{
|
||||
|
||||
@@ -65,7 +65,7 @@ public class SgmaValidationTests
|
||||
double[] sgmaOutput = new double[input.Length];
|
||||
double[] smaOutput = new double[input.Length];
|
||||
|
||||
Sgma.Calculate(input.AsSpan(), sgmaOutput.AsSpan(), 9, 0);
|
||||
Sgma.Batch(input.AsSpan(), sgmaOutput.AsSpan(), 9, 0);
|
||||
Sma.Batch(input.AsSpan(), smaOutput.AsSpan(), 9);
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
@@ -209,7 +209,7 @@ public class SgmaValidationTests
|
||||
// Span
|
||||
double[] input = series.Values.ToArray();
|
||||
double[] output = new double[input.Length];
|
||||
Sgma.Calculate(input.AsSpan(), output.AsSpan(), 9, degree);
|
||||
Sgma.Batch(input.AsSpan(), output.AsSpan(), 9, degree);
|
||||
Assert.Equal(expected, output[^1], Tolerance);
|
||||
|
||||
// Streaming
|
||||
@@ -238,7 +238,7 @@ public class SgmaValidationTests
|
||||
double[] sourceWithNaN = [100, 110, 120, double.NaN, 140, 150, 160, 170, 180];
|
||||
double[] output = new double[sourceWithNaN.Length];
|
||||
|
||||
Sgma.Calculate(sourceWithNaN.AsSpan(), output.AsSpan(), 5, 2);
|
||||
Sgma.Batch(sourceWithNaN.AsSpan(), output.AsSpan(), 5, 2);
|
||||
|
||||
// All outputs should be finite
|
||||
foreach (var val in output)
|
||||
|
||||
@@ -25,6 +25,7 @@ public sealed class Sgma : AbstractBase
|
||||
private readonly ITValuePublisher? _source;
|
||||
private readonly TValuePublishedHandler? _pubHandler;
|
||||
private bool _isNew = true;
|
||||
private bool _disposed;
|
||||
private double _lastValidValue = double.NaN;
|
||||
private double _p_lastValidValue = double.NaN;
|
||||
|
||||
@@ -163,9 +164,13 @@ public sealed class Sgma : AbstractBase
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
if (!_disposed)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
if (disposing && _source != null && _pubHandler != null)
|
||||
{
|
||||
_source.Pub -= _pubHandler;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -277,7 +282,7 @@ public sealed class Sgma : AbstractBase
|
||||
var tSpan = CollectionsMarshal.AsSpan(t);
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
Calculate(source.Values, vSpan, _period, _degree);
|
||||
Batch(source.Values, vSpan, _period, _degree);
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
// Restore state by replaying last period bars
|
||||
@@ -317,7 +322,7 @@ public sealed class Sgma : AbstractBase
|
||||
/// <param name="degree">Polynomial degree (default: 2)</param>
|
||||
/// <exception cref="ArgumentException">Thrown when output length doesn't match source length.</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period = 9, int degree = 2)
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period = 9, int degree = 2)
|
||||
{
|
||||
if (period < 3)
|
||||
{
|
||||
@@ -422,6 +427,13 @@ public sealed class Sgma : AbstractBase
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Sgma Indicator) Calculate(TSeries source, int period = 9, int degree = 2)
|
||||
{
|
||||
var indicator = new Sgma(period, degree);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static double CalculateWeightedSumFull(RingBuffer buffer, double[] weights, double invWeightSum, double fallbackValue)
|
||||
{
|
||||
@@ -557,4 +569,4 @@ public sealed class Sgma : AbstractBase
|
||||
_p_lastValidValue = double.NaN;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,4 +165,11 @@ public sealed class Trima : AbstractBase
|
||||
ArrayPool<double>.Shared.Return(tempArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static (TSeries Results, Trima Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var indicator = new Trima(period);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ public sealed class Wma : AbstractBase
|
||||
private readonly RingBuffer _buffer;
|
||||
private readonly ITValuePublisher? _source;
|
||||
private readonly TValuePublishedHandler? _handler;
|
||||
private bool _disposed;
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private record struct State(double Sum, double WSum, double LastInput, double LastValidValue, int TickCount, bool HasSeenValidData);
|
||||
@@ -68,9 +69,13 @@ public sealed class Wma : AbstractBase
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_source != null && _handler != null)
|
||||
if (!_disposed)
|
||||
{
|
||||
_source.Pub -= _handler;
|
||||
if (disposing && _source != null && _handler != null)
|
||||
{
|
||||
_source.Pub -= _handler;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -298,6 +303,13 @@ public sealed class Wma : AbstractBase
|
||||
CalculateScalarCore(source, output, period);
|
||||
}
|
||||
|
||||
public static (TSeries Results, Wma Indicator) Calculate(TSeries source, int period)
|
||||
{
|
||||
var indicator = new Wma(period);
|
||||
TSeries results = indicator.Update(source);
|
||||
return (results, indicator);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void CalculateScalarCore(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
@@ -856,4 +868,4 @@ public sealed class Wma : AbstractBase
|
||||
Unsafe.Add(ref outRef, idx) = wsum * invDivisor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user