Files
Miha Kralj eb9e41fc2e feat: add RRSI (Rocket RSI) — Ehlers TASC May 2018
Algorithm: SuperSmoother-filtered momentum → Ehlers RSI → Fisher Transform
- 2-pole Butterworth IIR pre-filter removes noise
- Ehlers RSI (raw summation, not Wilder) outputs [-1,1]
- arctanh produces Gaussian-distributed zero-mean oscillator

Files: Rrsi.cs, Rrsi.Quantower.cs, Rrsi.md, 31+7 tests
Integration: sidebar, indices, Python bridge (Exports, _bridge, oscillators, SPEC)
Build: 0 warnings, 0 errors | Tests: 15,963 passed, 0 failed
2026-03-17 09:25:32 -07:00

45 KiB
Raw Permalink Blame History

quantalib Python NativeAOT Wrapper — Specification v2

1) Objective

Deliver a Python package named quantalib that exposes the entire QuanTAlib indicator library through a stable, versioned C ABI compiled via .NET NativeAOT. Python ergonomics follow pandas-ta conventions where practical.

Primary goals

  • Near-zero-copy interop via ctypes + contiguous NumPy buffers (zero-copy when inputs are already C-contiguous float64; otherwise one normalization copy is allowed)
  • Stable C ABI surface (qtl_*) with explicit major-version evolution policy for future non-Python consumers
  • Full library coverage target: all supported Batch methods exported using a generated manifest and validated rollout gates
  • pandas-ta-compatible function signatures for common indicators

Non-goals (initial release)

  • Rewriting indicator math in Python
  • pybind11 / Cython extension modules
  • GPU acceleration
  • Streaming / stateful Python classes per indicator
  • Alternate numeric dtypes (float32, decimal)
  • Full pandas-ta edge-case parity on day one

2) Scope

In scope

  • python/ directory at repository root
  • NativeAOT shared library (quantalib.dll / quantalib.so / quantalib.dylib)
  • Single Exports.cs mapping all Batch methods to [UnmanagedCallersOnly] exports
  • Python package quantalib with loader, bridge, and indicator wrappers
  • Compatibility matrix documenting pandas-ta parity levels
  • CI matrix for win-x64, linux-x64, osx-arm64, osx-x64

Out of scope

  • Windows ARM64 packaging
  • Async / streaming Python APIs
  • TBarSeries / BatchInputs overloads (use flat span-based overloads only)

3) Design requirements

  1. ABI correctness — pointer, length, and parameter contracts are explicit and validated on both sides of the boundary.
  2. Performance — call QuanTAlib Batch methods directly; no managed allocations in the export shim beyond span construction.
  3. ABI stability — export names are prefixed (qtl_*) and versioned; existing symbols never change within a major version.
  4. Deterministic behavior — warmup slots filled with NaN; invalid inputs produce defined status codes, never exceptions.
  5. pandas-ta compatibility — Python names and signatures mirror pandas-ta where feasible, with documented deltas.
  6. Thread safety — exports are designed to be stateless and reentrant; concurrent calls are supported when each call uses distinct buffers. This must be validated by stress tests in CI.

4) Repository layout

python/
  SPEC.md                          # this file
  python.csproj                    # NativeAOT shared lib project
  Directory.Build.props            # local build overrides
  publish.ps1                      # multi-RID publish + stage script
  pyproject.toml                   # Python package metadata
  README.md                        # usage docs + indicator list

  src/
    StatusCodes.cs                 # QTL_OK / QTL_ERR_* constants
    ArrayBridge.cs                 # unsafe ptr→span helpers + validation
    Exports.cs                     # ALL [UnmanagedCallersOnly] entry points

  quantalib/
    __init__.py                    # re-export indicators; __version__
    _loader.py                     # platform+arch dispatch; ctypes.CDLL
    _bridge.py                     # ctypes argtypes/restype declarations
    indicators.py                  # pandas-ta-compatible wrappers
    _compat.py                     # pandas-ta name aliases + helpers
    py.typed                       # PEP 561 marker

    native/
      win_amd64/.gitkeep
      linux_x86_64/.gitkeep
      macosx_arm64/.gitkeep
      macosx_x86_64/.gitkeep

  tests/
    test_smoke.py                  # import + load + call one indicator
    test_shapes.py                 # output length == input length
    test_status_codes.py           # null ptr, bad length, bad params
    test_golden.py                 # golden-value vs managed QuanTAlib
    test_compat.py                 # pandas-ta parity subset

Design note: Exports.cs and indicators.py stay as single flat files. Internal splitting is allowed later without changing any public API or ABI.


5) Native ABI contract

5.1 Calling convention

  • All exports use [UnmanagedCallersOnly(EntryPoint = "qtl_<name>")].
  • All exports return int status code.
  • All pointer parameters use C ABI-compatible primitive types only.
  • No managed exceptions ever cross the ABI boundary. Every export wraps its body in a try/catch that returns QTL_ERR_INTERNAL on unhandled exceptions.

5.2 Status codes

Code Name Meaning
0 QTL_OK Success
1 QTL_ERR_NULL_PTR Required pointer is null
2 QTL_ERR_INVALID_LENGTH n <= 0 or output length mismatch
3 QTL_ERR_INVALID_PARAM Parameter out of valid range
4 QTL_ERR_INTERNAL Unhandled exception in managed code

Failure guarantees:

  • For validation failures (QTL_ERR_NULL_PTR, QTL_ERR_INVALID_LENGTH, QTL_ERR_INVALID_PARAM), outputs are untouched by contract.
  • For QTL_ERR_INTERNAL, output buffers are unspecified and must be treated as invalid by the caller.

5.3 ABI signature patterns

The ~290 Batch methods in QuanTAlib follow 9 distinct signature patterns. Each pattern maps to a specific C ABI template:

Pattern A: Single-input, single-output + scalar params

C# source: Sma.Batch(ReadOnlySpan<double> source, Span<double> output, int period)

C ABI:

int qtl_sma(double* src, int n, double* out, int period);

Indicators (~130): SMA, EMA, DEMA, TEMA, HMA, WMA, RSI, ROC, MOM, CMO, TSI, APO, PPO, DPO, TRIX, Fisher, Inertia, Zscore, StdDev, Variance, etc.

Pattern B: Multi-input HLCV, single-output

C# source: Mfi.Batch(ReadOnlySpan<double> high, ..low, ..close, ..volume, Span<double> output, int period)

C ABI:

int qtl_mfi(double* high, double* low, double* close, double* volume,
               int n, double* out, int period);

Indicators (~25): MFI, ADL, CMF, VWAP, VWAD, ADOsc, KVO, III, WAD, VA, EOM.

Pattern C: Multi-input OHLC (no volume), single-output

C# source: Bop.Batch(ReadOnlySpan<double> open, ..high, ..low, ..close, Span<double> destination)

C ABI:

int qtl_bop(double* open, double* high, double* low, double* close,
               int n, double* out);

Indicators (~20): BOP, ASI, BRAR, Avgprice, Typprice, Wclprice, Midbody, HA, ReverseEMA variants requiring OHLC, RVGI, DEM, etc.

Pattern D: Multi-input HL, single-output

C# source: Ao.Batch(ReadOnlySpan<double> high, ReadOnlySpan<double> low, Span<double> destination, int fast, int slow)

C ABI:

int qtl_ao(double* high, double* low, int n, double* out,
              int fast_period, int slow_period);

Indicators (~10): AO, AC, Medprice, Frama (HL overload), WillR, etc.

Pattern E: Multi-input HLC (no volume), single-output

C# source: Tr.Batch(ReadOnlySpan<double> high, ..low, ..close, Span<double> output)

C ABI:

int qtl_tr(double* high, double* low, double* close, int n, double* out);

Indicators (~15): TR, ATR, Etherm, GHLA, PGO, RWMA, etc.

Pattern F: Dual-input (actual + predicted), single-output

C# source: Mse.Batch(ReadOnlySpan<double> actual, ..predicted, Span<double> output, int period)

C ABI:

int qtl_mse(double* actual, double* predicted, int n, double* out, int period);

Indicators (~25): MSE, RMSE, MAE, MAPE, SMAPE, RMSLE, Rsquared, Huber, PseudoHuber, TheilU, WMAPE, LogCosh, QuantileLoss, TukeyBiweight, etc.

Pattern G: Dual-input (source + volume), single-output

C# source: Obv.Batch(ReadOnlySpan<double> close, ReadOnlySpan<double> volume, Span<double> output)

C ABI:

int qtl_obv(double* close, double* volume, int n, double* out);

Indicators (~12): OBV, PVT, PVR, VF, EFI, NVI, PVI, VWMA, EVWMA, TVI, PVD.

Pattern H: Dual-input statistics (seriesX + seriesY)

C# source: Correl.Batch(ReadOnlySpan<double> seriesX, ..seriesY, Span<double> output, int period)

C ABI:

int qtl_correlation(double* x, double* y, int n, double* out, int period);

Indicators (~6): Correlation, Covariance, Spearman, Kendall, Granger, Cointegration.

Pattern I: Multi-output indicators

C# source (example): Pvo.Batch(ReadOnlySpan<double> volume, Span<double> output, Span<double> signal, Span<double> histogram, ...)

C ABI: Each output gets its own pointer parameter:

int qtl_pvo(double* volume, int n,
               double* out_pvo, double* out_signal, double* out_hist,
               int fast, int slow, int signal_period);

Multi-output indicators:

Indicator Outputs Names
PVO 3 pvo, signal, histogram
PMA 2 pma, trigger
MAMA 2 mama, fama
HtPhasor 2 inPhase, quadrature
HtSine 2 sine, leadSine
AOBV 2 fast, slow
AMAT 2 trend, strength
Stoch 2 K, D
Stochf 2 K, D
KDJ 3 K, D, J
SMI 2+ smi, signal
BBands 3 upper, middle, lower
KC 3 upper, middle, lower
DCChannel 3 upper, middle, lower
AccBands 3 upper, middle, lower
AtrBands 3 upper, middle, lower
All channel indicators 3 upper, middle, lower
Vortex 2 viPlus, viMinus
Aroon 1* combined

5.4 Special parameter mappings

Several Batch methods use types that cannot cross the C ABI directly. Each requires a specific mapping:

C# type C ABI type Strategy
enum StcSmoothing int 0 = SMA, 1 = EMA
enum WindowType (Afirma) int Map enum ordinals
bool (isPopulation, annualize, etc.) int 0 = false, 1 = true
int[]? lengths (Cfb) int* lengths, int lengths_count Null-safe; null → use defaults
double[] kernel (Conv) double* kernel, int kernel_len Caller allocates
ReadOnlySpan<long> (Solar, Lunar) long* timestamps Direct pointer cast
TBarSeries overloads Skip Use flat span-based overload instead
BatchInputs/BatchOutput struct overloads Skip Use flat span-based overload instead

5.5 Dual overloads (period vs alpha)

Many IIR filters expose both Batch(src, out, int period) and Batch(src, out, double alpha). Export strategy:

  • Primary export: qtl_ema — takes int period
  • Alpha export: qtl_ema_alpha — takes double alpha

Both are exported; Python wrapper defaults to the period variant.

5.6 Validation in each export

Every export function validates before calling the inner Batch:

  1. Null checks for all required pointer parameters → QTL_ERR_NULL_PTR
  2. n > 0QTL_ERR_INVALID_LENGTH
  3. Range checks on scalar parameters (e.g., period > 0) → QTL_ERR_INVALID_PARAM
  4. Semantic checks (e.g., fastPeriod < slowPeriod where required) → QTL_ERR_INVALID_PARAM
  5. try/catch around Batch call → QTL_ERR_INTERNAL on any managed exception

5.7 Memory ownership rules

  • Input pointers are read-only by contract (cast to ReadOnlySpan<double>).
  • Caller allocates all output buffers with length n.
  • Native layer writes exactly n values per output buffer on success.
  • No ownership transfer across boundary. No heap allocation in the shim.

5.8 ABI versioning policy

  • All exports use the flat qtl_ prefix for v1.
  • Existing published symbols are immutable within ABI major v1.
  • New optional parameters require new symbol names (e.g. qtl_ema_alpha).
  • Deprecation policy: old symbols remain exported for at least one minor release after replacement and are marked deprecated in Python wrappers.
  • First breaking ABI revision introduces qtl2_ prefix and parallel support window.

5.9 Export manifest (authoritative source)

To reduce drift across Exports.cs, _bridge.py, and indicators.py, maintain one machine-readable manifest (e.g., JSON/YAML) containing:

  • indicator name and export symbol
  • input pattern (AI or special)
  • parameter schema (name, type, defaults, constraints)
  • output schema (count, names, order)

Generated artifacts from this manifest are preferred over hand-maintained parallel lists.

5.10 Concrete export example

// StatusCodes.cs
file static class StatusCodes
{
    public const int QTL_OK = 0;
    public const int QTL_ERR_NULL_PTR = 1;
    public const int QTL_ERR_INVALID_LENGTH = 2;
    public const int QTL_ERR_INVALID_PARAM = 3;
    public const int QTL_ERR_INTERNAL = 4;
}

// Exports.cs (excerpt)
[UnmanagedCallersOnly(EntryPoint = "qtl_sma")]
public static unsafe int Sma(double* src, int n, double* output, int period)
{
    if (src == null || output == null) return StatusCodes.QTL_ERR_NULL_PTR;
    if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
    if (period <= 0) return StatusCodes.QTL_ERR_INVALID_PARAM;
    try
    {
        var srcSpan = new ReadOnlySpan<double>(src, n);
        var outSpan = new Span<double>(output, n);
        QuanTAlib.Sma.Batch(srcSpan, outSpan, period);
        return StatusCodes.QTL_OK;
    }
    catch { return StatusCodes.QTL_ERR_INTERNAL; }
}

[UnmanagedCallersOnly(EntryPoint = "qtl_bbands")]
public static unsafe int Bbands(
    double* src, int n,
    double* outUpper, double* outMiddle, double* outLower,
    int period, double multiplier)
{
    if (src == null || outUpper == null || outMiddle == null || outLower == null)
        return StatusCodes.QTL_ERR_NULL_PTR;
    if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
    if (period <= 0) return StatusCodes.QTL_ERR_INVALID_PARAM;
    try
    {
        var srcSpan = new ReadOnlySpan<double>(src, n);
        var upper = new Span<double>(outUpper, n);
        var middle = new Span<double>(outMiddle, n);
        var lower = new Span<double>(outLower, n);
        QuanTAlib.Bbands.Batch(srcSpan, upper, middle, lower, period, multiplier);
        return StatusCodes.QTL_OK;
    }
    catch { return StatusCodes.QTL_ERR_INTERNAL; }
}

6) Python contract

6.1 Package identity

Property Value
PyPI name quantalib
Import name quantalib
AssemblyName quantalib

6.2 Loader (_loader.py)

  • Detect OS + architecture at import time.
  • Load bundled shared library from quantalib/native/<platform>/.
  • Platform directory mapping:
Platform Directory
Windows x64 native/win_amd64/quantalib.dll
Linux x64 native/linux_x86_64/quantalib.so
macOS ARM64 native/macosx_arm64/quantalib.dylib
macOS x64 native/macosx_x86_64/quantalib.dylib
  • On failure: raise OSError with actionable message listing expected path and detected platform.
  • Use explicit package-path loading; never rely on system library search order.

6.3 Bridge (_bridge.py)

  • Declare argtypes and restype for every native function at module level.
  • Validate function presence at import time (graceful AttributeError → skip).
  • Input normalization: np.ascontiguousarray(arr, dtype=np.float64).
  • Status code checking: helper raises Python exception mapped from return code:
class QtlError(Exception):
    """Base exception for quantalib native errors."""

class QtlNullPointerError(QtlError): ...    # status 1
class QtlInvalidLengthError(QtlError): ...  # status 2
class QtlInvalidParamError(QtlError): ...   # status 3
class QtlInternalError(QtlError): ...       # status 4

6.4 Indicator wrappers (indicators.py)

  • Expose pandas-ta-compatible function signatures where practical.
  • Return types adapt to the input container type ("same-type-in, same-type-out"):
Input type Output (single) Output (multi)
np.ndarray np.ndarray tuple of np.ndarray
pd.Series pd.Series pd.DataFrame
pd.DataFrame pd.Series (1st col) pd.DataFrame
pl.Series pl.Series pl.DataFrame
pl.DataFrame pl.Series (1st col) pl.DataFrame
pa.Array pa.Array (float64) dict[str, pa.Array]
pa.ChunkedArray pa.Array (float64) dict[str, pa.Array]
  • Series names follow pandas-ta conventions: SMA_14, BBU_20_2.0, etc.
  • DataFrame columns for multi-output: BBU_20_2.0, BBM_20_2.0, BBL_20_2.0.
  • Polars Series .name is set to the indicator name (e.g. "SMA_14").
  • PyArrow arrays are always returned as pa.float64() type.

6.5 Concrete Python wrapper example

# indicators.py (excerpt)
import numpy as np
import pandas as pd
from quantalib._bridge import _lib, _check

def sma(close, length=None, offset=None, **kwargs):
    """Simple Moving Average."""
    length = int(length) if length else 10
    offset = int(offset) if offset else 0

    # Extract numpy array
    index = None
    if isinstance(close, pd.Series):
        index = close.index
        close = close.to_numpy(dtype=np.float64, copy=False)
    close = np.ascontiguousarray(close, dtype=np.float64)

    n = len(close)
    out = np.empty(n, dtype=np.float64)
    _check(_lib.qtl_sma(
        close.ctypes.data_as(c_double_p), n,
        out.ctypes.data_as(c_double_p), length
    ))

    if offset:
        out = np.roll(out, offset)
        out[:offset] = np.nan

    if index is not None:
        result = pd.Series(out, index=index, name=f"SMA_{length}")
        result.category = "trend"
        return result
    return out

6.6 NaN / warmup behavior

  • Warmup slots are filled with NaN by the C# Batch methods.
  • This matches pandas-ta's convention for most indicators.
  • Known deltas from pandas-ta warmup lengths are documented in the compatibility table in README.md.

6.7 Optional dependency policy

numpy is the only hard dependency. All DataFrame libraries are optional extras:

Extra Install command Enables
pandas pip install quantalib[pandas] pd.Series / pd.DataFrame I/O
polars pip install quantalib[polars] pl.Series / pl.DataFrame I/O
pyarrow pip install quantalib[pyarrow] pa.Array / pa.ChunkedArray I/O
all pip install quantalib[all] All of the above

When a library is not installed, its input types are silently unsupported: the _arr() helper falls through to np.asarray(), which may produce an error or unexpected result. Because detection uses isinstance, there is no import overhead when a library is absent.

Origin token pattern: _arr() returns an opaque _Origin token that _wrap() / _wrap_multi() use to reconstruct the original container type. Category modules never inspect this token — they pass it through unchanged. This keeps all 15 category modules free of any polars/pyarrow awareness.


7) Build and packaging

7.1 .NET project (python.csproj)

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <OutputType>Library</OutputType>
    <PublishAot>true</PublishAot>
    <AssemblyName>quantalib</AssemblyName>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    <InvariantGlobalization>true</InvariantGlobalization>
    <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="src\**\*.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\lib\quantalib.csproj" />
  </ItemGroup>
</Project>

7.2 Local build props (Directory.Build.props)

<Project>
  <PropertyGroup>
    <IlcOptimizationPreference>Speed</IlcOptimizationPreference>
    <RunAnalyzers>false</RunAnalyzers>
    <ErrorReport>none</ErrorReport>
  </PropertyGroup>
</Project>

7.3 Publish script (publish.ps1)

Requirements:

  • Publish for RIDs: win-x64, linux-x64, osx-arm64, osx-x64
  • Clean target directory before each publish
  • Verify expected artifact exists per RID (.dll, .so, .dylib)
  • Copy artifact into quantalib/native/<platform>/
  • Exit non-zero on any missing artifact

7.4 Python packaging (pyproject.toml)

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "quantalib"
version = "0.1.0"
description = "High-performance technical analysis via QuanTAlib NativeAOT"
requires-python = ">=3.10"
dependencies = ["numpy>=1.24"]

[project.optional-dependencies]
pandas = ["pandas>=1.5"]
dev = ["pytest", "pandas>=1.5"]

[tool.hatch.build.targets.wheel]
packages = ["quantalib"]

7.5 NativeAOT considerations

  • Trimming: NativeAOT trims unreachable code. All Batch methods are reachable via explicit calls in Exports.cs, so no rd.xml needed.
  • Reflection: QuanTAlib indicators do not use reflection in Batch paths.
  • SIMD: NativeAOT preserves hardware intrinsics. Avx2.IsSupported works at runtime in the compiled native binary.
  • Startup: NativeAOT has near-zero startup overhead. No JIT warmup.

8) Full indicator catalog by category

8.1 Core (7 indicators)

Indicator C# Class Pattern Params
avgprice Avgprice C (OHLC)
ha Ha C (OHLC)
medprice Medprice D (HL)
midbody Midbody C (OC)
midpoint Midpoint A period
midprice Midprice D (HL) period
typprice Typprice C (OHLC)
wclprice Wclprice E (HLC)

8.2 Momentum (18 indicators)

Indicator C# Class Pattern Params
asi Asi C (OHLC) period, limitMove
bias Bias A period
bop Bop C (OHLC)
cfb Cfb A + int[]* lengths
cmo Cmo A period
macd Macd A fastPeriod, slowPeriod
mom Mom A period
pmo Pmo A timePeriods, smoothPeriods, signalPeriods
ppo Ppo A fastPeriod, slowPeriod
rs Rs H (dual) period
roc Roc A period
rocp Rocp A period
rocr Rocr A period
rsi Rsi A period
rsx Rsx A period
sam Sam A alpha, cutoff
tsi Tsi A longPeriod, shortPeriod
vel Vel A period
vwmacd Vwmacd multi fastPeriod, slowPeriod, signalPeriod

8.3 Oscillators (~35 indicators)

Indicator C# Class Pattern Key params
ac Ac D (HL) fastPeriod, slowPeriod, acPeriod
ao Ao D (HL) fastPeriod, slowPeriod
apo Apo A fastPeriod, slowPeriod
bbb Bbb A period, multiplier
bbi Bbi A periods...
bbs Bbs E (HLC) multi-output
brar Brar C (OHLC) period
cfo Cfo A period
coppock Coppock A roc1, roc2, wma
crsi Crsi A rsiPeriod, streakPeriod, rankPeriod
cti Cti A period
deco Deco A shortPeriod, longPeriod
dem Dem E (HLC) period
dpo Dpo A period
dstoch Dstoch E (HLC) period
dosc Dosc A rsiPeriod, ema1Period, ema2Period, sigPeriod
dymi Dymi A basePeriod, shortPeriod, longPeriod...
er Er A period
fisher Fisher A period, alpha
fisher04 Fisher04 A period
gator Gator A jawPeriod...
imi Imi C (OHLC) period
inertia Inertia A period
kdj Kdj I (HLC→3) period, kSmooth, dSmooth
kri Kri A period
kst Kst A multiple roc/ma periods
marketfi Marketfi D (HL) + vol
bw_mfi BwMfi D (HL) + vol → 2
mstoch Mstoch A period
pgo Pgo E (HLC) period
psl Psl A period
qqe Qqe A rsiPeriod, smoothFactor, qqeFactor
reflex Reflex A period
reverseema ReverseEma A period
rvgi Rvgi C (OHLC) period
rrsi Rrsi A smoothLength, rsiLength
smi Smi I (HLC→2+) period, smoothK, smoothD
squeeze Squeeze I (HLC→multi) bbPeriod, kcPeriod...
squeeze_pro SqueezePro I (HLC→multi) period, bbMult, kcMultWide/Normal/Narrow
stc Stc A + enum kPeriod, dPeriod, fastLen, slowLen, smoothing(int)
stoch Stoch I (HLC→2) kPeriod, kSmooth, dSmooth
stochf Stochf I (HLC→2) kPeriod, dPeriod
stochrsi Stochrsi A rsiLen, stochLen, kSmooth, dSmooth
td_seq Td_seq C (OHLC)
trendflex Trendflex A period
trix Trix A period
ttm_wave TtmWave I (HLC→multi)
ultosc Ultosc E (HLC) period1, period2, period3
willr Willr E (HLC) period
Indicator C# Class Pattern Key params
alma Alma A period, offset, sigma
blma Blma A period
bwma Bwma A period, order
conv Conv A + double[]* kernel
crma Crma A period
dwma Dwma A period
fwma Fwma A period
gwma Gwma A period, sigma
hamma Hamma A period
hanma Hanma A period
hend Hend A period
hma Hma A period
ilrs Ilrs A period
kaiser Kaiser A period, beta
lanczos Lanczos A period
lsma Lsma A period, offset
nlma Nlma A period
nyqma Nyqma A period, nyquistPeriod
parzen Parzen A period
pma Pma A / I (2) period
pwma Pwma A period
qrma Qrma A period
rain Rain A period
rwma Rwma D (CHL) period
sgma Sgma A period, degree
sinema Sinema A period
sma Sma A period
sp15 Sp15 A
swma Swma A period
trima Trima A period
tsf Tsf A period
tukey_w Tukey_w A period, alpha
wma Wma A period
Indicator C# Class Pattern Key params
ahrens Ahrens A period
coral Coral A period, cd
decycler Decycler A period
dema Dema A (+ alpha) period
dsma Dsma A period
ema Ema A (+ alpha) period
frama Frama A / D (HL) period
gdema Gdema A period, vfactor
hema Hema A period
holt Holt A period, gamma
ht_trendline HtTrendline A
hwma Hwma A period
jma Jma A period, phase, power
kama Kama A period, fastPeriod, slowPeriod
lema Lema A (+ alpha) period
ltma Ltma A period
mama Mama I (2) fastLimit, slowLimit
mavp Mavp H (src+periods) minPeriod, maxPeriod
mcnma Mcnma A (+ alpha) period
mgdi Mgdi A period, k
mma Mma A period
nma Nma A period
qema Qema A period
rema Rema A period, lambda
rgma Rgma A period, passes
rma Rma A period
t3 T3 A period, vfactor
tema Tema A (+ alpha) period
trama Trama A period
vama Vama A period
vidya Vidya A period
yzvama Yzvama A period
zldema Zldema A (+ alpha) period
zlema Zlema A (+ alpha) period
zltema Zltema A (+ alpha) period

8.6 Channels (~20 indicators)

All channel indicators output 3 spans: upper, middle, lower (Pattern I).

Indicator C# Class Input pattern Key params
abber Abber A period
accbands AccBands B (HLC) period
apchannel Apchannel D (HL) period
apz Apz B (HLC) period
atrbands AtrBands B (HLC) period, multiplier
bbands Bbands A period, multiplier
dc Dc D (HL) period
decaychannel Decaychannel B (HLC) period
fcb Fcb D (HL) period
hwc Hwc A→multi period, multiplier
jbands Jbands A period, phase, power
kc Kc B (HLC) period, multiplier
maenv Maenv A period, pct
mmchannel Mmchannel D (HL) period
pc Pc D (HL) period
regchannel Regchannel A period
sdchannel Sdchannel A period
starchannel Starchannel B (HLC) period
stbands Stbands B (HLC) period
ttm_lrc TtmLrc A period
ubands Ubands A period
uchannel Uchannel D (HL) period
vwapbands Vwapbands A + vol period
vwapsd Vwapsd A + vol period

8.7 Volatility (~20 indicators)

Indicator C# Class Pattern Key params
bbw Bbw A period, multiplier
bbwn Bbwn A period, multiplier, lookback
bbwp Bbwp A period, multiplier, lookback
ccv Ccv A period, method
cv Cv A period, alpha, beta
cvi Cvi A rocLength, smoothLength
etherm Etherm E (HLC) period
ewma Ewma A period, annualize, annualPeriods
gkv Gkv C (OHLC) period, annualPeriods
hlv Hlv D (HL) period, annualPeriods
hv Hv A period, annualPeriods
jvolty Jvolty A period, phase, power
jvoltyn Jvoltyn A period, phase, power
massi Massi A emaLength, sumLength
rsv Rsv C (OHLC) period, annualPeriods
rv Rv A period, annualPeriods
rvi Rvi A period
tr Tr E (HLC)
ui Ui A period
vov Vov A period, vovPeriod
vr Vr E (HLC) period
yzv Yzv C (OHLC) period, annualPeriods

8.8 Volume (~30 indicators)

Indicator C# Class Pattern Key params
ad Ad B (HLCV)
adosc Adosc B (HLCV) fastPeriod, slowPeriod
aobv Aobv I (CV→2)
cmf Cmf B (HLCV) period
efi Efi G (CV) period
eom Eom B (HLV) period, volumeScale
evwma Evwma G (SV) period
iii Iii B (HLCV) period
kvo Kvo B (HLCV) fastPeriod, slowPeriod
mfi Mfi B (HLCV) period
nvi Nvi G (CV) startValue
obv Obv G (CV)
pvd Pvd G (CV) pricePeriod, volumePeriod, smoothingPeriod
pvi Pvi G (CV) startValue
pvo Pvo I (V→3) fastPeriod, slowPeriod, signalPeriod
pvr Pvr G (PV)
pvt Pvt G (CV)
tvi Tvi G (PV) minTick
twap Twap A period
va Va B (HLCV)
vf Vf G (CV) period
vo Vo A (volume) shortPeriod, longPeriod
vroc Vroc A (volume) period, usePercent
vwad Vwad B (HLCV) period
vwap Vwap B (HLCV) period
vwma Vwma G (SV) period
wad Wad B (HLCV)

8.9 Statistics (~30 indicators)

Indicator C# Class Pattern Key params
acf Acf A period, lag
cma Cma A
correl Correl H period
covariance Covariance H period, isPopulation
entropy Entropy A period
geomean Geomean A period
granger Granger H period, maxlag
harmean Harmean A period
hurst Hurst A period
iqr Iqr A period
jb Jb A period
kendall Kendall H period
kurtosis Kurtosis A period, isPopulation
linreg LinReg A period, offset
meandev MeanDev A period
median Median A period
mode Mode A period
pacf Pacf A period, lag
percentile Percentile A period, percent
polyfit Polyfit A period, degree
quantile Quantile A period, quantileLevel
skew Skew A period, isPopulation
spearman Spearman H period
stddev StdDev A period, isPopulation
stderr Stderr A period
sum Sum A period
theil Theil A period
trim Trim A period, trimPct
variance Variance A period, isPopulation
wavg Wavg A period
wins Wins A period, winPct
zscore Zscore A period
ztest Ztest A period, mu0
cointegration Cointegration H
convexity Convexity multi period

8.10 Errors (~25 indicators)

Indicator C# Class Pattern Key params
huber Huber F period, delta
logcosh LogCosh F period
mae Mae F period
maape Maape F period
mape Mape F period
mapd Mapd F period
mase Mase F period
mdae Mdae F period
mdape Mdape F period
me Me F period
mpe Mpe F period
mrae Mrae F period
mse Mse F period
msle Msle F period
pseudohuber PseudoHuber F period, delta
quantileloss QuantileLoss F period, quantile
rae Rae F period
rmse Rmse F period
rmsle Rmsle F period
rse Rse F period
rsquared Rsquared F period
smape Smape F period
theilu TheilU F period
tukeybiweight TukeyBiweight F period, c
wmape Wmape F period
wrmse Wrmse F period (+ weighted variant)

8.11 Filters (~30 indicators)

Indicator C# Class Pattern Key params
agc Agc A decay
alaguerre ALaguerre A length, medianLength
baxterking BaxterKing A pLow, pHigh, k
bessel Bessel A length
bilateral Bilateral A period, sigmaSRatio, sigmaRMult
bpf Bpf A lowerPeriod, upperPeriod
butter2 Butter2 A period
butter3 Butter3 A period
cfitz Cfitz A pLow, pHigh
cheby1 Cheby1 A period, ripple
cheby2 Cheby2 A period, attenuation
edcf Edcf A length
elliptic Elliptic A period
gauss Gauss A sigma
hann Hann A length
hp Hp A lambda
hpf Hpf A length
kalman Kalman A q, r (or period, gain)
laguerre Laguerre A gamma
lms Lms A order, mu
loess Loess A period
modf Modf A period, beta, feedback, fbWeight
notch Notch A period, q
nw Nw A period, bandwidth
oneeuro OneEuro A minCutoff, beta, dCutoff
rls Rls A order, lambda
rmed Rmed A period
roofing Roofing A hpLength, ssLength
sgf Sgf A period, polyOrder
spbf Spbf A shortPeriod, longPeriod, rmsPeriod
ssf2 Ssf2 A period
ssf3 Ssf3 A period
usf Usf A period
voss Voss A period, predict, bandwidth
wavelet Wavelet A levels, threshMult
wiener Wiener A period, smoothPeriod

8.12 Cycles (~14 indicators)

Indicator C# Class Pattern Key params
ccor Ccor A period, threshold
ccyc Ccyc A alpha
cg Cg A period
dsp Dsp A period
eacp Eacp A minPeriod, maxPeriod, avgLength, enhance
ebsw Ebsw A hpLength, ssfLength
homod Homod A minPeriod, maxPeriod
ht_dcperiod HtDcperiod A
ht_dcphase HtDcphase A
ht_phasor HtPhasor I (→2)
ht_sine HtSine I (→2)
lunar Lunar Special (long*)
solar Solar Special (long*)
ssfdsp Ssfdsp A period

8.13 Dynamics (~10 indicators)

Indicator C# Class Pattern Key params
amat Amat I (→2) fastPeriod, slowPeriod
aroon Aroon D (HL) period
ghla Ghla E (HLC) period
ht_trendmode HtTrendmode A
pfe Pfe A period, smoothPeriod
ravi Ravi A shortPeriod, longPeriod
vhf Vhf A period
vortex Vortex I (HLC→2) period
qstick Qstick C (OC) period

8.14 Reversals (~10 indicators)

Indicator C# Class Pattern Key params
atrstop Atrstop E (HLC→1) period, multiplier
chandelier Chandelier C (OHLC→2) period, multiplier
ckstop Ckstop C (OHLC→2) period, multiplier
fractals Fractals D (HL→2) period
pivot Pivot D (HL→multi)
pivotcam Pivotcam E (HLC→multi)
pivotdem Pivotdem C (OHLC→multi)
pivotext Pivotext E (HLC→multi)
pivotfib Pivotfib E (HLC→multi)
pivotwood Pivotwood E (HLC→multi)
sar Sar C (OHLC) accelStart, accelMax
swings Swings D (HL→multi) period
ttm_scalper TtmScalper D (HL→multi) period
vstop Vstop E (HLC→1) period, multiplier

8.15 Numerics (~15 indicators)

Indicator C# Class Pattern Key params
accel Accel A
betadist Betadist A alpha, beta params
binomdist Binomdist A n, p
change Change A period
dwt Dwt A levels
expdist Expdist A lambda
exptrans Exptrans A
fdist Fdist A df1, df2
fft Fft A
gammadist Gammadist A alpha, beta
highest Highest A period
ifft Ifft A
jerk Jerk A
lineartrans Lineartrans A slope, intercept
lognormdist Lognormdist A mu, sigma
logtrans Logtrans A
lowest Lowest A period
normdist Normdist A mu, sigma
normalize Normalize A period
poissondist Poissondist A lambda
relu Relu A
sigmoid Sigmoid A k, x0
slope Slope A
sqrttrans Sqrttrans A
tdist Tdist A df
weibulldist Weibulldist A k, lambda

8.16 Forecasts (1 indicator)

Indicator C# Class Pattern Key params
afirma Afirma A + enum period, window(int), leastSquares(bool→int)

9) Compatibility strategy with pandas-ta

9.1 Compatibility levels

Level Description
L1 Signature-compatible: same parameter names and defaults
L2 Return-compatible: same return container shape and type
L3 Behavior-compatible: matching warmup NaN count, naming, core semantics

9.2 pandas-ta overlap indicators

These indicators exist in both pandas-ta and QuanTAlib. All target L2 minimum, L3 where QuanTAlib's algorithm matches:

Trend: SMA, EMA, DEMA, TEMA, HMA, WMA, KAMA, FWMA, T3, TRIMA, ALMA, LSMA, SINEMA, VIDYA, ZLEMA, FRAMA, HWMA

Momentum: RSI, ROC, MOM, CMO, MACD, PPO, TSI, RSX

Oscillators: STOCH, STOCHF, STOCHRSI, WILLR, AO, APO, TRIX, FISHER, DPO, ULTOSC, KDJ, QQE, SQUEEZE, STC, RVGI

Volatility: ATR, TR, BBANDS, BBW, MASSI, UI, EWMA (realized vol)

Volume: OBV, ADL, ADOSC, CMF, MFI, NVI, PVI, PVT, VWAP, VWMA, EFI, KVO

Statistics: ZSCORE, VARIANCE, STDDEV, SKEW, KURTOSIS, MEDIAN, ENTROPY, LINREG

9.3 Known deltas

  • pandas-ta uses talib=True to delegate to TA-Lib C library; quantalib uses its own C# implementations.
  • Warmup NaN counts may differ for recursive indicators (EMA, RSI) due to different convergence thresholds.
  • offset parameter: quantalib supports it via np.roll in the Python layer.
  • fillna/fill_method kwargs: supported in Python layer, not in native.

10) Testing protocol

10.1 Native ABI tests (C# xUnit in lib project)

  • Status-code contract tests for all exports
  • Null pointer, zero length, negative period → correct error codes
  • Known golden values vs managed Batch calls

10.2 Python tests (pytest)

Suite Description
test_smoke.py Import, load native lib, call SMA, check output is ndarray
test_shapes.py len(output) == len(input) for all single-output indicators
test_status_codes.py Null ptr, bad length, bad params raise correct exceptions
test_golden.py Compare outputs vs known golden values from managed QuanTAlib
test_compat.py pandas-ta parity for overlap indicators (Series/DataFrame)

10.3 Performance tests

Track per-indicator:

  • Python wrapper overhead (µs per call for 10k bars)
  • Throughput (rows/sec)
  • Compare vs pandas-ta for overlap indicators

Acceptance (initial baseline):

  • Median wrapper overhead does not exceed pandas-ta by more than 20% on overlap indicators where algorithms are directly comparable.
  • No unbounded memory growth across repeated runs.
  • Performance report is produced per release candidate and checked into CI artifacts.

11) CI and release

11.1 Build matrix

RID Runner Artifact
win-x64 windows-latest quantalib.dll
linux-x64 ubuntu-latest quantalib.so
osx-arm64 macos-14 quantalib.dylib
osx-x64 macos-13 quantalib.dylib

11.2 Pipeline stages

  1. dotnet publish -r <rid> -c Release per RID
  2. Stage artifacts into quantalib/native/<platform>/
  3. python -m pytest tests/ per platform
  4. Build wheel with python -m build
  5. Upload to PyPI (manual trigger for releases)

11.3 Release checklist

  • All RID artifacts present and loadable
  • All pytest suites pass on all platforms
  • Performance benchmark logged (no regressions)
  • CHANGELOG.md updated
  • Version bumped in pyproject.toml

12) Risk register

# Risk Impact Mitigation
1 ABI breakage on update High Versioned symbols (qtl_*) + ABI contract tests
2 NativeAOT trims used code High Explicit calls in Exports.cs guarantee reachability
3 Platform loader failures Medium Explicit package-path loading + startup diagnostics
4 Behavior drift vs pandas-ta Medium Compatibility suite + documented deltas
5 Enum/array params over ABI Medium int/ptr mapping with documented contracts
6 Large export surface (~290) Medium Code generation considered for Exports.cs if manual is too error-prone
7 Cross-compilation failures Medium CI matrix catches per-platform issues early
8 Thread safety Low Batch methods are stateless; no shared mutable state

13) Review checklist

  • Naming accepted: PyPI/import/binary all quantalib
  • Versioned ABI prefix accepted (qtl_*)
  • Flat structure accepted (Exports.cs, indicators.py)
  • Status-code model accepted (04)
  • 9 ABI signature patterns accepted
  • Special parameter mappings accepted (enum→int, bool→int, array→ptr+len)
  • Full indicator catalog reviewed
  • Compatibility levels accepted (L1/L2/L3)
  • RID matrix accepted (4 platforms)
  • NaN/warmup policy accepted
  • Thread safety model accepted
  • CI pipeline accepted

14) Implementation order

  1. Skeleton: python.csproj, Directory.Build.props, package layout, _loader.py, _bridge.py scaffolding, pyproject.toml
  2. ABI layer: StatusCodes.cs, ArrayBridge.cs, Exports.cs (all ~290)
  3. Python layer: indicators.py (all wrappers), _compat.py (aliases)
  4. Tests: test_smoke.py, test_shapes.py, test_status_codes.py, test_golden.py, test_compat.py
  5. Publish: publish.ps1, CI workflow, wheel packaging
  6. Docs: README.md with full indicator list and compatibility table

15) Open decisions

  1. Error mapping granularity: ValueError for bad params vs separate exception classes per status code (current spec uses separate classes).
  2. Alpha overloads: Export as qtl_ema_alpha or defer to v2?
  3. Code generation: Generate Exports.cs + _bridge.py + indicators.py from a manifest file, or write manually?
  4. Linux wheel tag: manylinux2014_x86_64 vs manylinux_2_17_x86_64?
  5. Version sync: Should quantalib Python version track QuanTAlib NuGet version, or version independently?

16) Review findings and prioritized refinements

High priority

  1. Finalize ABI evolution policy
    • Keep qtl_ immutable for v1 and reserve qtl2_ for first break.
  2. Adopt manifest-driven generation
    • Eliminate manual drift risk across native exports, ctypes bridge, and wrappers.
  3. Clarify output validity on internal failures
    • Treat outputs as invalid on QTL_ERR_INTERNAL.

Medium priority

  1. Lock pandas fallback behavior
    • NumPy-only mode should be deterministic and explicitly tested.
  2. Define parity subset for L3
    • Freeze exact Wave 1 L3 indicator list before coding starts.
  3. Set measurable perf gates
    • Keep CI benchmarks with comparable overlap indicators and stable datasets.

Low priority

  1. Catalog automation
    • Generate indicator catalog tables from manifest to avoid stale docs.
  2. Release metadata policy
    • Define whether Python package version mirrors or decouples from NuGet version.