325 technical-analysis indicators and patterns. Rust core, Python API.
Every indicator runs two ways: over a whole array, or one bar at a time. The two agree bitwise, so what you backtest is what you trade.
import numpy as np
import trendlib as tl
close = np.array([...])
tl.rsi(close, period=14) # one array out
macd, signal, hist = tl.macd(close) # three
tl.cdl_engulfing(open_, high, low, close) # int32: +100, -100 or 0pip install trendlibThe first release ships a source distribution, so this compiles the Rust core and needs a toolchain (1.95 or later). Nothing else is required: no system TA-Lib, no C dependencies.
Pre-built wheels are on the way. The extension is abi3, so one wheel per
platform will cover Python 3.11 and every version after it, and the install
becomes a download with no toolchain at all.
| Group | Count | Examples |
|---|---|---|
| Chart patterns | 63 | head and shoulders, double top, wedges, channels, flags, cup with handle |
| Candlestick patterns | 61 | engulfing, harami, morning star, three black crows, hikkake |
| Momentum | 51 | RSI, MACD, stochastic, ADX, CCI, Williams %R, Connors RSI, TSI |
| Overlap studies | 34 | SMA, EMA, WMA, HMA, KAMA, T3, Bollinger, Keltner, Ichimoku, SuperTrend |
| Statistics | 23 | linear regression, correlation, beta, standard deviation, percentile |
| Bar patterns | 19 | inside day, outside day, key reversal, pipe top, narrow range |
| Volume | 16 | OBV, A/D, Chaikin, MFI, VWAP, VWMA, Twiggs money flow |
| Math transforms | 15 | log, exp, trigonometric and hyperbolic functions |
| Harmonic patterns | 12 | Gartley, bat, butterfly, crab, ABCD, Wolfe wave |
| Volatility | 9 | ATR, NATR, true range, Chaikin volatility, GAPO |
| Levels | 7 | traditional, Camarilla, Fibonacci, Woodie and DeMark pivots, CPR |
| Cycle | 6 | Hilbert transform: dominant cycle period, phase, trend mode |
| Math operators | 5 | add, subtract, multiply, divide, cumulative sum |
| Price transforms | 4 | typical, weighted, median and average price |
201 of them carry a TA-Lib-style uppercase alias, so tl.RSI(close, timeperiod=14)
works beside tl.rsi(close, period=14) and takes TA-Lib's parameter names.
Three layers, each doing one job.
A Rust core holds the arithmetic. It has no runtime dependencies at all
(cargo tree prints one line) and is #![forbid(unsafe_code)].
One kernel per indicator, written once as a step that takes a bar and returns the next value. The batch function folds that step over an array and the stream calls it per bar, so they cannot drift apart: the bitwise agreement is structural, not something a test happens to confirm.
A thin Python layer does the conversion: NumPy in, NumPy out, with pandas and polars recognised and their index handed back.
Streams are values, not handles. Each one is independent, nothing is shared, and there are no global settings anywhere.
live = tl.stream.rsi(history) # seed with what you have
live.update(bar) # advance, and get the new value
live.peek(bar) # what it would read, without committing
live.copy() # fork the state to explore a branch
live.value, live.bars_seen # where it standstl.stream.<name> exists for all 325.
Pass a DataFrame and the bar columns are matched by name, case-insensitively:
tl.atr(frame) # finds high, low, close
tl.rsi(frame["close"]) # a Series comes back indexed like it went inEvery output starts where the indicator is first defined. Earlier rows are
NaN, or 0 for the integer columns patterns return, and the arrays always
come back the same length as the input.
tl.lookback("rsi", period=14) # 14 - the first row that carries a valuetl.TrendLibError # the base
tl.InvalidInput # wrong shape, wrong dtype, NaN mid-series
tl.InsufficientHistory # fewer bars than the lookback needsNothing is silently clamped or filled. A parameter outside its range raises rather than snapping to the nearest legal value.
Measurements, not advice. No function, output or parameter is named for a
trading action: there is no buy, entry or target anywhere. Pattern
functions return +100, -100 or 0, meaning this shape is present and
points up, points down, or is absent; what to do about it is yours.
Where a published formula and a widely used implementation disagree, the division and ordering are chosen to keep precision rather than to match an implementation quirk. Several indicators here are measurably closer to exact arithmetic than the reference libraries they are compared against.
| Python | 3.11 and later (abi3) |
| Rust | 1.95, edition 2024 |
| OS | Linux (glibc, musl), macOS, Windows |
| Arch | x86-64, aarch64 |
Apache License 2.0. It grants patent rights explicitly, which a permissive licence that is silent on patents does not.
Built by GAIIN Technologies.