Files
Miha Kralj 060649192f docs: remove C# Implementation Considerations sections, clean up temp scripts, reorganize test files
- Remove 'C# Implementation Considerations' sections from 34 indicator .md files
- Delete 29 temp PowerShell scripts (_fix_mojibake.ps1, _hex_scan.ps1, etc.)
- Move test files into tests/ subdirectories for consistent project structure
- Add trader-focused bullet points to indicator documentation
2026-03-12 12:34:16 -07:00

200 lines
6.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Xunit.Abstractions;
namespace QuanTAlib.Tests;
public sealed class NyqmaValidationTests : IDisposable
{
private const int DefaultPeriod = 10;
private const int DefaultNyquistPeriod = 4;
private const double ValidationTolerance = 1e-9;
private readonly ValidationTestData _testData;
private readonly ITestOutputHelper _output;
public NyqmaValidationTests(ITestOutputHelper output)
{
_output = output;
_testData = new ValidationTestData();
}
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{
_testData.Dispose();
}
}
// NYQMA has no direct external library equivalent, so we validate
// by verifying NYQMA = (1+α)·WMA(src,N1) α·WMA(WMA(src,N1),N2) (component consistency)
[Fact]
public void Validate_NyqmaEqualsComponentFormula_Batch()
{
int period = DefaultPeriod;
int nyquistPeriod = DefaultNyquistPeriod;
double alpha = (double)nyquistPeriod / (period - nyquistPeriod);
var source = _testData.Data;
// Compute WMA1 and WMA2(WMA1) separately
var wma1Result = Wma.Batch(source, period);
var wma2Result = Wma.Batch(wma1Result, nyquistPeriod);
// Compute NYQMA
var nyqmaResult = Nyqma.Batch(source, period, nyquistPeriod);
// NYQMA = (1+α)·WMA1 α·WMA2
int count = nyqmaResult.Count;
int warmup = period + nyquistPeriod - 1;
for (int i = warmup; i < count; i++)
{
double expected = Math.FusedMultiplyAdd(1.0 + alpha, wma1Result[i].Value, -alpha * wma2Result[i].Value);
Assert.Equal(expected, nyqmaResult[i].Value, ValidationTolerance);
}
_output.WriteLine($"NYQMA({period},{nyquistPeriod}) component consistency: PASS ({count - warmup} bars validated at {ValidationTolerance})");
}
[Fact]
public void Validate_NyqmaEqualsComponentFormula_Streaming()
{
int period = DefaultPeriod;
int nyquistPeriod = DefaultNyquistPeriod;
double alpha = (double)nyquistPeriod / (period - nyquistPeriod);
var source = _testData.Data;
var wma1 = new Wma(period);
var wma2 = new Wma(nyquistPeriod);
var nyqma = new Nyqma(period, nyquistPeriod);
int warmup = period + nyquistPeriod - 1;
int validated = 0;
for (int i = 0; i < source.Count; i++)
{
var w1Val = wma1.Update(source[i]);
var w2Val = wma2.Update(w1Val);
var nyqmaVal = nyqma.Update(source[i]);
if (i >= warmup)
{
double expected = Math.FusedMultiplyAdd(1.0 + alpha, w1Val.Value, -alpha * w2Val.Value);
Assert.Equal(expected, nyqmaVal.Value, ValidationTolerance);
validated++;
}
}
_output.WriteLine($"NYQMA({period},{nyquistPeriod}) streaming component consistency: PASS ({validated} bars validated)");
}
[Fact]
public void Validate_NyqmaEqualsComponentFormula_Span()
{
int period = DefaultPeriod;
int nyquistPeriod = DefaultNyquistPeriod;
double alpha = (double)nyquistPeriod / (period - nyquistPeriod);
var rawData = _testData.RawData;
var wma1Output = new double[rawData.Length];
var wma2Output = new double[rawData.Length];
var nyqmaOutput = new double[rawData.Length];
Wma.Batch(rawData.Span, wma1Output.AsSpan(), period);
Wma.Batch(wma1Output.AsSpan(), wma2Output.AsSpan(), nyquistPeriod);
Nyqma.Batch(rawData.Span, nyqmaOutput.AsSpan(), period, nyquistPeriod);
int warmup = period + nyquistPeriod - 1;
int validated = 0;
for (int i = warmup; i < rawData.Length; i++)
{
double expected = Math.FusedMultiplyAdd(1.0 + alpha, wma1Output[i], -alpha * wma2Output[i]);
Assert.Equal(expected, nyqmaOutput[i], ValidationTolerance);
validated++;
}
_output.WriteLine($"NYQMA({period},{nyquistPeriod}) span component consistency: PASS ({validated} bars validated)");
}
[Fact]
public void Validate_BatchAndStreamingMatch()
{
int period = DefaultPeriod;
int nyquistPeriod = DefaultNyquistPeriod;
var source = _testData.Data;
// Batch
var batchResult = Nyqma.Batch(source, period, nyquistPeriod);
// Streaming
var streaming = new Nyqma(period, nyquistPeriod);
for (int i = 0; i < source.Count; i++)
{
streaming.Update(source[i]);
}
Assert.Equal(batchResult.Last.Value, streaming.Last.Value, 1e-6);
_output.WriteLine($"NYQMA({period},{nyquistPeriod}) batch/streaming match: PASS");
}
[Fact]
public void Validate_BatchAndSpanMatch()
{
int period = DefaultPeriod;
int nyquistPeriod = DefaultNyquistPeriod;
var source = _testData.Data;
var rawData = _testData.RawData;
var batchResult = Nyqma.Batch(source, period, nyquistPeriod);
var spanOutput = new double[rawData.Length];
Nyqma.Batch(rawData.Span, spanOutput.AsSpan(), period, nyquistPeriod);
Assert.Equal(batchResult.Last.Value, spanOutput[^1], ValidationTolerance);
_output.WriteLine($"NYQMA({period},{nyquistPeriod}) batch/span match: PASS");
}
[Theory]
[InlineData(5, 2)]
[InlineData(10, 3)]
[InlineData(21, 8)]
[InlineData(89, 21)]
public void Validate_DifferentPeriods_ConstantInputConverges(int period, int nyquistPeriod)
{
double constant = 55.0;
var nyqma = new Nyqma(period, nyquistPeriod);
double last = 0;
for (int i = 0; i < period * 3; i++)
{
last = nyqma.Update(new TValue(DateTime.UtcNow, constant)).Value;
}
Assert.Equal(constant, last, 1e-6);
_output.WriteLine($"NYQMA({period},{nyquistPeriod}) constant convergence: PASS (converged to {last:F9})");
}
[Fact]
public void Validate_NyquistClamping_AlphaBoundsCorrect()
{
// When nyquistPeriod is at maximum (period/2), alpha = (N1/2)/(N1 - N1/2) = 1
var nyqma = new Nyqma(10, 5);
// alpha = 5/(10-5) = 1.0
// NYQMA = 2*WMA1 - WMA2 (same as PMA when nyquistPeriod = period)
double constant = 100.0;
double last = 0;
for (int i = 0; i < 50; i++)
{
last = nyqma.Update(new TValue(DateTime.UtcNow, constant)).Value;
}
Assert.Equal(constant, last, 1e-9);
_output.WriteLine("NYQMA(10,5) alpha=1.0 convergence: PASS");
}
}