Enhance documentation and validation for various indicators

This commit is contained in:
Miha Kralj
2025-12-22 20:42:26 -08:00
parent 5bb8c122c0
commit 4efa0e773e
81 changed files with 4267 additions and 640 deletions
+38
View File
@@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OoplesFinance.StockIndicators;
using OoplesFinance.StockIndicators.Enums;
using OoplesFinance.StockIndicators.Models;
using Skender.Stock.Indicators;
using Tulip;
using Xunit;
@@ -152,4 +155,39 @@ public class HmaValidationTests : IDisposable
}
_output.WriteLine("HMA Span validated successfully against Skender");
}
[Fact]
public void Validate_Ooples_Batch()
{
// Ooples uses Math.Round for sqrt(period) and period/2, while QuanTAlib uses integer truncation (floor).
// This causes discrepancies for periods where the fractional part is >= 0.5 (e.g., sqrt(14) = 3.74 -> 4 vs 3).
// We test only periods where the rounding logic yields the same result.
int[] periods = { 9, 20, 50 };
// Prepare data for Ooples (List<TickerData>)
var ooplesData = _testData.SkenderQuotes.Select(q => new TickerData
{
Date = q.Date,
Close = (double)q.Close,
High = (double)q.High,
Low = (double)q.Low,
Open = (double)q.Open,
Volume = (double)q.Volume
}).ToList();
foreach (var period in periods)
{
// Calculate QuanTAlib HMA (batch TSeries)
var hma = new global::QuanTAlib.Hma(period);
var qResult = hma.Update(_testData.Data);
// Calculate Ooples HMA
var stockData = new StockData(ooplesData);
var sResult = Calculations.CalculateHullMovingAverage(stockData, length: period).OutputValues.Values.First();
// Compare last 100 records
ValidationHelper.VerifyData(qResult, sResult, (s) => s, 100, 1.0);
}
_output.WriteLine("HMA Batch(TSeries) validated successfully against Ooples");
}
}
+25 -11
View File
@@ -31,23 +31,37 @@ Where $N$ is the period.
HMA is computationally more intensive than a simple WMA due to the three passes, but our implementation optimizes the intermediate step.
| Metric | Complexity | Notes |
| Metric | Score | Notes |
| :--- | :--- | :--- |
| **Throughput** | High | 3x WMA cost + vector math |
| **Complexity** | O(1) | Constant time update |
| **Accuracy** | 8/10 | Excellent at tracking price action |
| **Timeliness** | 9/10 | Very responsive, minimal lag |
| **Overshoot** | 5/10 | Prone to overshoot due to lag correction |
| **Smoothness** | 8/10 | Surprisingly smooth given its speed |
| **Throughput** | ★★★★☆ | 3x WMA cost + vector math. |
| **Allocations** | ★★★★★ | 0 bytes; hot path is allocation-free. |
| **Complexity** | ★★★★★ | O(1) constant time update. |
| **Precision** | ★★★★★ | `double` precision. |
### Zero-Allocation Design
HMA is implemented by chaining three `Wma` instances. Since `Wma` is zero-allocation, HMA inherits this property.
## Validation
Validated against Alan Hull's original formula and standard library implementations.
Validated against Skender, Tulip, and Ooples.
| Provider | Error Tolerance | Notes |
| Library | Status | Notes |
| :--- | :--- | :--- |
| **Fidelity** | $10^{-9}$ | Matches standard HMA |
| **Skender** | $10^{-9}$ | Matches `GetHma` |
| **Skender** | ✅ | Matches `GetHma`. |
| **Tulip** | | Matches `hma`. |
| **Ooples** | ✅ | Matches `CalculateHullMovingAverage` (with rounding caveats). |
| **TA-Lib** | ❌ | Not implemented. |
### External Library Discrepancies
**OoplesFinance.StockIndicators**:
Discrepancies exist due to different rounding methods for integer periods.
* **QuanTAlib**: Uses integer truncation (floor) for $N/2$ and $\sqrt{N}$.
* **Ooples**: Uses `Math.Round` (nearest integer).
This results in different effective periods for $N=14$ ($\sqrt{14} \approx 3.74 \to 3$ vs $4$) and others where the fractional part $\ge 0.5$. Validation tests match exactly for periods where rounding logic aligns (e.g., $N=9, 20, 50$).
### Common Pitfalls