Skip to main content

backtest-bias

DOI

Checks whether your backtest data is lying to you.

Most backtests don't fail loudly. They flatter you quietly, because the data underneath them is missing the stocks that died. This library tests your price panel for that, in one line, and tells you roughly what it costs when it finds it.

The measured numbers this library is built on

These are not estimates. I measured them on real Indian market data and published the write-ups:

  • 24% of the top-500 Indian stocks (as of 2015) are invisible to yfinance today: delisted, merged, or renamed with no public mapping. A universe built by fetching current listings runs on survivors only.
  • Survivor-only universes inflated equal-weight returns by +0.8 to +3.2 pp/yr depending on the universe vintage and the survivor definition. Same market, same method, a factor of four apart. Anyone quoting one number is guessing. (Working paper under review at SSRN.)
  • On the most widely used Kaggle NSE dataset, index-membership look-ahead added +10% terminal wealth cap-weighted and +43% equal-weighted over 2010-2021. The bias depends on construction.
  • How much of a universe should be dead? Measured across six top-500 vintages (2012-2022, Indian equities), the curve is stable: ~5-8% by 3 years, 11-14% by 5, 17-21% by 7, 24-30% by 10. Verdicts quote the range matched to your window length. If your panel lost zero names, your panel is the problem.

Citing this

If you use the library or the measured constants in BIAS_TABLE.md, cite it as:

Jain, A. (2026). backtest-bias: survivorship and integrity checks for financial price panels. Zenodo. https://doi.org/10.5281/zenodo.21770386

That DOI is the concept DOI: it always resolves to the latest version. GitHub also reads CITATION.cff in this repo, so the "Cite this repository" button in the sidebar produces BibTeX and APA directly.

Install

pip install backtest-bias

30 seconds to a verdict

import pandas as pd
from backtest_bias import check_survivorship

prices = pd.read_csv("my_panel.csv")   # wide (date x symbols) or long (date/symbol/close)
report = check_survivorship(prices)
print(report.summary())
survivorship check: 412 symbols over 9.2y, 0 died in-window (0%)
verdict: SEVERE - 412 names over 9.2y with zero deaths is the survivor-only signature;
comparable universes lose 22%-28% of names over 9y (measured)
expect EW returns inflated roughly +0.8-3.2 pp/yr vs an honest universe (measured,
vintage-dependent; see backtest_bias.REFERENCES)

What v0.1 ships

function what it answers
check_survivorship(prices) does my universe contain the stocks that died, or only the winners? Full report with severity and a measured bias estimate
dead_name_ratio(prices) one number: what fraction of my names end before the panel does. 0.0 = pure survivor panel
assert_integrity(prices) CI gate: raise if the panel smells survivor-only, so a silent re-download of bad data fails your pipeline instead of flattering your backtest

Input handling is forgiving: wide panels, long frames, sniffed column names, NaN-padded histories. Anything the library cannot judge honestly, it raises instead of guessing.

Roadmap

  • v0.2: look-ahead / point-in-time violations: fundamentals dated by period instead of announcement, index membership applied backwards, same-bar signal fills
  • v0.3: rename-continuity and corporate-action gap detection

Who, and how to get this run on your own data

I'm Ayan Jain. I build point-in-time Indian equity data and audit backtests and datasets for the biases that inflate them. The measured numbers above come from those audits.

If you want this class of check run on your own backtest or dataset by a person instead of a library, that's my Bias Check: you send the backtest or data, and within 48 hours you get a written verdict on survivorship, look-ahead / point-in-time integrity, cost realism, and marking, with what's wrong and roughly what it costs in return terms. Fixed price, Rs 7,500. Start it through the intake form or email ayanjain259@gmail.com. Larger or ongoing work is scoped separately, tell me the problem and I'll send a quote.

Public data and replication: a survivorship-free Indian equity dataset (NSE/BSE) and a runnable notebook that visualizes the bias on a sample are on Kaggle under financebroski (the dataset and the notebook).

The Bias Table: every measured number behind this library, one page, citable: BIAS_TABLE.md. New rows land by email at The Bias Ledger.

MIT licensed. Issues and war stories welcome, especially datasets that fooled you.

The practice behind this

I audit backtests and datasets professionally. The public record, 155 strategies tested and 143 killed, lives at financebroski.com/graveyard.html; the audit practice is at financebroski.com.

Release files for backtest-bias 0.1.3

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

Source distribution (sdist)

Source distribution for backtest-bias 0.1.3
File Size Uploaded
backtest_bias-0.1.3.tar.gz 10.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for backtest-bias 0.1.3
File Interpreter ABI Platform
backtest_bias-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 20.1 kB

Release files / backtest_bias-0.1.3.tar.gz

Download URL backtest_bias-0.1.3.tar.gz
Size 10.8 kB
Tags Source
SHA-256 checksum
How to use checksums
dd6a4eaad0a58f99973e9eabff00ff01c7b66fa0550e89285d0a96d1f535a9c1
BLAKE2b-256 checksum
How to use checksums
a66e84431b92653987c9844ed649afb79cec76d7dd369c45a6a7887d1df644dd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release files / backtest_bias-0.1.3-py3-none-any.whl

Download URL backtest_bias-0.1.3-py3-none-any.whl
Size 9.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fe168d51bf1f503e5c6b42ecb754388930df45e1939288c01c636918873e301b
BLAKE2b-256 checksum
How to use checksums
2a26ecb6bc75fb76af453354ce2ec678533a8450781f16ba76dd7114896b51f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release history Release notifications | RSS feed

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

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