Metadata-Version: 2.4
Name: sharpe-gate
Version: 0.1.0
Summary: Honest-N trial registry and multiple-testing gate for backtests: DSR, PSR, PBO (CSCV) and minBTL spent on the trial count you actually ran.
Project-URL: Homepage, https://github.com/bond-labs-dev/sharpe-gate
Project-URL: Issues, https://github.com/bond-labs-dev/sharpe-gate/issues
Author-email: bondlabs <hello@bondlabs.dev>
License-Expression: MIT
License-File: LICENSE
Keywords: backtesting,deflated-sharpe-ratio,multiple-testing,overfitting,pbo,quantitative-finance
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.10
Requires-Dist: numpy>=1.24
Requires-Dist: pandas>=2.0
Requires-Dist: pyarrow>=14
Requires-Dist: scipy>=1.10
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.16; extra == 'dev'
Requires-Dist: scikit-learn>=1.3; extra == 'dev'
Description-Content-Type: text/markdown

# sharpe-gate

Everyone computes a Sharpe ratio. Nobody stores the honest trial count behind
it. Without that count `N`, every multiple-testing statistic — the Deflated
Sharpe Ratio above all — is decoration: it deflates by however many trials you
*remembered* running, not by how many you actually ran across every session,
notebook, and parameter sweep.

This package is the machinery that keeps `N` honest, and the gates that spend
it:

- **Trial registry** (`sharpe_gate.registry`) — every backtest persists one
  append-only row keyed by a code-agnostic `config_hash`, plus its daily
  PnL/exposure stream. SQLite (WAL) — safe for concurrent writers. Studies are
  pre-registered (declared trial count, kill threshold) *before* the search;
  you cannot record a trial without one.
- **Canonical dataset fingerprints** (`sharpe_gate.fingerprint`) — the
  pooling key fingerprints the *data*, never the hypothesis, and is validated
  structurally at the write boundary. A spelling drift or a hypothesis suffix
  cannot carve a private, smaller-N pool out of shared data. Venues ship as
  prefixes; `register_source("bybit")` adds your own without turning the
  validation off.
- **Overfit gate** (`sharpe_gate.overfit`) — DSR (Bailey & López de Prado)
  on the registry-resolved pool, PSR fallback for N=1, expected-max-Sharpe
  under H0, PBO via CSCV on purged+embargoed folds, and a shuffled-label
  held-out AUC floor for ML overlays. The entry point takes a `trial_id`, not
  a bare float — an unrecorded Sharpe cannot reach the gate. Kurtosis is
  **Pearson** (normal = 3.0) throughout; passing excess kurtosis raises rather
  than quietly inflating the result.
- **minBTL** (`sharpe_gate.min_btl`) — the cheapest kill: is the sample
  even long enough to support the hurdle Sharpe given `N`? Pure arithmetic,
  runs before any backtest.

None of this contains alpha. It is machinery against self-deception — the part
that tells you the unpleasant truth before you pay for it.

## Install

```
pip install sharpe-gate
```

## Quick start

```python
from sharpe_gate import register_study, record_trial, overfit_screen

study = register_study(
    study_id="my_screen_v1",
    hypothesis_id="H42",
    generator="grid",
    dataset_keys=["binance_btc_perp_4h"],
    statistic="sharpe",
    n_declared=24,
    kill_threshold=0.95,
)
trial_id = record_trial(study, config={"lookback": 20}, stream=(daily_pnl, in_position))
result = overfit_screen(trial_id)   # -> OverfitResult(probability, method="dsr"|"psr", ...)
```

The store location resolves in order: explicit `store_dir=` argument →
`SHARPE_GATE_DIR` env var (`HL_REGISTRY_DIR` is honored as a legacy alias)
→ `sharpe_gate.registry.DEFAULT_STORE_DIR` (settable by a host repo) →
`./data/registry` under the current working directory.

## What it does not do

It contains no alpha, no strategies, and no data. It never asks the network,
holds no keys, and cannot place an order. The registry is a local SQLite file
plus Parquet stream sidecars.

## Development

```bash
git clone https://github.com/bond-labs-dev/sharpe-gate
cd sharpe-gate
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest -q
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for the checks CI runs and the
invariants a change must not break.

## Status

Alpha. The API is stable in practice but not promised across 0.x.

## License

MIT — see [LICENSE](LICENSE).
