Skip to main content

Pandas-native backtesting library with backtest/live parity

Project description

fastbt-quant

Pandas-native backtesting library with backtest/live parity.

PyPI Python License: MIT

Distribution name on PyPI is fastbt-quant; the import name stays fastbt.

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, BacktestResult out. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastbt_quant-0.5.0.tar.gz (129.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastbt_quant-0.5.0-py3-none-any.whl (68.9 kB view details)

Uploaded Python 3

File details

Details for the file fastbt_quant-0.5.0.tar.gz.

File metadata

  • Download URL: fastbt_quant-0.5.0.tar.gz
  • Upload date:
  • Size: 129.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.12

File hashes

Hashes for fastbt_quant-0.5.0.tar.gz
Algorithm Hash digest
SHA256 b2d0781cfbbd1a86f58624d00ab77737464543ae174933dfbef0ba26c2e741ac
MD5 63ca8647d5c2801d0ca4059d495a9558
BLAKE2b-256 d4deaef22e7c0fecf23c235dac574afb3b0483bfd22e4a6546135303c4a2e1ba

See more details on using hashes here.

File details

Details for the file fastbt_quant-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: fastbt_quant-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 68.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.12

File hashes

Hashes for fastbt_quant-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 995f0c3a50ed1ea16c5feffe22443683ccee16eb210cb0e13ec2fe5a40e8f21b
MD5 2ea8747885e1adac4b7333f2a2baf029
BLAKE2b-256 e43d6a53cfc7bd6844adbd728901c0c29f7e32c54fd82f50d2e201ea5174bea7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page