Spaces:
Running on Zero
A newer version of the Gradio SDK is available: 6.25.0
Backtest Lab v1.0.0 β Handoff
Built end-to-end and deployed. Everything below was verified against the live Space, not just locally.
Live URLs
| What | URL |
|---|---|
| Backtest Lab (Space) | https://huggingface.co/spaces/Bit-Trading-Company/bit-backtest-lab |
| Direct app | https://bit-trading-company-bit-backtest-lab.hf.space |
| Signal store (Dataset) | https://huggingface.co/datasets/The-Bit-Trading-Company/bit-signal-store |
Both tagged v1.0.0. Space stage: RUNNING on zero-a10g (ZeroGPU).
Deployment status
| Check | Result |
|---|---|
| Build | β 142s build + ~30s app start |
| Cold start (app boot) | β ~30s, under the 60s budget |
| HTTP response | β 200 in 0.74s |
| Hardware | β
ZeroGPU (zero-a10g) granted β no billing escalation needed |
| Example config end-to-end | β
/load_example returns the design's worked example |
| Backtest on live Space | β +33.2%, 12 trades, $486 costs β bit-identical to local |
| Warm run time | β 0.1s engine time for a 3-year daily run (budget: <2s) |
| Coverage map from live manifest | β
18 rows, all labelled real |
| Login button | β
present; /login/huggingface returns 302 |
| ZeroGPU detected in-app | β "ZEROGPU AVAILABLE Β· EXTENSION RUNS ON YOUR OWN QUOTA" |
Tests: all green
bash tests/run_all.sh --slow β 191 passed, run with -W error::FutureWarning.
Verified on both Python 3.11 (dev) and Python 3.10 (what the ZeroGPU image actually provides) β 190 offline + 1 real-model test.
The six known-answer tests
| # | Test | Status |
|---|---|---|
| 1 | Buy & Hold, zero costs, reproduces the asset return (tol 1e-9) | β |
| 2 | Deliberately lookahead-biased strategy is caught structurally | β |
| 3 | Zero-cost vs costed run differ by exactly the modelled costs | β |
| 4 | Zero-signal strategy β flat equity, zero trades | β |
| 5 | Walk-forward train/test never overlap; holdout untouched by selection | β |
| 6 | Same config + same data β bit-identical results | β |
31 tests cover these six directly. Test 2 catches three distinct cheats:
reading bar t+1, a centred rolling window, and full-sample normalisation.
Spec compliance
- β
No lookahead paths. Enforced two ways: the engine applies the one-bar
shift itself (a strategy cannot bypass it), and
assert_causalproves it by perturbing future prices up and down and requiring earlier outputs to be bit-identical. All seven shipped presets pass. - β
Fills at next bar open.
fillaccepts onlynext_open; anything else raises. A test asserts the entry price equals bar 1's open, not bar 0's. - β
Costs default ON.
Costs().enabled is True.gross β costs = netholds to 1e-9 on every trade row. Costs-off runs are labelled "these numbers are not achievable" in the UI. - β
Every displayed metric traceable to engine output. The stat band, trade
table and report are asserted against
metrics.pyvalues intest_ui.py. - β
No placeholder slices remain. All 18 signal slices are real
Chronos-Bolt output. The
PLACEHOLDERlabelling machinery is still present and tested for contributors on weaker hardware. - β
No arbitrary code execution. Adapter families are allow-listed, model
ids are validated,
trust_remote_codeis never set, and the design'sCustom (code)preset is rendered disabled with an explanation. - β
Token hygiene. Scanned every file in both repos: the token appears in
none of them. It exists only in the local gitignored
key.txtand as theHF_WRITE_TOKENSpace secret.
What shipped
Signal store β 18 signal slices / 60,375 real rows across
chronos-bolt-small + chronos-bolt-base, 6 assets (BTC, ETH, SOL, SPY, QQQ,
NVDA) Γ 3 timeframes (1d, 1h, 15m); 15 price slices; 4 precomputed comparison
tables (model performance, calibration, directional accuracy, strategyΓtimeframe
heatmap).
Derived data worth reading: Chronos-Bolt is consistently over-dispersed β its nominal 80% q10βq90 band actually covers 83β89% of outcomes across every slice. Worth knowing before trusting a q10 stop level.
Engine β vectorbt, next-bar-open fills, commission + fixed/volume-scaled slippage, SL/TP/trailing stops, fixed-% and vol-target sizing, three validation modes with a locked holdout, full IS/OOS/holdout metric split, trade list with R-multiple, MAE/MFE and per-trade trigger reasons.
UI β the design implemented in Gradio Blocks with the Bit design system vendored verbatim (tokens + Styrene/Mac Minecraft fonts): Strategy Builder (5 numbered sections), Results Canvas (Overview / Trades / Comparison / Robustness / Report / Coverage), Run Manager, pinned disclaimer footer.
β οΈ Human-verify checklist
These need a real human with a real account β I cannot complete them:
- OAuth login end-to-end. The button renders and
/login/huggingfaceredirects correctly, but I cannot complete a real sign-in. Click Sign in with Hugging Face and confirm you land back signed in. If it fails, check Space settings β thehf_oauth: trueheader is set inREADME.mdand may need the owner to approve the OAuth app once. - ZeroGPU extension with a real account. Sign in, open the Coverage tab, pick an uncovered range, press Estimate then Extend coverage. Confirm: progress stages appear, the commit lands in the dataset repo, and the toast reads "Coverage extended by @you". This is the only path that spends real GPU quota, so it is untested against real hardware.
- Add-model flow with a real Hub model id, same reason.
- Torch version difference. The Space runs torch 2.11.0 (ZeroGPU only accepts 2.11/2.10/2.9.1/2.8); my adapter smoke test ran locally on 2.13.0. The first real ZeroGPU extension doubles as the check that Chronos inference behaves identically on 2.11.
- Visual pass on a real screen. I verified the UI functionally and via screenshots, but the in-app browser stalled before I could click through every tab at full size. Worth a human eye on the Comparison and Robustness tabs with several runs loaded.
Open items from DECISIONS.md
- D-001 β Space is under the personal namespace. Gradio Spaces under an org
need a paid Team/Enterprise plan (402). You chose the personal namespace; the
dataset stays under the org. To move it later, subscribe and re-point
BIT_SPACE_REPO. - D-006 β Binance is geo-blocked (451) from the seeding machine; crypto came
from Coinbase via the fallback chain. Stooq now serves an HTML block page,
so the equity chain effectively runs on yfinance alone. Tiingo is wired but
needs a
TIINGO_KEYsecret to join the chain. - Sentiment is a stub.
Sentiment-Gated Momentumruns on a labelled, deterministic price-derived proxy behind aSentimentSourceinterface β not news sentiment. Wiring a real feed is the obvious next task. - Pairs Trading is in the design's preset list but not runnable in v1 (needs a second leg). It is shown disabled with that reason.
- Equity intraday depth is provider-capped (~730d hourly, ~60d 15m) and
recorded in the manifest as
provider_max_days, not hidden.
Things I'd flag as genuinely uncertain
- The example strategy loses money on the locked holdout (β5.2%, Sharpe β0.38). That is the system working: the holdout is doing its job and the app reports it rather than burying it. Don't read the headline +33.2% as a result.
- Walk-forward on short ranges produces no OOS windows at all. The app now
says so explicitly instead of printing
0.00, but it is easy to configure a run that looks complete and has no out-of-sample content. Watch for the red note. vectorbtis pinned to 1.0.0, not the 1.1.0 I developed against, because 1.1.0 requires Python β₯3.11 and the ZeroGPU image is 3.10. The full suite passes on both, but that is a real version skew between dev and prod.
Repo layout
hugging-face/
key.txt local only, gitignored, never committed
DECISIONS.md 15 logged decisions across all phases
HANDOFF.md this file
ops/ provisioning scripts (local only, not deployed)
store_stage/ dataset card staging
space/ β pushed to the Space
app.py src/{store,data,engine,strategies,adapters,charts,metrics,
comparisons,runtime,extension,config,ui/theme}.py
scripts/seed_store.py tests/ (191) assets/ (vendored design system)
DESIGN_NOTES.md the extracted design contract Phase 3 was built against