mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-24 05:28:05 +00:00
- Implemented unit tests for the following indicators: - KAMA (Kaufman Adaptive Moving Average) - SMA (Simple Moving Average) - T3 (Tillson T3 Moving Average) - TEMA (Triple Exponential Moving Average) - TRIMA (Triangular Moving Average) - WMA (Weighted Moving Average) - Each test class includes tests for constructor defaults, history depth, short name, initialization, processing updates, and source type handling. - Updated the Quantower.Tests.csproj to include all new test files in the lib directory.
158 lines
7.0 KiB
Markdown
158 lines
7.0 KiB
Markdown
# Good Indicator Guidelines
|
|
|
|
This document defines the strict standards for creating high-quality technical indicators in the QuanTAlib library. All new indicators MUST adhere to these rules to ensure consistency, performance, and reliability.
|
|
|
|
## 1. Architecture & Design Principles
|
|
|
|
* **Zero Allocation:** The core calculation loop must not allocate memory on the heap. Use `stackalloc`, `Span<T>`, and pinned memory where possible.
|
|
* **O(1) Complexity:** Streaming updates must be O(1) whenever mathematically possible. Use running sums/products or circular buffers to avoid re-iterating over history.
|
|
* **Dual API:** Provide both a stateful object-oriented API (`Update`) and a stateless static vector API (`Calculate`).
|
|
* **Bar Correction:** Support intra-bar updates via the `isNew` parameter. The indicator must be able to rollback the last update and apply a new value for the same timestamp.
|
|
* **Robustness:** Handle `NaN` and `Infinity` gracefully using last-valid-value substitution. Never propagate invalid values.
|
|
* **Reactive:** Implement `ITValuePublisher` to support event-driven architectures.
|
|
|
|
## 2. File Structure
|
|
|
|
Each indicator resides in its own directory such as `lib/trends/`, `lib/indicators/`, or `lib/oscillators/`.
|
|
|
|
**Directory:** `lib/[category]/[name]/`
|
|
|
|
| File | Purpose | Naming Convention |
|
|
|------|---------|-------------------|
|
|
| **Source** | Main implementation | `[Name].cs` (e.g., `Sma.cs`) |
|
|
| **Tests** | Unit tests | `[Name].Tests.cs` |
|
|
| **Validation** | Cross-library validation | `[Name].Validation.Tests.cs` |
|
|
| **Docs** | User documentation | `[Name].md` |
|
|
| **Quantower** | Quantower adapter | `[Name].Quantower.cs` |
|
|
| **Quantower Tests** | Quantower adapter tests | `[Name].Quantower.Tests.cs` |
|
|
|
|
## 3. Implementation Rules (`[Name].cs`)
|
|
|
|
### Class Definition
|
|
|
|
* **Namespace:** `QuanTAlib`
|
|
* **Attributes:** `[SkipLocalsInit]` for performance.
|
|
* **Modifiers:** `public sealed class`
|
|
* **Interface:** Implements `ITValuePublisher`
|
|
|
|
### State Management
|
|
|
|
* Use `RingBuffer` for sliding window data.
|
|
* Maintain separate state variables for the *current* calculation (`_sum`, `_lastVal`) and the *previous* valid state (`_p_sum`, `_p_lastVal`) to support `isNew=false` updates.
|
|
* **Resync:** Implement a periodic full recalculation (e.g., every 1000 ticks) to prevent floating-point drift in running sums.
|
|
|
|
### Constructor
|
|
|
|
* Validate all parameters (throw `ArgumentException` for invalid values).
|
|
* Initialize `Name` property (e.g., `$"Sma({period})"`);
|
|
* Support chaining: `public [Name](ITValuePublisher source, ...)`
|
|
|
|
### Update Method
|
|
|
|
* **Signature:** `public TValue Update(TValue input, bool isNew = true)`
|
|
* **Attribute:** `[MethodImpl(MethodImplOptions.AggressiveInlining)]`
|
|
* **Logic:**
|
|
1. **Input Validation:** Check `double.IsFinite`. If not, use `_lastValidValue`.
|
|
2. **State Management:**
|
|
* If `isNew=true`: Save current state to `_p_*` variables, then update.
|
|
* If `isNew=false`: Restore state from `_p_*` variables, then update.
|
|
3. **Calculation:** Perform the math.
|
|
4. **Publish:** Update `Last` property, invoke `Pub` event, return `Last`.
|
|
|
|
### Update Method (TSeries)
|
|
|
|
* **Signature:** `public TSeries Update(TSeries source)`
|
|
* **Placement:** Must be adjacent to the `Update(TValue)` method.
|
|
* **Logic:**
|
|
1. Create output series.
|
|
2. Call static `Calculate(Span)` for performance.
|
|
3. Restore internal state by replaying the last `Period` bars.
|
|
|
|
### Static Calculate (TSeries)
|
|
|
|
* Create a new instance of the indicator.
|
|
* Iterate through the source series.
|
|
* Return the resulting `TSeries`.
|
|
|
|
### Static Calculate (Span) - **Critical for Performance**
|
|
|
|
* **Signature:** `public static void Calculate(ReadOnlySpan<double> source, Span<double> output, ...)`
|
|
* **Attribute:** `[MethodImpl(MethodImplOptions.AggressiveInlining)]`
|
|
* **Optimization:**
|
|
|
|
* Check for SIMD support (`Avx2.IsSupported`).
|
|
* Use `stackalloc` for small buffers (threshold ~256).
|
|
* Implement a scalar fallback path that handles `NaN` safely.
|
|
* Implement a SIMD path for large, clean datasets (optional but recommended for simple averages).
|
|
|
|
## 4. Testing Standards
|
|
|
|
### Unit Tests (`[Name].Tests.cs`)
|
|
|
|
* **Framework:** xUnit
|
|
* **Coverage:**
|
|
|
|
* Constructor validation (invalid params).
|
|
* Basic calculation correctness (compare against manual calc).
|
|
* `isNew=true` vs `isNew=false` behavior (bar correction).
|
|
* `Reset()` functionality.
|
|
* `IsHot` property behavior.
|
|
* `NaN` / `Infinity` handling (must not crash, must return finite values).
|
|
* Consistency between Object API, Static TSeries API, and Static Span API.
|
|
* Edge cases: Period=1, empty input, single input.
|
|
|
|
### Validation Tests (`[Name].Validation.Tests.cs`)
|
|
|
|
* **Purpose:** Verify accuracy against established libraries (Skender, TA-Lib, Tulip).
|
|
* **Data:** Use `GBM` (Geometric Brownian Motion) to generate realistic test data.
|
|
* **Scenarios:**
|
|
|
|
* Batch processing.
|
|
* Streaming processing.
|
|
* Span/Vector processing.
|
|
|
|
* **Tolerance:** Typically `1e-6` or `1e-9` depending on the algorithm.
|
|
|
|
## 5. Documentation Standards (`[Name].md`)
|
|
|
|
Follow the standard template and ensure strict adherence to Markdownlint rules, specifically:
|
|
|
|
* **MD030:** Ensure exactly one space after list markers (e.g., `* Item`, not `*Item` or `* Item`).
|
|
* **MD032:** Ensure lists are surrounded by blank lines (one blank line before the first item and one after the last item).
|
|
|
|
Template structure:
|
|
|
|
1. **Title & Overview:** What is it? What does it do?
|
|
2. **Core Concepts:** Key features (e.g., equal weighting, noise reduction).
|
|
3. **Parameters:** Table of constructor parameters.
|
|
4. **Formula:** LaTeX formatted math ($$...$$).
|
|
5. **C# Implementation:** Code examples for:
|
|
* Standard usage.
|
|
* Span API (high performance).
|
|
* Bar correction (`isNew`).
|
|
* Eventing.
|
|
6. **Interpretation:** How to use it in trading.
|
|
7. **References:** Books or papers.
|
|
|
|
## 6. Performance Guidelines
|
|
|
|
* **Inlining:** Use `[MethodImpl(MethodImplOptions.AggressiveInlining)]` on all hot path methods (`Update`, `Calculate`).
|
|
* **Locals Init:** Use `[SkipLocalsInit]` on the class to skip zero-initialization of locals.
|
|
* **Loops:** Prefer `for` loops over `foreach` for arrays/spans.
|
|
* **Math:** Use `System.Math` or `System.Numerics`. Avoid LINQ in hot paths.
|
|
* **Memory:** **NEVER** use `new` inside the `Update` method. Pre-allocate everything in the constructor.
|
|
|
|
## 7. Checklist for New Indicators
|
|
|
|
* [ ] **File Structure:** Created all 6 required files?
|
|
* [ ] **Constructor:** Validates inputs? Sets `Name`?
|
|
* [ ] **Update:** Handles `isNew` correctly? Handles `NaN`? O(1)?
|
|
* [ ] **Static API:** Implemented `Calculate(Span)`?
|
|
* [ ] **Tests:** Unit tests pass? `NaN` tests included?
|
|
* [ ] **Validation:** Matches external libraries (Skender/TA-Lib)?
|
|
* [ ] **Docs:** Markdown file created with formula and examples?
|
|
* [ ] **Quantower:** Adapter created in `[Name].Quantower.cs`?
|
|
* [ ] **Quantower Tests:** Adapter tests created in `[Name].Quantower.Tests.cs`?
|
|
* [ ] **Index:** Added to category `_index.md` with link and description?
|
|
* [ ] **Performance:** No allocations in `Update`? `[SkipLocalsInit]` used?
|