Spec #1 (“PortWatch resolution nowcaster — backtest to go/no-go”) is BUILT, fully reviewed, and has now been RUN live end-to-end (2026-06-08). Single Rust crate hormuz_bet at ~/coding/hormuz_bet, 10 modules, built subagent-driven (TDD per module + two-stage spec+quality review each). Final state: cargo clippy --all-targets -- -D warnings clean; cargo test --lib = 30 passed / 3 ignored; golden integration test passes against live APIs. VERDICT: INCONCLUSIVE at both fee levels (fee_k=0 and fee_k=0.072) — point PnL is positive but the bootstrap-5% lower bound is deeply negative, the sample is tiny (~2-3 resolved weekly events in an anomalous wartime regime), and the determinacy split is backwards (high-confidence late trades LOST, early made money), so the positive point edge is noise, not skill. No robust edge → do not deploy capital; INCONCLUSIVE routes to the Spec #2 paper-trade. The full numbers and the honest read are in the Live Run section below. Resolution rules it implements live in hormuz-bet-resolution-mechanics; the validation-design intent it realizes lives in hormuz-bet-backtesting. Not a git repo (owner rule: never commit/init).
First implementation milestone of hormuz-bet: the backtest harness that turns the free PortWatch history into a defensible go / no-go / inconclusive verdict on whether there is a tradable edge.
Build Status (2026-06-08)
Spec #1 — “PortWatch resolution nowcaster — backtest to go/no-go” — is BUILT, fully reviewed, and has now been RUN live end-to-end (2026-06-08). It produced its verdict: INCONCLUSIVE at both fee levels — meaning no robust, deployable edge, route to the Spec #2 paper-trade. Full numbers, the honest read, and the three live-bring-up fixes are in the Live Run (2026-06-08) section below.
What “built” means here
Single Rust crate hormuz_bet, project dir /Users/levander/coding/hormuz_bet.
10 modules, built subagent-driven: TDD per module, with a two-stage review (spec-conformance + code-quality) on each module.
cargo test --lib — 30 passed / 3 ignored (the 3 ignored are the network-gated live tests).
The golden integration test (tests/golden_resolution.rs) compiles.
Not a git repo. Per the owner rule, the project is never committed or git init-ed.
Live run done (2026-06-08) → verdict INCONCLUSIVE (both fee levels)
The crate compiled, linted clean, its pure core was tested, and it has now been run end-to-end against the live APIs (read-only). The executed sequence was:
cargo run -- ingest-portwatch — snapshotted 2708 PortWatch daily rows (2019-01-01 → present) into local SQLite (data/hormuz.db).
README at repo root summarizes the run sequence and verdict meanings.
Live Run (2026-06-08) — VERDICT: INCONCLUSIVE
The engine ran end-to-end against the live APIs (read-only) on 2026-06-08. These are the real numbers printed by the binary, not estimates.
Golden spine passes live. The golden integration test reconstructs the real UMA-settled markets from the raw PortWatch series: the week-of-May-11 event reconstructs into the 40–59 bucket, matching the actual settlement. Ingestion pulled 2708 PortWatch daily rows (2019-01-01 → present) into data/hormuz.db. So the instrument is wired correctly to reality — the reconstruction is trustworthy.
Revision-sensitivity sweep ±0/10/25%: all INCONCLUSIVE — the verdict is stable to PortWatch revisions.
Honest read: NO robust edge — do not deploy capital
The point PnL is positive (+737 / +488) but the bootstrap-5% lower bound is deeply negative (−1461 / −1710) — the confidence interval straddles 0, so the point estimate is not distinguishable from luck. The sample is tiny (~2–3 resolved weekly events) and drawn from an anomalous wartime / depressed-traffic regime (non-stationary; see hormuz-bet-basis-risk).
The damning tell: the late-determinacy (high-confidence) trades LOST money (−1300) while the early trades made it (+2037) — that is backwards from what a real nowcaster edge would produce. A genuine edge should sharpen as the resolution window fills in (more signal → more confidence → more PnL). The opposite pattern means the +737 is noise, not skill.
Structural reason there is no edge: the count markets resolve on PUBLIC PortWatch data the market also sees → there is little-to-no informational asymmetry, and the books are thin (see hormuz-bet-liquidity). Forecasting a public number a hair faster than an efficient, thin market is not a tradable advantage.
Correct call: do not deploy capital. INCONCLUSIVE routes to the Spec #2 paper-trade, not to live trading. The engine is a reusable instrument that gains statistical power as more weekly markets accrue — re-run it as the sample grows.
Live Bring-up Fixes (three, all sound)
Getting the engine to run against the live payloads surfaced exactly three issues. All three fixes are correct (verified against the live payloads / the math), and the third is a reusable methodology rule.
1. PortWatch date is an ArcGIS date-only field → serialized as a STRING, not epoch-millis
The PortWatch FeatureServer date field is esriFieldTypeDateOnly, which serializes as a string "YYYY-MM-DD", not the epoch-milliseconds that ordinary ArcGIS date fields use. Fix:Attrs.date → Option<String>, parsed with NaiveDate::parse_from_str. (The Gamma and CLOB structs were verified correct against the live payloads and needed no change — only PortWatch’s date typing was the surprise.)
2. perturb panicked on rel == 0.0 (empty gen_range)
The revision-sensitivity perturb called gen_range(-0.0..0.0) when the relative perturbation was 0, which is an empty range and panics. Fix: short-circuit and return a clone when rel == 0.0. (This is why the ±0% arm of the sensitivity sweep now runs cleanly.)
3. METHODOLOGY (high value, reusable): the negative-control gate must be ONE-SIDED
A losing blind control is EXPECTED — only a winning one is a leak
The negative control originally gated on neg_ok = negc.total().abs() <= bound, i.e. it flagged any large-magnitude control PnL as a leak. That is wrong: a losing blind control is exactly what you expect against an efficient market, and flagging it produced a misleading “NO-GO (negative control leaked)“. A leak/lookahead signal only shows up as a control that spuriously WINS — a strongly positive control PnL it has no business earning.
Fix: make the gate one-sided — neg_ok = negc.total() <= bound (only a positive overshoot fails). This flipped the verdict from a misleading NO-GO to the honest INCONCLUSIVE.
Reusable rule for ALL backtest negative controls: gate one-sided on the positive side. A blind/null control that loses money is healthy evidence of an efficient market; only a control that makes money you can’t explain indicates leakage or lookahead. Pairs with learning #1 below (price the control at the null’s own belief). See hormuz-bet-backtesting.
Architecture As Built
The whole design rests on a single idea: one daily-count model, exposed to three market types as Monte-Carlo functionals. There is exactly one statistical model of the PortWatch daily transit-count series; every market structure from hormuz-bet-resolution-mechanics is just a function evaluated on simulated paths from that model.
One model, three markets, via resolve reuse
forecast simulates the not-yet-finalized window days. The generative model:
Negative-Binomial day observations (overdispersed counts).
Day-of-week effects layered on an EWMA log-scale local level (a slowly drifting log-level the day-of-week multipliers ride on).
A shared level path per draw, so all days within one simulated draw move together — this is what gives the simulation cross-day correlation (weekly sums and 7-day MAs are not sums of independent days).
For each simulated path, forecast calls resolve.
resolve is a pure function that maps a daily-count path to a market outcome (weekly TOTAL bucket, 7-day MA bucket, single-day-max threshold leg, or the deep MA≥60 binary).
Because the same resolve is used for both forecasting and settlement, forecast pricing and actual settlement cannot diverge — they are, by construction, the same code path. (See the consistency learning below: this property is the spine of the cost-model correctness too.)
Point-in-time correctness: portwatch::as_of
portwatch::as_of models the weekly publish calendar + finalization to produce point-in-time views of the series (what was knowable at a given historical instant).
A leakage-guard test forbids any future-published date from entering an as-of view — this is the concrete, automated defense for the purged/embargoed-walk-forward intent described in hormuz-bet-backtesting.
The verdict gate (three-way)
The harness emits GO / NO-GO / INCONCLUSIVE. It gates GO on ALL of:
bootstrap 5%-quantile PnL > 0 (lower CI bound positive, not just the point estimate), AND
beats price-follow (buying at the quoted probability), AND
beats random, AND
beats a modal-historical baseline, AND
calibrated (Brier score), AND
negative-control ≈ 0 (no leakage; see learnings).
If the point edge is positive but the bootstrap CI straddles 0 (small sample), the verdict is INCONCLUSIVE → proceed to the Spec #2 paper-trade rather than trading live. A negative-control leak forces NO-GO (it indicates a bug, not an edge).
Also included
Determinacy stratification — splits trades into early vs late (how settled the resolution window already was at trade time) and reports edge per stratum.
Revision-sensitivity band — how much the verdict moves if PortWatch later revises the published numbers.
A historicMoment ArcGIS probe — queries PortWatch’s time-travel layer to sanity-check point-in-time reconstruction against the source.
Module Map
Ten modules under src/ (verified layout), grouped:
Core / glue:lib.rs, main.rs (clap CLI: ingest-portwatch, backtest), types.rs, store.rs (local SQLite via sqlx), resolve.rs (the pure outcome function reused by forecast + settlement), edge.rs (signal → bet sizing / EV).
market/ — Polymarket side: gamma.rs (events), clob.rs (book), parse.rs (parse each event’s resolution text → structure + per-event bucket edges; never hard-coded — see hormuz-bet-resolution-mechanics).
High-value, hard-won. These are the reasons the harness is trustworthy.
1. Negative-control test design — price the synthetic market at the null’s own belief
A negative control must read ≈0 against an efficiently-priced market
A correct negative control reads ~0 edge only when the synthetic market it trades is priced at the null’s own belief. An initial implementation gamed the control by shrinking size_cap until PnL slipped under a hardcoded bound — that is defanging, not fixing. The actual root cause was a fixture that priced a near-certain outcome at a uniform 0.33, so even the blind null “won” against the mispricing. Fix: price the near-certain bucket at ~0.99 → the null makes 0 trades → it reads exactly 0 for the right reason, and the control still catches real leaks. Lesson: when a control “passes” only after you constrain sizing, suspect you defanged the test instead of removing the leak.
2. Fee consistency — the fee is charged unconditionally on BOTH sides (refuted a reviewer)
= stake × signals_EV exactly, only if the fee is charged unconditionally everywhere
The identity E[settle] = stake × signals_EV holds exactly when the fee is charged unconditionally in both:
the ex-ante EV — a flat -fee_k*(1-eff) term, and
the ex-post settlement — stake*(1-eff)*fee_k charged on win AND on loss.
This matches Polymarket’s documented taker fee shares*0.072*p*(1-p), charged at trade time (so it is paid regardless of outcome). A reviewer claimed settlement should skip the fee on losing trades — that is WRONG: it would break the signals↔settle consistency that the shared-resolve architecture exists to guarantee. Verify review findings against the math before implementing (receiving-code-review rigor). Related cleanup: removed a redundant BtCfg.fee_k that duplicated costs.fee_k — a divergence footgun that could have broken this very consistency by letting the two fee values drift apart.
3. RTK proxy hides test stderr (and can mis-display clippy)
rtk gotcha in this repo
The rtk proxy summarizes cargo test output and SUPPRESSES eprintln! / --nocapture stderr. To see test debug output here, bypass it:
rtk proxy cargo test ... -- --nocapture
Also, rtk’s clippy summary can cosmetically mis-display “1 errors / 1 warnings” when raw cargo clippy is actually clean — trust raw cargo over the summary. (Separately: rtk also intercepts find with compound predicates — use rtk proxy find ... or plain paths.)
4. Toolchain
sqlx (SQLite) — uses the RUNTIME query API, not compile-time macros, deliberately, so the build does not require a live DB. Backtest data store is local SQLite (data/hormuz.db).
reqwest — connect_timeout set (rustls-tls, no default features).
Live run sequence (needs network) — already executed 2026-06-08, output INCONCLUSIVE
cargo run -- ingest-portwatch # snapshot PortWatch daily history → data/hormuz.db (2708 rows)cargo run -- backtest --fee-k 0 # idealized (fees off) → INCONCLUSIVEcargo run -- backtest --fee-k 0.072 # real Polymarket taker fee → INCONCLUSIVE
Read the emitted GO / NO-GO / INCONCLUSIVE. Optional: cargo test -- --ignored --nocapture exercises the 3 network-gated live tests (PortWatch fetch, Gamma event, golden resolution reconstruction) — they pass live. This was the boundary between Research/Build and the Backtest milestone in the hormuz-bet pipeline; with INCONCLUSIVE in hand, the next milestone is the Spec #2 paper-trade.
Related
hormuz-bet — project index; this is the first build milestone of its pipeline.
hormuz-bet-backtesting — the validation-design intent this crate realizes (purged walk-forward, negative control, baselines, fee + slippage, regime-break caveat).
hormuz-bet-resolution-mechanics — the exact market structures the resolve function and market/parse.rs implement; the never-hard-code-buckets rule.
hormuz-bet-portwatch-data — the FeatureServer the portwatch/ client ingests and the as_of layer models point-in-time.
hormuz-bet-liquidity — the thin-book reality the sizing/edge model must respect.
polymarket-fetch — the purged-backtest validation bar this mirrors; reference for PM fee/slippage modelling.