# pyti: technical indicators as plain Python functions

> pyti is a library of financial technical indicators where each indicator is a function that takes a list of prices and a period and gives you back a list, with numpy as the only runtime dependency. Version 0.3.0 deliberately changed the numbers to match textbook definitions, which is a warning as much as a feature.

**kylejusticemagnuson/pyti** — Python library of various financial technical indicators

- Repository: https://github.com/kylejusticemagnuson/pyti
- Stars: 667 · Forks: 161
- Language: Python
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/kylejusticemagnuson-pyti

## One function per indicator, over a list of prices

The README describes pyti as a library containing various financial technical indicators that can be used to analyze data, and the usage example shows the shape of the whole API in a few lines:

```python
from pyti.exponential_moving_average import exponential_moving_average as ema

data = [6, 7, 3, 6, 3, 9, 5]
period = 2
res = ema(data, period)

# res = [nan, 6.5, 4.1667, 5.3889, 3.7963, 7.2654, 5.7551]
```

A plain Python list goes in, a period goes in, and a list comes out, with `nan` in the first position because there is not enough data yet to compute the first value. There is no object to construct, no state to keep, and no frame to align.

The import path is the other half of the design. Each indicator lives in its own module named after it, so `pyti.exponential_moving_average` gives you the exponential moving average and nothing else. The consequence is that the surface is discoverable by reading directory names, and that a project using ten indicators imports ten modules rather than one package with a large namespace.

The runtime dependency list is a single entry, numpy, and the library requires Python 3.10 or newer, tested on 3.10 to 3.14. Nothing in the runtime pulls in a data framework, a plotting library or a finance API.

## The catalogue covers the usual suspects, in families

The README lists the complete set of indicators, and the list is a good map of the library's coverage. It runs from the most classic to the more specialised.

The moving average family is the widest: Simple Moving Average, Exponential Moving Average, Double Exponential Moving Average, Triple Exponential Moving Average, Hull Moving Average, Linear Weighted Moving Average, Smoothed Moving Average, Triangular Moving Average, Volume Adjusted Moving Average, Moving Average Convergence Divergence, and Moving Average Envelope with its upper, center and lower bands.

The band and channel indicators follow: Bollinger Bands with the upper, middle and lower bands plus bandwidth, percent bandwidth, range and %B, Keltner Bands with bandwidth, center, upper and lower band, and Price Channels with an upper and lower channel.

Then come the oscillators: Relative Strength Index, Stochastic with %K and %D, StochRSI, Williams %R, Chande Momentum Oscillator, Commodity Channel Index, Detrended Price Oscillator, Price Oscillator, Rate of Change, Momentum, and the Ultimate Oscillator. The directional movement set is Average Directional Index with Negative and Positive Directional Index and Negative and Positive Directional Movement, alongside Average True Range, Average True Range Percent and True Range.

Volume work is covered too: On Balance Volume, Money Flow, Money Flow Index, Chaikin Money Flow, Volume Oscillator, and Volume Index with a positive and negative component. The Ichimoku Cloud is there with TenkanSen, KijunSen, Chiku Span, Senkou A and Senkou B, and the long tail includes Aroon with up and down, Accumulation/Distribution, Typical Price, Vertical Horizontal Filter, Volatility, Standard Deviation and Standard Variance, and Double Smoothed Stochastic.

## 0.3.0 changed the numbers on purpose

The most important thing in the README is a note, and it is a warning. Version 0.3.0 is a breaking release: several indicators were fixed or reworked to match their textbook definitions, so the numeric output differs from 0.2.x, and a few functions take new high and low arguments. The CHANGELOG.md is pointed to for the full list.

That is worth understanding rather than skimming. A technical indicator is a formula, and there are several common variants of most formulas, so a library that changes its output between versions is telling you that it picked a different variant and moved. If you are porting existing analysis code, or if you have saved results in a database, a minor-looking upgrade can silently change every number your strategy sees.

The release history shows how rare that kind of change is. The versions are v0.3.0 on 2026-07-02, v0.2.2 on 2018-05-08 with the note that it was a fix to ADX, and v0.2.1 on 2017-12-12 with a fix for Aroon. So the project sat on the 0.2 line for years, fixing individual indicators as errors were found, and then made one large correction to the definitions in a single release.

For an adopter, the practical rule is to read the changelog for the version you are moving to, and to pin the version in anything that produces numbers you store.

## numpy at runtime, pandas only for the tests

The dependency split in the packaging metadata is worth reading closely. The runtime dependencies are a single entry, numpy, and the development group holds pytest and pandas.

So pandas is not needed to use the library, only to run its test suite, which matches the README's note that the suite uses uv, or any virtual environment with pytest and pandas installed. That is a good sign about the design. A technical analysis library that drags a dataframe library into your production environment for no functional reason is a library you will end up fighting in a slim deployment.

numpy is the right dependency for this kind of work, since every indicator here is a moving average, a difference of two series, or a ratio of two series, and those are array operations. The return value being a list of floats with a leading `nan` also means the results line up with the input by position, which is the property you need when you want to plot a price series and its moving average against each other.

The one dependency question the packaging metadata leaves open is which numpy version the library is pinned against, since the requirement is written as a bare `numpy` with no version constraint. For a library whose value is numeric agreement with textbook definitions, an unpinned numerical dependency is a reasonable thing to check in your own environment before you rely on the output.

## Running the suite, and one test at a time

The test instructions are short. The suite runs with:

```
uv run pytest
```

and a single test is selected with the standard pytest node id syntax:

```
uv run pytest tests/test_file_you_want_to_test.py::TestClassName::test_method_name
```

The pytest configuration in the packaging metadata sets `testpaths` to `tests`, so a bare `pytest` invocation collects from that directory, and the repository root holds a `tests` directory alongside the `pyti` package, the CHANGELOG.md, the LICENSE.TXT, the pyproject.toml and the README.

The test class naming in the example, `TestClassName::test_method_name`, tells you the suite is organised as classes with methods, which is the convention pytest-textual style tooling aside, simply a way of grouping the cases for each indicator.

Contributions are invited in two directions, and the README asks for both. If you think an indicator is missing or believe there is an error in one of the existing ones, the request is to file an issue. If you want to add your own indicator, the request is to fork the project and submit a pull request. Given that the list of indicators is explicitly enumerated in the README and the package is a flat set of modules, adding one is a matter of writing a function and a test, which is a low bar for a contribution.

## Packaging that says beta, on a plain setuptools build

The packaging metadata is a single pyproject.toml with no separate requirements files, and the build system is setuptools with a minimum version of 77.

The project section names the package `pyti` at version 0.3.0, describes it as a Technical Indicator Library, and declares the licence as MIT with the licence file named LICENSE.TXT, which is also present at the repository root. The author is Kyle Justice Magnuson. The keywords are financial, technical, indicator and functions, and the trove classifier for development status is 4 - Beta. The supported Python versions are 3.10 through 3.14, and `requires-python` is `>=3.10`.

Package discovery is configured to include only the `pyti` package, so the tests and the metadata are not installed with the distribution.

The beta classification is the honest signal in all of this. The library has a well-scoped surface, a single dependency and a test suite, and it is also a small project with a long quiet period between releases, so treating it as a beta library whose indicator definitions you should verify against your own reference data is the right posture.

## Computing the indicator yourself, and why the library still earns its place

There is a real alternative to a library like this, and every indicator here is short enough to write yourself. An exponential moving average is a recursive mean, a rate of change is a difference of two series divided by the earlier one, and an RSI is a ratio of average gains to average losses over a window. With numpy in hand, each of those is a handful of lines.

The difference in approach is not the arithmetic, it is the agreement. A library gives you one definition of each formula, applied consistently, and a test suite that pins the expected values. Writing it yourself gives you control over the edge cases, the warm-up period, the treatment of missing data, and the exact convention for a smoothing constant, and it gives you as many subtly different results as you have subtly different implementations. For a single research script, that difference may not matter. For a team whose results are compared across people or across time, one agreed definition is worth a lot more than the ability to tweak it.

There is a second reason to use the library, and it is the catalogue. The list in the README is long enough that writing all of it, and then keeping each one correct against its textbook definition, is a project in itself. pyti has already done the part that is tedious, and the 0.3.0 release is the payoff: the definitions were corrected in one place so that you do not have to.

## Conclusion

pyti fits a Python project that wants indicator values as plain arrays to feed into its own analysis, charting or backtesting code, and that is willing to pin a version deliberately after the 0.3.0 rewrite. It is the wrong choice if you need a chart with indicators drawn on it, since the library computes and does not draw, and the wrong choice if you want a wide indicator set beyond what is listed. Verify first which indicator definitions you need against the 0.3.0 changelog, since several were reworked and a few functions now take high and low arguments, and check that your own environment satisfies the Python 3.10 floor.

## FAQ

### How do I install pyti and call an indicator?

Install it with pip install pyti, then import the function for the indicator you want, for example from pyti.exponential_moving_average import exponential_moving_average as ema, and call it with your data and a period. The result is a list of values aligned with the input.

### What changed in pyti version 0.3.0?

The README notes that 0.3.0 is a breaking release in which several indicators were fixed or reworked to match their textbook definitions, so the numeric output differs from 0.2.x and a few functions take new high and low arguments. The CHANGELOG.md in the repository has the full list.

### Does pyti need pandas?

Not at runtime. The only runtime dependency is numpy, and the packaging metadata lists pandas only in the development group alongside pytest, for running the test suite.

### Which Python versions does pyti support?

The README states that pyti requires Python 3.10 or newer and has been tested on 3.10 through 3.14, and the packaging metadata declares requires-python as >=3.10 with classifiers for those versions.

### How do I run the pyti test suite?

Run uv run pytest, or use any virtual environment that has pytest and pandas installed. A single test can be selected with a node id such as uv run pytest tests/test_file_you_want_to_test.py::TestClassName::test_method_name.

## Sources

- [Issues](https://github.com/kylejusticemagnuson/pyti/issues)
- [kylejusticemagnuson/pyti on GitHub](https://github.com/kylejusticemagnuson/pyti)
- [License: MIT](https://github.com/kylejusticemagnuson/pyti/blob/master/LICENSE)
- [README](https://github.com/kylejusticemagnuson/pyti/blob/master/README.md)
- [Releases](https://github.com/kylejusticemagnuson/pyti/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/kylejusticemagnuson-pyti
