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

9.6 KiB
Raw Blame History

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.

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.

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

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.

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.

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.

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.

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

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.

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


                     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.

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.

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.