mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-22 04:28:04 +00:00
Refactor code formatting and improve consistency across various test files
- Removed unnecessary blank lines in multiple test files to enhance readability. - Ensured consistent spacing and formatting in the `Trima`, `Usf`, `Vidya`, `Wma`, and `Atr` test classes. - Updated comments for clarity and consistency in the `Atr` and `Adl` classes. - Adjusted project files for better structure and maintainability.
This commit is contained in:
@@ -23,7 +23,7 @@ public class BetaTests
|
||||
{
|
||||
int period = 5;
|
||||
var beta = new Beta(period);
|
||||
|
||||
|
||||
// We need period returns.
|
||||
// 1st update: initializes prev prices. No return.
|
||||
// 2nd update: 1st return.
|
||||
@@ -45,7 +45,7 @@ public class BetaTests
|
||||
{
|
||||
// Scenario: Asset returns are exactly 2x Market returns.
|
||||
// We need variable market returns to have non-zero variance.
|
||||
|
||||
|
||||
int period = 10;
|
||||
var beta = new Beta(period);
|
||||
|
||||
@@ -66,9 +66,9 @@ public class BetaTests
|
||||
|
||||
marketPrice *= (1 + marketReturn);
|
||||
assetPrice *= (1 + assetReturn);
|
||||
|
||||
|
||||
TValue result = beta.Update(assetPrice, marketPrice);
|
||||
|
||||
|
||||
if (beta.IsHot)
|
||||
{
|
||||
Assert.Equal(2.0, result.Value, precision: 6);
|
||||
@@ -88,7 +88,7 @@ public class BetaTests
|
||||
|
||||
beta.Reset();
|
||||
Assert.False(beta.IsHot);
|
||||
|
||||
|
||||
// Re-initialize
|
||||
beta.Update(100, 100);
|
||||
Assert.False(beta.IsHot);
|
||||
|
||||
@@ -25,13 +25,13 @@ public sealed class BetaValidationTests : IDisposable
|
||||
{
|
||||
// Generate Market Data (use existing Data)
|
||||
var marketQuotes = _data.Data;
|
||||
|
||||
|
||||
// Generate Asset Data correlated to Market
|
||||
// Asset Returns = 1.5 * Market Returns + Noise
|
||||
var assetQuotes = new List<TBar>();
|
||||
double assetPrice = 100;
|
||||
double targetBeta = 1.5;
|
||||
|
||||
|
||||
// Use GBM for noise generation (sigma=0.2 gives ~0.0006 per step noise which matches original random noise level)
|
||||
var noiseGbm = new GBM(startPrice: 100, mu: 0, sigma: 0.2, seed: 777);
|
||||
|
||||
@@ -40,13 +40,13 @@ public sealed class BetaValidationTests : IDisposable
|
||||
for (int i = 1; i < marketQuotes.Count; i++)
|
||||
{
|
||||
double marketReturn = (marketQuotes[i].Value - marketQuotes[i-1].Value) / marketQuotes[i-1].Value;
|
||||
|
||||
|
||||
// Get noise from GBM return
|
||||
var noiseBar = noiseGbm.Next();
|
||||
double noise = (noiseBar.Close - noiseBar.Open) / noiseBar.Open;
|
||||
|
||||
|
||||
double assetReturn = targetBeta * marketReturn + noise;
|
||||
|
||||
|
||||
assetPrice *= (1 + assetReturn);
|
||||
assetQuotes.Add(new TBar(marketQuotes[i].Time, assetPrice, assetPrice, assetPrice, assetPrice, 1000));
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public sealed class BetaValidationTests : IDisposable
|
||||
// Skip warmup period. Skender Beta needs period returns, so period+1 prices?
|
||||
// Skender results align with input quotes.
|
||||
// First valid value should be at index 'period'.
|
||||
|
||||
|
||||
// We verify the last 100 values
|
||||
int count = qlBeta.Count;
|
||||
int skip = period + 5; // Safety margin
|
||||
@@ -82,7 +82,7 @@ public sealed class BetaValidationTests : IDisposable
|
||||
{
|
||||
double sk = (skenderBeta[i].Beta ?? 0);
|
||||
double ql = qlBeta[i];
|
||||
|
||||
|
||||
// Skender might return null/0 for warmup.
|
||||
if (Math.Abs(sk) > 1e-10)
|
||||
{
|
||||
|
||||
@@ -9,14 +9,14 @@ namespace QuanTAlib;
|
||||
/// <remarks>
|
||||
/// Beta is calculated as the covariance of the asset's returns and the market's returns,
|
||||
/// divided by the variance of the market's returns.
|
||||
///
|
||||
///
|
||||
/// Formula:
|
||||
/// Beta = Cov(Ra, Rm) / Var(Rm)
|
||||
///
|
||||
///
|
||||
/// Where:
|
||||
/// Ra = Return of Asset
|
||||
/// Rm = Return of Market
|
||||
///
|
||||
///
|
||||
/// This implementation uses the O(1) slope formula for linear regression of Ra vs Rm:
|
||||
/// Beta = (N * Sum(Ra*Rm) - Sum(Ra) * Sum(Rm)) / (N * Sum(Rm^2) - Sum(Rm)^2)
|
||||
/// </remarks>
|
||||
@@ -25,7 +25,7 @@ public sealed class Beta : AbstractBase
|
||||
{
|
||||
private readonly RingBuffer _returnsAsset;
|
||||
private readonly RingBuffer _returnsMarket;
|
||||
|
||||
|
||||
private double _prevAsset;
|
||||
private double _prevMarket;
|
||||
private double _p_prevAsset;
|
||||
|
||||
@@ -63,7 +63,7 @@ public sealed class CovarianceIndicator : Indicator, IWatchlistIndicator
|
||||
|
||||
var input1 = new TValue(time, val1);
|
||||
var input2 = new TValue(time, val2);
|
||||
|
||||
|
||||
TValue result = _cov!.Update(input1, input2, args.IsNewBar());
|
||||
|
||||
_series!.SetValue(result.Value, _cov.IsHot, ShowColdValues);
|
||||
|
||||
@@ -31,7 +31,7 @@ public class CovarianceSimdTests
|
||||
// This will use SIMD if available and length >= 256
|
||||
var simdResult = Covariance.Calculate(sourceX, sourceY, period);
|
||||
|
||||
// Calculate expected using scalar loop (simulating by using small chunks or manual calc,
|
||||
// Calculate expected using scalar loop (simulating by using small chunks or manual calc,
|
||||
// but easier to just use the streaming update which is scalar)
|
||||
var scalarCov = new Covariance(period);
|
||||
var expectedValues = new double[count];
|
||||
@@ -56,7 +56,7 @@ public class CovarianceSimdTests
|
||||
int period = 50;
|
||||
var dataX = Enumerable.Range(0, count).Select(x => (double)x).ToArray();
|
||||
var dataY = Enumerable.Range(0, count).Select(x => (double)x * 2).ToArray();
|
||||
|
||||
|
||||
// Inject NaN
|
||||
dataX[300] = double.NaN;
|
||||
dataY[350] = double.NaN;
|
||||
@@ -74,9 +74,9 @@ public class CovarianceSimdTests
|
||||
|
||||
// Assert
|
||||
// Verify around the NaN values
|
||||
// Index 300 has NaN in X. Covariance should handle it (likely treat as 0 or propagate last valid if logic dictates,
|
||||
// Index 300 has NaN in X. Covariance should handle it (likely treat as 0 or propagate last valid if logic dictates,
|
||||
// but current implementation replaces non-finite with 0 in scalar core).
|
||||
|
||||
|
||||
// Let's verify against streaming which we know uses scalar logic
|
||||
// BUT: Batch implementation replaces NaN with 0, while Streaming propagates NaN.
|
||||
// To compare, we must feed 0 instead of NaN to streaming.
|
||||
@@ -92,7 +92,7 @@ public class CovarianceSimdTests
|
||||
Assert.Equal(res.Value, result.Values[i], precision: 9);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Covariance_Simd_Resync_Check()
|
||||
{
|
||||
@@ -102,13 +102,13 @@ public class CovarianceSimdTests
|
||||
// The SIMD loop starts at 'period' and goes up to 'simdEnd'.
|
||||
// So we need length > period + 1000.
|
||||
int period = 10;
|
||||
int count = 2000;
|
||||
|
||||
int count = 2000;
|
||||
|
||||
// Use simple linear data to make verification easy
|
||||
// y = 2x
|
||||
var dataX = Enumerable.Range(0, count).Select(x => (double)x).ToArray();
|
||||
var dataY = Enumerable.Range(0, count).Select(x => (double)x * 2).ToArray();
|
||||
|
||||
|
||||
var sourceX = new TSeries();
|
||||
sourceX.Add(dataX);
|
||||
var sourceY = new TSeries();
|
||||
@@ -123,7 +123,7 @@ public class CovarianceSimdTests
|
||||
// For period 10: 0..9. Variance is constant.
|
||||
// Var(0..9) = 9.16666... (Population) or 10.185... (Sample)?
|
||||
// Let's just compare with scalar truth.
|
||||
|
||||
|
||||
var scalarCov = new Covariance(period);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ public class CovarianceTests
|
||||
{
|
||||
// Arrange
|
||||
var cov = new Covariance(3, isPopulation: false);
|
||||
|
||||
|
||||
// Act & Assert
|
||||
// 1. Add (1, 2)
|
||||
// MeanX = 1, MeanY = 2
|
||||
@@ -50,11 +50,11 @@ public class CovarianceTests
|
||||
{
|
||||
// Arrange
|
||||
var cov = new Covariance(3, isPopulation: true);
|
||||
|
||||
|
||||
// Act & Assert
|
||||
cov.Update(1, 2);
|
||||
cov.Update(2, 4);
|
||||
|
||||
|
||||
// 3. Add (3, 6)
|
||||
// X: {1, 2, 3}, Y: {2, 4, 6}
|
||||
// MeanX = 2, MeanY = 4
|
||||
@@ -69,12 +69,12 @@ public class CovarianceTests
|
||||
{
|
||||
// Arrange
|
||||
var cov = new Covariance(3);
|
||||
|
||||
|
||||
// Act
|
||||
cov.Update(1, 1);
|
||||
cov.Update(2, 1);
|
||||
var res = cov.Update(3, 1); // Y is constant, variance Y is 0, covariance is 0
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0, res.Value);
|
||||
}
|
||||
@@ -84,18 +84,18 @@ public class CovarianceTests
|
||||
{
|
||||
// Arrange
|
||||
var cov = new Covariance(3);
|
||||
|
||||
|
||||
// Act
|
||||
cov.Update(1, 3);
|
||||
cov.Update(2, 2);
|
||||
var res = cov.Update(3, 1);
|
||||
|
||||
|
||||
// X: {1, 2, 3}, MeanX = 2
|
||||
// Y: {3, 2, 1}, MeanY = 2
|
||||
// Cov = ((1-2)(3-2) + (2-2)(2-2) + (3-2)(1-2)) / 2
|
||||
// = ((-1)(1) + 0 + (1)(-1)) / 2
|
||||
// = (-1 - 1) / 2 = -1
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.Equal(-1, res.Value);
|
||||
}
|
||||
@@ -105,16 +105,16 @@ public class CovarianceTests
|
||||
{
|
||||
// Arrange
|
||||
var cov = new Covariance(3);
|
||||
|
||||
|
||||
// Act
|
||||
// Force many updates to trigger resync (ResyncInterval = 1000)
|
||||
// We can't easily force 1000 updates in a simple test without loop,
|
||||
// We can't easily force 1000 updates in a simple test without loop,
|
||||
// but we can verify the logic holds for a sequence.
|
||||
for (int i = 0; i < 1100; i++)
|
||||
{
|
||||
cov.Update(i, i * 2);
|
||||
}
|
||||
|
||||
|
||||
// Last 3: {1097, 1098, 1099}, {2194, 2196, 2198}
|
||||
// This is a perfect linear relationship y = 2x
|
||||
// Cov(X, 2X) = 2 * Var(X)
|
||||
@@ -123,7 +123,7 @@ public class CovarianceTests
|
||||
// SumSqDiff = (-1)^2 + 0 + 1^2 = 2
|
||||
// Var = 2 / 2 = 1
|
||||
// Cov = 2 * 1 = 2
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, cov.Last.Value, precision: 10);
|
||||
}
|
||||
@@ -133,29 +133,29 @@ public class CovarianceTests
|
||||
{
|
||||
// Arrange
|
||||
var cov = new Covariance(3);
|
||||
|
||||
|
||||
// Act
|
||||
cov.Update(1, 2);
|
||||
cov.Update(2, 4);
|
||||
cov.Update(3, 6); // Cov = 2
|
||||
|
||||
|
||||
// Update last bar with new values
|
||||
// Change (3, 6) to (4, 8)
|
||||
// X: {1, 2, 4}, MeanX = 7/3 = 2.333...
|
||||
// Y: {2, 4, 8}, MeanY = 14/3 = 4.666...
|
||||
// This is harder to calc manually, let's use the property that it should match adding (4, 8) directly
|
||||
|
||||
|
||||
var res = cov.Update(4, 8, isNew: false);
|
||||
|
||||
|
||||
var cov2 = new Covariance(3);
|
||||
cov2.Update(1, 2);
|
||||
cov2.Update(2, 4);
|
||||
var expected = cov2.Update(4, 8);
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected.Value, res.Value, precision: 10);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Covariance_Throws_On_Single_Input()
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ public class CovarianceValidationTests
|
||||
var cov = new Covariance(period, isPopulation: false);
|
||||
var gbmX = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 123);
|
||||
var gbmY = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 456);
|
||||
|
||||
|
||||
double[] x = new double[100];
|
||||
double[] y = new double[100];
|
||||
for (int i = 0; i < 100; i++)
|
||||
@@ -21,7 +21,7 @@ public class CovarianceValidationTests
|
||||
x[i] = gbmX.Next().Close;
|
||||
y[i] = gbmY.Next().Close;
|
||||
cov.Update(x[i], y[i]);
|
||||
|
||||
|
||||
if (i >= period - 1)
|
||||
{
|
||||
// Manual calculation for last 'period' items
|
||||
@@ -34,13 +34,13 @@ public class CovarianceValidationTests
|
||||
}
|
||||
double meanX = sumX / period;
|
||||
double meanY = sumY / period;
|
||||
|
||||
|
||||
double sumProd = 0;
|
||||
for (int j = 0; j < period; j++)
|
||||
{
|
||||
sumProd += (x[i - j] - meanX) * (y[i - j] - meanY);
|
||||
}
|
||||
|
||||
|
||||
double expected = sumProd / (period - 1);
|
||||
Assert.Equal(expected, cov.Last.Value, precision: 8);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class CovarianceValidationTests
|
||||
var cov = new Covariance(period, isPopulation: true);
|
||||
var gbmX = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 456);
|
||||
var gbmY = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 789);
|
||||
|
||||
|
||||
double[] x = new double[100];
|
||||
double[] y = new double[100];
|
||||
for (int i = 0; i < 100; i++)
|
||||
@@ -63,7 +63,7 @@ public class CovarianceValidationTests
|
||||
x[i] = gbmX.Next().Close;
|
||||
y[i] = gbmY.Next().Close;
|
||||
cov.Update(x[i], y[i]);
|
||||
|
||||
|
||||
if (i >= period - 1)
|
||||
{
|
||||
// Manual calculation for last 'period' items
|
||||
@@ -76,13 +76,13 @@ public class CovarianceValidationTests
|
||||
}
|
||||
double meanX = sumX / period;
|
||||
double meanY = sumY / period;
|
||||
|
||||
|
||||
double sumProd = 0;
|
||||
for (int j = 0; j < period; j++)
|
||||
{
|
||||
sumProd += (x[i - j] - meanX) * (y[i - j] - meanY);
|
||||
}
|
||||
|
||||
|
||||
double expected = sumProd / period;
|
||||
Assert.Equal(expected, cov.Last.Value, precision: 8);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public sealed class LinRegValidationTests : IDisposable
|
||||
{
|
||||
var period = 14;
|
||||
var skender = _data.SkenderQuotes.GetSlope(period).ToList();
|
||||
|
||||
|
||||
var linreg = new LinReg(period);
|
||||
var slopeSeries = new TSeries();
|
||||
foreach (var item in _data.Data)
|
||||
@@ -47,7 +47,7 @@ public sealed class LinRegValidationTests : IDisposable
|
||||
{
|
||||
var period = 14;
|
||||
var skender = _data.SkenderQuotes.GetSlope(period).ToList();
|
||||
|
||||
|
||||
var linreg = new LinReg(period);
|
||||
var r2Series = new TSeries();
|
||||
foreach (var item in _data.Data)
|
||||
|
||||
@@ -166,7 +166,7 @@ public sealed class LinReg : AbstractBase
|
||||
_state.SumY = _buffer.Sum;
|
||||
_state.SumXY = 0;
|
||||
var span = _buffer.GetSpan();
|
||||
|
||||
|
||||
// Vectorized SumY2
|
||||
_state.SumY2 = span.DotProduct(span);
|
||||
|
||||
@@ -248,7 +248,7 @@ public sealed class LinReg : AbstractBase
|
||||
// R2 = (n * sum_xy - sum_x * sum_y)^2 / ( (n * sum_x2 - sum_x^2) * (n * sum_y2 - sum_y^2) )
|
||||
double numerator = Math.FusedMultiplyAdd(n, _state.SumXY, -sx * _state.SumY);
|
||||
double term2 = Math.FusedMultiplyAdd(n, _state.SumY2, -_state.SumY * _state.SumY);
|
||||
|
||||
|
||||
RSquared = Math.Abs(term2) < MinDenominator
|
||||
? 1.0 // All y are same
|
||||
: numerator * numerator / (denom * term2);
|
||||
|
||||
@@ -9,7 +9,7 @@ public class MedianTests
|
||||
{
|
||||
// Arrange
|
||||
var median = new Median(3);
|
||||
|
||||
|
||||
// Act
|
||||
median.Update(new TValue(DateTime.MinValue, 10));
|
||||
median.Update(new TValue(DateTime.MinValue, 30));
|
||||
@@ -25,7 +25,7 @@ public class MedianTests
|
||||
{
|
||||
// Arrange
|
||||
var median = new Median(4);
|
||||
|
||||
|
||||
// Act
|
||||
median.Update(new TValue(DateTime.MinValue, 10));
|
||||
median.Update(new TValue(DateTime.MinValue, 40));
|
||||
@@ -42,13 +42,13 @@ public class MedianTests
|
||||
{
|
||||
// Arrange
|
||||
var median = new Median(3);
|
||||
|
||||
|
||||
// Act
|
||||
median.Update(new TValue(DateTime.MinValue, 10));
|
||||
median.Update(new TValue(DateTime.MinValue, 20));
|
||||
|
||||
|
||||
// Update with 30 (isNew=true)
|
||||
var r1 = median.Update(new TValue(DateTime.MinValue, 30));
|
||||
var r1 = median.Update(new TValue(DateTime.MinValue, 30));
|
||||
// Window: [10, 20, 30] -> Median 20
|
||||
Assert.Equal(20, r1.Value);
|
||||
|
||||
@@ -79,7 +79,7 @@ public class MedianTests
|
||||
var medianBatch = Median.Batch(source, period);
|
||||
var medianStream = new Median(period);
|
||||
var streamResults = new List<double>();
|
||||
|
||||
|
||||
foreach (var val in source)
|
||||
{
|
||||
streamResults.Add(medianStream.Update(val).Value);
|
||||
@@ -91,7 +91,7 @@ public class MedianTests
|
||||
Assert.Equal(medianBatch.Values[i], streamResults[i], 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Median_StaticBatch_Matches_ClassBatch()
|
||||
{
|
||||
@@ -99,11 +99,11 @@ public class MedianTests
|
||||
int period = 5;
|
||||
double[] data = new double[20];
|
||||
for(int i=0; i<data.Length; i++) data[i] = i;
|
||||
|
||||
|
||||
// Act
|
||||
double[] output = new double[data.Length];
|
||||
Median.Batch(data, output, period);
|
||||
|
||||
|
||||
var series = new TSeries();
|
||||
for(int i=0; i<data.Length; i++) series.Add(new TValue(DateTime.MinValue, data[i]));
|
||||
var batchSeries = Median.Batch(series, period);
|
||||
|
||||
@@ -17,7 +17,7 @@ public sealed class MedianValidationTests : IDisposable
|
||||
_data.Dispose();
|
||||
}
|
||||
|
||||
// Note: Standard TA libraries (Skender, TA-Lib, Tulip, Ooples) do not provide a
|
||||
// Note: Standard TA libraries (Skender, TA-Lib, Tulip, Ooples) do not provide a
|
||||
// "Rolling Median" indicator. They typically provide "Median Price" which is (High+Low)/2.
|
||||
// Therefore, we validate against a robust LINQ-based reference implementation and MathNet.
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ public class SkewTests
|
||||
// Variance (Sample) = 2.5
|
||||
// StdDev (Sample) = 1.58113883
|
||||
// Skewness (Sample) = 0 (Symmetric)
|
||||
|
||||
|
||||
var skew = new Skew(5, isPopulation: false);
|
||||
skew.Update(new TValue(DateTime.UtcNow, 1));
|
||||
skew.Update(new TValue(DateTime.UtcNow, 2));
|
||||
@@ -38,7 +38,7 @@ public class SkewTests
|
||||
// Test data: 1, 1, 1, 10
|
||||
// Mean = 3.25
|
||||
// Skewness should be positive (right tail)
|
||||
|
||||
|
||||
var skew = new Skew(4, isPopulation: false);
|
||||
skew.Update(new TValue(DateTime.UtcNow, 1));
|
||||
skew.Update(new TValue(DateTime.UtcNow, 1));
|
||||
@@ -54,7 +54,7 @@ public class SkewTests
|
||||
// Test data: 10, 10, 10, 1
|
||||
// Mean = 7.75
|
||||
// Skewness should be negative (left tail)
|
||||
|
||||
|
||||
var skew = new Skew(4, isPopulation: false);
|
||||
skew.Update(new TValue(DateTime.UtcNow, 10));
|
||||
skew.Update(new TValue(DateTime.UtcNow, 10));
|
||||
@@ -68,16 +68,16 @@ public class SkewTests
|
||||
public void Update_HandlesUpdates_IsNewFalse()
|
||||
{
|
||||
var skew = new Skew(5);
|
||||
|
||||
|
||||
// 1, 2, 3, 4
|
||||
skew.Update(new TValue(DateTime.UtcNow, 1));
|
||||
skew.Update(new TValue(DateTime.UtcNow, 2));
|
||||
skew.Update(new TValue(DateTime.UtcNow, 3));
|
||||
skew.Update(new TValue(DateTime.UtcNow, 4));
|
||||
|
||||
|
||||
// Add 5
|
||||
skew.Update(new TValue(DateTime.UtcNow, 5), isNew: true);
|
||||
|
||||
|
||||
// Update 5 to 10
|
||||
var res2 = skew.Update(new TValue(DateTime.UtcNow, 10), isNew: false);
|
||||
|
||||
@@ -97,10 +97,10 @@ public class SkewTests
|
||||
{
|
||||
var skew = new Skew(5);
|
||||
for (int i = 0; i < 5; i++) skew.Update(new TValue(DateTime.UtcNow, i));
|
||||
|
||||
|
||||
skew.Reset();
|
||||
Assert.False(skew.IsHot);
|
||||
|
||||
|
||||
// Should behave like new
|
||||
skew.Update(new TValue(DateTime.UtcNow, 1));
|
||||
Assert.Equal(0, skew.Last.Value); // Not enough data
|
||||
@@ -111,7 +111,7 @@ public class SkewTests
|
||||
{
|
||||
var data = new double[] { 1, 2, 3, 4, 5, 10, 1, 2, 3 };
|
||||
int period = 5;
|
||||
|
||||
|
||||
// Streaming
|
||||
var skew = new Skew(period);
|
||||
var streamingResults = new System.Collections.Generic.List<double>();
|
||||
@@ -139,7 +139,7 @@ public class SkewTests
|
||||
// StdDev (Pop) = sqrt(2/3)
|
||||
// M3 (Pop) = ((1-2)^3 + (2-2)^3 + (3-2)^3) / 3 = 0
|
||||
// Skew (Pop) = 0
|
||||
|
||||
|
||||
var skew = new Skew(3, isPopulation: true);
|
||||
skew.Update(new TValue(DateTime.UtcNow, 1));
|
||||
skew.Update(new TValue(DateTime.UtcNow, 2));
|
||||
@@ -166,7 +166,7 @@ public class SkewTests
|
||||
skew.Update(new TValue(DateTime.UtcNow, 1));
|
||||
skew.Update(new TValue(DateTime.UtcNow, 2));
|
||||
skew.Update(new TValue(DateTime.UtcNow, double.NaN)); // Should be treated as 0 or handled gracefully
|
||||
|
||||
|
||||
var result = skew.Last.Value;
|
||||
Assert.True(double.IsNaN(result) || result == 0);
|
||||
}
|
||||
@@ -177,12 +177,12 @@ public class SkewTests
|
||||
// Run for > 1000 updates to trigger Resync
|
||||
var skew = new Skew(10);
|
||||
var gbm = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 123);
|
||||
|
||||
|
||||
for (int i = 0; i < 1100; i++)
|
||||
{
|
||||
skew.Update(new TValue(DateTime.UtcNow, gbm.Next().Close));
|
||||
}
|
||||
|
||||
|
||||
Assert.True(double.IsFinite(skew.Last.Value));
|
||||
}
|
||||
|
||||
@@ -195,10 +195,10 @@ public class SkewTests
|
||||
for (int i = 0; i < count; i++) data[i] = (double)i;
|
||||
|
||||
var series = new TSeries(new System.Collections.Generic.List<long>(new long[count]), new System.Collections.Generic.List<double>(data));
|
||||
|
||||
|
||||
// Batch calculation
|
||||
var batchResult = Skew.Calculate(series, 10);
|
||||
|
||||
|
||||
// Verify last value against streaming
|
||||
var skew = new Skew(10);
|
||||
double lastStreaming = 0;
|
||||
|
||||
@@ -22,7 +22,7 @@ public sealed class SkewValidationTests : IDisposable
|
||||
int period = 20;
|
||||
var skew = new Skew(period, isPopulation: false);
|
||||
var popSkew = new Skew(period, isPopulation: true);
|
||||
|
||||
|
||||
var quotes = _data.SkenderQuotes.ToList();
|
||||
double[] input = quotes.Select(q => (double)q.Close).ToArray();
|
||||
|
||||
@@ -37,7 +37,7 @@ public sealed class SkewValidationTests : IDisposable
|
||||
var window = input[(i - period + 1)..(i + 1)];
|
||||
double expected = Statistics.Skewness(window);
|
||||
double expectedPop = Statistics.PopulationSkewness(window);
|
||||
|
||||
|
||||
Assert.Equal(expected, val.Value, 1e-6);
|
||||
Assert.Equal(expectedPop, popVal.Value, 1e-6);
|
||||
}
|
||||
|
||||
+19
-19
@@ -80,7 +80,7 @@ public sealed class Skew : AbstractBase
|
||||
{
|
||||
double oldNewest = _buffer.Newest;
|
||||
_buffer.UpdateNewest(input.Value);
|
||||
|
||||
|
||||
double val = input.Value;
|
||||
_sum = _sum - oldNewest + val;
|
||||
_sumSq = _sumSq - (oldNewest * oldNewest) + (val * val);
|
||||
@@ -92,7 +92,7 @@ public sealed class Skew : AbstractBase
|
||||
{
|
||||
double n = _buffer.Count;
|
||||
double mean = _sum / n;
|
||||
|
||||
|
||||
// Calculate 2nd moment (Variance)
|
||||
// m2 = Sum((x-mean)^2) / n = (SumSq - Sum^2/n) / n
|
||||
double m2Numerator = _sumSq - (_sum * _sum) / n;
|
||||
@@ -106,7 +106,7 @@ public sealed class Skew : AbstractBase
|
||||
// = SumCu - 3*mean*SumSq + 3*mean^2*Sum - n*mean^3
|
||||
// Since Sum = n*mean:
|
||||
// = SumCu - 3*mean*SumSq + 2*n*mean^3
|
||||
|
||||
|
||||
double m3Numerator = _sumCu - 3 * mean * _sumSq + 2 * n * mean * mean * mean;
|
||||
double m3 = m3Numerator / n;
|
||||
|
||||
@@ -232,7 +232,7 @@ public sealed class Skew : AbstractBase
|
||||
double sum = 0;
|
||||
double sumSq = 0;
|
||||
double sumCu = 0;
|
||||
|
||||
|
||||
int i = 0;
|
||||
|
||||
// Warmup phase
|
||||
@@ -259,11 +259,11 @@ public sealed class Skew : AbstractBase
|
||||
|
||||
double oldVal = source[i - period];
|
||||
if (!double.IsFinite(oldVal)) oldVal = 0;
|
||||
|
||||
|
||||
sum = sum - oldVal + val;
|
||||
sumSq = sumSq - (oldVal * oldVal) + (val * val);
|
||||
sumCu = sumCu - (oldVal * oldVal * oldVal) + (val * val * val);
|
||||
|
||||
|
||||
output[i] = CalculateSkewFromSums(sum, sumSq, sumCu, period, isPopulation);
|
||||
|
||||
tickCount++;
|
||||
@@ -293,7 +293,7 @@ public sealed class Skew : AbstractBase
|
||||
private static double CalculateSkewFromSums(double sum, double sumSq, double sumCu, double n, bool isPopulation)
|
||||
{
|
||||
double mean = sum / n;
|
||||
|
||||
|
||||
double m2Numerator = sumSq - (sum * sum) / n;
|
||||
if (m2Numerator < Epsilon) return 0;
|
||||
double m2 = m2Numerator / n;
|
||||
@@ -326,7 +326,7 @@ public sealed class Skew : AbstractBase
|
||||
sum += val;
|
||||
sumSq += val * val;
|
||||
sumCu += val * val * val;
|
||||
|
||||
|
||||
double n = i + 1;
|
||||
Unsafe.Add(ref outRef, i) = (n >= 3) ? CalculateSkewFromSums(sum, sumSq, sumCu, n, isPopulation) : 0;
|
||||
}
|
||||
@@ -383,34 +383,34 @@ public sealed class Skew : AbstractBase
|
||||
var vShift1 = Avx2.Permute4x64(vDelta.AsUInt64(), 0b_10_01_00_00).AsDouble(); // skipcq: CS-R1131
|
||||
vShift1 = Avx.Blend(vZero, vShift1, 0b_1110);
|
||||
var vP1 = Avx.Add(vDelta, vShift1);
|
||||
|
||||
|
||||
// Shift 2: [0, 0, d0, d0+d1]
|
||||
var vShift2 = Avx2.Permute4x64(vP1.AsUInt64(), 0b_01_00_00_00).AsDouble(); // skipcq: CS-R1131
|
||||
vShift2 = Avx.Blend(vZero, vShift2, 0b_1100);
|
||||
var vP2 = Avx.Add(vP1, vShift2);
|
||||
|
||||
|
||||
var vSums = Avx.Add(Vector256.Create(sum), vP2);
|
||||
|
||||
// Prefix sum for SumSq
|
||||
var vShiftSq1 = Avx2.Permute4x64(vDeltaSq.AsUInt64(), 0b_10_01_00_00).AsDouble(); // skipcq: CS-R1131
|
||||
vShiftSq1 = Avx.Blend(vZero, vShiftSq1, 0b_1110);
|
||||
var vP1Sq = Avx.Add(vDeltaSq, vShiftSq1);
|
||||
|
||||
|
||||
var vShiftSq2 = Avx2.Permute4x64(vP1Sq.AsUInt64(), 0b_01_00_00_00).AsDouble(); // skipcq: CS-R1131
|
||||
vShiftSq2 = Avx.Blend(vZero, vShiftSq2, 0b_1100);
|
||||
var vP2Sq = Avx.Add(vP1Sq, vShiftSq2);
|
||||
|
||||
|
||||
var vSumSqs = Avx.Add(Vector256.Create(sumSq), vP2Sq);
|
||||
|
||||
// Prefix sum for SumCu
|
||||
var vShiftCu1 = Avx2.Permute4x64(vDeltaCu.AsUInt64(), 0b_10_01_00_00).AsDouble(); // skipcq: CS-R1131
|
||||
vShiftCu1 = Avx.Blend(vZero, vShiftCu1, 0b_1110);
|
||||
var vP1Cu = Avx.Add(vDeltaCu, vShiftCu1);
|
||||
|
||||
|
||||
var vShiftCu2 = Avx2.Permute4x64(vP1Cu.AsUInt64(), 0b_01_00_00_00).AsDouble(); // skipcq: CS-R1131
|
||||
vShiftCu2 = Avx.Blend(vZero, vShiftCu2, 0b_1100);
|
||||
var vP2Cu = Avx.Add(vP1Cu, vShiftCu2);
|
||||
|
||||
|
||||
var vSumCus = Avx.Add(Vector256.Create(sumCu), vP2Cu);
|
||||
|
||||
// Calculate Skewness
|
||||
@@ -440,13 +440,13 @@ public sealed class Skew : AbstractBase
|
||||
// g1 = m3 / (m2 * sqrt(m2))
|
||||
var vM2Sqrt = Avx.Sqrt(vM2);
|
||||
var vDenom = Avx.Multiply(vM2, vM2Sqrt);
|
||||
|
||||
|
||||
// Check for small m2
|
||||
var vMask = Avx.Compare(vM2, vEpsilon, FloatComparisonMode.OrderedGreaterThanNonSignaling);
|
||||
|
||||
|
||||
var vG1 = Avx.Divide(vM3, vDenom);
|
||||
var vSkew = Avx.Multiply(vG1, vCorrection);
|
||||
|
||||
|
||||
// Apply mask
|
||||
vSkew = Avx.BlendVariable(vZero, vSkew, vMask);
|
||||
|
||||
@@ -481,11 +481,11 @@ public sealed class Skew : AbstractBase
|
||||
{
|
||||
double val = Unsafe.Add(ref srcRef, i);
|
||||
double oldVal = Unsafe.Add(ref srcRef, i - period);
|
||||
|
||||
|
||||
sum = sum - oldVal + val;
|
||||
sumSq = sumSq - (oldVal * oldVal) + (val * val);
|
||||
sumCu = sumCu - (oldVal * oldVal * oldVal) + (val * val * val);
|
||||
|
||||
|
||||
Unsafe.Add(ref outRef, i) = CalculateSkewFromSums(sum, sumSq, sumCu, n, isPopulation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public class StdDevTests
|
||||
// Sample StdDev: Sqrt(4.571428...) = 2.1380899...
|
||||
|
||||
var data = new double[] { 2, 4, 4, 4, 5, 5, 7, 9 };
|
||||
|
||||
|
||||
// Test Population StdDev
|
||||
var popStd = new StdDev(8, isPopulation: true);
|
||||
foreach (var val in data)
|
||||
@@ -48,7 +48,7 @@ public class StdDevTests
|
||||
{
|
||||
int period = 5;
|
||||
var stdDev = new StdDev(period);
|
||||
|
||||
|
||||
for (int i = 0; i < period; i++)
|
||||
{
|
||||
Assert.False(stdDev.IsHot);
|
||||
@@ -79,7 +79,7 @@ public class StdDevTests
|
||||
int count = 1000;
|
||||
var data = new double[count];
|
||||
var gbm = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 123);
|
||||
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
data[i] = gbm.Next().Close;
|
||||
@@ -104,7 +104,7 @@ public class StdDevTests
|
||||
Assert.Equal(iterativeResults[i], batchResults[i], precision: 6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Update_TSeries_Matches_Iterative()
|
||||
{
|
||||
@@ -112,7 +112,7 @@ public class StdDevTests
|
||||
int count = 1000;
|
||||
var data = new TSeries();
|
||||
var gbm = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 123);
|
||||
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var bar = gbm.Next();
|
||||
|
||||
@@ -27,7 +27,7 @@ public class StdDevValidationTests
|
||||
|
||||
var skenderList = skenderStdDev.ToList();
|
||||
var quotes = _data.SkenderQuotes.ToList();
|
||||
|
||||
|
||||
for (int i = 0; i < quotes.Count; i++)
|
||||
{
|
||||
var tValue = stdDev.Update(new TValue(quotes[i].Date, (double)quotes[i].Close));
|
||||
@@ -46,7 +46,7 @@ public class StdDevValidationTests
|
||||
// TA-Lib STDDEV uses Population Standard Deviation (N)
|
||||
int period = 20;
|
||||
var stdDev = new StdDev(period, isPopulation: true);
|
||||
|
||||
|
||||
var quotes = _data.SkenderQuotes.ToList();
|
||||
double[] input = quotes.Select(q => (double)q.Close).ToArray();
|
||||
double[] output = new double[input.Length];
|
||||
@@ -74,18 +74,18 @@ public class StdDevValidationTests
|
||||
// Tulip STDDEV uses Population Standard Deviation (N)
|
||||
int period = 20;
|
||||
var stdDev = new StdDev(period, isPopulation: true);
|
||||
|
||||
|
||||
var quotes = _data.SkenderQuotes.ToList();
|
||||
double[] input = quotes.Select(q => (double)q.Close).ToArray();
|
||||
|
||||
|
||||
// Tulip calculation
|
||||
var stdDevInd = Tulip.Indicators.stddev;
|
||||
double[][] inputs = { input };
|
||||
double[] options = { period };
|
||||
double[][] outputs = { new double[input.Length - stdDevInd.Start(options)] };
|
||||
|
||||
|
||||
stdDevInd.Run(inputs, options, outputs);
|
||||
|
||||
|
||||
double[] output = outputs[0];
|
||||
int lookback = stdDevInd.Start(options);
|
||||
|
||||
@@ -107,7 +107,7 @@ public class StdDevValidationTests
|
||||
int period = 20;
|
||||
var stdDev = new StdDev(period, isPopulation: false);
|
||||
var popStdDev = new StdDev(period, isPopulation: true);
|
||||
|
||||
|
||||
var quotes = _data.SkenderQuotes.ToList();
|
||||
double[] input = quotes.Select(q => (double)q.Close).ToArray();
|
||||
|
||||
@@ -121,7 +121,7 @@ public class StdDevValidationTests
|
||||
var window = input[(i - period + 1)..(i + 1)];
|
||||
double expected = Statistics.StandardDeviation(window);
|
||||
double expectedPop = Statistics.PopulationStandardDeviation(window);
|
||||
|
||||
|
||||
Assert.Equal(expected, val.Value, ValidationHelper.DefaultTolerance);
|
||||
Assert.Equal(expectedPop, popVal.Value, ValidationHelper.DefaultTolerance);
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ namespace QuanTAlib;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Standard Deviation is the square root of Variance.
|
||||
///
|
||||
///
|
||||
/// Formula:
|
||||
/// StdDev = Sqrt(Variance)
|
||||
///
|
||||
///
|
||||
/// This implementation wraps the optimized Variance indicator and applies a square root.
|
||||
/// </remarks>
|
||||
[SkipLocalsInit]
|
||||
@@ -47,7 +47,7 @@ public sealed class StdDev : AbstractBase
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
{
|
||||
TValue varResult = _variance.Update(input, isNew);
|
||||
|
||||
|
||||
// Sqrt(Variance)
|
||||
// Handle potential negative zero or extremely small negative noise from Variance
|
||||
double val = varResult.Value;
|
||||
@@ -73,12 +73,12 @@ public sealed class StdDev : AbstractBase
|
||||
|
||||
// 1. Calculate Variance
|
||||
Variance.Batch(source.Values, vSpan, _period, _isPopulation);
|
||||
|
||||
|
||||
// 2. Calculate Sqrt in-place
|
||||
SqrtSpan(vSpan);
|
||||
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
|
||||
// Prime the state
|
||||
// We need to feed the last 'period' values into the _variance instance
|
||||
// so that subsequent streaming updates work correctly.
|
||||
@@ -122,7 +122,7 @@ public sealed class StdDev : AbstractBase
|
||||
{
|
||||
// 1. Calculate Variance
|
||||
Variance.Batch(source, output, period, isPopulation);
|
||||
|
||||
|
||||
// 2. Sqrt
|
||||
SqrtSpan(output);
|
||||
}
|
||||
@@ -139,7 +139,7 @@ public sealed class StdDev : AbstractBase
|
||||
const int VectorWidth = 8;
|
||||
int simdEnd = len - (len % VectorWidth);
|
||||
ref double dataRef = ref MemoryMarshal.GetReference(data);
|
||||
|
||||
|
||||
for (; i < simdEnd; i += VectorWidth)
|
||||
{
|
||||
var v = Vector512.LoadUnsafe(ref Unsafe.Add(ref dataRef, i));
|
||||
@@ -153,7 +153,7 @@ public sealed class StdDev : AbstractBase
|
||||
const int VectorWidth = 4;
|
||||
int simdEnd = len - (len % VectorWidth);
|
||||
ref double dataRef = ref MemoryMarshal.GetReference(data);
|
||||
|
||||
|
||||
for (; i < simdEnd; i += VectorWidth)
|
||||
{
|
||||
var v = Vector256.LoadUnsafe(ref Unsafe.Add(ref dataRef, i));
|
||||
@@ -167,7 +167,7 @@ public sealed class StdDev : AbstractBase
|
||||
const int VectorWidth = 2;
|
||||
int simdEnd = len - (len % VectorWidth);
|
||||
ref double dataRef = ref MemoryMarshal.GetReference(data);
|
||||
|
||||
|
||||
for (; i < simdEnd; i += VectorWidth)
|
||||
{
|
||||
var v = Vector128.LoadUnsafe(ref Unsafe.Add(ref dataRef, i));
|
||||
|
||||
@@ -23,7 +23,7 @@ public class VarianceTests
|
||||
// Sample Variance (N-1=7): 32 / 7 = 4.571428...
|
||||
|
||||
var data = new double[] { 2, 4, 4, 4, 5, 5, 7, 9 };
|
||||
|
||||
|
||||
// Test Population Variance
|
||||
var popVar = new Variance(8, isPopulation: true);
|
||||
foreach (var val in data)
|
||||
@@ -46,7 +46,7 @@ public class VarianceTests
|
||||
{
|
||||
int period = 5;
|
||||
var variance = new Variance(period);
|
||||
|
||||
|
||||
for (int i = 0; i < period; i++)
|
||||
{
|
||||
Assert.False(variance.IsHot);
|
||||
@@ -75,18 +75,18 @@ public class VarianceTests
|
||||
{
|
||||
// Test differential update
|
||||
var variance = new Variance(3, isPopulation: true);
|
||||
|
||||
|
||||
// Add 1, 2, 3. Mean=2. Var = ((1-2)^2 + (2-2)^2 + (3-2)^2)/3 = (1+0+1)/3 = 2/3 = 0.666...
|
||||
variance.Update(new TValue(DateTime.UtcNow, 1));
|
||||
variance.Update(new TValue(DateTime.UtcNow, 2));
|
||||
variance.Update(new TValue(DateTime.UtcNow, 3));
|
||||
|
||||
|
||||
Assert.Equal(2.0/3.0, variance.Last.Value, precision: 6);
|
||||
|
||||
// Update last value from 3 to 6.
|
||||
// Data: 1, 2, 6. Mean=3. Var = ((1-3)^2 + (2-3)^2 + (6-3)^2)/3 = (4+1+9)/3 = 14/3 = 4.666...
|
||||
variance.Update(new TValue(DateTime.UtcNow, 6), isNew: false);
|
||||
|
||||
|
||||
Assert.Equal(14.0/3.0, variance.Last.Value, precision: 6);
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ public class VarianceTests
|
||||
int count = 1000;
|
||||
var data = new double[count];
|
||||
var gbm = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 123);
|
||||
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
data[i] = gbm.Next().Close;
|
||||
@@ -144,7 +144,7 @@ public class VarianceTests
|
||||
variance.Update(new TValue(DateTime.UtcNow, 1));
|
||||
variance.Update(new TValue(DateTime.UtcNow, 2));
|
||||
variance.Update(new TValue(DateTime.UtcNow, double.NaN));
|
||||
|
||||
|
||||
var result = variance.Last.Value;
|
||||
Assert.True(double.IsNaN(result));
|
||||
}
|
||||
@@ -155,12 +155,12 @@ public class VarianceTests
|
||||
// Run for > 1000 updates to trigger Resync
|
||||
var variance = new Variance(10);
|
||||
var gbm = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 123);
|
||||
|
||||
|
||||
for (int i = 0; i < 1100; i++)
|
||||
{
|
||||
variance.Update(new TValue(DateTime.UtcNow, gbm.Next().Close));
|
||||
}
|
||||
|
||||
|
||||
Assert.True(double.IsFinite(variance.Last.Value));
|
||||
Assert.True(variance.Last.Value >= 0);
|
||||
}
|
||||
@@ -174,10 +174,10 @@ public class VarianceTests
|
||||
for (int i = 0; i < count; i++) data[i] = (double)i;
|
||||
|
||||
var series = new TSeries(new System.Collections.Generic.List<long>(new long[count]), new System.Collections.Generic.List<double>(data));
|
||||
|
||||
|
||||
// Batch calculation
|
||||
var batchResult = Variance.Calculate(series, 10);
|
||||
|
||||
|
||||
// Verify last value against streaming
|
||||
var variance = new Variance(10);
|
||||
double lastStreaming = 0;
|
||||
|
||||
@@ -23,14 +23,14 @@ public class VarianceValidationTests
|
||||
// Skender StdDev uses Population Standard Deviation (N) for calculation,
|
||||
// despite documentation often implying Sample (N-1).
|
||||
// Variance(isPopulation: true) should match StdDev^2.
|
||||
|
||||
|
||||
int period = 20;
|
||||
var variance = new Variance(period, isPopulation: true);
|
||||
var skenderStdDev = _data.SkenderQuotes.GetStdDev(period);
|
||||
|
||||
var skenderList = skenderStdDev.ToList();
|
||||
var quotes = _data.SkenderQuotes.ToList();
|
||||
|
||||
|
||||
for (int i = 0; i < quotes.Count; i++)
|
||||
{
|
||||
var tValue = variance.Update(new TValue(quotes[i].Date, (double)quotes[i].Close));
|
||||
@@ -50,7 +50,7 @@ public class VarianceValidationTests
|
||||
// TA-Lib VAR uses Population Variance (N)
|
||||
int period = 20;
|
||||
var variance = new Variance(period, isPopulation: true);
|
||||
|
||||
|
||||
var quotes = _data.SkenderQuotes.ToList();
|
||||
double[] input = quotes.Select(q => (double)q.Close).ToArray();
|
||||
double[] output = new double[input.Length];
|
||||
@@ -78,18 +78,18 @@ public class VarianceValidationTests
|
||||
// Tulip VAR uses Population Variance (N)
|
||||
int period = 20;
|
||||
var variance = new Variance(period, isPopulation: true);
|
||||
|
||||
|
||||
var quotes = _data.SkenderQuotes.ToList();
|
||||
double[] input = quotes.Select(q => (double)q.Close).ToArray();
|
||||
|
||||
|
||||
// Tulip calculation
|
||||
var varInd = Tulip.Indicators.var;
|
||||
double[][] inputs = { input };
|
||||
double[] options = { period };
|
||||
double[][] outputs = { new double[input.Length - varInd.Start(options)] };
|
||||
|
||||
|
||||
varInd.Run(inputs, options, outputs);
|
||||
|
||||
|
||||
double[] output = outputs[0];
|
||||
int lookback = varInd.Start(options);
|
||||
|
||||
@@ -111,7 +111,7 @@ public class VarianceValidationTests
|
||||
int period = 20;
|
||||
var variance = new Variance(period, isPopulation: false);
|
||||
var popVariance = new Variance(period, isPopulation: true);
|
||||
|
||||
|
||||
var quotes = _data.SkenderQuotes.ToList();
|
||||
double[] input = quotes.Select(q => (double)q.Close).ToArray();
|
||||
|
||||
@@ -125,7 +125,7 @@ public class VarianceValidationTests
|
||||
var window = input[(i - period + 1)..(i + 1)];
|
||||
double expected = Statistics.Variance(window);
|
||||
double expectedPop = Statistics.PopulationVariance(window);
|
||||
|
||||
|
||||
Assert.Equal(expected, val.Value, ValidationHelper.DefaultTolerance);
|
||||
Assert.Equal(expectedPop, popVal.Value, ValidationHelper.DefaultTolerance);
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ namespace QuanTAlib;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Variance is calculated as the average of the squared differences from the Mean.
|
||||
///
|
||||
///
|
||||
/// Formula:
|
||||
/// Population Variance = Sum((x - Mean)^2) / N
|
||||
/// Sample Variance = Sum((x - Mean)^2) / (N - 1)
|
||||
///
|
||||
///
|
||||
/// This implementation uses the O(1) running sum of squares formula:
|
||||
/// Variance = (SumSq - (Sum * Sum) / N) / (N - 1) (for Sample)
|
||||
/// </remarks>
|
||||
@@ -76,7 +76,7 @@ public sealed class Variance : AbstractBase
|
||||
// Differential update
|
||||
double oldNewest = _buffer.Newest;
|
||||
_buffer.UpdateNewest(input.Value);
|
||||
|
||||
|
||||
// Reconstruct SumSq from previous state is safer/cleaner than differential on current
|
||||
// But we updated buffer already.
|
||||
// _sumSq currently includes oldNewest^2.
|
||||
@@ -93,12 +93,12 @@ public sealed class Variance : AbstractBase
|
||||
// Var = (SumSq - 2*Mean*(N*Mean) + N*Mean^2) / ...
|
||||
// Var = (SumSq - 2*N*Mean^2 + N*Mean^2) / ...
|
||||
// Var = (SumSq - N*Mean^2) / ...
|
||||
|
||||
|
||||
// Using Sum:
|
||||
// Var = (SumSq - (Sum*Sum)/N) / ...
|
||||
|
||||
|
||||
double numerator = _sumSq - (_buffer.Sum * _buffer.Sum) / n;
|
||||
|
||||
|
||||
// Handle floating point noise
|
||||
if (numerator < 0) numerator = 0;
|
||||
|
||||
@@ -221,14 +221,14 @@ public sealed class Variance : AbstractBase
|
||||
int len = source.Length;
|
||||
double sum = 0;
|
||||
double sumSq = 0;
|
||||
|
||||
|
||||
// We need a buffer to handle the sliding window removal
|
||||
// For scalar path, we can use a simple array or stackalloc
|
||||
const int StackAllocThreshold = 256;
|
||||
Span<double> buffer = period <= StackAllocThreshold
|
||||
? stackalloc double[period]
|
||||
: new double[period];
|
||||
|
||||
|
||||
int bufferIndex = 0;
|
||||
int i = 0;
|
||||
|
||||
@@ -265,11 +265,11 @@ public sealed class Variance : AbstractBase
|
||||
if (!double.IsFinite(val)) val = 0; // Fallback
|
||||
|
||||
double oldVal = buffer[bufferIndex];
|
||||
|
||||
|
||||
sum = sum - oldVal + val;
|
||||
sumSq = Math.FusedMultiplyAdd(-oldVal, oldVal, sumSq);
|
||||
sumSq = Math.FusedMultiplyAdd(val, val, sumSq);
|
||||
|
||||
|
||||
buffer[bufferIndex] = val;
|
||||
bufferIndex++;
|
||||
if (bufferIndex >= period) bufferIndex = 0;
|
||||
@@ -300,7 +300,7 @@ public sealed class Variance : AbstractBase
|
||||
double val = Unsafe.Add(ref srcRef, i);
|
||||
sum += val;
|
||||
sumSq = Math.FusedMultiplyAdd(val, val, sumSq);
|
||||
|
||||
|
||||
double n = i + 1;
|
||||
if (n > 1)
|
||||
{
|
||||
@@ -382,9 +382,9 @@ public sealed class Variance : AbstractBase
|
||||
var vSumSquared = Avx512F.Multiply(vSums, vSums);
|
||||
var vMeanTerm = Avx512F.Multiply(vSumSquared, vInvN);
|
||||
var vNumerator = Avx512F.Subtract(vSumSqs, vMeanTerm);
|
||||
|
||||
|
||||
vNumerator = Avx512F.Max(vZero, vNumerator);
|
||||
|
||||
|
||||
var vResult = Avx512F.Multiply(vNumerator, vInvDenom);
|
||||
Vector512.StoreUnsafe(vResult, ref Unsafe.Add(ref outRef, i));
|
||||
|
||||
@@ -414,11 +414,11 @@ public sealed class Variance : AbstractBase
|
||||
{
|
||||
double val = Unsafe.Add(ref srcRef, i);
|
||||
double oldVal = Unsafe.Add(ref srcRef, i - period);
|
||||
|
||||
|
||||
sum = sum - oldVal + val;
|
||||
sumSq = Math.FusedMultiplyAdd(-oldVal, oldVal, sumSq);
|
||||
sumSq = Math.FusedMultiplyAdd(val, val, sumSq);
|
||||
|
||||
|
||||
double numerator = sumSq - (sum * sum) * invN;
|
||||
if (numerator < 0) numerator = 0;
|
||||
Unsafe.Add(ref outRef, i) = numerator * invDenom;
|
||||
@@ -479,9 +479,9 @@ public sealed class Variance : AbstractBase
|
||||
var vSumSquared = AdvSimd.Arm64.Multiply(vSums, vSums);
|
||||
var vMeanTerm = AdvSimd.Arm64.Multiply(vSumSquared, vInvN);
|
||||
var vNumerator = AdvSimd.Arm64.Subtract(vSumSqs, vMeanTerm);
|
||||
|
||||
|
||||
vNumerator = AdvSimd.Arm64.Max(vZero, vNumerator);
|
||||
|
||||
|
||||
var vResult = AdvSimd.Arm64.Multiply(vNumerator, vInvDenom);
|
||||
Vector128.StoreUnsafe(vResult, ref Unsafe.Add(ref outRef, i));
|
||||
|
||||
@@ -511,11 +511,11 @@ public sealed class Variance : AbstractBase
|
||||
{
|
||||
double val = Unsafe.Add(ref srcRef, i);
|
||||
double oldVal = Unsafe.Add(ref srcRef, i - period);
|
||||
|
||||
|
||||
sum = sum - oldVal + val;
|
||||
sumSq = Math.FusedMultiplyAdd(-oldVal, oldVal, sumSq);
|
||||
sumSq = Math.FusedMultiplyAdd(val, val, sumSq);
|
||||
|
||||
|
||||
double numerator = sumSq - (sum * sum) * invN;
|
||||
if (numerator < 0) numerator = 0;
|
||||
Unsafe.Add(ref outRef, i) = numerator * invDenom;
|
||||
@@ -593,10 +593,10 @@ public sealed class Variance : AbstractBase
|
||||
var vSumSquared = Avx.Multiply(vSums, vSums);
|
||||
var vMeanTerm = Avx.Multiply(vSumSquared, vInvN);
|
||||
var vNumerator = Avx.Subtract(vSumSqs, vMeanTerm);
|
||||
|
||||
|
||||
// Max(0, numerator) to handle floating point noise
|
||||
vNumerator = Avx.Max(vZero, vNumerator);
|
||||
|
||||
|
||||
var vResult = Avx.Multiply(vNumerator, vInvDenom);
|
||||
Vector256.StoreUnsafe(vResult, ref Unsafe.Add(ref outRef, i));
|
||||
|
||||
@@ -628,11 +628,11 @@ public sealed class Variance : AbstractBase
|
||||
{
|
||||
double val = Unsafe.Add(ref srcRef, i);
|
||||
double oldVal = Unsafe.Add(ref srcRef, i - period);
|
||||
|
||||
|
||||
sum = sum - oldVal + val;
|
||||
sumSq = Math.FusedMultiplyAdd(-oldVal, oldVal, sumSq);
|
||||
sumSq = Math.FusedMultiplyAdd(val, val, sumSq);
|
||||
|
||||
|
||||
double numerator = sumSq - (sum * sum) * invN;
|
||||
if (numerator < 0) numerator = 0;
|
||||
Unsafe.Add(ref outRef, i) = numerator * invDenom;
|
||||
|
||||
Reference in New Issue
Block a user