Skip to main content

ebt

Backtesting and evaluation toolkit for position-based trading strategies.

You bring a price frame (DatetimeIndex × assets) and a position frame on the same assets. ebt fits models walk-forward, turns predictions into positions, backtests them with an execution lag, reports key figures, and sweeps strategy variants into sub-portfolios. The hot loops (backtest, execution simulation, position combination, outlier detection) are Rust kernels built into the package as ebt_rust.

Features

  • Walk-forward prediction (ebt.predict): retrain any fit/predict model on a rolling window with an embargo gap, for sklearn-style linear models and PyTorch nets.
  • Feature and label helpers (ebt.features): multi-scale EMA filter banks, forward labels, OHLC resampling with causal timestamps, cross-sectional ranks.
  • Backtest and key figures (ebt.simulate.kf): PnL from positions and prices with an integer or timedelta execution lag, Sharpe decomposition, turnover, holding time, Sharpe-by-lag, trend correlation. Wide (one column per asset) and stacked/long input.
  • One-pager and ensemble sweeps (ebt.simulate.plot): one_pager evaluates one book and draws the standard figure; plot_ensemble evaluates a list of variants, splits them into sub-portfolios by config variable and summarises the sweep.
  • Risk sizing and sanity checks: risk_adjust to a target annualised risk, risk_to_lots, cost-aware sizing (ebt.simulate.costaware), and a position_sanity_gate that flags broken books before anyone reads their Sharpe.
  • Execution models: VWAP-ramped fills, spread-aware timing, trade suppression below a risk threshold; effective-spread estimators from daily OHLC.
  • Futures tooling (ebt.futures): contract selection, rolls, Panama adjustment, blended rolls, interpolated and open-interest-weighted continuous series, position decomposition back into contracts.
  • Streaming outlier detection (ebt.data.outlier): point-in-time detectors for trades, quotes and OHLCV bars.
  • Parallel map (ebt.parallel): fork or lean-subprocess workers with zero-copy shared frames.

Install

Requires Python 3.9+ and a Rust toolchain (https://rustup.rs) to build the extension.

pip install ebt-backtest

Wheels are published for Linux x86_64 and Windows (Python 3.9+); other platforms, including macOS, build from the sdist with a Rust toolchain. From a checkout:

pip install .
# editable install (the top-level ebt_rust extension needs compat mode):
pip install -e . --config-settings editable_mode=compat

Extras: ebt[notebook] (IPython widgets for the tabbed sweep views), ebt[optimize] (cvxpy for the allocation optimiser), ebt[test].

Quickstart

import numpy as np, pandas as pd
import ebt
from ebt.simulate.linear import Ridge
from ebt.simulate.plot import one_pager, plot_ensemble
from ebt.simulate.tricks import risk_adjust

# synthetic 5-minute prices for four assets
rng = np.random.default_rng(0)
idx = pd.date_range('2022-01-03', '2023-12-29', freq='5min')
rets = 1e-3 * rng.standard_normal((len(idx), 4))
prices = pd.DataFrame(100 * np.exp(np.cumsum(rets, axis=0)), index=idx, columns=list('ABCD'))

# daily features and forward labels; a bar's timestamp is when its price is known
daily_ret = prices.resample('1D', label='right').last().dropna().pct_change()
features = ebt.apply_filters(daily_ret, lengths=[4, 16, 64]).replace([np.inf, -np.inf], np.nan).dropna()
labels = ebt.make_forward_labels(daily_ret, smoothing_length=5).reindex(features.index)

# walk-forward fit: retrain every 60 days on an expanding window, 10-day embargo
def walk_forward(model):
    out = ebt.predict(features, labels, models=[model], min_lookback='120D',
                      retrain_interval='60D', embargo='10D', progressbar=False)
    return risk_adjust(out[model], prices, shift='2min')   # scale to annualised risk 1.0

pos = walk_forward(Ridge(alpha=1.0))

# evaluate against the raw prices with a 2-minute execution lag
res = one_pager(pos, prices, shift='2min')
print(res['kf'])            # sharpe, sharpe exbias, turnover, holding(days), lptp, ...

# sweep variants and compare sub-portfolios per config variable
configs = [{'alpha': a} for a in (0.1, 1.0, 10.0)]
positions = [walk_forward(Ridge(alpha=c['alpha'])) for c in configs]
ens = plot_ensemble(positions, prices, configs_df=pd.DataFrame(configs), shift='2min')
print(ens['kfs_df'])        # one row per variant: config columns + key figures

Neural-net models work the same way: ebt.simulate.nn.NeuralNetModel(create_net=FeedForwardNet, n_hidden=[16], ...) trains with a masked MSE loss by default; pass create_loss_f for your own objective.

Conventions

  • Timestamps mark when a value is known. Resample prices with label='right'; the bar stamped T holds prints in (T - freq, T]. make_ohlc and make_forward_labels follow this.
  • Positions are frames on the same assets as the prices. NaN means "carry the previous position", 0 means flat. After risk_adjust a book has annualised risk 1.0, so key figures across books are comparable.
  • Execution lag shift is an integer number of price bars or a timedelta ('2min', pd.Timedelta('5m')). A timedelta fills at the first price print at or after stamp + shift, so pass raw prices at a resolution finer than the signal.
  • Stacked (long) input: backtest_stacked and friends accept one row per (time, contract) in pandas or Polars. Column names are auto-detected (ts / time / timestamp for time, series / symbol / contract for the contract, price / close / return for the value) and can be given explicitly with time_col=, contract_col=, pos_value_col=, price_value_col=.
  • Futures tools take a columns={...} mapping (time, contract, expiry, price, weight) so any source schema works without renaming; outputs add a series column naming the continuous series ('FUT(front)', 'FUT(30d)', 'FUT(top3)', ...).

Key figures

Computed by one_pager and plot_ensemble ("kf" = key figure). Sharpe and risk use daily PnL.

Key figure Meaning
sharpe Annualised Sharpe of the book's daily PnL.
sharpe exbias / sharpe bias Sharpe of the timing component and of the static component. Each asset's position (in risk units) is split into its time-mean and the remainder; bias is the PnL of the constant mean position, exbias the rest. Slow books can earn most of their raw Sharpe from bias.
sharpe long / sharpe short Sharpe of the long and short legs.
sharpe w costs Sharpe after spread costs when spreads= is given.
turnover Daily `sum(
holding(days) Average holding period implied by the turnover.
lptp Long-position-to-total-position: `0.5 + sum(pos) / (2 sum(
up_days Fraction of days with positive PnL.
risk Annualised std of daily PnL (1.0 after risk_adjust).
ncvar Normalised covariance ratio: the book's variance under an EWMA correlation model divided by its variance if the assets were uncorrelated. 1 for uncorrelated positions, above 1 when positions load on correlated assets, below 1 when they hedge each other.
sharpe_decay Log-log slope of Sharpe against holding time across smoothed versions of the book; near 0 the signal persists, around -0.5 it decays like noise.
skew, kurtosis Of the daily PnL.

Package map

Module Contents
ebt.features.core apply_filters, make_forward_labels, make_ohlc, make_daily_from_intraday, normalized_rank, ffill_timeseries
ebt.simulate.predict predict walk-forward driver, generator, enough_data
ebt.simulate.model, .linear, .nn Model base class, sklearn wrappers (Ridge, LassoLars, SparseMarkowitz, ...), NeuralNetModel / LSTMNeuralNetModel
ebt.simulate.kf backtest, backtest_stacked, sharpe, turnover, holding_time, lptp, ncvar, sharpe_per_lag, holding_curve, sharpe_decay, correlation_to_trend
ebt.simulate.plot one_pager, plot_ensemble, per_year_table, figure building blocks
ebt.simulate.postprocessing, .tricks risk_adjust, risk_to_lots, combine_positions, split_portfolios, nanagg
ebt.simulate.gate position_sanity_gate
ebt.simulate.grid ReducedGrid: evaluate sparse signals on the price rows that matter
ebt.simulate.execution, .spread, .costaware execution simulators, spread estimators, cost-aware sizing and turnover budgets
ebt.simulate.optimize ensemble weight optimisation
ebt.futures contract schedules, rolls, continuous prices and weights, position decomposition
ebt.data.outlier detect_outliers for trades, quotes and bars
ebt.parallel pmap
ebt_rust the compiled kernels (not a public API)

Tests

pip install -e . --config-settings editable_mode=compat
python -m pytest tests
cd ebt_rust && cargo test --profile perf

Releasing

scripts/release.sh X.Y.Z tags a release and the GitHub workflow publishes it; see RELEASING.md.

License

MIT, see LICENSE.

Metadata

Release files for ebt-backtest 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ebt-backtest 0.1.0
File Size Uploaded Provenance Actions
ebt_backtest-0.1.0.tar.gz 242.6 kB With provenance Details for ebt_backtest-0.1.0.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for ebt-backtest 0.1.0
File Interpreter ABI Platform Actions
ebt_backtest-0.1.0-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details for ebt_backtest-0.1.0-cp39-abi3-win_amd64.whl
ebt_backtest-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details for ebt_backtest-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Total release size: 1.4 MB

Release files / ebt_backtest-0.1.0.tar.gz

Download URL ebt_backtest-0.1.0.tar.gz
Size 242.6 kB
Tags Source
SHA-256 checksum
How to use checksums
58f6d0ac465b1ef7ffcf1ac31b0ce82decf12c56a74fafe73359482e00c7a681
BLAKE2b-256 checksum
How to use checksums
db0ffea20ecbba4dce7bc2d2fc88b455a4ce20e110d96bfb63da44e171d0c7ff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

Release files / ebt_backtest-0.1.0-cp39-abi3-win_amd64.whl

Download URL ebt_backtest-0.1.0-cp39-abi3-win_amd64.whl
Size 510.1 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
14edf5323fe59a9e03bbdb9ee07441b40546282e8042e225555e82f598c9f071
BLAKE2b-256 checksum
How to use checksums
28b76ef4de0af4b4a53700d0094700536cd08ca4172e4029f595e5ebb513919c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

Release files / ebt_backtest-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL ebt_backtest-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 635.6 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
25447793922c81b603172ab84ecfa25db7755b8f122b6c452fcf001d1456dc52
BLAKE2b-256 checksum
How to use checksums
95aaa36119acdcb68975bc2c2f916d283e6c713c1cf885c886eb97de4f423634
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

Security for ebt-backtest 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 3 files in this release have been uploaded with provenance metadata.

Select from 3 files

Attestation for ebt_backtest-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

3 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page