docs: document Python wheel matrix and wickra benchmark results

Published the pre-built wheel matrix (win/linux/macOS including arm64) and added wickra columns with fresh numbers to the Python benchmark docs.
This commit is contained in:
Miha Kralj
2026-09-27 18:00:16 -07:00
parent c5c93a0350
commit f0d91478e1
5 changed files with 49 additions and 28 deletions
+10
View File
@@ -45,6 +45,16 @@ Skender and Ooples are measured on their list-based APIs; QuanTAlib on its zero-
| **Python** | `pip install quantalib` | [Python Guide](docs/python.md) |
| **PineScript v6** | Copy-paste to TradingView | [PineScript Guide](docs/pinescript.md) |
`pip install quantalib` ships a compiled native wheel — no source build, no C++ toolchain:
| Platform | Wheel tag |
| :--- | :--- |
| Windows x64 | `win_amd64` |
| Linux x64 | `manylinux_2_17_x86_64` |
| Linux arm64 (AWS Graviton) | `manylinux_2_17_aarch64` |
| macOS arm64 (Apple Silicon) | `macosx_11_0_arm64` |
| macOS x64 (Intel) | `macosx_10_13_x86_64` |
## Show Me the Code
### C# Streaming (Real-time incoming data, value by value)
+21 -19
View File
@@ -125,9 +125,9 @@ Span mode represents maximum speed. Production code often needs different trade-
Even QuanTAlib's slowest mode (Eventing with complete event infrastructure, 16.8 MB allocations) processes 500,000 EMA values in 3.4 milliseconds. Still faster than Ooples at 14.8 ms and Skender at 25.9 ms for identical calculation. The "slow" path here beats other libraries' only path.
## Python Benchmark: quantalib vs pandas-ta
## Python Benchmark: quantalib vs pandas-ta vs wickra
The same indicators benchmarked in C# are also available through Python via NativeAOT shared library + ctypes FFI. This comparison measures the real-world cost of calling QuanTAlib from Python versus using pandas-ta (the most popular pure-Python technical analysis library).
The same indicators benchmarked in C# are also available through Python via NativeAOT shared library + ctypes FFI. This comparison measures the real-world cost of calling QuanTAlib from Python versus pandas-ta (the most popular pure-Python technical analysis library) and wickra (a Rust-core streaming library with a PyO3 binding).
### Test Environment
@@ -139,20 +139,22 @@ The same indicators benchmarked in C# are also available through Python via Nati
| NumPy | 2.2.6 |
| pandas | 3.0.1 |
| pandas-ta | 0.4.71b0 |
| quantalib | 0.8.7 (NativeAOT via ctypes) |
| wickra | 1.0.6 (Rust via PyO3) |
| quantalib | 0.8.12 (NativeAOT via ctypes) |
### quantalib vs pandas-ta
### quantalib vs pandas-ta and wickra
| Indicator | quantalib | pandas-ta | Speedup |
| :-------- | --------: | --------: | ------: |
| **SMA** | **1,308 μs** | 64,110 μs | **49×** |
| **EMA** | **1,083 μs** | 4,486 μs | **4.1×** |
| **WMA** | **1,223 μs** | 83,763 μs | **68×** |
| **HMA** | **2,709 μs** | 154,508 μs | **57×** |
| **ADOSC** | **1,655 μs** | 14,821 μs | **9.0×** |
| **SKEW** | **3,987 μs** | 8,077 μs | **2.0×** |
| Indicator | quantalib | pandas-ta | wickra |
| :-------- | --------: | --------: | -----: |
| **SMA** | **1,298 μs** | 63,721 μs | 12,548 μs |
| **EMA** | **1,067 μs** | 4,280 μs | 12,179 μs |
| **WMA** | **1,139 μs** | 83,903 μs | 12,547 μs |
| **HMA** | **2,647 μs** | 152,733 μs | 14,768 μs |
| **ADOSC** | **1,621 μs** | 15,761 μs | 40,779 μs |
| **CORRELATION** | **5,995 μs** | — | 25,062 μs |
| **SKEW** | **4,187 μs** | 7,846 μs | 19,298 μs |
SMA and WMA expose the largest gaps. pandas-ta implements SMA as a rolling window in pure Python/numpy, while quantalib calls the same SIMD-optimized C# code (via NativeAOT) that beats TA-Lib in the C# benchmarks above. WMA at 68× faster reflects the dot-product advantage: eight FMA operations per cycle versus Python's element-at-a-time loop.
quantalib beats pandas-ta by 1.9-74× and wickra by 4.2-25×. The pandas-ta gap is widest on SMA and WMA: pandas-ta implements SMA as a pure-Python rolling window, and WMA falls back to an element-at-a-time dot product. wickra's Rust core compiles to native code, so its margin is narrower — but its stdlib `array.array` return and PyO3 FFI boundary still leave it behind on the SIMD hot paths. pandas-ta ships no rolling correlation.
### quantalib vs pandas builtins
@@ -160,13 +162,13 @@ pandas itself provides optimized C implementations for common rolling operations
| Indicator | quantalib | pandas | Speedup |
| :-------- | --------: | -----: | ------: |
| **SMA** | **1,308 μs** | 6,950 μs (rolling.mean) | **5.3×** |
| **EMA** | **1,083 μs** | 3,652 μs (ewm.mean) | **3.4×** |
| **WMA** | **1,223 μs** | 686,561 μs (rolling+apply) | **561×** |
| **CORRELATION** | **20,450 μs** | 35,904 μs (rolling.corr) | **1.8×** |
| **SKEW** | **3,987 μs** | 8,164 μs (rolling.skew) | **2.0×** |
| **SMA** | **1,298 μs** | 7,265 μs (rolling.mean) | **5.6×** |
| **EMA** | **1,067 μs** | 3,692 μs (ewm.mean) | **3.5×** |
| **WMA** | **1,139 μs** | 686,466 μs (rolling+apply) | **603×** |
| **CORRELATION** | **5,995 μs** | 35,337 μs (rolling.corr) | **5.9×** |
| **SKEW** | **4,187 μs** | 8,137 μs (rolling.skew) | **1.9×** |
Even against pandas' C-optimized rolling operations, quantalib NativeAOT wins 2-5× on simple indicators. WMA is the extreme case: pandas lacks a native WMA implementation, falling back to `rolling().apply()` with a Python lambda — 561× slower.
Even against pandas' C-optimized rolling operations, quantalib NativeAOT wins 2-6× on simple indicators. WMA is the extreme case: pandas lacks a native WMA implementation, falling back to `rolling().apply()` with a Python lambda — 603× slower.
### FFI Overhead
+18 -9
View File
@@ -54,16 +54,16 @@ pip install quantalib[pyarrow] # pyarrow / pa.Array support
pip install quantalib[all] # all three
```
## Performance (500,000 bars, AVX-512)
## Performance (500,000 bars)
| Indicator | quantalib | pandas-ta | Ratio |
| --------- | --------: | --------: | ----: |
| SMA | 328 μs | ~50 ms | ~150× |
| EMA | 421 μs | ~45 ms | ~107× |
| WMA | 302 μs | ~60 ms | ~199× |
| RSI | 517 μs | ~80 ms | ~155× |
| Indicator | quantalib | wickra | pandas-ta |
| --------- | --------: | -----: | --------: |
| SMA | 1,298 μs | 12,548 μs | 63,721 μs |
| EMA | 1,067 μs | 12,179 μs | 4,280 μs |
| WMA | 1,139 μs | 12,547 μs | 83,903 μs |
| HMA | 2,647 μs | 14,768 μs | 152,733 μs |
The `ctypes` call adds 5-15 μs overhead. For arrays above a few hundred bars, NativeAOT wins by two orders of magnitude.
quantalib calls the same SIMD-optimized NativeAOT core as the .NET package through a single `ctypes` call (5-15 μs overhead). wickra (Rust/PyO3) returns stdlib `array.array`; pandas-ta stays in pure Python/numpy. For arrays above a few hundred bars, NativeAOT wins by an order of magnitude or more.
## Categories
@@ -89,7 +89,16 @@ The `ctypes` call adds 5-15 μs overhead. For arrays above a few hundred bars, N
- Python 3.10+
- NumPy >= 1.24
- Pre-built wheels: `win-x64`, `linux-x64`, `osx-x64`, `osx-arm64`
Pre-built wheels — the NativeAOT core is compiled per platform in CI and ships inside the wheel, so there is no source build and no C++ toolchain:
| Platform | Wheel tag |
| --- | --- |
| Windows x64 | `win_amd64` |
| Linux x64 | `manylinux_2_17_x86_64` |
| Linux arm64 (AWS Graviton) | `manylinux_2_17_aarch64` |
| macOS arm64 (Apple Silicon) | `macosx_11_0_arm64` |
| macOS x64 (Intel) | `macosx_10_13_x86_64` |
### Optional dependencies