Files
QuanTAlib/lib/cycles/stc/Stc.Validation.Tests.cs
T
2026-01-25 16:01:45 -08:00

78 lines
2.5 KiB
C#

using System;
using System.Linq;
using Skender.Stock.Indicators;
using Xunit;
using Xunit.Abstractions;
namespace QuanTAlib.Tests;
public sealed class StcValidationTests : IDisposable
{
private readonly ValidationTestData _testData;
private readonly ITestOutputHelper _output;
public StcValidationTests(ITestOutputHelper output)
{
_output = output;
_testData = new ValidationTestData();
}
public void Dispose()
{
_testData.Dispose();
}
[Fact]
public void Validate_Skender_Stc_Deviation()
{
// Skender's STC implementation uses a "Single Smoothed" approach (Stoch of MACD).
// QuanTAlib implements the standard "Double Smoothed" approach (Stoch of Stoch of MACD),
// as originally defined by Schaff.
//
// Example mismatch at index 333:
// QuanTAlib (Double Smoothed) = 50.0
// Skender (Single Smoothed) = 97.05
//
// This test documents this known deviation rather than failing on it.
const int cycle = 10;
int fast = 23;
int slow = 50;
var sResult = _testData.SkenderQuotes.GetStc(cycle, fast, slow).ToList();
var qStc = new Stc(kPeriod: cycle, dPeriod: 3, fastLength: fast, slowLength: slow, smoothing: StcSmoothing.Ema);
var qResult = qStc.Update(_testData.Data);
// Skender recommends S+C+250 warmup. 50+10+250 = 310.
int skip = 310;
double sumSq = 0;
int count = 0;
for (int i = skip; i < qResult.Count; i++)
{
double sVal = sResult[i].Stc ?? double.NaN;
double qVal = qResult[i].Value;
if (!double.IsNaN(sVal) && !double.IsNaN(qVal))
{
sumSq += (sVal - qVal) * (sVal - qVal);
count++;
}
}
double rmse = Math.Sqrt(sumSq / count);
_output.WriteLine($"Known Methodology Deviation - RMSE: {rmse:F4}");
// Assert that we are essentially different (RMSE > 5.0 implies significant deviation)
// If they accidentally matched (e.g. if we broke our logic to match Skender), this should fail.
Assert.True(rmse > 5.0, "QuanTAlib STC matches Skender STC, which suggests regression to Single Smoothed logic.");
// Assert values are valid
for (int i = skip; i < qResult.Count; i++)
{
Assert.True(double.IsFinite(qResult[i].Value));
Assert.InRange(qResult[i].Value, 0, 100);
}
}
}