Files
QuanTAlib/python/SPEC.md
T

43 KiB
Raw 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: Correlation.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
KChannel 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:
Input type Output (single) Output (multi)
pd.Series pd.Series pd.DataFrame
pd.DataFrame pd.Series pd.DataFrame
np.ndarray np.ndarray tuple of np.ndarray
  • 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.

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 pandas fallback policy

When pandas is not installed:

  • Functions accept and return np.ndarray only.
  • pd.Series / pd.DataFrame input raises ImportError with message "pandas required for Series/DataFrame input".
  • numpy is a hard dependency; pandas is an optional extra.

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
prs Prs 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

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
dosc Dosc A rsiPeriod, ema1Period, ema2Period, sigPeriod
dymoi Dymoi 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
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
smi Smi I (HLC→2+) period, smoothK, smoothD
squeeze Squeeze I (HLC→multi) bbPeriod, kcPeriod...
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
htit Htit 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
dchannel Dchannel D (HL) period
decaychannel Decaychannel B (HLC) period
fcb Fcb D (HL) period
jbands Jbands A period, phase, power
kchannel Kchannel B (HLC) period, multiplier
maenv Maenv A period, pct
mmchannel Mmchannel D (HL) period
pchannel Pchannel 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
adl Adl 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
correlation Correlation 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

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
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)
psar Psar C (OHLC) accelStart, accelMax
swings Swings D (HL→multi) period
ttm_scalper TtmScalper D (HL→multi) period

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.