Using Indicators¶
pandas only
The mintalib.indicators module provides composable indicator objects that bind a calculation with its parameters (pandas only — for polars, use mintalib.expressions).
Indicators are named in upper case (e.g. SMA, EMA, MACD). An indicator instance is callable and can be passed directly to prices.assign() or invoked as SMA(50)(prices). The | operator chains indicators: EMA(20) | ROC(1) means ROC applied after EMA.
import pandas as pd
from mintalib.samples import sample_prices
from mintalib.indicators import EMA, SMA, ROC, RSI, LOG, BBANDS, MACD
prices = sample_prices()
Basic Usage¶
An indicator instance is a callable. Applied to a DataFrame, series-based indicators use the close column by default — the item parameter selects another column. A pandas Series or numpy array can be passed directly as well (results always come back as pandas objects):
prices.pipe(SMA(50))
date
1980-12-12 NaN
1980-12-15 NaN
1980-12-16 NaN
1980-12-17 NaN
1980-12-18 NaN
...
2026-07-31 309.499400
2026-08-03 309.522800
2026-08-04 309.610601
2026-08-05 309.654200
2026-08-06 309.772801
Length: 11504, dtype: float64
prices.pipe(RSI(14))
date
1980-12-12 NaN
1980-12-15 NaN
1980-12-16 NaN
1980-12-17 NaN
1980-12-18 NaN
...
2026-07-31 43.245950
2026-08-03 40.340781
2026-08-04 44.685147
2026-08-05 45.839619
2026-08-06 48.183314
Length: 11504, dtype: float64
Chaining¶
The | operator chains indicators left to right: LOG() | EMA(20) | ROC(1) applies LOG first, then EMA, then ROC.
prices.assign(
trend=LOG() | EMA(20) | ROC(1)
)
| open | high | low | close | volume | trend | |
|---|---|---|---|---|---|---|
| date | ||||||
| 1980-12-12 | 0.098207 | 0.098634 | 0.098207 | 0.098207 | 469033600 | NaN |
| 1980-12-15 | 0.093510 | 0.093510 | 0.093083 | 0.093083 | 175884800 | NaN |
| 1980-12-16 | 0.086678 | 0.086678 | 0.086251 | 0.086251 | 105728000 | NaN |
| 1980-12-17 | 0.088386 | 0.088813 | 0.088386 | 0.088386 | 86441600 | NaN |
| 1980-12-18 | 0.090949 | 0.091376 | 0.090949 | 0.090949 | 73449600 | NaN |
| ... | ... | ... | ... | ... | ... | ... |
| 2026-07-31 | 304.809998 | 310.690002 | 300.000000 | 308.910004 | 132489100 | -0.077130 |
| 2026-08-03 | 309.579987 | 311.799988 | 302.559998 | 303.420013 | 75052000 | -0.099409 |
| 2026-08-04 | 302.730011 | 310.420013 | 301.320007 | 309.380005 | 68001000 | -0.057922 |
| 2026-08-05 | 309.359985 | 311.709991 | 305.670013 | 311.000000 | 49178700 | -0.043810 |
| 2026-08-06 | 314.339996 | 316.289398 | 313.489990 | 314.260010 | 7165536 | -0.022424 |
11504 rows × 6 columns
The Assign Idiom¶
Because indicators are callables, they can be passed directly to prices.assign, which invokes each with the DataFrame. Since assign processes keyword arguments sequentially, pd.col expressions can reference columns created earlier in the same call:
prices.assign(
sma50 = SMA(50),
sma200 = SMA(200),
rsi = RSI(14),
slope = LOG() | EMA(20) | ROC(1),
uptrend = pd.col("sma50") > pd.col("sma200")
).iloc[:, -5:]
| sma50 | sma200 | rsi | slope | uptrend | |
|---|---|---|---|---|---|
| date | |||||
| 1980-12-12 | NaN | NaN | NaN | NaN | False |
| 1980-12-15 | NaN | NaN | NaN | NaN | False |
| 1980-12-16 | NaN | NaN | NaN | NaN | False |
| 1980-12-17 | NaN | NaN | NaN | NaN | False |
| 1980-12-18 | NaN | NaN | NaN | NaN | False |
| ... | ... | ... | ... | ... | ... |
| 2026-07-31 | 309.499400 | 277.661274 | 43.245950 | -0.077130 | True |
| 2026-08-03 | 309.522800 | 277.943019 | 40.340781 | -0.099409 | True |
| 2026-08-04 | 309.610601 | 278.246737 | 44.685147 | -0.057922 | True |
| 2026-08-05 | 309.654200 | 278.567977 | 45.839619 | -0.043810 | True |
| 2026-08-06 | 309.772801 | 278.881386 | 48.183314 | -0.022424 | True |
11504 rows × 5 columns
Multi-Output Indicators¶
Multi-output indicators return a DataFrame, so they cannot be assigned to a single column — join the result instead:
prices.pipe(BBANDS(20))
| upperband | middleband | lowerband | |
|---|---|---|---|
| date | |||
| 1980-12-12 | NaN | NaN | NaN |
| 1980-12-15 | NaN | NaN | NaN |
| 1980-12-16 | NaN | NaN | NaN |
| 1980-12-17 | NaN | NaN | NaN |
| 1980-12-18 | NaN | NaN | NaN |
| ... | ... | ... | ... |
| 2026-07-31 | 344.002618 | 324.3670 | 304.731382 |
| 2026-08-03 | 345.001166 | 323.9050 | 302.808834 |
| 2026-08-04 | 345.104607 | 323.8410 | 302.577394 |
| 2026-08-05 | 345.243987 | 323.7215 | 302.199012 |
| 2026-08-06 | 345.299028 | 323.6235 | 301.947972 |
11504 rows × 3 columns
Pandas Expressions¶
With pandas >= 3.0, as_expr() converts an indicator into a pandas Expression. For multi-output indicators, as_expr(item) picks a single output — which makes them usable inside assign after all:
prices.assign(
oversold=RSI(14).as_expr()<30,
overbought=RSI(14).as_expr()>70,
)
| open | high | low | close | volume | oversold | overbought | |
|---|---|---|---|---|---|---|---|
| date | |||||||
| 1980-12-12 | 0.098207 | 0.098634 | 0.098207 | 0.098207 | 469033600 | False | False |
| 1980-12-15 | 0.093510 | 0.093510 | 0.093083 | 0.093083 | 175884800 | False | False |
| 1980-12-16 | 0.086678 | 0.086678 | 0.086251 | 0.086251 | 105728000 | False | False |
| 1980-12-17 | 0.088386 | 0.088813 | 0.088386 | 0.088386 | 86441600 | False | False |
| 1980-12-18 | 0.090949 | 0.091376 | 0.090949 | 0.090949 | 73449600 | False | False |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 2026-07-31 | 304.809998 | 310.690002 | 300.000000 | 308.910004 | 132489100 | False | False |
| 2026-08-03 | 309.579987 | 311.799988 | 302.559998 | 303.420013 | 75052000 | False | False |
| 2026-08-04 | 302.730011 | 310.420013 | 301.320007 | 309.380005 | 68001000 | False | False |
| 2026-08-05 | 309.359985 | 311.709991 | 305.670013 | 311.000000 | 49178700 | False | False |
| 2026-08-06 | 314.339996 | 316.289398 | 313.489990 | 314.260010 | 7165536 | False | False |
11504 rows × 7 columns
Studies over multiple symbols¶
An IndicatorBundle collects several indicators into a reusable study. Apply it to each symbol independently with pandas groupby.apply.
from mintalib.indicators import IndicatorBundle, MACD, SMA
dataset = pd.concat(
[prices.assign(symbol=symbol) for symbol in ["AAA", "BBB", "CCC"]]
)
study = IndicatorBundle(MACD(), sma20=SMA(20), sma50=SMA(50))
dataset.groupby("symbol")[prices.columns].apply(study)
| macd | macdsignal | macdhist | sma20 | sma50 | ||
|---|---|---|---|---|---|---|
| symbol | date | |||||
| AAA | 1980-12-12 | NaN | NaN | NaN | NaN | NaN |
| 1980-12-15 | NaN | NaN | NaN | NaN | NaN | |
| 1980-12-16 | NaN | NaN | NaN | NaN | NaN | |
| 1980-12-17 | NaN | NaN | NaN | NaN | NaN | |
| 1980-12-18 | NaN | NaN | NaN | NaN | NaN | |
| ... | ... | ... | ... | ... | ... | ... |
| CCC | 2026-07-31 | 6.894136 | 8.260785 | -1.366649 | 324.3670 | 309.499400 |
| 2026-08-03 | 4.536518 | 7.515932 | -2.979414 | 323.9050 | 309.522800 | |
| 2026-08-04 | 3.113124 | 6.635370 | -3.522246 | 323.8410 | 309.610601 | |
| 2026-08-05 | 2.091683 | 5.726633 | -3.634950 | 323.7215 | 309.654200 | |
| 2026-08-06 | 1.527629 | 4.886832 | -3.359203 | 323.6235 | 309.772801 |
34512 rows × 5 columns