Recreate the unified minute-odds mart¶
Build polymarket_wc2026_marts.polymarket_wc2026_market_minute_odds from a clean
clone or preserved raw warehouse. Complete
shared setup first.
This pipeline is the minute-grain counterpart of the hourly golden mart. It
reuses the match-minute path for in-game markets and adds a futures-minute path
for every other registry-eligible WC2026 market over the tournament span
([2026-06-11, 2026-07-19], capped by each market's close/resolution time).
Load and validate the 104-match Scraper reference¶
Follow the same reference-bundle steps as Recreate the match-minute mart:
uv run make match-minute-inputs-validate
Do not continue until the command prints:
104 Scraper reference fixture rows
Create the unified minute-odds mart¶
Default job config refreshes the shared Polymarket event catalog and registry
before minute fetch, then runs both raw minute legs. After a successful land,
set the matching POLYMARKET_WC2026_MINUTE_ODDS_REFRESH_*=false flags in .env
and restart Dagster to skip completed stages on reruns (see
Configuration). For example,
after match-minute raw is published but futures still needs work:
POLYMARKET_WC2026_MINUTE_ODDS_REFRESH_CATALOG=false
POLYMARKET_WC2026_MINUTE_ODDS_REFRESH_MATCH=false
POLYMARKET_WC2026_MINUTE_ODDS_REFRESH_FUTURES=true
uv run make minute-odds-backfill
Catalog refresh uses routine tag/series discovery (not the multi-hour slug-prefix recall audit). The job obtains:
- market inventory from the public Polymarket Gamma API;
- minute token history from the public Polymarket CLOB API;
- fixture identity and result validation from the previously activated,
checksummed Scraper
oddsfox.reference.v1bundle.
It runs both raw legs:
- Match-minute odds for the curated 104-game / 248-market inventory.
- Futures-minute odds for registry-eligible markets that are not
moneyline/soccer_team_to_advance.
Both legs share the hourly odds fetch stack: CLOB batch POST (≤20 tokens), 24-hour window pre-chunking, workers/RPS 40 with auto-tune up to 90, and a publish path that spills bounded typed Arrow batches to temporary Parquet shards, then promotes an immutable partitioned snapshot (all-token raw + primary-token minute OHLC) under the runtime root and registers stable DuckDB views (warehouse lock is not held during shard construction). Unchanged tokens reuse the prior snapshot without a CLOB refetch. Discovery includes open markets so in-tournament futures are eligible; match selection still requires closed game markets for the 104/248/496 inventory.
Then dbt builds +polymarket_wc2026_market_minute_odds_data_quality
(tagged minute_odds). Raw history retains every CLOB token; primary-token
minute OHLC is produced at publish and exposed as pass-through views, and the
public mart is a view over that narrow fact plus market metadata:
polymarket_wc2026_marts.polymarket_wc2026_market_minute_odds
polymarket_wc2026_observability.polymarket_wc2026_market_minute_odds_data_quality
After raw legs are landed, set all three
POLYMARKET_WC2026_MINUTE_ODDS_REFRESH_*=false flags and restart Dagster for a
dbt-only rebuild. Measure disposable synthetic dbt timing with:
uv run make minute-odds-dbt-benchmark
# optional: MINUTE_ODDS_DBT_BENCHMARK_TIER=production-shaped
Measured on disposable hardware (candidate graph): performance tier (~10M
primary / 20M raw rows) finished dbt in ~27s with no DuckDB temp spill. The
prior full-backfill dbt phase on ~377M raw rows exceeded 71 minutes with ~27GB
sort spill before cancel; acceptance is dbt ≤25 minutes so the measured ~34
minute raw phase keeps end-to-end under 60 minutes.
Inspect health with:
select *
from polymarket_wc2026_observability.polymarket_wc2026_market_minute_odds_data_quality;
blocking_issue_keys must be null before treating the mart as publication-ready.
Disposable live smoke (sampled)¶
For a cheaper end-to-end check against live Gamma/CLOB without refetching every market, run:
uv run make minute-odds-live-smoke
Cold runs reset .cache/minute_odds_live_smoke.duckdb and the disposable
.cache/runtime/smoke/minute-odds-live runtime root (Parquet snapshots). The
job still validates the full 104/248/496 match inventory before sampling, then
fetches about 5% of match markets and 5% of futures markets independently (all
tokens retained per selected market) and caps sampled futures windows to their
final 24 hours. Catalog refresh uses routine tag/series discovery and does not
run the multi-hour slug-prefix recall audit. dbt builds only the unified DQ
select; it does not prove the full match publication gate. Warm reruns:
MINUTE_ODDS_LIVE_SMOKE_RESET=false \
MINUTE_ODDS_LIVE_SMOKE_REFRESH_CATALOG=false \
uv run make minute-odds-live-smoke
Historical API availability is not guaranteed. If Gamma or CLOB no longer returns the complete interval, use an operator's previously completed raw warehouse through the completed-warehouse route.
Troubleshooting¶
| Failure | What to check |
|---|---|
supply a complete operator-local 104-match schedule |
Same as match-minute: exactly 104 IDs 1..104. |
No registry-eligible WC2026 futures markets |
Registry refresh must admit non-match markets under the volume eligibility gate. |
| Futures fetch empty/error | Empty in-window CLOB history is audited and skipped on publish; hard error/cancelled fail the run. Retry transient failures or rebuild from a completed raw warehouse. |
blocking_issue_keys not null |
Inspect the observability row and match-minute / futures audit tables. |
| Live smoke sample count mismatch | Confirm smoke seed/fraction defaults and that both legs recorded population_markets / selected_markets in sync_run_metrics. |