Getting Started
Short answer
Install pip install "quantwave[polars]", then copy-paste the 5 commands
below — no external files needed, sample data is bundled. You'll have a
real RSI column printed to your terminal in under 5 minutes.
Evaluating vs TA-Lib or pandas-ta?
Read QuantWave vs alternatives first if you are comparing stacks.
Zero to RSI in 5 commands
Every command below was run against a real quantwave install and the output
blocks are pasted verbatim — copy-paste the whole thing and you should see
the same numbers.
1. Install
2. Verify the install
quantwave 0.8.0
✓ core extension (_quantwave)
✓ metadata registry
✓ streaming (RSI)
✓ polars installed
✓ backtest native (_backtest)
✓ Polars .bt namespace
✓ Polars expression plugins (pl.col().ta)
All checks passed.
If any line shows ✗ instead of ✓, jump to Troubleshooting
before continuing — the rest of this walkthrough assumes a clean doctor run.
3. Load data — no external file needed
QuantWave ships a small, bundled, deterministic sample dataset
(quantwave.datasets.load_sample()) so this tutorial needs zero network
access and zero files of your own. It's synthetic (a NIFTY-like index
plus two stock-like instruments, ~10 years of daily bars) — not real
exchange data — but it exercises the exact same code path your own OHLCV
Parquet file would.
import polars as pl
from quantwave import datasets
df = datasets.load_sample().filter(pl.col("symbol") == "NIFTY")
print(df.shape)
print(df.head())
(2520, 7)
shape: (5, 7)
┌────────────────┬──────────────┬──────────────┬──────────────┬──────────────┬────────────┬────────┐
│ ts ┆ open ┆ high ┆ low ┆ close ┆ volume ┆ symbol │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ datetime[μs, ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ str │
│ Asia/Kolkata] ┆ ┆ ┆ ┆ ┆ ┆ │
╞════════════════╪══════════════╪══════════════╪══════════════╪══════════════╪════════════╪════════╡
│ 2015-01-01 ┆ 20078.36675 ┆ 20226.917751 ┆ 19948.022175 ┆ 20096.573176 ┆ 787001.0 ┆ NIFTY │
│ 09:15:00 IST ┆ ┆ ┆ ┆ ┆ ┆ │
│ 2015-01-02 ┆ 19817.560137 ┆ 20011.255661 ┆ 19616.134524 ┆ 19809.830047 ┆ 2.509591e6 ┆ NIFTY │
│ 09:15:00 IST ┆ ┆ ┆ ┆ ┆ ┆ │
│ 2015-01-03 ┆ 19969.396569 ┆ 20040.00522 ┆ 19897.539828 ┆ 19968.148478 ┆ 639508.0 ┆ NIFTY │
│ 09:15:00 IST ┆ ┆ ┆ ┆ ┆ ┆ │
│ 2015-01-04 ┆ 20024.939546 ┆ 20206.715656 ┆ 19657.051393 ┆ 19838.827503 ┆ 1.594052e6 ┆ NIFTY │
│ 09:15:00 IST ┆ ┆ ┆ ┆ ┆ ┆ │
│ 2015-01-05 ┆ 19578.928615 ┆ 19737.498442 ┆ 19407.807979 ┆ 19566.377805 ┆ 1.59632e6 ┆ NIFTY │
│ 09:15:00 IST ┆ ┆ ┆ ┆ ┆ ┆ │
└────────────────┴──────────────┴──────────────┴──────────────┴──────────────┴────────────┴────────┘
Already have your own OHLCV data? pl.read_parquet("ohlcv.parquet") drops
in wherever datasets.load_sample() is used below, as long as it has
open/high/low/close/volume columns.
4. Compute RSI
import quantwave # registers pl.col().ta and LazyFrame.bt
df = df.lazy().with_columns(
pl.col("close").ta.rsi(timeperiod=14).alias("rsi"),
).collect()
print(df.select("ts", "close", "rsi").head())
shape: (5, 3)
┌────────────────────────────┬──────────────┬─────┐
│ ts ┆ close ┆ rsi │
│ --- ┆ --- ┆ --- │
│ datetime[μs, Asia/Kolkata] ┆ f64 ┆ f64 │
╞════════════════════════════╪══════════════╪═════╡
│ 2015-01-01 09:15:00 IST ┆ 20096.573176 ┆ NaN │
│ 2015-01-02 09:15:00 IST ┆ 19809.830047 ┆ NaN │
│ 2015-01-03 09:15:00 IST ┆ 19968.148478 ┆ NaN │
│ 2015-01-04 09:15:00 IST ┆ 19838.827503 ┆ NaN │
│ 2015-01-05 09:15:00 IST ┆ 19566.377805 ┆ NaN │
└────────────────────────────┴──────────────┴─────┘
Don't panic about the NaN — that's expected. RSI needs 14 bars of warmup
before its first real value; see Warmup and NaN semantics
for why NaN (not null) is the convention and why it matters for backtests.
5. See the real output
shape: (5, 3)
┌────────────────────────────┬──────────────┬───────────┐
│ ts ┆ close ┆ rsi │
│ --- ┆ --- ┆ --- │
│ datetime[μs, Asia/Kolkata] ┆ f64 ┆ f64 │
╞════════════════════════════╪══════════════╪═══════════╡
│ 2021-11-20 09:15:00 IST ┆ 37635.8664 ┆ 47.160391 │
│ 2021-11-21 09:15:00 IST ┆ 37924.793503 ┆ 51.783841 │
│ 2021-11-22 09:15:00 IST ┆ 37514.078853 ┆ 45.666771 │
│ 2021-11-23 09:15:00 IST ┆ 37131.884029 ┆ 40.832963 │
│ 2021-11-24 09:15:00 IST ┆ 37075.538921 ┆ 40.158096 │
└────────────────────────────┴──────────────┴───────────┘
That's it — a real, warmup-correct RSI column, computed on a Polars
LazyFrame, from a clean install, with no external files. Everything past
this point is about where to go deeper, not how to get started.
Troubleshooting
Quick answers for the two failure modes people hit before they've even seen
output. Full list (import errors, platform/wheel matrix, doctor output
reference) lives in the Python guide.
quantwave doctorshows a✗— see Interpretingdoctoroutput.ImportError/ModuleNotFoundError: quantwave— see Import failures.AttributeError: 'LazyFrame' object has no attribute 'ta'/'bt'— you importedpolarsbut neverimport quantwave(registration is a side effect of the import — see step 4 above).
Now go deeper
You've already run your first indicator. From here, pick where you want to go next:
flowchart LR
A[RSI computed ✓] --> B{Goal?}
B --> C[Polars batch research]
B --> D[Live streaming]
B --> E[Backtest a signal]
B --> F[ML features]
B --> G[Rust instead of Python]
C --> H[Indicator catalog]
D --> H
E --> I[Backtest quickstart]
F --> J[ML features guide]
G --> K[Rust guide]
Polars batch research
Build feature columns on LazyFrame, then backtest.
Live / streaming
Same math as batch — streaming_class + wrap_streaming.
Python streaming section · qw.assert_parity()
Backtest a strategy
.bt namespace — sweeps, walk-forward, tear sheets.
Explore indicators
225 native tools — search, gallery, or full catalog.
ML feature pipelines
Hurst, frac-diff, build_feature_matrix(), regime gates.
Rust instead of Python
Next<T> streaming and Polars .ta() in native crates.
Conventions worth knowing early
| Topic | Where it lives |
|---|---|
| Warmup / NaN rules | qw.warmup_bars(), qw.boundary_info() — Python guide |
| Batch vs streaming parity | qw.assert_parity() — same Next<T> core |
| Indicator discovery | qw.indicators(), qw.metadata("rsi") |
| Performance claims | Benchmarks |