Python bindings for the fugazi incremental technical-analysis library
Project description
fugazi (Python)
Python bindings for fugazi, a library of incremental,
composable technical-analysis primitives.
- Incremental — every indicator and signal carries its own state and is
advanced one sample at a time with
update(), in ~O(1) and with no full-history recomputation. The same object serves live streaming and batch backtesting. - Composable — indicators own their input source, so you build complex
indicators and signals by nesting constructors. There is no pipe or glue
step: an "EMA of an SMA of the close" is literally
ta.ema(ta.sma(ta.close(), 10), 20), and a trade condition is a single object you can feed bars.
Install
pip install maturin
maturin develop --release # editable install into the active virtualenv
Then import fugazi.
Quick start
You build indicators by nesting constructors. Every indicator is rooted at
a leaf source — usually a candle field (close(), high(), volume(), ...):
import fugazi as ta
ema = ta.ema(ta.close(), 20) # EMA-20 of the close
node = ta.ema(ta.sma(ta.close(), 10), 20) # EMA-20 of an SMA-10 — just keep nesting
The root decides what the indicator consumes. A candle-rooted indicator takes
Candles (any of OHLCV); to work on a bare stream of numbers instead, root
it at identity() — the leaf that passes raw values straight through:
prices = ta.rsi(ta.identity(), 14) # RSI of a plain float series
Then drive it one of two ways: streaming (a bar at a time) or batch (a whole series at once). They share the same indicators; pick by how your data arrives.
What you feed update()/feed() follows from the root: a candle-rooted
indicator consumes candles, an identity()-rooted one consumes plain
numbers.
Streaming API — one sample at a time
Feed one sample to update(); it returns a float, or None until warmed up.
This is the live/incremental path. Every node also has value() (or is_true() for a boolean Signal),
is_ready(), and reset().
node = ta.ema(ta.sma(ta.close(), 10), 20) # candle-rooted
for o, h, l, c, v in bars:
value = node.update(ta.Candle(o, h, l, c, v)) # feed a Candle -> float | None
print(value)
prices = ta.rsi(ta.identity(), 14) # identity-rooted
for px in [100.0, 101.5, 100.8]:
prices.update(px) # feed a float
Batch API — a whole series at once
feed(data) computes every bar in one call. For a candle-rooted indicator,
data is a dataframe with OHLCV columns — pandas and polars both work (also
a dict of columns) — and only the columns an indicator needs have to be
present:
import pandas as pd # or: import polars as pl
# df is your OHLCV frame (open/high/low/close/volume columns)
df["ema20"] = ta.ema(ta.close(), 20).feed(df) # assigns straight back
ta.atr(14).feed(df) # uses high/low/close
ta.vwap().feed(df) # uses high/low/close/volume
Column names are matched case-insensitively (Close/CLOSE/close), and
close is required. An identity()-rooted indicator instead takes a plain
1-D series — a list, NumPy array, or pandas/polars Series:
ta.ema(ta.identity(), 20).feed([100.0, 101.5, 100.8, 102.3, 101.9])
ta.ema(ta.identity(), 20).feed(df["close"])
(The root is the contract: a candle indicator won't silently treat a bare array as the close, and a value indicator won't accept a frame — pick the root that matches your data.)
The output mirrors the input library, one value per bar, with warm-up bars
as NaN (so the result lines up with your rows and assigns straight back):
| Input | Indicator | Multi-line (macd, bollinger, …) | Signal |
|---|---|---|---|
| pandas | Series (index preserved) |
DataFrame (one column per line) |
bool Series |
| polars | Series |
DataFrame |
bool Series |
| list / dict / NumPy | ndarray |
dict of ndarrays |
bool ndarray |
ta.ema(ta.close(), 20).feed(df) # pandas Series, df.index
ta.macd(ta.close()).feed(df) # pandas DataFrame: macd/signal/histogram
ta.macd(ta.identity()).feed(prices_list) # {"macd": ndarray, "signal": ndarray, ...}
(If NumPy isn't installed, list/dict input falls back to plain Python lists.)
feed is itself incremental — it just loops update over the batch through
the node's own state and never auto-resets. So calling it on successive chunks
continues the same stream: the warm-up is paid once, and the concatenated
outputs equal a single feed over the whole series. This is what lets you process
data as it arrives without recomputing history:
node = ta.sma(ta.identity(), 3)
x1 = node.feed(series1) # warms up, emits for series1
x2 = node.feed(series2) # continues from where series1 left off
# np.concatenate([x1, x2]) == ta.sma(ta.identity(), 3).feed(series1 + series2)
node.reset() # call reset() to start a fresh, independent pass
A source can be reused after you pass it into a constructor:
src = ta.close() fast = ta.ema(src, 10) slow = ta.ema(src, 20) # `src` is still usable here
Indicators
| Constructor | Output |
|---|---|
open() high() low() close() volume() typical() median() |
the candle field |
identity() |
the raw value stream (root for a bare numeric series) |
value(x) |
a constant |
sma ema rma wma hma rsi stddev stochastic cci (source, period) |
a value |
stoch_rsi(source, rsi_period=14, stoch_period=14) |
a value |
atr mfi williams_r (period) |
a value |
obv() vwap() ad() true_range() |
a value |
sar(step=0.02, max=0.2) |
a value |
macd(source, fast=12, slow=26, signal=9) |
dict {macd, signal, histogram} |
bollinger(source, period=20, k=2.0) |
dict {upper, middle, lower} |
keltner(source, ema_period=20, atr_period=10, multiplier=2.0) |
dict {upper, middle, lower} |
donchian(high, low, period) |
dict {upper, middle, lower} |
adx(period) |
dict {plus_di, minus_di, adx} |
dmi(period) |
dict {plus_di, minus_di} |
aroon(period) |
dict {up, down, oscillator} |
Multi-line indicators return a dict of their named lines (or None while
warming up).
Operators
Combine value indicators into other indicators:
ta.close().add(other) # also: sub, mul, div — or the + - * / operators
ta.close().lag(1) # also: diff, ratio, roc
ta.close().rolling_max(20) # also: rolling_min
...or into signals (booleans):
fast.gt(slow) # also: lt, ge, le, eq, ne (optional epsilon=...)
ta.rsi(ta.close(), 14).above(70.0) # also: below(level)
fast.crosses_above(slow) # also: crosses_below
Signals compose with each other and update to a bool:
sig = a.and_(b) # also: or_, xor_, not_(), changed() — or a & b | ~c
sig.update(candle) # -> bool
Example
"Fast EMA crosses above slow EMA while RSI is not already overbought" — one signal, usable either way:
import fugazi as ta
def golden():
return (
ta.ema(ta.close(), 12)
.crosses_above(ta.ema(ta.close(), 26))
.and_(ta.rsi(ta.close(), 14).below(70.0))
)
# streaming: react bar by bar
signal = golden()
for bar in stream:
if signal.update(bar):
print("entry signal")
# batch: a boolean Series/array over the whole frame
entries = golden().feed(df)
Trading: the wallet
The strategy layer is exposed as a wallet you trade into. There is no
strategy class to subclass — a "strategy" in Python is just your own code that,
each bar, reads signals and calls wallet methods. PaperWallet is the built-in,
in-memory book (funds + positions + a trade blotter); live execution belongs in
your own code, not here.
import fugazi as ta
wallet = ta.PaperWallet(10_000.0) # seed with cash
wallet.update("AAPL", 185.0) # feed the price each tick (before trading)
# set: absolute target (opposite side reverses) · set_position: absolute units · close: flat
wallet.set("AAPL", "buy", 10) # target 10 units (a number = units)
wallet.set("AAPL", "buy", ta.Size.value_frac(0.25)) # target 25% of equity
wallet.set("AAPL", "buy", ta.Size.position_frac(0.5)) # trim to 50% of the position
wallet.set_position("AAPL", 4) # drive straight to 4 units
wallet.close("AAPL") # flatten
wallet.funds # cash balance
wallet.position("AAPL") # signed position (negative = short)
wallet.price("AAPL") # last fed price (or None)
wallet.positions() # {symbol: quantity}
wallet.equity() # funds + positions marked at the fed prices
wallet.orders() # the blotter: list of Order(symbol, side, quantity)
The wallet is fed each symbol's price with update(symbol, price) and is
otherwise market-agnostic. Sizes are an absolute number of units, or
ta.Size.funds_frac(f) (cash) / ta.Size.value_frac(f) (equity; 1.0 is
all-in) / ta.Size.position_frac(f); sides are "buy"/"sell". A movement that
can't be carried out — no/zero price fed, or a buy beyond available funds —
raises ValueError. A full strategy loop — price the wallet, advance every
signal each bar, then act:
enter = ta.sma(ta.close(), 3).crosses_above(ta.sma(ta.close(), 10))
exit_ = ta.sma(ta.close(), 3).crosses_below(ta.sma(ta.close(), 10))
wallet = ta.PaperWallet(10_000.0)
for o, h, l, c, v in bars:
candle = ta.Candle(o, h, l, c, v)
wallet.update("AAPL", c) # price the wallet
went_long, went_flat = enter.update(candle), exit_.update(candle)
if went_long:
wallet.set("AAPL", "buy", ta.Size.value_frac(1.0)) # all-in long
elif went_flat:
wallet.close("AAPL")
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fugazi-0.1.1.tar.gz.
File metadata
- Download URL: fugazi-0.1.1.tar.gz
- Upload date:
- Size: 145.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa695bd38e68fbcf68a695a6f2657c7ea02720d3da6799cf733fe1a2c957f882
|
|
| MD5 |
ee081a394212b54daa289a48f5a83c2a
|
|
| BLAKE2b-256 |
83b1c682efcffad0d782b5c8b952bea17f49997df8e259dcaeb559a5786cb200
|
Provenance
The following attestation bundles were made for fugazi-0.1.1.tar.gz:
Publisher:
release.yml on acpuchades/fugazi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fugazi-0.1.1.tar.gz -
Subject digest:
fa695bd38e68fbcf68a695a6f2657c7ea02720d3da6799cf733fe1a2c957f882 - Sigstore transparency entry: 1986452595
- Sigstore integration time:
-
Permalink:
acpuchades/fugazi@d685136d352073a9e99d3210b623c2d5833e8af6 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/acpuchades
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d685136d352073a9e99d3210b623c2d5833e8af6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fugazi-0.1.1-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: fugazi-0.1.1-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 258.5 kB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6de17a2c5ce39908e7a18c4b378897af992d8dc4bb555011c3f868e7cb3edec
|
|
| MD5 |
415372ba45908d810eae1c2b4964620e
|
|
| BLAKE2b-256 |
0982dc2773cdc3d2efe33f2379f245f491876d123ec257a876cc5578ea8fe442
|
Provenance
The following attestation bundles were made for fugazi-0.1.1-cp39-abi3-win_amd64.whl:
Publisher:
release.yml on acpuchades/fugazi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fugazi-0.1.1-cp39-abi3-win_amd64.whl -
Subject digest:
a6de17a2c5ce39908e7a18c4b378897af992d8dc4bb555011c3f868e7cb3edec - Sigstore transparency entry: 1986452838
- Sigstore integration time:
-
Permalink:
acpuchades/fugazi@d685136d352073a9e99d3210b623c2d5833e8af6 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/acpuchades
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d685136d352073a9e99d3210b623c2d5833e8af6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fugazi-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: fugazi-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 422.1 kB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e70839678169a6ec40dafec8eff55c3a8890e30253c0331f1b8fdd507aa9b92e
|
|
| MD5 |
87a70e3033c9508bc7f8ec5fdf651318
|
|
| BLAKE2b-256 |
b2dedbc156b9a15a811e18a760845ae878ed14b0577e152f8a0d51d246afd806
|
Provenance
The following attestation bundles were made for fugazi-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on acpuchades/fugazi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fugazi-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
e70839678169a6ec40dafec8eff55c3a8890e30253c0331f1b8fdd507aa9b92e - Sigstore transparency entry: 1986452972
- Sigstore integration time:
-
Permalink:
acpuchades/fugazi@d685136d352073a9e99d3210b623c2d5833e8af6 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/acpuchades
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d685136d352073a9e99d3210b623c2d5833e8af6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fugazi-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: fugazi-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 422.5 kB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eca45516e196f13a0b7ba05d2ffe9bdab7eda301e5a1bacb16279a6e32915e11
|
|
| MD5 |
32289f0a1120f32ec06b00aa8d9064d1
|
|
| BLAKE2b-256 |
048ce13665dceaad11317d8b741142b3ff7cf96561a8272457586bf5a0254ff6
|
Provenance
The following attestation bundles were made for fugazi-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on acpuchades/fugazi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fugazi-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
eca45516e196f13a0b7ba05d2ffe9bdab7eda301e5a1bacb16279a6e32915e11 - Sigstore transparency entry: 1986453504
- Sigstore integration time:
-
Permalink:
acpuchades/fugazi@d685136d352073a9e99d3210b623c2d5833e8af6 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/acpuchades
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d685136d352073a9e99d3210b623c2d5833e8af6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fugazi-0.1.1-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: fugazi-0.1.1-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 376.4 kB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d9b0b6b7d19c79add3bdc9599e7fe3e765a19880d4829106cfe1e564c69e5d7d
|
|
| MD5 |
7d509574e68203ca8483ae6cd0c57c57
|
|
| BLAKE2b-256 |
5e11196b6715b637c89e5fdd3e44327ffdced5ed1bf7d7ffde8c575bf12d230f
|
Provenance
The following attestation bundles were made for fugazi-0.1.1-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on acpuchades/fugazi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fugazi-0.1.1-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
d9b0b6b7d19c79add3bdc9599e7fe3e765a19880d4829106cfe1e564c69e5d7d - Sigstore transparency entry: 1986453349
- Sigstore integration time:
-
Permalink:
acpuchades/fugazi@d685136d352073a9e99d3210b623c2d5833e8af6 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/acpuchades
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d685136d352073a9e99d3210b623c2d5833e8af6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fugazi-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: fugazi-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 376.1 kB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f228e86a0a247caa504567f16d403af9f2383697caf05c828e54d3a8cd059e6
|
|
| MD5 |
8d3404aaa6b3eba7dcab611a92e4ad1c
|
|
| BLAKE2b-256 |
661fd1a6d4ec762333b1faa9476d11196298233aeb9f77541fc13a32fbb4864e
|
Provenance
The following attestation bundles were made for fugazi-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl:
Publisher:
release.yml on acpuchades/fugazi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fugazi-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl -
Subject digest:
2f228e86a0a247caa504567f16d403af9f2383697caf05c828e54d3a8cd059e6 - Sigstore transparency entry: 1986453127
- Sigstore integration time:
-
Permalink:
acpuchades/fugazi@d685136d352073a9e99d3210b623c2d5833e8af6 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/acpuchades
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d685136d352073a9e99d3210b623c2d5833e8af6 -
Trigger Event:
push
-
Statement type: