mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-23 04:58:08 +00:00
Enhance documentation and validation for various indicators
This commit is contained in:
@@ -4,12 +4,14 @@ using System.Linq;
|
||||
using OoplesFinance.StockIndicators;
|
||||
using OoplesFinance.StockIndicators.Models;
|
||||
using Skender.Stock.Indicators;
|
||||
using TALib;
|
||||
using Tulip;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class KamaValidationTests
|
||||
public class KamaValidationTests : IDisposable
|
||||
{
|
||||
private readonly ValidationTestData _testData;
|
||||
private readonly ITestOutputHelper _output;
|
||||
@@ -20,6 +22,20 @@ public class KamaValidationTests
|
||||
_testData = new ValidationTestData();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_testData.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Skender_Batch()
|
||||
{
|
||||
@@ -90,6 +106,134 @@ public class KamaValidationTests
|
||||
_output.WriteLine("KAMA Span validated successfully against Skender");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Talib_Batch()
|
||||
{
|
||||
int[] periods = { 10, 14, 20 };
|
||||
// TA-Lib KAMA uses default fast=2, slow=30 and doesn't expose them in the standard API
|
||||
|
||||
// Prepare data for TA-Lib (double[])
|
||||
double[] cData = _testData.Data.Select(x => x.Value).ToArray();
|
||||
double[] output = new double[cData.Length];
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Calculate QuanTAlib KAMA (batch TSeries)
|
||||
// Use default fast=2, slow=30 to match TA-Lib
|
||||
var kama = new global::QuanTAlib.Kama(period);
|
||||
var qResult = kama.Update(_testData.Data);
|
||||
|
||||
// Calculate TA-Lib KAMA
|
||||
var retCode = TALib.Functions.Kama(cData, 0..^0, output, out var outRange, period);
|
||||
Assert.Equal(Core.RetCode.Success, retCode);
|
||||
|
||||
int lookback = TALib.Functions.KamaLookback(period);
|
||||
|
||||
// Compare last 100 records
|
||||
ValidationHelper.VerifyData(qResult, output, outRange, lookback);
|
||||
}
|
||||
_output.WriteLine("KAMA Batch(TSeries) validated successfully against TA-Lib");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Talib_Streaming()
|
||||
{
|
||||
int[] periods = { 10, 14, 20 };
|
||||
|
||||
// Prepare data for TA-Lib (double[])
|
||||
double[] cData = _testData.Data.Select(x => x.Value).ToArray();
|
||||
double[] output = new double[cData.Length];
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Calculate QuanTAlib KAMA (streaming)
|
||||
var kama = new global::QuanTAlib.Kama(period);
|
||||
var qResults = new List<double>();
|
||||
foreach (var item in _testData.Data)
|
||||
{
|
||||
qResults.Add(kama.Update(item).Value);
|
||||
}
|
||||
|
||||
// Calculate TA-Lib KAMA
|
||||
var retCode = TALib.Functions.Kama(cData, 0..^0, output, out var outRange, period);
|
||||
Assert.Equal(Core.RetCode.Success, retCode);
|
||||
|
||||
int lookback = TALib.Functions.KamaLookback(period);
|
||||
|
||||
// Compare last 100 records
|
||||
ValidationHelper.VerifyData(qResults, output, outRange, lookback);
|
||||
}
|
||||
_output.WriteLine("KAMA Streaming validated successfully against TA-Lib");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Tulip_Batch()
|
||||
{
|
||||
int[] periods = { 10, 14, 20 };
|
||||
|
||||
// Prepare data for Tulip (double[])
|
||||
double[] cData = _testData.Data.Select(x => x.Value).ToArray();
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Calculate QuanTAlib KAMA (batch TSeries)
|
||||
var kama = new global::QuanTAlib.Kama(period);
|
||||
var qResult = kama.Update(_testData.Data);
|
||||
|
||||
// Calculate Tulip KAMA
|
||||
var kamaIndicator = Tulip.Indicators.kama;
|
||||
double[][] inputs = { cData };
|
||||
double[] options = { period };
|
||||
|
||||
// Tulip KAMA lookback
|
||||
int lookback = kamaIndicator.Start(options);
|
||||
double[][] outputs = { new double[cData.Length - lookback] };
|
||||
|
||||
kamaIndicator.Run(inputs, options, outputs);
|
||||
var tResult = outputs[0];
|
||||
|
||||
// Compare last 100 records
|
||||
ValidationHelper.VerifyData(qResult, tResult, lookback, tolerance: ValidationHelper.TulipTolerance);
|
||||
}
|
||||
_output.WriteLine("KAMA Batch(TSeries) validated successfully against Tulip");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Tulip_Streaming()
|
||||
{
|
||||
int[] periods = { 10, 14, 20 };
|
||||
|
||||
// Prepare data for Tulip (double[])
|
||||
double[] cData = _testData.Data.Select(x => x.Value).ToArray();
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Calculate QuanTAlib KAMA (streaming)
|
||||
var kama = new global::QuanTAlib.Kama(period);
|
||||
var qResults = new List<double>();
|
||||
foreach (var item in _testData.Data)
|
||||
{
|
||||
qResults.Add(kama.Update(item).Value);
|
||||
}
|
||||
|
||||
// Calculate Tulip KAMA
|
||||
var kamaIndicator = Tulip.Indicators.kama;
|
||||
double[][] inputs = { cData };
|
||||
double[] options = { period };
|
||||
|
||||
// Tulip KAMA lookback
|
||||
int lookback = kamaIndicator.Start(options);
|
||||
double[][] outputs = { new double[cData.Length - lookback] };
|
||||
|
||||
kamaIndicator.Run(inputs, options, outputs);
|
||||
var tResult = outputs[0];
|
||||
|
||||
// Compare last 100 records
|
||||
ValidationHelper.VerifyData(qResults, tResult, lookback, tolerance: ValidationHelper.TulipTolerance);
|
||||
}
|
||||
_output.WriteLine("KAMA Streaming validated successfully against Tulip");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Against_Ooples()
|
||||
{
|
||||
|
||||
@@ -34,7 +34,8 @@ KAMA is very efficient, with O(1) complexity thanks to the incremental volatilit
|
||||
|
||||
| Metric | Score | Notes |
|
||||
| :--- | :--- | :--- |
|
||||
| **Throughput** | High | O(1) updates |
|
||||
| **Throughput** | [N] ns/bar | O(1) updates |
|
||||
| **Allocations** | 0 | Stack-based calculations only |
|
||||
| **Complexity** | O(1) | Constant time update |
|
||||
| **Accuracy** | 7/10 | Flattens in noise, tracks in trends |
|
||||
| **Timeliness** | 8/10 | Accelerates quickly in strong trends |
|
||||
@@ -43,12 +44,15 @@ KAMA is very efficient, with O(1) complexity thanks to the incremental volatilit
|
||||
|
||||
## Validation
|
||||
|
||||
Validated against TA-Lib and Skender.
|
||||
Validated against TA-Lib, Skender, Tulip, and Ooples.
|
||||
|
||||
| Provider | Error Tolerance | Notes |
|
||||
| Library | Status | Notes |
|
||||
| :--- | :--- | :--- |
|
||||
| **TA-Lib** | $10^{-9}$ | Matches `TA_KAMA` |
|
||||
| **Skender** | $10^{-9}$ | Matches `GetKama` |
|
||||
| **QuanTAlib** | ✅ | Validated. |
|
||||
| **TA-Lib** | ✅ | Matches `Kama` |
|
||||
| **Skender** | ✅ | Matches `GetKama` |
|
||||
| **Tulip** | ✅ | Matches `kama` |
|
||||
| **Ooples** | ✅ | Matches `CalculateKaufmanAdaptiveMovingAverage` |
|
||||
|
||||
### Common Pitfalls
|
||||
|
||||
|
||||
Reference in New Issue
Block a user