mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-23 13:08:04 +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,4 @@
|
||||
using TALib;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
@@ -533,4 +534,130 @@ public sealed class RegchannelValidationTests : IDisposable
|
||||
|
||||
_output.WriteLine("Regchannel stdDev formula validated");
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// TALib Validation
|
||||
// TALib LinearReg computes the linear regression value at the end
|
||||
// of the lookback window — same as Regchannel's midline (centerline).
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public void Validate_Talib_LinearReg_Centerline()
|
||||
{
|
||||
int[] periods = { 5, 10, 20, 50 };
|
||||
double[] sourceData = _testData.RawData.ToArray();
|
||||
double[] linregOutput = new double[sourceData.Length];
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
var (qMid, _, _) = Regchannel.Batch(_testData.Data, period, 2.0);
|
||||
|
||||
var retCode = Functions.LinearReg<double>(
|
||||
sourceData,
|
||||
0..^0,
|
||||
linregOutput,
|
||||
out var outRange,
|
||||
period);
|
||||
|
||||
Assert.Equal(Core.RetCode.Success, retCode);
|
||||
|
||||
int lookback = Functions.LinearRegLookback(period);
|
||||
|
||||
ValidationHelper.VerifyData(qMid, linregOutput, outRange, lookback);
|
||||
}
|
||||
_output.WriteLine("Regchannel centerline validated against TALib LinearReg for all periods");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Talib_LinearRegSlope()
|
||||
{
|
||||
int[] periods = { 5, 10, 20, 50 };
|
||||
double[] sourceData = _testData.RawData.ToArray();
|
||||
double[] slopeOutput = new double[sourceData.Length];
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Stream Regchannel and collect slopes
|
||||
var ind = new Regchannel(period, 2.0);
|
||||
var slopes = new List<double>();
|
||||
foreach (var tv in _testData.Data)
|
||||
{
|
||||
ind.Update(tv);
|
||||
slopes.Add(ind.Slope);
|
||||
}
|
||||
|
||||
var retCode = Functions.LinearRegSlope<double>(
|
||||
sourceData,
|
||||
0..^0,
|
||||
slopeOutput,
|
||||
out var outRange,
|
||||
period);
|
||||
|
||||
Assert.Equal(Core.RetCode.Success, retCode);
|
||||
|
||||
int lookback = Functions.LinearRegSlopeLookback(period);
|
||||
|
||||
// Compare slopes from end of series (converged)
|
||||
int count = slopes.Count;
|
||||
int start = Math.Max(0, count - 100);
|
||||
var (offset, _) = outRange.GetOffsetAndLength(slopeOutput.Length);
|
||||
|
||||
for (int i = start; i < count; i++)
|
||||
{
|
||||
if (i < lookback)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int tIndex = i - offset;
|
||||
if (tIndex < 0 || tIndex >= slopeOutput.Length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert.True(
|
||||
Math.Abs(slopes[i] - slopeOutput[tIndex]) <= ValidationHelper.TalibTolerance,
|
||||
$"Slope mismatch at {i}: QuanTAlib={slopes[i]:G17}, TALib={slopeOutput[tIndex]:G17}");
|
||||
}
|
||||
}
|
||||
_output.WriteLine("Regchannel slope validated against TALib LinearRegSlope for all periods");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Tulip_LinearReg_Centerline()
|
||||
{
|
||||
int[] periods = { 5, 10, 20, 50 };
|
||||
double[] sourceData = _testData.RawData.ToArray();
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
var (qMid, _, _) = Regchannel.Batch(_testData.Data, period, 2.0);
|
||||
|
||||
var linregIndicator = Tulip.Indicators.linreg;
|
||||
double[][] inputs = { sourceData };
|
||||
double[] options = { period };
|
||||
double[][] outputs = { new double[sourceData.Length - period + 1] };
|
||||
linregIndicator.Run(inputs, options, outputs);
|
||||
|
||||
var tLinreg = outputs[0];
|
||||
int offset = period - 1; // Tulip output starts at index (period-1)
|
||||
|
||||
// Compare last 100 values
|
||||
int count = qMid.Count;
|
||||
int start = Math.Max(0, count - 100);
|
||||
for (int i = start; i < count; i++)
|
||||
{
|
||||
int tIndex = i - offset;
|
||||
if (tIndex < 0 || tIndex >= tLinreg.Length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert.True(
|
||||
Math.Abs(qMid[i].Value - tLinreg[tIndex]) <= ValidationHelper.TulipTolerance,
|
||||
$"Mismatch at {i}: QuanTAlib={qMid[i].Value:G17}, Tulip={tLinreg[tIndex]:G17}");
|
||||
}
|
||||
}
|
||||
_output.WriteLine("Regchannel centerline validated against Tulip linreg for all periods");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user