Files
QuanTAlib/docs/api.md
T
Miha Kralj 86fe32a682 SIMD Refactor: Merge simd-dev into dev (#55)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: aider (openrouter/anthropic/claude-sonnet-4) <aider@aider.chat>
Co-authored-by: Warp <agent@warp.dev>
2026-01-18 19:02:03 -08:00

269 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# API Reference
QuanTAlib employs a Tri-Modal Architecture to unify high-performance batch processing with low-latency streaming updates. This design addresses the fundamental tension in quantitative finance: backtesting requires processing millions of bars quickly, while live trading requires sub-microsecond updates with zero allocation.
All indicators inherit from `AbstractBase` and implement `ITValuePublisher`. The API is consistent across every indicator in the library.
## Core Interface
Every indicator exposes these properties and methods:
### Properties
| Property | Type | Description |
| :------- | :--- | :---------- |
| `Name` | `string` | Descriptive identifier (e.g., `"Sma(14)"`) |
| `Last` | `TValue` | Most recent calculated value (timestamp + value) |
| `IsHot` | `bool` | True when enough data has accumulated for valid output |
| `WarmupPeriod` | `int` | Samples required before `IsHot` becomes true |
| `Pub` | `event` | Fires when a new value is calculated (reactive mode) |
### Methods
| Method | Signature | Purpose |
| :----- | :-------- | :------ |
| `Update` | `TValue Update(TValue input, bool isNew = true)` | Process single value (streaming) |
| `Batch` | `static void Batch(ReadOnlySpan<double>, Span<double>, int)` | Bulk calculation (batch) |
| `Prime` | `void Prime(ReadOnlySpan<double> source)` | Initialize state from history |
| `Reset` | `void Reset()` | Clear state, return to initial condition |
## Mode A: Batch Processing
**Use case:** Backtesting, historical analysis, optimization runs.
Batch mode provides stateless, SIMD-accelerated processing. Zero heap allocation. Maximum throughput.
### Span-Based (Zero Allocation)
The fastest path. Operates directly on memory spans. Uses AVX2/AVX-512/NEON instructions where available.
```csharp
double[] prices = LoadHistoricalData(); // 1,000,000 bars
double[] results = new double[prices.Length];
// In-place calculation, zero allocation
Sma.Batch(prices.AsSpan(), results.AsSpan(), period: 14);
```
Performance: 500,000 bars in ~320 ¼s on modern hardware.
### TSeries-Based (Convenience)
Wrapper for timestamp-aligned series. Slightly slower, but handles metadata.
```csharp
TSeries history = LoadTimeSeriesData();
TSeries smaValues = Sma.Batch(history, period: 14);
// Timestamps preserved and aligned
Console.WriteLine($"SMA at {smaValues[100].Time}: {smaValues[100].Value}");
```
## Mode B: Streaming Updates
**Use case:** Live trading, real-time charting, tick-by-tick processing.
Streaming mode handles sequential data with O(1) complexity per update. Internal state (circular buffers, running sums) enables sub-microsecond latency.
### Standard Update
```csharp
var sma = new Sma(period: 14);
foreach (var bar in liveDataFeed)
{
TValue result = sma.Update(new TValue(bar.Time, bar.Close));
if (result.IsHot)
{
// Valid output, safe to use for trading decisions
ProcessSignal(result.Value);
}
}
```
### Bar Correction (isNew Parameter)
Markets send corrections. The `isNew` parameter distinguishes between new bars and updates to the current bar.
```csharp
var indicator = new Sma(14);
// Bar opens at 09:30:00
indicator.Update(new TValue(time_0930, 100.0), isNew: true);
// Ticks update current bar (same timestamp)
indicator.Update(new TValue(time_0930, 100.5), isNew: false); // Correction
indicator.Update(new TValue(time_0930, 101.2), isNew: false); // Correction
indicator.Update(new TValue(time_0930, 100.8), isNew: false); // Correction
// New bar opens at 09:31:00
indicator.Update(new TValue(time_0931, 101.0), isNew: true);
```
When `isNew: false`, the indicator rolls back to the previous state before applying the new value. No cumulative drift from repeated corrections.
### Reactive Chaining
Indicators subscribe to `ITValuePublisher` sources. Updates propagate automatically.
```csharp
var source = new TSeries();
var sma = new Sma(source, period: 14); // Subscribes to source
var ema = new Ema(sma, period: 5); // Subscribes to sma
// Single add triggers cascade: source sma ema
source.Add(new TValue(DateTime.UtcNow, 100.0));
```
Event-based chaining enables complex indicator pipelines without manual orchestration.
## Mode C: Priming
**Use case:** Transitioning from historical data to live streaming.
Priming hydrates a streaming instance using the minimal tail of historical data. The indicator processes only enough history to fill its internal buffers, then becomes ready for live updates.
```csharp
var indicator = new Sma(period: 14);
double[] history = LoadHistoricalData(); // 100,000 bars
// Processes only the last ~14 bars needed for state
// O(warmup) initialization instead of O(history)
indicator.Prime(history.AsSpan());
Console.WriteLine(indicator.IsHot); // true
Console.WriteLine(indicator.Last); // Contains valid value
// Ready for live data
indicator.Update(nextLiveTick);
```
## One-Shot Hybrid (Calculate)
Combines batch processing with priming. Returns both the complete historical results and a warmed-up instance ready for streaming.
```csharp
TSeries history = LoadTimeSeriesData();
// Single call: batch process history + prime indicator
var (results, indicator) = Sma.Calculate(history, period: 14);
// 'results' contains full calculated history
PlotChart(results);
// 'indicator' is warmed up, ready for live updates
indicator.Update(nextLiveTick);
```
This pattern eliminates the common mistake of running batch calculation, then creating a separate streaming instance that starts cold.
## Validity and Convergence
### IsHot Property
`IsHot` indicates whether the indicator has accumulated enough data for mathematically valid output.
| Indicator Type | Warmup Period | IsHot Becomes True |
| :------------- | :------------ | :----------------- |
| Fixed-window (SMA, RSI) | `period` | After `period` bars |
| Recursive (EMA, MACD) | `~3 × period` | After convergence threshold |
| Multi-component (Bollinger) | `max(period, stddev_period)` | After longest component |
### Streaming Context
```csharp
var sma = new Sma(10);
for (int i = 0; i < 15; i++)
{
var result = sma.Update(new TValue(DateTime.UtcNow, prices[i]));
Console.WriteLine($"Bar {i}: IsHot = {result.IsHot}");
}
// Bars 0-8: IsHot = false
// Bars 9-14: IsHot = true
```
### Batch Context
Batch output contains "cold" values at the beginning. The number of cold values equals `WarmupPeriod`.
```csharp
TSeries results = Sma.Batch(history, period: 14);
// Skip cold values when analyzing
for (int i = 13; i < results.Count; i++) // Start at index 13 (period - 1)
{
ProcessValidValue(results[i]);
}
```
For recursive indicators (EMA, DEMA, T3), the warmup extends beyond the nominal period. A 14-period EMA requires approximately 42 bars (3 × period) to converge within 1% of the true value.
## Architecture Diagram
```text

 QuanTAlib Tri-Modal API 
$
 
 Historical Data Live Data 
   
 ¼  
   
  Batch    
  Mode    
 ,   
    
  Results   
 ¼ ¼ ¼ 
   
 Backtest   Streaming  
  Output   Mode  
  , 
  
 $ 
   
 ¼ ¼ 
   
  Prime   IsHot  
  Mode  Hydrate State  = true  
   
 

```
## Error Handling
### Invalid Parameters
Constructors validate parameters and throw `ArgumentException` with the parameter name.
```csharp
try
{
var sma = new Sma(period: 0); // Invalid
}
catch (ArgumentException ex)
{
// ex.ParamName == "period"
// ex.Message contains "must be greater than 0"
}
```
### NaN and Infinity Handling
When input contains non-finite values (`NaN`, `+`, `-`), QuanTAlib substitutes the last valid value. This prevents NaN propagation through indicator chains.
```csharp
var sma = new Sma(14);
sma.Update(new TValue(t1, 100.0)); // Valid
sma.Update(new TValue(t2, double.NaN)); // Uses 100.0 internally
sma.Update(new TValue(t3, 101.0)); // Valid
```
The output never contains NaN unless all inputs were NaN.
## Thread Safety
QuanTAlib indicators are not thread-safe. Each indicator instance should be accessed from a single thread. For multi-threaded scenarios, create separate indicator instances per thread or implement external synchronization.