Bit-Trading-Company commited on
Commit
39c225c
Β·
verified Β·
1 Parent(s): d8478d9

Add DECISIONS.md

Browse files
Files changed (1) hide show
  1. DECISIONS.md +182 -0
DECISIONS.md ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DECISIONS
2
+
3
+ Running log of choices the build spec left open, and of anything that failed.
4
+ Newest phase last.
5
+
6
+ ---
7
+
8
+ ## Phase βˆ’1 β€” Provisioning
9
+
10
+ ### D-001 β€” The Space lives under the personal namespace, not the org β€” **decided by the human**
11
+
12
+ `create_repo(..., repo_type="space", space_sdk="gradio")` under
13
+ `The-Bit-Trading-Company` returns **402 Payment Required**:
14
+
15
+ > Static Spaces are free for everyone, but hosting Gradio and Docker Spaces on
16
+ > free cpu-basic requires a Team or Enterprise plan for organization
17
+ > The-Bit-Trading-Company.
18
+
19
+ Verified this is a plan limit, not a token-scope problem: the token has
20
+ `repo.write` on the org, the dataset repo created fine under the org, and a
21
+ *static* Space probe under the org succeeded. The org has no paid plan; the
22
+ owner's personal account is PRO.
23
+
24
+ This hit the "anything requiring payment" flag, so it went to the human, who
25
+ chose the personal namespace.
26
+
27
+ | Repo | Location |
28
+ |---|---|
29
+ | Signal store (dataset) | `The-Bit-Trading-Company/bit-signal-store` |
30
+ | Backtest Lab (Space) | `Bit-Trading-Company/bit-backtest-lab` |
31
+
32
+ The shared data asset keeps the company namespace; only the app URL changes.
33
+ The personal account being PRO means ZeroGPU is still available for Phase 4, so
34
+ no capability is lost. Moving the Space to the org later is a rename, and
35
+ `src/config.py` reads both ids from env (`BIT_SPACE_REPO`, `BIT_STORE_REPO`).
36
+
37
+ ### D-002 β€” Python 3.11 toolchain
38
+
39
+ The machine had only Python 3.14, which numba (and therefore vectorbt) does not
40
+ support. Used `uv` to install 3.11.15 into a local `.venv`. Final resolved
41
+ stack: vectorbt 1.1.0, numpy 2.4.6, pandas 2.3.3, numba 0.67.0, pyarrow 25.0.1,
42
+ plotly 6.9.0, gradio 5.49.1. `requirements.txt` pins these for the Space, and
43
+ the Space is pinned to `python_version: "3.11"` to match.
44
+
45
+ ### D-003 β€” Writes are one atomic commit, not "manifest last"
46
+
47
+ The spec asks for `CommitScheduler` with "atomic manifest update last". A single
48
+ `create_commit` carrying the parquet slices *and* the manifest is strictly
49
+ stronger: readers can never observe a manifest referencing a slice that has not
50
+ landed yet. `SignalStore.flush()` does that, ordering the manifest as the final
51
+ operation within the commit. `SignalStore.attach_scheduler()` still provides
52
+ `CommitScheduler`-based background batching for the running Space, pointed at
53
+ the same local mirror, so the two paths cannot disagree about what is on disk.
54
+
55
+ ### D-004 β€” `Custom (code)` preset is present but inert
56
+
57
+ The design's preset list ends with `Custom (code)`, backed by a code editor. The
58
+ build spec says "No arbitrary code execution from user input anywhere", which is
59
+ the stronger constraint. The control is rendered, disabled, with an explanation,
60
+ rather than silently dropped from the design β€” see `DESIGN_NOTES.md`.
61
+
62
+ ### D-005 β€” Design source recovered from local files
63
+
64
+ `claude.ai/design/p/00cbd10f…` returned 403 to every unauthenticated fetch and
65
+ no Chrome instance was connected, so the design was unreachable at first. The
66
+ human supplied the export locally. The Bit design system tokens and fonts are
67
+ vendored verbatim into `space/assets/`; `space/DESIGN_NOTES.md` records the
68
+ extracted layout, copy and component inventory that Phase 3 is built against.
69
+
70
+ ### Provisioning results
71
+
72
+ | Action | Result |
73
+ |---|---|
74
+ | `whoami` with token | OK β€” user `Bit-Trading-Company`, PRO, admin of the org |
75
+ | Token scope | fine-grained; `repo.write` on org + user |
76
+ | Create dataset `The-Bit-Trading-Company/bit-signal-store` | **created**, public |
77
+ | Create Space under org | **failed, 402** β€” see D-001 |
78
+ | Create Space `Bit-Trading-Company/bit-backtest-lab` | **created**, gradio, cpu-basic, public |
79
+ | Space secret `HF_WRITE_TOKEN` | **set** via API; value never logged or committed |
80
+ | Signal store init | **committed** β€” `manifest.json` (schema_version 1), `signals/ prices/ comparisons/ runs/`, dataset card |
81
+ | Hardware `zero-a10g` | not yet requested β€” deferred to Phase 4 per spec |
82
+
83
+ Token hygiene: the value lives only in the local `key.txt` (gitignored at the
84
+ repo root) and in the Space secret. It appears in no source file, no committed
85
+ config, and no log line. `.env.example` documents variable names only.
86
+
87
+ ---
88
+
89
+ ## Phase 0 β€” Data layer
90
+
91
+ ### D-006 β€” Providers are config; both primaries failed and the chain absorbed it
92
+
93
+ Two provider-level blocks appeared during seeding, and both were handled by the
94
+ fallback chain rather than by editing code:
95
+
96
+ - **Binance returns HTTP 451** ("restricted location") from the seeding machine.
97
+ Crypto OHLCV therefore came from **Coinbase**, the configured fallback.
98
+ - **Stooq now serves an HTML block page** instead of CSV. `data.py` detects the
99
+ missing CSV header and advances the chain, so equities ran on **yfinance**.
100
+
101
+ Neither was silently swallowed: every fallback is recorded in the fetch result's
102
+ notes, and `source` is stored per price row. Recorded here rather than escalated
103
+ because the spec's flag-to-human bar is provider blocking that persists *after
104
+ backoff and fallback exhaustion* β€” the fallbacks succeeded.
105
+
106
+ ### D-007 β€” Equity gap counts are calendar gaps, not data faults
107
+
108
+ Daily equity series report ~171 "gaps" over three years. Those are weekends and
109
+ holidays. The gap report measures distance from a continuous bar grid and is
110
+ surfaced as information; it never fails validation.
111
+
112
+ ## Phase 1 β€” Engine
113
+
114
+ ### D-008 β€” Causality is checked by perturbation, in both directions
115
+
116
+ The spec asks for a structural lookahead assertion. `assert_causal` scales the
117
+ tail of the price frame and requires every earlier output to be bit-identical.
118
+ Scaling **up only** proved insufficient: a boolean comparison that is already
119
+ `True` can survive an upward bump unchanged, and a peeking strategy slipped
120
+ through on one seed. The check now perturbs up *and* down at four probe points,
121
+ which catches obvious lookahead, centred rolling windows, and full-sample
122
+ normalisation.
123
+
124
+ ### D-009 β€” R-multiple when no stop is configured
125
+
126
+ R is P&L over initial risk, and initial risk is the stop distance. With no stop
127
+ there is no such distance, so the engine falls back to
128
+ `risk_per_trade_pct` (default 2% of entry notional). It is a documented config
129
+ field rather than a hidden constant.
130
+
131
+ ### D-010 β€” Costs are one atomic identity on every trade row
132
+
133
+ Slippage is embedded in the fill price rather than booked as a fee, so it is
134
+ reconstructed from the *unslipped* reference price (direction-aware: a buy fills
135
+ high, a sell fills low). This makes `gross βˆ’ costs = net` exact to 1e-9 on every
136
+ row, which is what known-answer test 3 asserts.
137
+
138
+ ### D-011 β€” The holdout applies to every validation mode
139
+
140
+ Originally only `mode="holdout"` reserved one. Walk-forward could then roll its
141
+ windows across the whole period. Since the design shows rolling windows and an
142
+ "OOS holdout LAST 6MO" side by side, `holdout_months > 0` now reserves a holdout
143
+ in every mode except `none`.
144
+
145
+ ### D-012 β€” An empty segment renders as "β€”", never 0.00
146
+
147
+ A short range under walk-forward can produce no out-of-sample window at all. The
148
+ stat band was reporting `OOS Sharpe 0.00` for that case, which asserts something
149
+ false. Segments with zero bars now render an em dash, the plan carries an
150
+ explicit note explaining why no OOS exists, and the overfit verdict counts it as
151
+ a failed check.
152
+
153
+ ## Phase 2 β€” Adapters & seed
154
+
155
+ ### D-013 β€” The v1 seed contains no placeholder slices
156
+
157
+ The spec allows clearly-labelled synthetic placeholders where GPU capacity is
158
+ short. Batched Chronos-Bolt inference on local MPS ran at roughly **1 ms/step**,
159
+ so the entire v1 seed was generated for real: **18 slices, 60,375 rows**, across
160
+ `chronos-bolt-small` and `chronos-bolt-base`, 6 assets, 3 timeframes.
161
+ `PlaceholderAdapter` and its `PLACEHOLDER` labelling remain in the codebase and
162
+ are still tested, because contributors on weaker hardware may need them.
163
+
164
+ ### D-014 β€” Dedup compares the *producible* range, not the requested one
165
+
166
+ A forecast needs a full trailing context window, so the first producible
167
+ timestamp sits `context_len` bars after the start of a price slice. Comparing
168
+ the user's typed range against manifest coverage therefore reported
169
+ already-covered slices as uncovered and would have paid for the same inference
170
+ twice. Both `scripts/seed_store.py` and `extension.estimate()` now compare the
171
+ range the request would actually produce. This was caught by the Phase 4 dedup
172
+ test, not by inspection.
173
+
174
+ ## Phase 5 β€” Ship
175
+
176
+ ### D-015 β€” torch pinned to 2.11.0 for ZeroGPU
177
+
178
+ The first deploy landed in `CONFIG_ERROR`: ZeroGPU accepts only torch 2.11.0,
179
+ 2.10.0, 2.9.1 or 2.8.0, and the build was pinned to the locally-tested 2.13.0.
180
+ Pinned to **2.11.0**. The local test environment still runs 2.13.0, so the
181
+ adapter code path is exercised on a newer torch than the Space uses β€” noted in
182
+ HANDOFF.md as a residual difference worth a post-deploy check.