mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-26 06:18:05 +00:00
test: setup common stability and robustness properties tracking
This commit is contained in:
@@ -294,4 +294,4 @@ public class AcfIndicatorTests
|
||||
Assert.True(value >= -1 && value <= 1, $"ACF value at index {i} should be bounded [-1, 1], got {value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,4 +539,4 @@ public class AcfTests
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,11 +114,13 @@ public class AcfValidationTests
|
||||
// For white noise, ACF at any lag > 0 should be close to zero
|
||||
var acf = new Acf(100, 5);
|
||||
|
||||
// Generate pseudo-random values with zero mean via GBM log-returns
|
||||
var random = new GBM(startPrice: 100.0, sigma: 1.0, seed: 42);
|
||||
for (int i = 0; i < 500; i++)
|
||||
// Generate incremental bar-to-bar log-returns: log(close_i / close_{i-1})
|
||||
// These are approximately i.i.d. N(0, σ²·dt) — genuine white noise
|
||||
var gbm = new GBM(startPrice: 100.0, sigma: 0.2, seed: 42);
|
||||
var bars = gbm.Fetch(501, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
for (int i = 1; i < 501; i++)
|
||||
{
|
||||
double val = Math.Log(random.Next().Close / 100.0); // ~N(0, vol²*dt) centered near 0
|
||||
double val = Math.Log(bars[i].Close / bars[i - 1].Close); // incremental log-return
|
||||
acf.Update(new TValue(DateTime.UtcNow.AddSeconds(i), val));
|
||||
}
|
||||
|
||||
@@ -215,12 +217,15 @@ public class AcfValidationTests
|
||||
|
||||
double[] ar1Data = new double[n];
|
||||
ar1Data[0] = 0;
|
||||
var random = new GBM(startPrice: 100.0, sigma: 1.0, seed: 42);
|
||||
|
||||
// Use incremental bar-to-bar log-returns as i.i.d. noise: log(close_i / close_{i-1})
|
||||
var gbm = new GBM(startPrice: 100.0, sigma: 0.2, seed: 42);
|
||||
var bars = gbm.Fetch(n, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
// Generate AR(1) process
|
||||
for (int i = 1; i < n; i++)
|
||||
{
|
||||
double epsilon = Math.Log(random.Next().Close / 100.0) * 0.1; // Small noise
|
||||
double epsilon = Math.Log(bars[i].Close / bars[i - 1].Close) * 0.5; // incremental log-return scaled as noise
|
||||
ar1Data[i] = phi * ar1Data[i - 1] + epsilon;
|
||||
}
|
||||
|
||||
@@ -280,4 +285,4 @@ public class AcfValidationTests
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,12 +42,10 @@ public sealed class Acf : AbstractBase
|
||||
// Running sums for O(1) updates
|
||||
private double _sum;
|
||||
private double _sumSq;
|
||||
private double _sumLagged;
|
||||
|
||||
// Snapshot state for bar correction
|
||||
private double _p_sum;
|
||||
private double _p_sumSq;
|
||||
private double _p_sumLagged;
|
||||
|
||||
private int _updateCount;
|
||||
private const int ResyncInterval = 1000;
|
||||
@@ -110,7 +108,6 @@ public sealed class Acf : AbstractBase
|
||||
// Snapshot state for rollback
|
||||
_p_sum = _sum;
|
||||
_p_sumSq = _sumSq;
|
||||
_p_sumLagged = _sumLagged;
|
||||
_buffer.Snapshot();
|
||||
}
|
||||
else
|
||||
@@ -118,7 +115,6 @@ public sealed class Acf : AbstractBase
|
||||
// Restore state from snapshot
|
||||
_sum = _p_sum;
|
||||
_sumSq = _p_sumSq;
|
||||
_sumLagged = _p_sumLagged;
|
||||
_buffer.Restore();
|
||||
}
|
||||
|
||||
@@ -129,12 +125,6 @@ public sealed class Acf : AbstractBase
|
||||
_sum -= oldVal;
|
||||
_sumSq = Math.FusedMultiplyAdd(-oldVal, oldVal, _sumSq);
|
||||
|
||||
// Remove contribution to lagged sum
|
||||
if (_buffer.Count > _lag)
|
||||
{
|
||||
double oldLaggedVal = _buffer[_lag]; // value that was _lag positions from oldest
|
||||
_sumLagged -= oldVal * oldLaggedVal;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new value
|
||||
@@ -142,14 +132,6 @@ public sealed class Acf : AbstractBase
|
||||
_sum += value;
|
||||
_sumSq = Math.FusedMultiplyAdd(value, value, _sumSq);
|
||||
|
||||
// Update lagged sum: add product of new value and value at lag positions before
|
||||
if (_buffer.Count > _lag)
|
||||
{
|
||||
int lagIndex = _buffer.Count - 1 - _lag;
|
||||
double laggedVal = _buffer[lagIndex];
|
||||
_sumLagged += value * laggedVal;
|
||||
}
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
_updateCount++;
|
||||
@@ -257,18 +239,11 @@ public sealed class Acf : AbstractBase
|
||||
int n = _buffer.Count;
|
||||
_sum = 0;
|
||||
_sumSq = 0;
|
||||
_sumLagged = 0;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
double val = _buffer[i];
|
||||
_sum += val;
|
||||
_sumSq += val * val;
|
||||
|
||||
if (i >= _lag)
|
||||
{
|
||||
_sumLagged += val * _buffer[i - _lag];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,19 +252,18 @@ public sealed class Acf : AbstractBase
|
||||
_buffer.Clear();
|
||||
_sum = 0;
|
||||
_sumSq = 0;
|
||||
_sumLagged = 0;
|
||||
_p_sum = 0;
|
||||
_p_sumSq = 0;
|
||||
_p_sumLagged = 0;
|
||||
_updateCount = 0;
|
||||
Last = default;
|
||||
}
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
Reset();
|
||||
foreach (double value in source)
|
||||
{
|
||||
Update(new TValue(DateTime.UtcNow, value));
|
||||
Update(new TValue(DateTime.MinValue, value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
- Parameterized by `period`, `lag` (default 1).
|
||||
- Output range: Varies (see docs).
|
||||
- Requires `period` bars of warmup before first valid output (IsHot = true).
|
||||
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
|
||||
- Validated against mathematical properties and theoretical AR-process expectations.
|
||||
|
||||
> "The past doesn't predict the future, but it whispers patterns to those who listen."
|
||||
|
||||
|
||||
Reference in New Issue
Block a user