mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-05 20:47:43 +00:00
060649192f
- 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
207 lines
6.3 KiB
C#
207 lines
6.3 KiB
C#
namespace QuanTAlib.Tests;
|
|
|
|
/// <summary>
|
|
/// HAMMA validation tests.
|
|
/// Note: HAMMA is not available in TA-Lib, Tulip, Skender, or OoplesFinance.
|
|
/// Validation is performed against internal consistency checks and mathematical verification.
|
|
/// </summary>
|
|
public sealed class HammaValidationTests : IDisposable
|
|
{
|
|
private readonly ValidationTestData _testData;
|
|
private bool _disposed;
|
|
|
|
public HammaValidationTests()
|
|
{
|
|
_testData = new ValidationTestData(count: 10000, seed: 42);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
}
|
|
|
|
private void Dispose(bool disposing)
|
|
{
|
|
if (_disposed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_disposed = true;
|
|
|
|
if (disposing)
|
|
{
|
|
_testData?.Dispose();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Hamma_BatchMatchesStreaming()
|
|
{
|
|
int[] periods = { 5, 10, 20, 50 };
|
|
|
|
foreach (var period in periods)
|
|
{
|
|
// Calculate QuanTAlib HAMMA (batch TSeries)
|
|
var hammaBatch = new Hamma(period);
|
|
var batchResult = hammaBatch.Update(_testData.Data);
|
|
|
|
// Calculate QuanTAlib HAMMA (streaming)
|
|
var hammaStreaming = new Hamma(period);
|
|
var streamingResults = new List<double>();
|
|
foreach (var item in _testData.Data)
|
|
{
|
|
streamingResults.Add(hammaStreaming.Update(item).Value);
|
|
}
|
|
|
|
// Compare all records
|
|
Assert.Equal(batchResult.Count, streamingResults.Count);
|
|
for (int i = 0; i < batchResult.Count; i++)
|
|
{
|
|
Assert.Equal(batchResult[i].Value, streamingResults[i], 1e-10);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Hamma_SpanMatchesBatch()
|
|
{
|
|
int[] periods = { 5, 10, 20, 50 };
|
|
|
|
// Prepare data for Span API
|
|
ReadOnlySpan<double> sourceData = _testData.RawData.Span;
|
|
|
|
foreach (var period in periods)
|
|
{
|
|
// Calculate QuanTAlib HAMMA (Span API)
|
|
double[] spanOutput = new double[sourceData.Length];
|
|
Hamma.Batch(sourceData, spanOutput.AsSpan(), period);
|
|
|
|
// Calculate QuanTAlib HAMMA (batch TSeries)
|
|
var hammaBatch = new Hamma(period);
|
|
var batchResult = hammaBatch.Update(_testData.Data);
|
|
|
|
// Compare all records
|
|
Assert.Equal(batchResult.Count, spanOutput.Length);
|
|
for (int i = 0; i < batchResult.Count; i++)
|
|
{
|
|
Assert.Equal(batchResult[i].Value, spanOutput[i], 1e-10);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Hamma_EventingMatchesBatch()
|
|
{
|
|
int[] periods = { 5, 10, 20, 50 };
|
|
|
|
foreach (var period in periods)
|
|
{
|
|
// Calculate QuanTAlib HAMMA (batch TSeries)
|
|
var hammaBatch = new Hamma(period);
|
|
var batchResult = hammaBatch.Update(_testData.Data);
|
|
|
|
// Calculate QuanTAlib HAMMA (eventing)
|
|
var pubSource = new TSeries();
|
|
var hammaEventing = new Hamma(pubSource, period);
|
|
var eventingResults = new List<double>();
|
|
hammaEventing.Pub += (object? sender, in TValueEventArgs e) => eventingResults.Add(e.Value.Value);
|
|
|
|
foreach (var item in _testData.Data)
|
|
{
|
|
pubSource.Add(item);
|
|
}
|
|
|
|
// Compare all records
|
|
Assert.Equal(batchResult.Count, eventingResults.Count);
|
|
for (int i = 0; i < batchResult.Count; i++)
|
|
{
|
|
Assert.Equal(batchResult[i].Value, eventingResults[i], 1e-10);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Hamma_HammingWindow_WeightsAreSymmetric()
|
|
{
|
|
// Hamming window is symmetric: w[i] = w[period-1-i]
|
|
int period = 11; // Odd period for exact center
|
|
|
|
var hamma = new Hamma(period);
|
|
|
|
// Feed symmetric data: [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]
|
|
double[] symmetricData = [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1];
|
|
|
|
foreach (var val in symmetricData)
|
|
{
|
|
hamma.Update(new TValue(DateTime.UtcNow, val));
|
|
}
|
|
|
|
// The HAMMA result should be reasonable (between min and max of data)
|
|
Assert.True(hamma.Last.Value >= 1 && hamma.Last.Value <= 6);
|
|
}
|
|
|
|
[Fact]
|
|
public void Hamma_KnownValues_ManualCalculation()
|
|
{
|
|
// Manual verification of HAMMA calculation with known values
|
|
// period=5: w[i] = 0.54 - 0.46 * cos(2πi/4)
|
|
|
|
int period = 5;
|
|
var hamma = new Hamma(period);
|
|
|
|
// Feed 5 values: [100, 102, 104, 103, 101]
|
|
double[] prices = [100, 102, 104, 103, 101];
|
|
foreach (var price in prices)
|
|
{
|
|
hamma.Update(new TValue(DateTime.UtcNow, price));
|
|
}
|
|
|
|
// Calculate expected manually using Hamming window formula
|
|
double twoPiOverPm1 = 2.0 * Math.PI / (period - 1);
|
|
double[] weights = new double[period];
|
|
double weightSum = 0;
|
|
for (int i = 0; i < period; i++)
|
|
{
|
|
weights[i] = 0.54 - 0.46 * Math.Cos(twoPiOverPm1 * i);
|
|
weightSum += weights[i];
|
|
}
|
|
|
|
double expected = 0;
|
|
for (int i = 0; i < period; i++)
|
|
{
|
|
expected += prices[i] * weights[i];
|
|
}
|
|
expected /= weightSum;
|
|
|
|
Assert.Equal(expected, hamma.Last.Value, 1e-10);
|
|
}
|
|
|
|
[Fact]
|
|
public void Hamma_HammingCoefficients_Verify()
|
|
{
|
|
// Verify Hamming window coefficients match the standard formula
|
|
// w[i] = 0.54 - 0.46 * cos(2πi/(N-1))
|
|
// For period=5: w[0]=0.08, w[1]≈0.54, w[2]=1.0, w[3]≈0.54, w[4]=0.08
|
|
|
|
int period = 5;
|
|
double twoPiOverPm1 = 2.0 * Math.PI / (period - 1);
|
|
|
|
double w0 = 0.54 - 0.46 * Math.Cos(0); // 0.08
|
|
double w1 = 0.54 - 0.46 * Math.Cos(twoPiOverPm1 * 1); // ≈0.54
|
|
double w2 = 0.54 - 0.46 * Math.Cos(twoPiOverPm1 * 2); // 1.0
|
|
double w3 = 0.54 - 0.46 * Math.Cos(twoPiOverPm1 * 3); // ≈0.54
|
|
double w4 = 0.54 - 0.46 * Math.Cos(twoPiOverPm1 * 4); // 0.08
|
|
|
|
Assert.Equal(0.08, w0, 1e-10);
|
|
Assert.Equal(0.08, w4, 1e-10);
|
|
Assert.Equal(1.0, w2, 1e-10);
|
|
|
|
// w1 and w3 should be equal (symmetric)
|
|
Assert.Equal(w1, w3, 1e-10);
|
|
|
|
// All edge weights should be equal
|
|
Assert.Equal(w0, w4, 1e-10);
|
|
}
|
|
}
|