mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-25 13:58:04 +00:00
Add TRAMA implementation and comprehensive tests
- Implemented the TRAMA (Trend Regularity Adaptive Moving Average) class with adaptive EMA logic. - Added unit tests for TRAMA functionality, including constructor validation, basic calculations, state management, and robustness checks. - Created validation tests to ensure consistency across different modes of operation (streaming, batch, and static calculations). - Enhanced documentation for TRAMA, including performance profiles and quality metrics. - Updated workspace configuration by removing unnecessary folder references.
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class Sp15ValidationTests
|
||||
{
|
||||
private static TSeries MakeSeries(int count = 500)
|
||||
{
|
||||
var source = new TSeries();
|
||||
var gbm = new GBM(startPrice: 100, seed: 42);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var bar = gbm.Next();
|
||||
source.Add(bar.C);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchVsStreaming_Match()
|
||||
{
|
||||
var source = MakeSeries(100);
|
||||
|
||||
// Streaming
|
||||
var sp15 = new Sp15();
|
||||
var streaming = new double[100];
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
streaming[i] = sp15.Update(source[i]).Value;
|
||||
}
|
||||
|
||||
// Batch
|
||||
var batchResult = Sp15.Batch(source);
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
Assert.Equal(streaming[i], batchResult[i].Value, 1e-10);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpanVsStreaming_Match()
|
||||
{
|
||||
var source = MakeSeries(100);
|
||||
|
||||
// Streaming
|
||||
var sp15 = new Sp15();
|
||||
var streaming = new double[100];
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
streaming[i] = sp15.Update(source[i]).Value;
|
||||
}
|
||||
|
||||
// Span
|
||||
double[] spanOutput = new double[100];
|
||||
Sp15.Batch(source.Values, spanOutput);
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
Assert.Equal(streaming[i], spanOutput[i], 1e-10);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LinearPolynomial_ExactFit()
|
||||
{
|
||||
var sp15 = new Sp15();
|
||||
const int total = 50;
|
||||
const double a = 5.0, b = 3.0;
|
||||
|
||||
for (int i = 0; i < total; i++)
|
||||
{
|
||||
double val = a + b * i;
|
||||
sp15.Update(new TValue(DateTime.UtcNow.AddSeconds(i), val));
|
||||
}
|
||||
|
||||
int centerIdx = total - 1 - 7;
|
||||
double expected = a + b * centerIdx;
|
||||
Assert.Equal(expected, sp15.Last.Value, 1e-6);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QuadraticPolynomial_ExactFit()
|
||||
{
|
||||
var sp15 = new Sp15();
|
||||
const int total = 50;
|
||||
const double a = 2.0, b = 1.5, c = 0.3;
|
||||
|
||||
for (int i = 0; i < total; i++)
|
||||
{
|
||||
double val = a + b * i + c * i * i;
|
||||
sp15.Update(new TValue(DateTime.UtcNow.AddSeconds(i), val));
|
||||
}
|
||||
|
||||
int centerIdx = total - 1 - 7;
|
||||
double expected = a + b * centerIdx + c * centerIdx * centerIdx;
|
||||
Assert.Equal(expected, sp15.Last.Value, 1e-4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CubicPolynomial_ExactFit()
|
||||
{
|
||||
var sp15 = new Sp15();
|
||||
const int total = 50;
|
||||
const double a = 1.0, b = 0.5, c = 0.1, d = 0.005;
|
||||
|
||||
for (int i = 0; i < total; i++)
|
||||
{
|
||||
double val = a + b * i + c * i * i + d * i * i * i;
|
||||
sp15.Update(new TValue(DateTime.UtcNow.AddSeconds(i), val));
|
||||
}
|
||||
|
||||
int centerIdx = total - 1 - 7;
|
||||
double expected = a + b * centerIdx + c * centerIdx * centerIdx + d * centerIdx * centerIdx * centerIdx;
|
||||
Assert.Equal(expected, sp15.Last.Value, 1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Calculate_ReturnsHotIndicator()
|
||||
{
|
||||
var source = MakeSeries(50);
|
||||
|
||||
var (results, indicator) = Sp15.Calculate(source);
|
||||
|
||||
Assert.True(indicator.IsHot);
|
||||
Assert.Equal(50, results.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConstantPropagation_AllModes()
|
||||
{
|
||||
const double c = 77.0;
|
||||
const int len = 30;
|
||||
|
||||
// Build constant series
|
||||
var source = new TSeries();
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
source.Add(new TValue(DateTime.UtcNow.AddSeconds(i), c));
|
||||
}
|
||||
|
||||
// Streaming
|
||||
var sp15 = new Sp15();
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
sp15.Update(source[i]);
|
||||
}
|
||||
Assert.Equal(c, sp15.Last.Value, 1e-10);
|
||||
|
||||
// Batch
|
||||
var batch = Sp15.Batch(source);
|
||||
for (int i = 15; i < len; i++)
|
||||
{
|
||||
Assert.Equal(c, batch[i].Value, 1e-10);
|
||||
}
|
||||
|
||||
// Span
|
||||
double[] spanOut = new double[len];
|
||||
Sp15.Batch(source.Values, spanOut);
|
||||
for (int i = 15; i < len; i++)
|
||||
{
|
||||
Assert.Equal(c, spanOut[i], 1e-10);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WeightSymmetry_ForwardReverse()
|
||||
{
|
||||
// Symmetric weights: reversing input gives same center value for linear input
|
||||
var sp15Fwd = new Sp15();
|
||||
var sp15Rev = new Sp15();
|
||||
|
||||
double[] forward = new double[15];
|
||||
double[] reverse = new double[15];
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
forward[i] = 10.0 + 2.0 * i;
|
||||
reverse[i] = 10.0 + 2.0 * (14 - i);
|
||||
}
|
||||
|
||||
TValue fwdResult = default;
|
||||
TValue revResult = default;
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
fwdResult = sp15Fwd.Update(new TValue(DateTime.UtcNow.AddSeconds(i), forward[i]));
|
||||
revResult = sp15Rev.Update(new TValue(DateTime.UtcNow.AddSeconds(i), reverse[i]));
|
||||
}
|
||||
|
||||
// For linear input centered at i=7: forward center = 10+14=24, reverse center = 10+14=24
|
||||
// Both should give the same result for symmetric weights applied to symmetric-about-center linear data
|
||||
double expected = 2.0 * (10.0 + 2.0 * 7.0);
|
||||
Assert.Equal(expected, fwdResult.Value + revResult.Value, 1e-6);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Period4_Sinusoid_Suppressed()
|
||||
{
|
||||
// Spencer filter zeros out period-4 signals
|
||||
var sp15 = new Sp15();
|
||||
const int n = 60;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
// Pure period-4 sinusoid centered at 100
|
||||
double val = 100.0 + 10.0 * Math.Sin(2.0 * Math.PI * i / 4.0);
|
||||
sp15.Update(new TValue(DateTime.UtcNow.AddSeconds(i), val));
|
||||
}
|
||||
// After warmup, the output should be ~100 (sinusoid suppressed)
|
||||
Assert.Equal(100.0, sp15.Last.Value, 0.5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Period5_Sinusoid_Suppressed()
|
||||
{
|
||||
// Spencer filter zeros out period-5 signals
|
||||
var sp15 = new Sp15();
|
||||
const int n = 60;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
double val = 100.0 + 10.0 * Math.Sin(2.0 * Math.PI * i / 5.0);
|
||||
sp15.Update(new TValue(DateTime.UtcNow.AddSeconds(i), val));
|
||||
}
|
||||
Assert.Equal(100.0, sp15.Last.Value, 0.5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DifferentSeeds_ProduceDifferentResults()
|
||||
{
|
||||
var source1 = new TSeries();
|
||||
var gbm1 = new GBM(startPrice: 100, seed: 42);
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
source1.Add(gbm1.Next().C);
|
||||
}
|
||||
|
||||
var source2 = new TSeries();
|
||||
var gbm2 = new GBM(startPrice: 100, seed: 99);
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
source2.Add(gbm2.Next().C);
|
||||
}
|
||||
|
||||
var batch1 = Sp15.Batch(source1);
|
||||
var batch2 = Sp15.Batch(source2);
|
||||
|
||||
// At least one value should differ
|
||||
bool anyDifferent = false;
|
||||
for (int i = 15; i < 30; i++)
|
||||
{
|
||||
if (Math.Abs(batch1[i].Value - batch2[i].Value) > 1e-6)
|
||||
{
|
||||
anyDifferent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.True(anyDifferent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LargeDataset_Consistency()
|
||||
{
|
||||
var source = MakeSeries(1000);
|
||||
var sp15 = new Sp15();
|
||||
var streaming = new double[1000];
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
streaming[i] = sp15.Update(source[i]).Value;
|
||||
}
|
||||
|
||||
double[] spanOut = new double[1000];
|
||||
Sp15.Batch(source.Values, spanOut);
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
Assert.Equal(streaming[i], spanOut[i], 1e-10);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user