honest-bt
Intra-bar-fidelity crypto backtester with overfit gates built in.
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:
- 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.
- 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%). - Funding accrual — 8-hour marks, sign-aware (longs pay positive funding, shorts receive). Plus a funding-drain exit that closes rotten longs.
- 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
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
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
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
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:
- 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.
- 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.
- Limit-timeout fee blend — maker entries were never charged taker fees on the ~7% of orders that timed out, systematically under-counting costs.
- 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.
- MR clock-source mismatch — backtest cooldown gate used
time.monotonic()while live used wall-clock; cooldowns effectively disabled in backtest, fired in live. - 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.
- Variant-yaml params no-op — evolution loop's
params:wrapper silently dropped overrides; "promising" variant results were just baseline noise. - 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. - 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 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.
Metadata
Release files for honest-bt 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| honest_bt-0.1.0.tar.gz | 39.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| honest_bt-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 72.8 kB
Release files / honest_bt-0.1.0.tar.gz
| Download URL | honest_bt-0.1.0.tar.gz |
|---|---|
| Size | 39.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
25c8e5b822f78f845dd02ed60e4ac1f26fd7dc70bbf19d038b5893960e74c672
|
|
BLAKE2b-256 checksum How to use checksums |
77130a3f7ac12c7ff724c7920f2b65b6ec9a9b30da7cd726df1482a10ad284e6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.11
|
Release files / honest_bt-0.1.0-py3-none-any.whl
| Download URL | honest_bt-0.1.0-py3-none-any.whl |
|---|---|
| Size | 33.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b13f32c0460cef762496c6621be33389c758f11671b45975c704fb94ba3c6bab
|
|
BLAKE2b-256 checksum How to use checksums |
ed0173de7020f2a3ce8819a7c9042924323d7d700215ee9faa76168a816daa92
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.11
|