8.5 KiB
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
- Source Material: The algorithm and markdown documentation foundation should be sourced from https://github.com/mihakralj/pinescript/blob/main/indicators/.
- 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
isNewparameter. The indicator must be able to rollback the last update and apply a new value for the same timestamp. - Robustness: Handle
NaNandInfinitygracefully using last-valid-value substitution. Never propagate invalid values. - Reactive: Implement
ITValuePublisherto support event-driven architectures. - Time Handling: Always use
DateTime.UtcNowinstead ofDateTime.Nowto ensure consistent time handling across timezones.
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
- Scalar State: Use a
private record struct Stateto group all scalar state variables. This ensures value semantics, automaticIEquatableimplementation, and cleaner rollback logic. - State Variables: Maintain
private State _state;(current) andprivate State _p_state;(previous valid state). - Buffers: Use
RingBufferfor sliding window data. - Resync: Implement a periodic full recalculation (e.g., every 1000 ticks) to prevent floating-point drift in running sums.
Constructor
- Validate all parameters (throw
ArgumentExceptionfor invalid values). - Initialize
Nameproperty (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:
-
State Rollback:
if (isNew) { _p_state = _state; // ... update state (e.g. counters) ... } else { _state = _p_state; // ... update state ... } -
Input Validation: Check
double.IsFinite. If not, use_lastValidValue(stored inState). -
Calculation: Perform the math.
-
Publish: Update
Lastproperty, invokePubevent, returnLast.
-
Update Method (TSeries)
- Signature:
public TSeries Update(TSeries source) - Placement: Must be adjacent to the
Update(TValue)method. - Logic:
- Create output series.
- Call static
Calculate(Span)for performance. - Restore internal state by replaying the last
Periodbars (or full series if recursive).
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
stackallocfor small buffers (threshold ~256). -
Implement a scalar fallback path that handles
NaNsafely. -
Implement a SIMD path for large, clean datasets (optional but recommended for simple averages).
4. Testing Standards
Unit Tests ([Name].Tests.cs)
-
Framework: xUnit
-
Data Generation: Use
GBM(Geometric Brownian Motion) for generating realistic test data. Avoid usingSystem.Randomdirectly. -
Coverage:
-
Constructor validation (invalid params).
-
Basic calculation correctness (compare against manual calc).
-
isNew=truevsisNew=falsebehavior (bar correction). -
Reset()functionality. -
IsHotproperty behavior. -
NaN/Infinityhandling (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 ALL available external libraries (Skender, TA-Lib, Tulip, OoplesFinance, Python libraries, etc.) where the indicator is implemented. You must actively search for existing implementations to validate against.
-
Data: Use
GBM(Geometric Brownian Motion) to generate realistic test data. -
Scenarios:
-
Batch processing.
-
Streaming processing.
-
Span/Vector processing.
-
Tolerance: Typically
1e-6or1e-9depending 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*Itemor* Item). - MD032: Ensure lists are surrounded by blank lines (one blank line before the first item and one after the last item).
Template structure:
- Title & Overview: What is it? What does it do?
- Core Concepts: Key features (e.g., equal weighting, noise reduction).
- Parameters: Table of constructor parameters.
- Formula: LaTeX formatted math (
...). - C# Implementation: Code examples for:
- Standard usage.
- Span API (high performance).
- Bar correction (
isNew). - Eventing.
- Interpretation: How to use it in trading.
- References: Books or papers.
6. Quantower Adapter
- Implementation: Create a wrapper class in
[Name].Quantower.csthat adapts the QuanTAlib indicator for the Quantower platform. - Tests: Create unit tests in
[Name].Quantower.Tests.csto verify the adapter's functionality using mocks where necessary.
7. Code Review
- Tool: Run CodeRabbit on the changes.
- Requirement: Address and fix ALL issues identified by the CodeRabbit review before considering the task complete.
8. 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
forloops overforeachfor arrays/spans. - Math: Use
System.MathorSystem.Numerics. Avoid LINQ in hot paths. - Memory: NEVER use
newinside theUpdatemethod. Pre-allocate everything in the constructor.
9. Checklist for New Indicators
- Source Material: Sourced algorithm and docs from
mihakralj/pinescriptormihakralj/quantalib? - File Structure: Created all 6 required files?
- Constructor: Validates inputs? Sets
Name? - Update: Handles
isNewcorrectly? HandlesNaN? O(1)? - Static API: Implemented
Calculate(Span)? - Tests: Unit tests pass?
NaNtests included? - Validation: Matches ALL available external libraries?
- Docs: Markdown file created with formula and examples? Linted (MD030, MD032)?
- Quantower: Adapter created in
[Name].Quantower.cs? - Quantower Tests: Adapter tests created in
[Name].Quantower.Tests.cs? - Code Review: Ran CodeRabbit and fixed all issues?
- Index: Added to category
_index.mdwith link and description? - Performance: No allocations in
Update?[SkipLocalsInit]used?