mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-26 06:18:05 +00:00
feat: add FSI (Ehlers Fourier Series Indicator, TASC June 2019)
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class FsiValidationTests
|
||||
{
|
||||
private static readonly Random _rng = new(42);
|
||||
|
||||
private static TSeries MakeSeries(int count = 500)
|
||||
{
|
||||
var series = new TSeries();
|
||||
double price = 100.0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
price += (_rng.NextDouble() - 0.5) * 2.0;
|
||||
series.Add(new TValue(DateTime.UtcNow.AddMinutes(i), price));
|
||||
}
|
||||
return series;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchStreaming_Match()
|
||||
{
|
||||
var series = MakeSeries(300);
|
||||
int period = 20;
|
||||
double bw = 0.1;
|
||||
|
||||
// Streaming
|
||||
var streaming = new Fsi(period, bw);
|
||||
var streamResults = new double[series.Count];
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
streamResults[i] = streaming.Update(series[i]).Value;
|
||||
}
|
||||
|
||||
// Batch
|
||||
var batchResult = Fsi.Batch(series, period, bw);
|
||||
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
Assert.Equal(streamResults[i], batchResult[i].Value, 10);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpanStreaming_Match()
|
||||
{
|
||||
var series = MakeSeries(300);
|
||||
int period = 20;
|
||||
double bw = 0.1;
|
||||
|
||||
// Streaming
|
||||
var streaming = new Fsi(period, bw);
|
||||
var streamResults = new double[series.Count];
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
streamResults[i] = streaming.Update(series[i]).Value;
|
||||
}
|
||||
|
||||
// Span batch
|
||||
var spanResults = new double[series.Count];
|
||||
Fsi.Batch(series.Values, spanResults, period, bw);
|
||||
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
Assert.Equal(streamResults[i], spanResults[i], 10);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DifferentPeriods_ProduceDifferentOutputs()
|
||||
{
|
||||
var series = MakeSeries(300);
|
||||
|
||||
var result1 = new double[series.Count];
|
||||
var result2 = new double[series.Count];
|
||||
Fsi.Batch(series.Values, result1, 20, 0.1);
|
||||
Fsi.Batch(series.Values, result2, 40, 0.1);
|
||||
|
||||
bool allEqual = true;
|
||||
for (int i = 50; i < series.Count; i++)
|
||||
{
|
||||
if (Math.Abs(result1[i] - result2[i]) > 1e-12)
|
||||
{
|
||||
allEqual = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.False(allEqual, "Different periods should produce different outputs");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConstantInput_ProducesZero()
|
||||
{
|
||||
int count = 200;
|
||||
var src = new double[count];
|
||||
var dst = new double[count];
|
||||
Array.Fill(src, 100.0);
|
||||
|
||||
Fsi.Batch(src, dst, 20, 0.1);
|
||||
|
||||
// After warmup, constant input → all-zero bandpass → output = 0
|
||||
for (int i = 20; i < count; i++)
|
||||
{
|
||||
Assert.Equal(0.0, dst[i], 10);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Calculate_ReturnsHotIndicator()
|
||||
{
|
||||
var series = MakeSeries(200);
|
||||
var (results, indicator) = Fsi.Calculate(series, 20, 0.1);
|
||||
Assert.Equal(series.Count, results.Count);
|
||||
Assert.True(indicator.IsHot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BarCorrection_Consistency()
|
||||
{
|
||||
var series = MakeSeries(100);
|
||||
var fsi = new Fsi(20, 0.1);
|
||||
|
||||
foreach (var bar in series)
|
||||
{
|
||||
fsi.Update(bar);
|
||||
}
|
||||
|
||||
// New bar
|
||||
double v1 = fsi.Update(new TValue(DateTime.UtcNow, 105.0), isNew: true).Value;
|
||||
|
||||
// Corrections
|
||||
_ = fsi.Update(new TValue(DateTime.UtcNow, 108.0), isNew: false);
|
||||
_ = fsi.Update(new TValue(DateTime.UtcNow, 112.0), isNew: false);
|
||||
double v4 = fsi.Update(new TValue(DateTime.UtcNow, 105.0), isNew: false).Value;
|
||||
|
||||
Assert.Equal(v1, v4, 10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubsetStability()
|
||||
{
|
||||
// Running on a longer series should not change earlier values
|
||||
var series = MakeSeries(300);
|
||||
int period = 20;
|
||||
double bw = 0.1;
|
||||
|
||||
var result200 = new double[200];
|
||||
Fsi.Batch(series.Values[..200], result200, period, bw);
|
||||
|
||||
var result300 = new double[300];
|
||||
Fsi.Batch(series.Values, result300, period, bw);
|
||||
|
||||
// First 200 bars of both runs must match exactly
|
||||
for (int i = 0; i < 200; i++)
|
||||
{
|
||||
Assert.Equal(result200[i], result300[i], 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user