Pandas-native backtesting library with backtest/live parity
Project description
fastbt-quant
Pandas-native backtesting library with backtest/live parity.
Distribution name on PyPI is
fastbt-quant; the import name staysfastbt.
Install
pip install fastbt-quant # core
pip install "fastbt-quant[data]" # + idata.fyi data fetching
pip install "fastbt-quant[report]" # + HTML tearsheets via quantstats
pip install "fastbt-quant[signal]" # + live signal server (aiohttp)
pip install "fastbt-quant[data,report,signal]" # everything
Requires Python 3.11+.
Design principles
- BYO data. Pandas DataFrame in,
BacktestResultout. No data providers bundled. - One strategy class, two run modes. Same code for backtest and (eventually) live — the engine swaps, the strategy doesn't.
- PaperBroker only in v0.0.1. Real broker adapters (Alpaca, CCXT, Kite) arrive in v0.2+.
- Declarative JSON strategies coming in v0.0.2. The Python subclass API stays as the escape hatch.
Quick start
from fastbt import Strategy, backtest
from fastbt.indicators import sma
class SmaCross(Strategy):
def on_bar(self, ctx):
if len(ctx.history) < 30:
return
close = ctx.history["close"]
fast = sma(close, 10).iloc[-1]
slow = sma(close, 30).iloc[-1]
prev_fast = sma(close, 10).iloc[-2]
prev_slow = sma(close, 30).iloc[-2]
if prev_fast <= prev_slow and fast > slow and ctx.position.is_flat:
ctx.buy(qty=100)
elif prev_fast >= prev_slow and fast < slow and ctx.position.is_long:
ctx.close()
# `data` is any OHLCV DataFrame — bring your own source.
result = backtest(SmaCross(), data=data, initial_cash=100_000)
print(result)
# BacktestResult(trades=7, return=12.40%, max_dd=5.20%, sharpe=1.18, win_rate=57.1%)
JSON strategies (v0.4.0)
Single-symbol and portfolio strategies can be expressed entirely in JSON. One loader handles both:
import json
from fastbt import load_strategy, portfolio_backtest
with open("examples/momentum_portfolio.json") as f:
strategy = load_strategy(json.load(f))
# `data` is a dict[str, pd.DataFrame] for portfolio mode.
result = portfolio_backtest(strategy, data=universe, initial_cash=1_000_000)
A portfolio JSON spec describes the same lifecycle (universe → rank → select → allocate → schedule) the Python PortfolioStrategy exposes. See
examples/momentum_portfolio.json for a complete momentum-rotation example.
OHLCV schema contract
Your DataFrame must have:
- Columns:
open,high,low,close,volume(case-insensitive) - Index:
pd.DatetimeIndex, monotonic increasing, no duplicates - No NaN values
high >= max(open, close),low <= min(open, close),volume >= 0
Call fastbt.validate_ohlcv(df) at your ingest boundary to fail fast.
Fill policy (important)
The single biggest source of "backtest was profitable but live loses money" is fill timing. fastbt makes this explicit:
fill_policy="next_open"(default, realistic) — order submitted on bar N fills at bar N+1's open. You only knew close after bar N ended.fill_policy="current_close"(optimistic) — fills at bar N's close. Use only when your signal is known intra-bar.
Develop
git clone https://github.com/Vbhadala/fastbt
cd fastbt
uv sync --extra dev
uv run pytest
uv run python examples/sma_crossover.py
Portfolio strategies (v0.3.0)
Built-in allocators and risk controls so portfolio strategies don't hand-code these every time.
from fastbt import PortfolioStrategy, portfolio_backtest, RebalanceSchedule
from fastbt.allocators import (
annualized_volatility, inverse_volatility, momentum_score,
)
from fastbt.indicators import roc
class MomentumTop3(PortfolioStrategy):
def rank(self, ctx, universe):
scored = []
for sym in universe:
close = ctx.history[sym]["close"]
score = momentum_score(
{"r90d": roc(close, 90).iloc[-1] if len(close) >= 90 else None},
{"r90d": 1.0},
)
scored.append((sym, score))
return sorted(scored, key=lambda x: x[1], reverse=True)
def select(self, ctx, ranked):
return [s for s, _ in ranked[:3]]
def allocate(self, ctx, selected):
vols = {s: annualized_volatility(ctx.history[s]["close"], 60) for s in selected}
return inverse_volatility(selected, vols)
result = portfolio_backtest(
MomentumTop3(), data,
schedule=RebalanceSchedule.monthly(day=1),
min_drift_pct=2.0, # skip trades under 2% drift
vol_trailing_multiplier=2.0, # vol-adaptive trailing stop
vol_trailing_fallback_pct=10.0,
risk_free_rate=6.0, # for Sharpe / Sortino
)
print(result.metrics.cagr, result.metrics.sortino)
print(len(result.stop_outs), "stop-outs recorded")
See examples/momentum_portfolio.py for a complete runnable walkthrough.
Roadmap
| Version | Scope |
|---|---|
| v0.0.1 | Single-symbol backtest, PaperBroker, 6 indicators, OHLCV validator |
| v0.0.2 | JSON declarative strategies, short positions, 15 more indicators |
| v0.1.0 | Portfolio/universe strategies (rank + select), live mode via async feed |
| v0.2.0 | Live signal engine + webhook server + idata.fyi data module |
| v0.3.0 | Allocators, vol-trailing stops, drift-aware rebalancing, CAGR/Sortino metrics |
| v0.4.0 | JSON portfolio strategy spec, vol_trailing in single-symbol JSON |
| v0.5.0 | First PyPI release as fastbt-quant. Symbol-aware fills (breaking: Bar now requires symbol). |
| v0.6.0+ | Real broker adapters (Alpaca/Kite), walk-forward validation, intrabar stop simulation |
License
MIT
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 Distribution
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 fastbt_quant-0.6.0.tar.gz.
File metadata
- Download URL: fastbt_quant-0.6.0.tar.gz
- Upload date:
- Size: 149.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6b130b780a186a13fdd3faadc17a4fe7e46e2af3ea1ad545679ba3a25ab5a64
|
|
| MD5 |
4cac46321af51aaf54bc9c4b57012b50
|
|
| BLAKE2b-256 |
5d1e8a20309a5b8a67bf92dc7847004c4dedb94e1fbbd315ecbc4334d07a0f5f
|
Provenance
The following attestation bundles were made for fastbt_quant-0.6.0.tar.gz:
Publisher:
release.yml on Vbhadala/fastbt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastbt_quant-0.6.0.tar.gz -
Subject digest:
a6b130b780a186a13fdd3faadc17a4fe7e46e2af3ea1ad545679ba3a25ab5a64 - Sigstore transparency entry: 1472142436
- Sigstore integration time:
-
Permalink:
Vbhadala/fastbt@7f2d1d99476db8d8e44fb064b4a084c0735136bb -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/Vbhadala
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7f2d1d99476db8d8e44fb064b4a084c0735136bb -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastbt_quant-0.6.0-py3-none-any.whl.
File metadata
- Download URL: fastbt_quant-0.6.0-py3-none-any.whl
- Upload date:
- Size: 77.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8230de1b2cf20143a16f254fd07f078f548806c81f499e47c17df13fa13def21
|
|
| MD5 |
74f94a8238b27265a767a76a30af0e0a
|
|
| BLAKE2b-256 |
0c841f875caa87cdce0fd5727ea00371433913eaefef919fe3da7b6fcc85e6ce
|
Provenance
The following attestation bundles were made for fastbt_quant-0.6.0-py3-none-any.whl:
Publisher:
release.yml on Vbhadala/fastbt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastbt_quant-0.6.0-py3-none-any.whl -
Subject digest:
8230de1b2cf20143a16f254fd07f078f548806c81f499e47c17df13fa13def21 - Sigstore transparency entry: 1472142557
- Sigstore integration time:
-
Permalink:
Vbhadala/fastbt@7f2d1d99476db8d8e44fb064b4a084c0735136bb -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/Vbhadala
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7f2d1d99476db8d8e44fb064b4a084c0735136bb -
Trigger Event:
push
-
Statement type: