mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-15 17:18:05 +00:00
Enhance validation tests for various indicators with external library comparisons
- Added detailed comments explaining the validation limitations for MMA and ZLEMA due to differences in algorithm implementations. - Implemented validation tests for True Range against TALib and Tulip, ensuring directional agreement. - Updated Ulcer Index validation to clarify differences in algorithmic approaches between QuanTAlib and Skender. - Enhanced Ease of Movement tests to verify directional agreement with Tulip's EMV, noting differences in volume scaling. - Expanded Klinger Volume Oscillator tests to validate against Skender and Tulip, focusing on directional agreement across multiple period configurations. - Improved Negative Volume Index tests to compare percentage changes with Tulip, addressing differences in starting values. - Updated Positive Volume Index tests to validate against Tulip, emphasizing percentage change comparisons. - Enhanced Williams Accumulation/Distribution tests to verify directional agreement with Tulip, highlighting formula differences.
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
using TALib;
|
||||
|
||||
namespace QuanTAlib.Test;
|
||||
|
||||
using Xunit;
|
||||
@@ -657,4 +659,58 @@ public class TrValidationTests
|
||||
Assert.True(output[i] >= 0, $"Output at index {i} should be non-negative");
|
||||
}
|
||||
}
|
||||
|
||||
// === External Library Validation ===
|
||||
|
||||
[Fact]
|
||||
public void Validate_Talib_TrueRange()
|
||||
{
|
||||
var bars = GenerateTestData(500);
|
||||
double[] high = bars.Select(b => b.High).ToArray();
|
||||
double[] low = bars.Select(b => b.Low).ToArray();
|
||||
double[] close = bars.Select(b => b.Close).ToArray();
|
||||
double[] output = new double[high.Length];
|
||||
|
||||
var retCode = Functions.TRange<double>(high, low, close, 0..^0, output, out var outRange);
|
||||
Assert.Equal(Core.RetCode.Success, retCode);
|
||||
|
||||
int lookback = Functions.TRangeLookback();
|
||||
|
||||
// Batch comparison
|
||||
double[] qOutput = new double[high.Length];
|
||||
Tr.Batch(high, low, close, qOutput);
|
||||
|
||||
// Use ValidationHelper for correct TALib index mapping
|
||||
QuanTAlib.Tests.ValidationHelper.VerifyData(qOutput, output, outRange, lookback);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Tulip_TrueRange()
|
||||
{
|
||||
var bars = GenerateTestData(500);
|
||||
double[] high = bars.Select(b => b.High).ToArray();
|
||||
double[] low = bars.Select(b => b.Low).ToArray();
|
||||
double[] close = bars.Select(b => b.Close).ToArray();
|
||||
|
||||
var trIndicator = Tulip.Indicators.tr;
|
||||
double[][] inputs = { high, low, close };
|
||||
double[] options = Array.Empty<double>();
|
||||
int lookback = trIndicator.Start(options);
|
||||
double[][] outputs = { new double[high.Length - lookback] };
|
||||
trIndicator.Run(inputs, options, outputs);
|
||||
|
||||
double[] qOutput = new double[high.Length];
|
||||
Tr.Batch(high, low, close, qOutput);
|
||||
|
||||
int tulipLen = outputs[0].Length;
|
||||
int count = Math.Min(tulipLen, 100);
|
||||
int start = tulipLen - count;
|
||||
for (int i = start; i < tulipLen; i++)
|
||||
{
|
||||
int qIdx = lookback + i;
|
||||
Assert.True(
|
||||
Math.Abs(qOutput[qIdx] - outputs[0][i]) <= 1e-7,
|
||||
$"TR mismatch at {qIdx}: QuanTAlib={qOutput[qIdx]:G17}, Tulip={outputs[0][i]:G17}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -661,4 +661,12 @@ public class UiValidationTests
|
||||
double expected = Math.Sqrt(22.6757369614512 / 3.0);
|
||||
Assert.Equal(expected, result.Value, 5);
|
||||
}
|
||||
|
||||
// === External Library Validation ===
|
||||
// NOTE: Skender.Stock.Indicators uses a different Ulcer Index algorithm variant:
|
||||
// Skender: For each bar j in the period window, highestClose = max(closes from window_start to j)
|
||||
// Each bar gets its own "growing" highest reference within the evaluation window.
|
||||
// QuanTAlib: highestClose = max(closes over the entire rolling period window)
|
||||
// Both are valid implementations of the Ulcer Index concept, but produce different values.
|
||||
// No external validation test is added for UI due to this algorithmic difference.
|
||||
}
|
||||
Reference in New Issue
Block a user