Metadata-Version: 2.4
Name: honest-bt
Version: 0.1.0
Summary: Intra-bar-fidelity crypto backtester with overfit gates built in
Project-URL: Homepage, https://github.com/mekamello/honest-bt
Project-URL: Issues, https://github.com/mekamello/honest-bt/issues
Author: mekamello
License: MIT
License-File: LICENSE
Keywords: backtesting,cpcv,crypto,quant,trading,walk-forward
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.10
Requires-Dist: numpy>=1.24
Provides-Extra: dev
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Description-Content-Type: text/markdown

# honest-bt

**Intra-bar-fidelity crypto backtester with overfit gates built in.**

```bash
pip install honest-bt
```

---

## Why this exists

In May 2026 I caught **9 separate bugs** in my own backtester that were inflating reported Sharpe by up to **+5.0**. After fixing them, my mean-reversion strategy went from `Sharpe -2.12` to `Sharpe +1.04` on the **same 14 entries** — same data, no overfitting, just an honest fee + slippage + exit model.

The tools that caused those bugs (and the ones that caught them) are in this library.

If you've been backtesting against close-to-close prices with flat percentage fees, **your Sharpe is wrong**. This library fixes the four things that matter most:

1. **Intra-bar SL/TP fidelity** — bar-path inference (bullish: low-then-high; bearish: high-then-low) so the simulator never uses a future extreme's information to tighten SL before checking the prior extreme.
2. **Realistic fee model** — per-fill maker (limit) vs taker (market) distinction × leverage. Ladder TPs charged at maker (`0.02%`); SL/trailing/market exits at taker (`0.055%`).
3. **Funding accrual** — 8-hour marks, sign-aware (longs pay positive funding, shorts receive). Plus a funding-drain exit that closes rotten longs.
4. **Overfit gates** — CPCV (combinatorial purged cross-validation) + PBO (probability of backtest overfit) + Probabilistic / Deflated Sharpe per Bailey & López de Prado 2014.

No live trading. No exchange connectors. No strategy templates. **This is a library, not a framework.**

---

## 3-minute quickstart

```python
from datetime import datetime, timedelta, timezone
from honest_bt import Position, simulate_exit

start = datetime(2026, 5, 1, tzinfo=timezone.utc)

position = Position(
    symbol="BTCUSDT", side="long", entry_price=100.0, entry_ts=start,
    qty=1.0, leverage=5,
    stop_loss_pct=4.0, take_profit_pct=8.0,
    vol_regime="medium",
)

# Each candle: dict with timestamp, open, high, low, close, (optional) volume.
# Must be ASCENDING by timestamp, AT or AFTER position.entry_ts.
candles = [
    {"timestamp": start + timedelta(minutes=i), "open": 100, "high": 105,
     "low": 99, "close": 103, "volume": 100.0}
    for i in range(60)
]

outcome = simulate_exit(position, candles)

print(f"Exit reason: {outcome.final_reason}")
print(f"Net PnL: {outcome.realized_pnl_pct:+.2f}%")
print(f"Hold:    {outcome.hold_minutes} min")
print(f"Fees:    {outcome.fees_paid_pct:.4f}%  Funding: {outcome.funding_paid_pct:.4f}%")
for fill in outcome.fills:
    print(f"  fill {fill.reason} @ {fill.price:.2f}  (portion {fill.portion:.2f})")
```

---

## What `honest-bt` does that Backtrader / VectorBT don't

| Feature | honest-bt | Backtrader | VectorBT |
|---|---|---|---|
| Intra-bar SL/TP path inference | ✓ | partial | ✗ |
| Per-fill maker/taker fees | ✓ | flat % | flat % |
| Funding accrual (crypto perps) | ✓ | ✗ | ✗ |
| Trailing SL two-phase (ease-out + linear) | ✓ | linear only | ✗ |
| Ladder TP (3 levels, vol-regime spacing) | ✓ | manual | manual |
| Funding-drain exit | ✓ | ✗ | ✗ |
| Reversal-detection exit (7 HTF signals) | ✓ | ✗ | ✗ |
| CPCV + PBO overfit gate | ✓ | ✗ | ✗ |
| Probabilistic / Deflated Sharpe (Bailey-LdP 2014) | ✓ | ✗ | ✗ |
| Slippage model (bps per fill side) | ✓ | manual | ✗ |
| Production-validated (caught 9 of its own bugs) | ✓ | ? | ? |

This is a *crypto-first* library. Spot / margin trading works fine, but the differentiator is in perpetual futures.

---

## API surface

### Core

```python
from honest_bt import (
    Position,          # input dataclass
    FundingPoint,      # optional funding rate history
    FillEvent,         # output: each partial fill
    ExitOutcome,       # output: full per-position result
    simulate_exit,     # the main function
)
```

### Metrics & validation

```python
from honest_bt import (
    BacktestMetrics, compute_metrics,
    deflated_sharpe, expected_max_sharpe,
    beats_baseline, beats_baseline_deflated,
    walk_forward_gate,
    split_outcomes_at, split_outcomes_into_folds,
)
```

### CPCV

```python
from honest_bt import (
    CPCVSplit, cpcv_split,
    compute_pbo, aggregate_cpcv_metrics,
)
```

---

## The 9 bugs we caught (May 2026)

Long-form post coming. Short list:

1. **Sharpe double-flip bug** — metrics aggregator was negating short-side returns twice (sign error). Variants ranked best on training Sharpe were actually the worst on test.
2. **DCA simulator MTM bug** — accumulation strategy was reporting realized-only PnL, hiding mid-cycle drawdowns that hit -30%. Honest MTM Sharpe collapsed from inflated +16 → -0.13.
3. **Limit-timeout fee blend** — maker entries were never charged taker fees on the ~7% of orders that timed out, systematically under-counting costs.
4. **HTF cursor per-timeframe bug** — the higher-timeframe indicator cache was silently skipping 5-15% of evaluations when the cursor advanced across two timeframes simultaneously.
5. **MR clock-source mismatch** — backtest cooldown gate used `time.monotonic()` while live used wall-clock; cooldowns effectively disabled in backtest, fired in live.
6. **Funding-arb paper-mode signal swallow** — paper-mode emitted NOTIFICATION events instead of SignalEvents; "0 trades dormant" verdict was a harness bug, not a strategy result.
7. **Variant-yaml params no-op** — evolution loop's `params:` wrapper silently dropped overrides; "promising" variant results were just baseline noise.
8. **Graveyard dead-code on live** — `AssetGraveyard.refresh_for_asset()` was never called in production; backtest enforced it strictly. Live looked better than it should have, backtest worse.
9. **K8 magnitude scaling inverted** — liquidation-cascade reversion strategy's signal size was inversely correlated with cascade magnitude. Smaller cascades → larger positions.

**Lesson:** a backtest that doesn't reproduce live behavior to within sub-1% PnL is lying. `honest-bt` is the de-couple-and-port of the harness that found these.

---

## Roadmap

- **v0.2** (Q2 2026): live data adapters for Binance / Bybit / Hyperliquid (CSV ingestion only in v0.1)
- **v0.3** (Q3 2026): stocks / options examples (exit model is asset-agnostic once SL/TP/trailing semantics are defined)
- **v0.4** (Q4 2026): hosted backtest API (paid tier — see `honest-bt-cloud`)

---

## Contributing

PRs welcome. Please include a regression test for any bug fix. Style: `ruff check src/ tests/` must pass.

---

## License

MIT. Use it freely, sell paid services around it, fork it. If it saves you from a Sharpe-inflation bug, [open an issue](https://github.com/mekamello/honest-bt/issues) and tell us — we'd love to add it to the case-studies list.

---

## Related work

- López de Prado, M. (2018). *Advances in Financial Machine Learning*. Wiley. (CPCV chapter)
- Bailey, D. & López de Prado, M. (2014). *The Deflated Sharpe Ratio: Correcting for Selection Bias, Backtest Overfitting, and Non-Normality*. Journal of Portfolio Management.
- Stoic.ai / QuantPedia blog posts on slippage modeling for crypto perps.
