mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-21 03:58:04 +00:00
style patterns
This commit is contained in:
@@ -46,11 +46,19 @@ public sealed class Alma : AbstractBase
|
||||
public Alma(int period, double offset = 0.85, double sigma = 6.0)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
if (sigma <= 0)
|
||||
{
|
||||
throw new ArgumentException("Sigma must be greater than 0", nameof(sigma));
|
||||
}
|
||||
|
||||
if (offset < 0 || offset > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(offset), "Offset must be between 0 and 1");
|
||||
}
|
||||
|
||||
_period = period;
|
||||
_offset = offset;
|
||||
@@ -161,7 +169,10 @@ public sealed class Alma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return new TSeries([], []);
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return new TSeries([], []);
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -201,7 +212,10 @@ public sealed class Alma : AbstractBase
|
||||
private double CalculateWeightedSum()
|
||||
{
|
||||
int count = _buffer.Count;
|
||||
if (count == 0) return 0;
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count < _period)
|
||||
{
|
||||
@@ -250,13 +264,24 @@ public sealed class Alma : AbstractBase
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, double offset = 0.85, double sigma = 6.0)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
if (sigma <= 0)
|
||||
{
|
||||
throw new ArgumentException("Sigma must be greater than 0", nameof(sigma));
|
||||
}
|
||||
|
||||
if (offset < 0 || offset > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(offset), "Offset must be between 0 and 1");
|
||||
}
|
||||
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
// Allocation Strategy: Stack for small periods, Pool for large
|
||||
double[]? weightsArray = period > 256 ? ArrayPool<double>.Shared.Rent(period) : null;
|
||||
@@ -352,8 +377,15 @@ public sealed class Alma : AbstractBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (weightsArray != null) ArrayPool<double>.Shared.Return(weightsArray);
|
||||
if (bufferArray != null) ArrayPool<double>.Shared.Return(bufferArray);
|
||||
if (weightsArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(weightsArray);
|
||||
}
|
||||
|
||||
if (bufferArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(bufferArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -170,4 +170,4 @@ public class BlmaTests
|
||||
Assert.Equal(input[1].AsDateTime, timestamps[1]);
|
||||
Assert.Equal(input[2].AsDateTime, timestamps[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +308,11 @@ public sealed class Blma : AbstractBase
|
||||
{
|
||||
int srcIdx = i - count + 1 + j;
|
||||
double srcVal = source[srcIdx];
|
||||
if (!double.IsFinite(srcVal)) srcVal = lastValid;
|
||||
if (!double.IsFinite(srcVal))
|
||||
{
|
||||
srcVal = lastValid;
|
||||
}
|
||||
|
||||
sum += srcVal * currentWeights[j];
|
||||
}
|
||||
|
||||
@@ -317,7 +321,11 @@ public sealed class Blma : AbstractBase
|
||||
{
|
||||
int srcIdx = i - count + 1 + j;
|
||||
double srcVal = source[srcIdx];
|
||||
if (!double.IsFinite(srcVal)) srcVal = lastValid;
|
||||
if (!double.IsFinite(srcVal))
|
||||
{
|
||||
srcVal = lastValid;
|
||||
}
|
||||
|
||||
avg += srcVal;
|
||||
}
|
||||
avg /= count;
|
||||
@@ -334,7 +342,11 @@ public sealed class Blma : AbstractBase
|
||||
{
|
||||
int srcIdx = i - period + 1 + j;
|
||||
double srcVal = source[srcIdx];
|
||||
if (!double.IsFinite(srcVal)) srcVal = lastValid;
|
||||
if (!double.IsFinite(srcVal))
|
||||
{
|
||||
srcVal = lastValid;
|
||||
}
|
||||
|
||||
sum += srcVal * weights[j];
|
||||
avg += srcVal;
|
||||
}
|
||||
@@ -361,4 +373,4 @@ public sealed class Blma : AbstractBase
|
||||
{
|
||||
Calculate(source, destination, period);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,4 +214,4 @@ public class BwmaIndicatorTests
|
||||
|
||||
Assert.Contains("Bessel", indicator.Description, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,9 @@ public sealed class BwmaIndicator : Indicator, IWatchlistIndicator
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
if (args.Reason != UpdateReason.NewBar && args.Reason != UpdateReason.HistoricalBar && args.Reason != UpdateReason.NewTick)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
TValue result = _ma.Update(new TValue(item.TimeLeft.Ticks, _priceSelector(item)), args.IsNewBar());
|
||||
@@ -62,4 +64,4 @@ public sealed class BwmaIndicator : Indicator, IWatchlistIndicator
|
||||
_series.SetValue(result.Value, _ma.IsHot, ShowColdValues);
|
||||
_series.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,11 @@ public sealed class BwmaValidationTests : IDisposable
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
if (disposing)
|
||||
{
|
||||
@@ -345,4 +349,4 @@ public sealed class BwmaValidationTests : IDisposable
|
||||
// The parabolic window emphasizes the center, so result should be > mean (1.8)
|
||||
Assert.True(result.Value > 1.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+68
-11
@@ -47,9 +47,14 @@ public sealed class Bwma : AbstractBase
|
||||
public Bwma(int period, int order = 0)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
if (order < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(order), "Order must be non-negative");
|
||||
}
|
||||
|
||||
_period = period;
|
||||
_order = order;
|
||||
@@ -180,7 +185,10 @@ public sealed class Bwma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return new TSeries([], []);
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return new TSeries([], []);
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -241,13 +249,20 @@ public sealed class Bwma : AbstractBase
|
||||
private double CalculateWeightedSum(double fallbackValue)
|
||||
{
|
||||
int count = _buffer.Count;
|
||||
if (count == 0) return 0;
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count < _period)
|
||||
{
|
||||
return CalculateWeightedSumWarmup(_buffer.GetSpan(), count, _order, _power, fallbackValue);
|
||||
}
|
||||
|
||||
if (_invWeightSum == 0.0)
|
||||
{
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
ReadOnlySpan<double> internalBuf = _buffer.InternalBuffer;
|
||||
int head = _buffer.StartIndex;
|
||||
@@ -262,9 +277,20 @@ public sealed class Bwma : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static double CalculateWeightedSumWarmup(ReadOnlySpan<double> window, int p, int order, double power, double fallbackValue)
|
||||
{
|
||||
if (p <= 0) return 0.0;
|
||||
if (p == 1) return fallbackValue;
|
||||
if (p == 2) return fallbackValue;
|
||||
if (p <= 0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (p == 1)
|
||||
{
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
if (p == 2)
|
||||
{
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
double scale = 2.0 / (p - 1);
|
||||
double sum = 0.0;
|
||||
@@ -275,7 +301,9 @@ public sealed class Bwma : AbstractBase
|
||||
double x = Math.FusedMultiplyAdd(i, scale, -1.0);
|
||||
double arg = Math.FusedMultiplyAdd(-x, x, 1.0);
|
||||
if (arg <= 0.0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double w;
|
||||
if (order == 0)
|
||||
@@ -292,7 +320,9 @@ public sealed class Bwma : AbstractBase
|
||||
}
|
||||
|
||||
if (w == 0.0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sum = Math.FusedMultiplyAdd(window[i], w, sum);
|
||||
wSum += w;
|
||||
@@ -311,14 +341,25 @@ public sealed class Bwma : AbstractBase
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, int order = 0)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
if (order < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(order), "Order must be non-negative");
|
||||
}
|
||||
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
int len = source.Length;
|
||||
if (len == 0) return;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double power = order * 0.5 + 0.5;
|
||||
|
||||
@@ -352,7 +393,10 @@ public sealed class Bwma : AbstractBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (bufferArray != null) ArrayPool<double>.Shared.Return(bufferArray);
|
||||
if (bufferArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(bufferArray);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -390,9 +434,15 @@ public sealed class Bwma : AbstractBase
|
||||
|
||||
ring[ringIdx] = val;
|
||||
ringIdx++;
|
||||
if (ringIdx >= period) ringIdx = 0;
|
||||
if (ringIdx >= period)
|
||||
{
|
||||
ringIdx = 0;
|
||||
}
|
||||
|
||||
if (count < period) count++;
|
||||
if (count < period)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count < period)
|
||||
{
|
||||
@@ -415,8 +465,15 @@ public sealed class Bwma : AbstractBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (weightsArray != null) ArrayPool<double>.Shared.Return(weightsArray);
|
||||
if (ringArray != null) ArrayPool<double>.Shared.Return(ringArray);
|
||||
if (weightsArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(weightsArray);
|
||||
}
|
||||
|
||||
if (ringArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(ringArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,9 @@ public sealed class Conv : AbstractBase
|
||||
public Conv(double[] kernel)
|
||||
{
|
||||
if (kernel == null || kernel.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Kernel must not be empty", nameof(kernel));
|
||||
}
|
||||
|
||||
_period = kernel.Length;
|
||||
_kernel = new double[_period];
|
||||
@@ -134,7 +136,10 @@ public sealed class Conv : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return [];
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
List<long> t = new(len);
|
||||
@@ -209,13 +214,21 @@ public sealed class Conv : AbstractBase
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, double[] kernel)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (kernel == null || kernel.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Kernel must not be empty", nameof(kernel));
|
||||
}
|
||||
|
||||
int len = source.Length;
|
||||
int period = kernel.Length;
|
||||
if (len == 0) return;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Use stackalloc for small kernels to avoid heap allocation
|
||||
Span<double> window = period <= 256 ? stackalloc double[period] : new double[period];
|
||||
@@ -240,9 +253,15 @@ public sealed class Conv : AbstractBase
|
||||
|
||||
window[windowIdx] = val;
|
||||
windowIdx = (windowIdx + 1);
|
||||
if (windowIdx >= period) windowIdx = 0;
|
||||
if (windowIdx >= period)
|
||||
{
|
||||
windowIdx = 0;
|
||||
}
|
||||
|
||||
if (count < period) count++;
|
||||
if (count < period)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
double sum = 0;
|
||||
|
||||
|
||||
@@ -33,7 +33,9 @@ public sealed class Dwma : AbstractBase
|
||||
public Dwma(int period)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
_period = period;
|
||||
_wma1 = new Wma(period);
|
||||
@@ -61,7 +63,10 @@ public sealed class Dwma : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
{
|
||||
if (isNew) _sampleCount++;
|
||||
if (isNew)
|
||||
{
|
||||
_sampleCount++;
|
||||
}
|
||||
|
||||
TValue wma1Result = _wma1.Update(input, isNew);
|
||||
Last = _wma2.Update(wma1Result, isNew);
|
||||
@@ -71,7 +76,10 @@ public sealed class Dwma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return [];
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -124,12 +132,20 @@ public sealed class Dwma : AbstractBase
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
int len = source.Length;
|
||||
if (len == 0) return;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double[]? tempArray = len > 1024 ? ArrayPool<double>.Shared.Rent(len) : null;
|
||||
Span<double> temp = len <= 1024
|
||||
@@ -143,7 +159,10 @@ public sealed class Dwma : AbstractBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (tempArray != null) ArrayPool<double>.Shared.Return(tempArray);
|
||||
if (tempArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(tempArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -203,4 +203,4 @@ public class GwmaIndicatorTests
|
||||
// Different sigma should produce different results
|
||||
Assert.NotEqual(narrowResult, wideResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,4 +58,4 @@ public class GwmaIndicator : Indicator, IWatchlistIndicator
|
||||
|
||||
Series.SetValue(result.Value, ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,4 +242,4 @@ public sealed class GwmaValidationTests : IDisposable
|
||||
|
||||
Assert.Equal(expected, gwma.Last.Value, 1e-10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+64
-11
@@ -49,11 +49,19 @@ public sealed class Gwma : AbstractBase
|
||||
public Gwma(int period, double sigma = 0.4)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
if (sigma <= 0)
|
||||
{
|
||||
throw new ArgumentException("Sigma must be greater than 0", nameof(sigma));
|
||||
}
|
||||
|
||||
if (sigma > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(sigma), "Sigma must be between 0 and 1");
|
||||
}
|
||||
|
||||
_period = period;
|
||||
_sigma = sigma;
|
||||
@@ -159,7 +167,10 @@ public sealed class Gwma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return new TSeries([], []);
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return new TSeries([], []);
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -218,7 +229,10 @@ public sealed class Gwma : AbstractBase
|
||||
private double CalculateWeightedSum(double fallbackValue)
|
||||
{
|
||||
int count = _buffer.Count;
|
||||
if (count == 0) return 0;
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count < _period)
|
||||
{
|
||||
@@ -226,7 +240,9 @@ public sealed class Gwma : AbstractBase
|
||||
}
|
||||
|
||||
if (_invWeightSum == 0.0)
|
||||
{
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
ReadOnlySpan<double> internalBuf = _buffer.InternalBuffer;
|
||||
int head = _buffer.StartIndex;
|
||||
@@ -242,8 +258,15 @@ public sealed class Gwma : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static double CalculateWeightedSumWarmup(ReadOnlySpan<double> window, int p, double sigma, double fallbackValue)
|
||||
{
|
||||
if (p <= 0) return 0.0;
|
||||
if (p == 1) return fallbackValue;
|
||||
if (p <= 0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (p == 1)
|
||||
{
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
double center = (p - 1) * 0.5;
|
||||
double invSigmaP = 1.0 / (sigma * p);
|
||||
@@ -271,16 +294,30 @@ public sealed class Gwma : AbstractBase
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, double sigma = 0.4)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
if (sigma <= 0)
|
||||
{
|
||||
throw new ArgumentException("Sigma must be greater than 0", nameof(sigma));
|
||||
}
|
||||
|
||||
if (sigma > 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(sigma), "Sigma must be between 0 and 1");
|
||||
}
|
||||
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
int len = source.Length;
|
||||
if (len == 0) return;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (period > len)
|
||||
{
|
||||
@@ -316,7 +353,10 @@ public sealed class Gwma : AbstractBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (bufferArray != null) ArrayPool<double>.Shared.Return(bufferArray);
|
||||
if (bufferArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(bufferArray);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -358,9 +398,15 @@ public sealed class Gwma : AbstractBase
|
||||
|
||||
ring[ringIdx] = val;
|
||||
ringIdx++;
|
||||
if (ringIdx >= period) ringIdx = 0;
|
||||
if (ringIdx >= period)
|
||||
{
|
||||
ringIdx = 0;
|
||||
}
|
||||
|
||||
if (count < period) count++;
|
||||
if (count < period)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count < period)
|
||||
{
|
||||
@@ -383,8 +429,15 @@ public sealed class Gwma : AbstractBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (weightsArray != null) ArrayPool<double>.Shared.Return(weightsArray);
|
||||
if (ringArray != null) ArrayPool<double>.Shared.Return(ringArray);
|
||||
if (weightsArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(weightsArray);
|
||||
}
|
||||
|
||||
if (ringArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(ringArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,4 +448,4 @@ public sealed class Gwma : AbstractBase
|
||||
_p_state = _state;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,4 +164,4 @@ public class HammaIndicatorTests
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(0, HammaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,4 +55,4 @@ public class HammaIndicator : Indicator, IWatchlistIndicator
|
||||
|
||||
Series.SetValue(result.Value, ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,4 +409,4 @@ public class HammaTests
|
||||
Assert.Equal(i * 10.0, result.Value, 1e-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,4 +203,4 @@ public sealed class HammaValidationTests : IDisposable
|
||||
// All edge weights should be equal
|
||||
Assert.Equal(w0, w4, 1e-10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,9 @@ public sealed class Hamma : AbstractBase
|
||||
public Hamma(int period = 10)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
_period = period;
|
||||
_buffer = new RingBuffer(period);
|
||||
@@ -184,7 +186,10 @@ public sealed class Hamma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return new TSeries([], []);
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return new TSeries([], []);
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -224,7 +229,10 @@ public sealed class Hamma : AbstractBase
|
||||
private double CalculateWeightedSum()
|
||||
{
|
||||
int count = _buffer.Count;
|
||||
if (count == 0) return 0;
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count < _period)
|
||||
{
|
||||
@@ -283,9 +291,14 @@ public sealed class Hamma : AbstractBase
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period = 10)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
// Allocation Strategy: Stack for small periods, Pool for large
|
||||
double[]? weightsArray = period > 256 ? ArrayPool<double>.Shared.Rent(period) : null;
|
||||
@@ -381,8 +394,15 @@ public sealed class Hamma : AbstractBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (weightsArray != null) ArrayPool<double>.Shared.Return(weightsArray);
|
||||
if (bufferArray != null) ArrayPool<double>.Shared.Return(bufferArray);
|
||||
if (weightsArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(weightsArray);
|
||||
}
|
||||
|
||||
if (bufferArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(bufferArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -164,4 +164,4 @@ public class HanmaIndicatorTests
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(0, HanmaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,4 +55,4 @@ public class HanmaIndicator : Indicator, IWatchlistIndicator
|
||||
|
||||
Series.SetValue(result.Value, ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,4 +444,4 @@ public class HanmaTests
|
||||
// Result should be 100.0 (weighted average of middle values only)
|
||||
Assert.Equal(100.0, hanma.Last.Value, 1e-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,4 +172,4 @@ public class HanmaValidationTests
|
||||
// Should be different (different window coefficients)
|
||||
Assert.NotEqual(hanmaResults.Last.Value, hammaResults.Last.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,9 @@ public sealed class Hanma : AbstractBase
|
||||
public Hanma(int period = 10)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
_period = period;
|
||||
_buffer = new RingBuffer(period);
|
||||
@@ -178,7 +180,10 @@ public sealed class Hanma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return new TSeries([], []);
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return new TSeries([], []);
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -218,7 +223,10 @@ public sealed class Hanma : AbstractBase
|
||||
private double CalculateWeightedSum()
|
||||
{
|
||||
int count = _buffer.Count;
|
||||
if (count == 0) return 0;
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count < _period)
|
||||
{
|
||||
@@ -289,9 +297,14 @@ public sealed class Hanma : AbstractBase
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period = 10)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
// Allocation Strategy: Stack for small periods, Pool for large
|
||||
double[]? weightsArray = period > 256 ? ArrayPool<double>.Shared.Rent(period) : null;
|
||||
@@ -393,15 +406,22 @@ public sealed class Hanma : AbstractBase
|
||||
if (startIdx + count <= period)
|
||||
{
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
avg += buffer[startIdx + j];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int p1Len = period - startIdx;
|
||||
for (int j = 0; j < p1Len; j++)
|
||||
{
|
||||
avg += buffer[startIdx + j];
|
||||
}
|
||||
|
||||
for (int j = 0; j < count - p1Len; j++)
|
||||
{
|
||||
avg += buffer[j];
|
||||
}
|
||||
}
|
||||
output[i] = avg / count;
|
||||
}
|
||||
@@ -410,8 +430,15 @@ public sealed class Hanma : AbstractBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (weightsArray != null) ArrayPool<double>.Shared.Return(weightsArray);
|
||||
if (bufferArray != null) ArrayPool<double>.Shared.Return(bufferArray);
|
||||
if (weightsArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(weightsArray);
|
||||
}
|
||||
|
||||
if (bufferArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(bufferArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,4 +449,4 @@ public sealed class Hanma : AbstractBase
|
||||
_p_state = _state;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,10 @@ public sealed class Hma : AbstractBase
|
||||
|
||||
public Hma(int period)
|
||||
{
|
||||
if (period <= 1) throw new ArgumentException("Period must be greater than 1", nameof(period));
|
||||
if (period <= 1)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 1", nameof(period));
|
||||
}
|
||||
|
||||
_period = period;
|
||||
int halfPeriod = period / 2;
|
||||
@@ -56,7 +59,10 @@ public sealed class Hma : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
{
|
||||
if (isNew) _sampleCount++;
|
||||
if (isNew)
|
||||
{
|
||||
_sampleCount++;
|
||||
}
|
||||
|
||||
// 1. Calculate WMA(n)
|
||||
TValue full = _wmaFull.Update(input, isNew);
|
||||
@@ -76,7 +82,10 @@ public sealed class Hma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return [];
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -147,12 +156,20 @@ public sealed class Hma : AbstractBase
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (period <= 1)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 1", nameof(period));
|
||||
}
|
||||
|
||||
int len = source.Length;
|
||||
if (len == 0) return;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int halfPeriod = period / 2;
|
||||
int sqrtPeriod = (int)Math.Sqrt(period);
|
||||
|
||||
@@ -164,4 +164,4 @@ public class HwmaIndicatorTests
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(0, HwmaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,4 +55,4 @@ public class HwmaIndicator : Indicator, IWatchlistIndicator
|
||||
|
||||
Series.SetValue(result.Value, ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,4 +435,4 @@ public class HwmaTests
|
||||
Assert.True(double.IsFinite(result.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,9 @@ public sealed class Hwma : AbstractBase
|
||||
public Hwma(int period = 10)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
_alpha = 2.0 / (period + 1.0);
|
||||
_beta = 1.0 / period;
|
||||
@@ -90,11 +92,19 @@ public sealed class Hwma : AbstractBase
|
||||
public Hwma(double alpha, double beta, double gamma)
|
||||
{
|
||||
if (alpha <= 0 || alpha > 1)
|
||||
{
|
||||
throw new ArgumentException("Alpha must be between 0 (exclusive) and 1 (inclusive)", nameof(alpha));
|
||||
}
|
||||
|
||||
if (beta < 0 || beta > 1)
|
||||
{
|
||||
throw new ArgumentException("Beta must be between 0 and 1", nameof(beta));
|
||||
}
|
||||
|
||||
if (gamma < 0 || gamma > 1)
|
||||
{
|
||||
throw new ArgumentException("Gamma must be between 0 and 1", nameof(gamma));
|
||||
}
|
||||
|
||||
int effectivePeriod = (int)(2.0 / alpha - 1.0); // Reverse calculate for display
|
||||
_alpha = alpha;
|
||||
@@ -169,7 +179,11 @@ public sealed class Hwma : AbstractBase
|
||||
{
|
||||
// First value is NaN - return NaN
|
||||
Last = new TValue(input.Time, double.NaN);
|
||||
if (publish) PubEvent(Last);
|
||||
if (publish)
|
||||
{
|
||||
PubEvent(Last);
|
||||
}
|
||||
|
||||
return Last;
|
||||
}
|
||||
|
||||
@@ -215,7 +229,10 @@ public sealed class Hwma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return new TSeries([], []);
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return new TSeries([], []);
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -268,11 +285,19 @@ public sealed class Hwma : AbstractBase
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period = 10)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
if (source.Length != output.Length)
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (source.Length == 0) return;
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (source.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double alpha = 2.0 / (period + 1.0);
|
||||
double beta = 1.0 / period;
|
||||
|
||||
@@ -62,7 +62,9 @@ public sealed class Lsma : AbstractBase
|
||||
public Lsma(int period, int offset = 0)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
_period = period;
|
||||
_offset = offset;
|
||||
@@ -219,7 +221,10 @@ public sealed class Lsma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return new TSeries([], []);
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return new TSeries([], []);
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -295,12 +300,20 @@ public sealed class Lsma : AbstractBase
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, int offset = 0, double initialLastValid = double.NaN)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
int len = source.Length;
|
||||
if (len == 0) return;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int StackAllocThreshold = 256;
|
||||
Span<double> buffer = period <= StackAllocThreshold
|
||||
@@ -322,9 +335,13 @@ public sealed class Lsma : AbstractBase
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsFinite(val))
|
||||
{
|
||||
lastValid = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = lastValid;
|
||||
}
|
||||
|
||||
if (count < period)
|
||||
{
|
||||
@@ -382,7 +399,9 @@ public sealed class Lsma : AbstractBase
|
||||
|
||||
bufferIndex++;
|
||||
if (bufferIndex >= period)
|
||||
{
|
||||
bufferIndex = 0;
|
||||
}
|
||||
|
||||
double m = Math.FusedMultiplyAdd(period, sum_xy, -full_sum_x * sum_y) / full_denom;
|
||||
double b = Math.FusedMultiplyAdd(-m, full_sum_x, sum_y) / period;
|
||||
@@ -418,4 +437,4 @@ public sealed class Lsma : AbstractBase
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,10 @@ public sealed class Pwma : AbstractBase
|
||||
|
||||
public Pwma(int period)
|
||||
{
|
||||
if (period <= 0) throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
_period = period;
|
||||
_divisor = (double)period * ((double)period + 1.0) * (2.0 * (double)period + 1.0) / 6.0;
|
||||
@@ -171,7 +174,10 @@ public sealed class Pwma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return new TSeries([], []);
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return new TSeries([], []);
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
List<long> t = new(len);
|
||||
@@ -244,12 +250,20 @@ public sealed class Pwma : AbstractBase
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
int len = source.Length;
|
||||
if (len == 0) return;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CalculateScalarCore(source, output, period);
|
||||
}
|
||||
@@ -273,9 +287,13 @@ public sealed class Pwma : AbstractBase
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsFinite(val))
|
||||
{
|
||||
lastValid = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = lastValid;
|
||||
}
|
||||
|
||||
sum += val;
|
||||
wsum = Math.FusedMultiplyAdd(i + 1, val, wsum);
|
||||
@@ -291,9 +309,13 @@ public sealed class Pwma : AbstractBase
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsFinite(val))
|
||||
{
|
||||
lastValid = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = lastValid;
|
||||
}
|
||||
|
||||
double oldSum = sum;
|
||||
double oldWSum = wsum;
|
||||
@@ -306,7 +328,9 @@ public sealed class Pwma : AbstractBase
|
||||
buffer[bufferIdx] = val;
|
||||
bufferIdx++;
|
||||
if (bufferIdx >= period)
|
||||
{
|
||||
bufferIdx = 0;
|
||||
}
|
||||
|
||||
tickCount++;
|
||||
if (tickCount >= ResyncInterval)
|
||||
@@ -319,7 +343,10 @@ public sealed class Pwma : AbstractBase
|
||||
for (int k = 0; k < period; k++)
|
||||
{
|
||||
int idx = bufferIdx + k;
|
||||
if (idx >= period) idx -= period;
|
||||
if (idx >= period)
|
||||
{
|
||||
idx -= period;
|
||||
}
|
||||
|
||||
double v = buffer[idx];
|
||||
recalcSum += v;
|
||||
@@ -342,4 +369,4 @@ public sealed class Pwma : AbstractBase
|
||||
_p_state = default;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,9 @@ public class SgmaTests
|
||||
var sgma = new Sgma(5, 0);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
sgma.Update(new TValue(DateTime.UtcNow, 100.0), isNew: true);
|
||||
}
|
||||
|
||||
Assert.Equal(100.0, sgma.Last.Value, 1e-9);
|
||||
|
||||
@@ -390,8 +392,15 @@ public class SgmaTests
|
||||
public void Sgma_ShapePreservation_HighDegreePreservesPeaks()
|
||||
{
|
||||
double[] prices = new double[20];
|
||||
for (int i = 0; i < 10; i++) prices[i] = 100 + i * 5;
|
||||
for (int i = 10; i < 20; i++) prices[i] = 145 - (i - 10) * 5;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
prices[i] = 100 + i * 5;
|
||||
}
|
||||
|
||||
for (int i = 10; i < 20; i++)
|
||||
{
|
||||
prices[i] = 145 - (i - 10) * 5;
|
||||
}
|
||||
|
||||
var sgma2 = new Sgma(5, 2);
|
||||
var sgma4 = new Sgma(5, 4);
|
||||
|
||||
+68
-15
@@ -58,9 +58,14 @@ public sealed class Sgma : AbstractBase
|
||||
public Sgma(int period = 9, int degree = 2)
|
||||
{
|
||||
if (period < 3)
|
||||
{
|
||||
throw new ArgumentException("Period must be at least 3", nameof(period));
|
||||
}
|
||||
|
||||
if (degree < 0 || degree > 4)
|
||||
{
|
||||
throw new ArgumentException("Degree must be between 0 and 4", nameof(degree));
|
||||
}
|
||||
|
||||
// Ensure period is odd
|
||||
_period = period % 2 == 0 ? period + 1 : period;
|
||||
@@ -115,7 +120,11 @@ public sealed class Sgma : AbstractBase
|
||||
weights[5] = 0.0952;
|
||||
weights[6] = -0.0476;
|
||||
double sum7 = 0.0;
|
||||
for (int i = 0; i < 7; i++) sum7 += weights[i];
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
sum7 += weights[i];
|
||||
}
|
||||
|
||||
invWeightSum = Math.Abs(sum7) > double.Epsilon ? 1.0 / sum7 : 0.0;
|
||||
return;
|
||||
}
|
||||
@@ -132,7 +141,11 @@ public sealed class Sgma : AbstractBase
|
||||
weights[7] = 0.0337;
|
||||
weights[8] = -0.0281;
|
||||
double sum9 = 0.0;
|
||||
for (int i = 0; i < 9; i++) sum9 += weights[i];
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
sum9 += weights[i];
|
||||
}
|
||||
|
||||
invWeightSum = Math.Abs(sum9) > double.Epsilon ? 1.0 / sum9 : 0.0;
|
||||
return;
|
||||
}
|
||||
@@ -209,7 +222,11 @@ public sealed class Sgma : AbstractBase
|
||||
if (!double.IsFinite(val))
|
||||
{
|
||||
Last = new TValue(input.Time, double.NaN);
|
||||
if (publish) PubEvent(Last, isNew);
|
||||
if (publish)
|
||||
{
|
||||
PubEvent(Last, isNew);
|
||||
}
|
||||
|
||||
return Last;
|
||||
}
|
||||
|
||||
@@ -264,7 +281,10 @@ public sealed class Sgma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return new TSeries([], []);
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return new TSeries([], []);
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -318,13 +338,24 @@ public sealed class Sgma : AbstractBase
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period = 9, int degree = 2)
|
||||
{
|
||||
if (period < 3)
|
||||
{
|
||||
throw new ArgumentException("Period must be at least 3", nameof(period));
|
||||
if (degree < 0 || degree > 4)
|
||||
throw new ArgumentException("Degree must be between 0 and 4", nameof(degree));
|
||||
if (source.Length != output.Length)
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (source.Length == 0) return;
|
||||
if (degree < 0 || degree > 4)
|
||||
{
|
||||
throw new ArgumentException("Degree must be between 0 and 4", nameof(degree));
|
||||
}
|
||||
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (source.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int usePeriod = period % 2 == 0 ? period + 1 : period;
|
||||
int useDegree = degree >= usePeriod ? 2 : degree;
|
||||
@@ -366,9 +397,15 @@ public sealed class Sgma : AbstractBase
|
||||
|
||||
ring[ringIdx] = val;
|
||||
ringIdx++;
|
||||
if (ringIdx >= usePeriod) ringIdx = 0;
|
||||
if (ringIdx >= usePeriod)
|
||||
{
|
||||
ringIdx = 0;
|
||||
}
|
||||
|
||||
if (count < usePeriod) count++;
|
||||
if (count < usePeriod)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count < usePeriod)
|
||||
{
|
||||
@@ -391,8 +428,15 @@ public sealed class Sgma : AbstractBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (weightsArray != null) ArrayPool<double>.Shared.Return(weightsArray);
|
||||
if (ringArray != null) ArrayPool<double>.Shared.Return(ringArray);
|
||||
if (weightsArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(weightsArray);
|
||||
}
|
||||
|
||||
if (ringArray != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(ringArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,7 +444,9 @@ public sealed class Sgma : AbstractBase
|
||||
private static double CalculateWeightedSumFull(RingBuffer buffer, double[] weights, double invWeightSum, double fallbackValue)
|
||||
{
|
||||
if (Math.Abs(invWeightSum) < double.Epsilon)
|
||||
{
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
ReadOnlySpan<double> internalBuf = buffer.InternalBuffer;
|
||||
int head = buffer.StartIndex;
|
||||
@@ -416,8 +462,15 @@ public sealed class Sgma : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static double CalculateWeightedSumWarmup(ReadOnlySpan<double> window, int p, int degree, double fallbackValue)
|
||||
{
|
||||
if (p <= 0) return 0.0;
|
||||
if (p == 1) return fallbackValue;
|
||||
if (p <= 0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (p == 1)
|
||||
{
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
if (degree == 2)
|
||||
{
|
||||
|
||||
@@ -157,4 +157,4 @@ public class SinemaIndicatorTests
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(0, SinemaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,4 +51,4 @@ public sealed class SinemaIndicator : Indicator, IWatchlistIndicator
|
||||
double value = _sinema.Update(new TValue(item.TimeLeft.Ticks, _priceSelector(item)), isNew).Value;
|
||||
_series.SetValue(value, _sinema.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,7 +420,9 @@ public class SinemaTests
|
||||
double[] output = new double[10000];
|
||||
var gbm = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 42);
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
source[i] = gbm.Next().Close;
|
||||
}
|
||||
|
||||
// Warm up
|
||||
Sinema.Batch(source.AsSpan(), output.AsSpan(), 100);
|
||||
@@ -562,7 +564,10 @@ public class SinemaTests
|
||||
public void Calculate_ReturnsCorrectResultsAndHotIndicator()
|
||||
{
|
||||
var series = new TSeries();
|
||||
for (int i = 1; i <= 10; i++) series.Add(DateTime.UtcNow, i * 10);
|
||||
for (int i = 1; i <= 10; i++)
|
||||
{
|
||||
series.Add(DateTime.UtcNow, i * 10);
|
||||
}
|
||||
|
||||
var (results, indicator) = Sinema.Calculate(series, 5);
|
||||
|
||||
@@ -579,4 +584,4 @@ public class SinemaTests
|
||||
indicator.Update(new TValue(DateTime.UtcNow, 110));
|
||||
Assert.True(double.IsFinite(indicator.Last.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,11 @@ public sealed class SinemaValidationTests : IDisposable
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
if (disposing)
|
||||
{
|
||||
@@ -319,4 +323,4 @@ public sealed class SinemaValidationTests : IDisposable
|
||||
|
||||
_output.WriteLine($"SINEMA({period}) all modes consistent: {batchResult:F10}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,9 @@ public sealed class Sinema : AbstractBase
|
||||
public Sinema(int period)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
_period = period;
|
||||
_buffer = new RingBuffer(period);
|
||||
@@ -101,7 +103,10 @@ public sealed class Sinema : AbstractBase
|
||||
/// <param name="step">Optional time step (unused)</param>
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
if (source.Length == 0) return;
|
||||
if (source.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset state
|
||||
_buffer.Clear();
|
||||
@@ -165,7 +170,10 @@ public sealed class Sinema : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double CalculateFromBuffer()
|
||||
{
|
||||
if (_buffer.Count == 0) return double.NaN;
|
||||
if (_buffer.Count == 0)
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
|
||||
int count = _buffer.Count;
|
||||
double sum = 0;
|
||||
@@ -222,7 +230,10 @@ public sealed class Sinema : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return [];
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -269,12 +280,20 @@ public sealed class Sinema : AbstractBase
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
int len = source.Length;
|
||||
if (len == 0) return;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CalculateScalarCore(source, output, period);
|
||||
}
|
||||
@@ -318,9 +337,13 @@ public sealed class Sinema : AbstractBase
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsFinite(val))
|
||||
{
|
||||
lastValid = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = lastValid;
|
||||
}
|
||||
|
||||
buffer[i] = val;
|
||||
|
||||
@@ -351,14 +374,20 @@ public sealed class Sinema : AbstractBase
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsFinite(val))
|
||||
{
|
||||
lastValid = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = lastValid;
|
||||
}
|
||||
|
||||
buffer[bufferIndex] = val;
|
||||
bufferIndex++;
|
||||
if (bufferIndex >= period)
|
||||
{
|
||||
bufferIndex = 0;
|
||||
}
|
||||
|
||||
// Calculate weighted sum using circular buffer
|
||||
double sum = 0;
|
||||
@@ -368,7 +397,9 @@ public sealed class Sinema : AbstractBase
|
||||
sum += buffer[bufIdx] * weights[j];
|
||||
bufIdx++;
|
||||
if (bufIdx >= period)
|
||||
{
|
||||
bufIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
output[i] = sum / fullWeightSum;
|
||||
@@ -377,9 +408,14 @@ public sealed class Sinema : AbstractBase
|
||||
finally
|
||||
{
|
||||
if (rentedBuffer != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(rentedBuffer);
|
||||
}
|
||||
|
||||
if (rentedWeights != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(rentedWeights);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,4 +443,4 @@ public sealed class Sinema : AbstractBase
|
||||
_p_state = default;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +425,9 @@ public class SmaTests
|
||||
double[] output = new double[10000];
|
||||
var gbm = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 42);
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
source[i] = gbm.Next().Close;
|
||||
}
|
||||
|
||||
// Warm up
|
||||
Sma.Batch(source.AsSpan(), output.AsSpan(), 100);
|
||||
@@ -571,7 +573,10 @@ public class SmaTests
|
||||
public void Calculate_ReturnsCorrectResultsAndHotIndicator()
|
||||
{
|
||||
var series = new TSeries();
|
||||
for (int i = 1; i <= 10; i++) series.Add(DateTime.UtcNow, i * 10);
|
||||
for (int i = 1; i <= 10; i++)
|
||||
{
|
||||
series.Add(DateTime.UtcNow, i * 10);
|
||||
}
|
||||
// 10, 20, 30, 40, 50, 60, 70, 80, 90, 100
|
||||
|
||||
// SMA(5)
|
||||
|
||||
@@ -45,7 +45,9 @@ public sealed class Sma : AbstractBase
|
||||
public Sma(int period)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
_period = period;
|
||||
_buffer = new RingBuffer(period);
|
||||
@@ -92,7 +94,10 @@ public sealed class Sma : AbstractBase
|
||||
/// <param name="source">Historical data (only the last 'period' is actually needed)</param>
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
if (source.Length == 0) return;
|
||||
if (source.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset state
|
||||
_buffer.Clear();
|
||||
@@ -213,7 +218,10 @@ public sealed class Sma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return [];
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -262,12 +270,20 @@ public sealed class Sma : AbstractBase
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
int len = source.Length;
|
||||
if (len == 0) return;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Try SIMD path for large, clean datasets
|
||||
// Requirements: SIMD support, large enough dataset, no NaN values
|
||||
@@ -345,9 +361,13 @@ public sealed class Sma : AbstractBase
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsFinite(val))
|
||||
{
|
||||
lastValid = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = lastValid;
|
||||
}
|
||||
|
||||
sum += val;
|
||||
buffer[i] = val;
|
||||
@@ -359,16 +379,22 @@ public sealed class Sma : AbstractBase
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsFinite(val))
|
||||
{
|
||||
lastValid = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = lastValid;
|
||||
}
|
||||
|
||||
sum = Math.FusedMultiplyAdd(-1.0, buffer[bufferIndex], sum + val);
|
||||
buffer[bufferIndex] = val;
|
||||
|
||||
bufferIndex++;
|
||||
if (bufferIndex >= period)
|
||||
{
|
||||
bufferIndex = 0;
|
||||
}
|
||||
|
||||
output[i] = sum / period;
|
||||
|
||||
@@ -388,7 +414,9 @@ public sealed class Sma : AbstractBase
|
||||
finally
|
||||
{
|
||||
if (rented != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(rented);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,7 +440,9 @@ public sealed class Sma : AbstractBase
|
||||
}
|
||||
|
||||
if (len <= period)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var vInvPeriod = Vector512.Create(invPeriod);
|
||||
int simdEnd = period + (len - period) / VectorWidth * VectorWidth;
|
||||
@@ -486,7 +516,9 @@ public sealed class Sma : AbstractBase
|
||||
}
|
||||
|
||||
if (len <= period)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var vInvPeriod = Vector256.Create(invPeriod);
|
||||
var vZero = Vector256<double>.Zero;
|
||||
@@ -559,7 +591,9 @@ public sealed class Sma : AbstractBase
|
||||
}
|
||||
|
||||
if (len <= period)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var vInvPeriod = Vector128.Create(invPeriod);
|
||||
int simdEnd = period + (len - period) / VectorWidth * VectorWidth;
|
||||
|
||||
@@ -51,7 +51,9 @@ public sealed class TrimaIndicator : Indicator, IWatchlistIndicator
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
if (args.Reason != UpdateReason.NewBar && args.Reason != UpdateReason.HistoricalBar && args.Reason != UpdateReason.NewTick)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
TValue result = _ma.Update(new TValue(item.TimeLeft.Ticks, _priceSelector(item)), args.IsNewBar());
|
||||
|
||||
@@ -34,7 +34,10 @@ public sealed class Trima : AbstractBase
|
||||
|
||||
public Trima(int period)
|
||||
{
|
||||
if (period <= 0) throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
_period = period;
|
||||
int p1 = (period + 1) / 2;
|
||||
@@ -81,7 +84,10 @@ public sealed class Trima : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return [];
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -143,9 +149,14 @@ public sealed class Trima : AbstractBase
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
int p1 = (period + 1) / 2;
|
||||
int p2 = period / 2 + 1;
|
||||
@@ -163,4 +174,4 @@ public sealed class Trima : AbstractBase
|
||||
ArrayPool<double>.Shared.Return(tempArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,10 @@ public class WmaCoverageTests
|
||||
const int period = 10;
|
||||
int len = 100; // < 256
|
||||
double[] source = new double[len];
|
||||
for (int i = 0; i < len; i++) source[i] = i;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
source[i] = i;
|
||||
}
|
||||
|
||||
double[] output = new double[len];
|
||||
|
||||
@@ -25,12 +28,19 @@ public class WmaCoverageTests
|
||||
[Fact]
|
||||
public void Cover_Avx2_Explicitly()
|
||||
{
|
||||
if (!Avx2.IsSupported) return;
|
||||
if (!Avx2.IsSupported)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int period = 10;
|
||||
int len = 1000;
|
||||
double[] source = new double[len];
|
||||
for (int i = 0; i < len; i++) source[i] = i;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
source[i] = i;
|
||||
}
|
||||
|
||||
double[] output = new double[len];
|
||||
|
||||
// Use reflection to invoke private static CalculateSimdCore
|
||||
@@ -78,7 +88,11 @@ public class WmaCoverageTests
|
||||
int period = 10;
|
||||
int len = 1000;
|
||||
double[] source = new double[len];
|
||||
for (int i = 0; i < len; i++) source[i] = i;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
source[i] = i;
|
||||
}
|
||||
|
||||
double[] output = new double[len];
|
||||
|
||||
InvokePrivateStaticMethod_WithSpans("CalculateScalarCore", source, output, period);
|
||||
|
||||
@@ -51,7 +51,9 @@ public sealed class WmaIndicator : Indicator, IWatchlistIndicator
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
if (args.Reason != UpdateReason.NewBar && args.Reason != UpdateReason.HistoricalBar && args.Reason != UpdateReason.NewTick)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
TValue result = _ma.Update(new TValue(item.TimeLeft.Ticks, _priceSelector(item)), args.IsNewBar());
|
||||
|
||||
@@ -27,9 +27,16 @@ public sealed class WmaValidationTests : IDisposable
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
if (disposing) _testData?.Dispose();
|
||||
if (disposing)
|
||||
{
|
||||
_testData?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -53,7 +53,10 @@ public sealed class Wma : AbstractBase
|
||||
|
||||
public Wma(int period)
|
||||
{
|
||||
if (period <= 0) throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
_period = period;
|
||||
_divisor = (double)period * (period + 1) * 0.5;
|
||||
@@ -173,7 +176,10 @@ public sealed class Wma : AbstractBase
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return [];
|
||||
if (source.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
@@ -197,7 +203,10 @@ public sealed class Wma : AbstractBase
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
if (source.Length == 0) return;
|
||||
if (source.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int len = source.Length;
|
||||
int windowSize = Math.Min(len, _period);
|
||||
@@ -258,12 +267,20 @@ public sealed class Wma : AbstractBase
|
||||
public static void Batch(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
if (source.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and output must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
int len = source.Length;
|
||||
if (len == 0) return;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int simdThreshold = 256;
|
||||
if (Avx512F.IsSupported && len >= simdThreshold && !source.ContainsNonFinite())
|
||||
@@ -305,9 +322,13 @@ public sealed class Wma : AbstractBase
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsFinite(val))
|
||||
{
|
||||
lastValid = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = lastValid;
|
||||
}
|
||||
|
||||
sum += val;
|
||||
wsum = Math.FusedMultiplyAdd(i + 1, val, wsum);
|
||||
@@ -322,9 +343,13 @@ public sealed class Wma : AbstractBase
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsFinite(val))
|
||||
{
|
||||
lastValid = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = lastValid;
|
||||
}
|
||||
|
||||
double oldSum = sum;
|
||||
double oldest = buffer[bufferIdx];
|
||||
@@ -334,7 +359,9 @@ public sealed class Wma : AbstractBase
|
||||
buffer[bufferIdx] = val;
|
||||
bufferIdx++;
|
||||
if (bufferIdx >= period)
|
||||
{
|
||||
bufferIdx = 0;
|
||||
}
|
||||
|
||||
output[i] = wsum / divisor;
|
||||
|
||||
@@ -349,7 +376,10 @@ public sealed class Wma : AbstractBase
|
||||
for (int k = 0; k < period; k++)
|
||||
{
|
||||
int idx = bufferIdx + k;
|
||||
if (idx >= period) idx -= period;
|
||||
if (idx >= period)
|
||||
{
|
||||
idx -= period;
|
||||
}
|
||||
|
||||
double v = buffer[idx];
|
||||
recalcSum += v;
|
||||
@@ -386,7 +416,9 @@ public sealed class Wma : AbstractBase
|
||||
}
|
||||
|
||||
if (len <= period)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var vInvDivisor = Vector512.Create(invDivisor);
|
||||
var vPeriod = Vector512.Create((double)period);
|
||||
@@ -501,7 +533,9 @@ public sealed class Wma : AbstractBase
|
||||
}
|
||||
|
||||
if (len <= period)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var vInvDivisor = Vector256.Create(invDivisor);
|
||||
var vPeriod = Vector256.Create((double)period);
|
||||
@@ -704,7 +738,9 @@ public sealed class Wma : AbstractBase
|
||||
}
|
||||
|
||||
if (len <= period)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var vInvDivisor = Vector128.Create(invDivisor);
|
||||
int simdEnd = period + ((len - period) / vectorWidth) * vectorWidth;
|
||||
|
||||
Reference in New Issue
Block a user