One gauge. One verdict. Every threshold published so you can argue with a number instead of a black box.
Every AI-bubble take you've read is somebody's opinion. OMEN is a price.
It blends what prediction markets think will happen with what options, credit, volatility and equity markets are actually charging for protection β plus the one non-market anchor that matters, hyperscaler capex against operating cash flow, straight out of SEC XBRL. Out comes a single 0β100 crash-pressure gauge, a stated regime, and one of five verdicts.
No database. No framework. No build step. No server-side rendering. Static HTML, a handful of stdlib-only Python fetchers, and a small Cloudflare Worker β the whole thing is about 11,000 lines, and every number on it comes from a free, mostly keyless endpoint.
Not investment advice. This is a measurement instrument, not a recommendation.
The landing page exists to produce exactly one of five states. It is deterministic, published, and unit-tested cell by cell. No discretion. No vibes.
Direction (the Bull side's share of the Bull/Bear pair, read from 20 live Polymarket AI markets) Γ Stress (the regime, from the gauge):
| π’ Calm | π‘ Elevated | π΄ Stressed | |
|---|---|---|---|
| Bullish β share β₯ 55% | Risk-on | Constructive Β· hedged | Caution |
| Mixed β 45β55% | No edge | No edge | Caution |
| Bearish β share < 45% | Risk-off | Risk-off | Risk-off |
Read the bottom row carefully: stress cannot soften a bearish read. If the market has already picked the bear side, a quiet gauge doesn't get to talk it back down.
Source of truth:
omen/omen-common.js(thresholds) andomen/index.html(matrix). Tested inomen/test-verdict.mjsβ all nine cells, both band edges.
Five equally-weighted families, each normalized 0β100 against a fixed calmβstress range, then averaged.
| Family | What it reads | |
|---|---|---|
| Leading priced before the fact |
PRED |
Polymarket bubble-burst odds |
OPT |
25Ξ risk reversals, NVDA + SOXX (Nasdaq OPRA composite) | |
CREDIT |
HY OAS, CCC OAS, HYG drawdown, HYG/LQD ratio | |
| Confirming moves with or after prices |
VOL |
VIX/VIX3M term structure, VXN, SKEW, VVIX |
EQUITY |
NVDA and SOXX drawdown from the running high |
Only the leading side can warn. That split is the entire point: a gauge that goes red because equities already fell is a thermometer, not a warning.
One headline, stated once. The regime everywhere β the landing dial, the monitor's chip, the escalation alert β reads the server-side gauge above, computed once per run and embedded in market-data.json, so the three can never disagree. The monitor's gauge card additionally shows an exploratory six-family read: the prediction leg widened to the NVDA-tail and H100 components, plus a macro family (recession, Fed cuts, China top-3) and a weighting toggle. It is labelled as exploratory and does not drive the regime; only when the fetcher payload is missing or more than 2h stale does the chip fall back to it, and it says so on the card.
STRESSED blended gauge β₯ 55 OR crash basket β₯ 40
ELEVATED blended gauge β₯ 35 OR crash basket β₯ 25 OR any single market β₯ 15%
CALM everything else
A single market β the bubble-burst market included β can raise Elevated but can never trip red on its own. That rule exists because it used to: one distress market at 35% would paint the whole board red while the blended gauge sat perfectly calm. The monitor layers two extra Stressed/Elevated triggers on top for a crash basket climbing unusually fast (z β₯ 2 with level β₯ 25, and z β₯ 1.5).
The regime chip always states which threshold fired, in plain English, with the live number interpolated β no pre-baked strings. That explainer has its own 13-scenario test suite.
| Page | What it's for |
|---|---|
| index.html | The verdict. The whole read in one viewport: pair price, gauge, regime, verdict, reasoning. |
| polymarket-ai-index.html | The monitor β the big one, ~2,700 lines. One document, five views at /polymarket-ai-index/<view>: today, markets, gpu, prediction-markets, methodology. Includes the LEAPS tail panel: risk-neutral P(NVDA β50% / SOXX β40% in ~1y) as a put-spread digital (BreedenβLitzenberger) off long-dated puts β a deep-market cross-check on the thin Polymarket bubble book. |
| gauge.html | The gauge. Family-by-family breakdown, leading vs confirming, 90-day reconstruction with regime bands. |
| indexes.html | The indexes. Bull and Bear as one two-sided market that always sums to 1. Bull splits TECH vs CAP; Bear splits MKT vs GOV β because technology progress can survive a financial unwind, and a regulatory clampdown is not a crash. |
| ai-capex.html | AI capex. Eight fundamentals theses β debt saturation, credit stress, equity raises, capex vs GDP, stranded >1 GW projects, the dark-fiber overcapacity analogy, FCF erosion, reflexive treasury vehicles β plus a live tape. |
| china-ai-monitor.html | China substitution. Demand for Chinese LLMs (DeepSeek, Qwen, GLM, Kimi, MiniMax, MiMo) as a leading indicator of substitution pressure on US AI equities. |
| influencers.html | KOL board. Curated editorial snapshot, or auto-scored β100β¦+100 with evidence when XAI_API_KEY is set. |
| methodology.html | Methodology β including the honesty section on what none of this can prove. |
All free. All keyless unless marked.
| Panel | Source |
|---|---|
| Prediction markets, order book | Polymarket Gamma + CLOB |
| Equity / vol / credit proxies | Yahoo Finance chart API |
| 25Ξ risk reversal, IV term structure | Nasdaq option chains (OPRA composite); IV + delta computed locally |
| LEAPS 1y tail β put-spread digital (BreedenβLitzenberger) | Nasdaq option chains (same windows) |
| HY OAS, CCC OAS, NFCI | FRED fredgraph.csv (honest UA required) |
| Hyperscaler capex / OCF, contracted backlog | SEC XBRL companyconcept (honest UA) |
| Insider net-selling | SEC EDGAR Form 4 (open-market S/P only) |
| Realized H100 spot rent | vast.ai public bundles API |
| Speculator positioning | CFTC Commitments of Traders (Socrata) |
| Short interest Β· market caps | FINRA via api.nasdaq.com Β· Nasdaq screener |
| Cross-venue | Kalshi, Manifold (Metaculus: free token) |
| TSMC monthly revenue Β· issuance velocity Β· paid adoption | TWSE OpenAPI Β· EDGAR full-text search Β· Ramp AI Index CSV |
| Generator pipeline | EIA-860M monthly workbook |
| Chinese-model demand | OpenRouter, HuggingFace, GitHub, LMArena, Vercel AI Gateway, Ollama, PyPI, Play/App Store |
A handful of fields have no free machine-readable source and are labelled as hand-updated rather than quietly faked: Korea 20-day semiconductor exports, PJM capacity auction clears, LBNL interconnection-queue totals, per-CUSIP TRACE spreads.
omen-ai/
βββ worker.js ..................... Cloudflare Worker: static assets +
β live R2 streaming for the data paths
βββ wrangler.jsonc ................ Worker config, assets + R2 bindings
βββ ci-branch-guard.mjs ........... vetoes a Workers Builds deploy of any branch but main
βββ ruff.toml ..................... Python lint rules, and why each one is on
βββ BACKLOG.md .................... open work, with acceptance criteria
βββ docs/updates/ ................. dated change notes
β
βββ .github/workflows/
β βββ refresh.yml ............... the ~30-minute data pipeline
β βββ test.yml .................. CI gate on push + pull request
β βββ deploy.yml ................ production deploy, main only, behind test.yml
β
βββ omen/
β ββ pages ββ
βββ index.html the verdict
βββ polymarket-ai-index.html the monitor (5 views, 1 document)
βββ gauge.html family-by-family gauge breakdown
βββ indexes.html Bull/Bear as one two-sided market
βββ ai-capex.html capex theses + the live tape
βββ china-ai-monitor.html Chinese-model substitution demand
βββ influencers.html the KOL board
βββ methodology.html method, and what none of it can prove
β
β ββ shared front end ββ
βββ omen-common.js ............. $ Β· esc Β· safeUrl Β· Polymarket shapes Β·
β REGIME thresholds Β· GAUGE_REFS Β· index math
βββ omen.css ................... the design tokens
βββ omen-nav.js ................ the shared nav
β
β ββ fetchers (stdlib only) ββ
βββ update-market-data.py ...... the main one; gauge, regime, alerts
βββ update-china-data.py ....... China substitution monitor
βββ update-capex-data.py ....... AI capex live tape
βββ update-influencers.py ...... KOL scoring (needs XAI_API_KEY)
βββ update-app-charts.mjs ...... Play charts (the only Node dep)
β
β ββ guards ββ
βββ deploy-guard.py ............ refuses a hand-run deploy from a dirty tree
βββ seed-market-data.py ........ R2 baseline adoption rules
β
β ββ tests: python3 -m pytest omen/ ββ
βββ test_update_market_data.py . parsers, gauge, retry, carry-forward
βββ test_update_china_data.py
βββ test_update_capex_data.py
βββ test_gauge_parity.py ....... server gauge vs the shared fixture
βββ test_gauge_refs.py ......... the Python range mirror vs OMEN.GAUGE_REFS
βββ test_docs_truth.py ......... this tree, and no unverifiable doc claims
βββ test_update_influencers.py .. the KOL scorer's containment rules
βββ test_deploy_guard.py, test_seed_market_data.py
βββ test_documented_secrets.py, test_mobile_nav.py
βββ test_regime_explainer.py ... runs every Node suite under pytest
βββ test-omen-common.mjs ....... the shared module, incl. safeUrl XSS
βββ test-regime-explainer.mjs .. 13 regime scenarios, prose assertions
βββ test-gauge-parity.mjs ...... the monitor's gauge vs the same fixture
βββ test-worker.mjs ............ routing, R2-first, etag/304, fallbacks
βββ test-verdict.mjs ........... all nine verdict cells
βββ test-a11y.mjs .............. chart text alternatives, keyboard sorting
βββ test-pure-helpers.mjs, test-china-helpers.mjs, test-ci-branch-guard.mjs
β
β ββ data (committed; see below) ββ
βββ market-data.json ........... full state β R2 seed, ~weekly
βββ snapshots.csv .............. append-only history, every run
βββ fixtures/gauge-parity.json . the one fixture both gauge suites read
βββ alert-state.json ........... alert dedup
Python 3.12 stdlib and a browser. That's the entire dependency list.
python3 omen/update-market-data.py --snapshot # fetch + append one history row
cd omen && python3 -m http.server 8844 # fetch() won't read siblings over file://
open http://localhost:8844/index.htmlWant it to keep refreshing while you work?
python3 omen/update-market-data.py --watch 600 --snapshotOpen the files straight off disk and the pages fall back to market-data.js β the same payload delivered as a <script src>, because browsers block fetch() under file:// but not script tags.
The other fetchers:
python3 omen/update-china-data.py # china-data.json
python3 omen/update-capex-data.py # capex-data.json
cd omen && npm ci && node update-app-charts.mjspython3 -m pytest omen/One command, both languages. test_regime_explainer.py shells out to the Node suites, so the browser JS is covered by the same invocation as the Python fetchers. There's no bundler, so those suites slice the functions they test out of the HTML by comment marker β and fail loudly if a marker moves, rather than silently testing nothing.
cd omen && npm test # the Node suites alone; same list as the pytest bridgeCI runs the pytest command on every push and pull request, plus ruff check . over the Python (rules and rationale in ruff.toml) and node --check over the Worker and every .mjs file.
What's covered: FRED and Form 4 parsing, the server-side gauge and regime, the fetch retry policy and its sleep budget, the carry-forward policy, COT and short-interest reducers, the verdict matrix, the regime explainer's prose across 13 scenarios, the Worker's routing and R2/304 behaviour, and the shared module β including safeUrl's rejection of javascript:, data:, and control-character scheme smuggling.
Two things are pinned by contract rather than by assertion, because they are the places this repo has actually drifted: fixtures/gauge-parity.json holds one input set that both gauge implementations are scored against, with the intended divergences written down as numbers; and test_gauge_refs.py parses OMEN.GAUGE_REFS out of omen-common.js to check the Python mirror of the reference ranges.
This section deliberately quotes no test count. The two manuals used to claim 141 and 135 while the suite collected 289; a number in prose that nothing checks is a number that goes stale.
test_docs_truth.pynow fails the build if one reappears.
The most valuable output here is accumulated history, and that shouldn't live on a laptop.
flowchart LR
A["β±οΈ GitHub Action<br/>every ~30 min"] -->|fresh JSON/CSV| B["πͺ£ R2: omen-data"]
A -->|commit history| C["π¦ git repo"]
C -->|assets at deploy| D["βοΈ Cloudflare Worker"]
B -->|live read| D
D --> E["π₯οΈ browser"]
worker.js runs first for /market-data.json, /snapshots.csv, /influencers.json, /capex-data.json and /china-data.json (declared under assets.run_worker_first in wrangler.jsonc) and streams them from R2 β so the dashboard is current with no redeploy. On an R2 miss it falls back to the bundled copy, so the site never hard-breaks during bootstrap.
It also routes the monitor's five views: /polymarket-ai-index/<view> serves the same document for every allowlisted view and lets the page read location.pathname. One fetch, instant view switching, real shareable URLs.
The pipeline runs every 30 minutes during US market hours and hourly the rest of the time:
fetch ββ> snapshot ββ> alert ββ> upload to R2 ββ> commit
Alerts work with the browser closed. --alert computes the gauge server-side and pushes only when the regime escalates β deduped in alert-state.json, so you get one ping per escalation, not one per run.
What gets committed each run, and why it differs per file
- Every run β
*-snapshots.csv(append-only history that exists nowhere else),alert-state.json(dedup state; ephemeral CI would re-alert without it),china-data.json/china-history.json/app-charts.json(bundled-asset-only, so the committed copy is the copy the site serves),influencers.json/capex-data.json(in R2, but kilobytes, kept as a warm fallback). - ~Weekly β
market-data.json. It's 171 KB rewritten in full every 30 minutes and served live from R2 anyway; committing it per run was 86% of all data churn and had pushed bot commits to 74% of repo history. It stays tracked because the Worker's R2-miss fallback needs a bundled copy at deploy time, so the Action re-seeds it only once the committed copy is over 7 days old. - Never β
market-data.js, the hand-refreshedfile://bundle.
One-time deploy setup
npx wrangler r2 bucket create omen-data
npx wrangler deploy
npx wrangler r2 object put omen-data/market-data.json \
--file omen/market-data.json --content-type application/json --remoteEvery secret is optional. Without it the relevant panel degrades to a dated snapshot or hides itself β nothing breaks.
| Secret | Unlocks |
|---|---|
CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID |
R2 upload (an R2-Edit token) |
TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID |
Regime-escalation alerts |
NTFY_TOPIC |
β¦or ntfy.sh instead |
METACULUS_TOKEN |
Forecaster-crowd panel (free account) |
XAI_API_KEY |
Auto-scored KOL board |
ARTIFICIAL_ANALYSIS_API_KEY |
AA scores on the China monitor |
CF_RADAR_TOKEN |
Cloudflare Radar panel |
Secrets are scoped to the individual workflow steps that need them β the step that runs third-party scraper code deliberately runs with none in scope.
Each of these is here because the alternative already cost something.
- One price read per run. The gauge in
market-data.json, the row appended tosnapshots.csv, and the regime an alert fires on all come from a single Polymarket fetch. Three separate fetches meant three tapes and three answers that could disagree. - Nothing hard-fails. Every panel carries the previous run's value forward when its source fails, because the gauge reads those keys directly β a dropped key doesn't blank one panel, it removes a whole family from the mean and can flip the regime. Requests retry with exponential backoff (honouring
Retry-After, failing fast on 4xx) under a whole-process sleep budget, so a broad outage can't stretch a 30-minute job. - Escaping is not URL safety.
esc()makes a string safe inside an attribute; it does nothing about the scheme, and an escapedjavascript:β¦is still a live URL. Everyhrefbuilt from remote data goes throughOMEN.safeUrl, which allowlists http/https. - The pages share code.
omen-common.jsandomen.cssexist because one page'sesc()had quietly lost its null guard and threw where every other page degraded cleanly. - Sleeves are reads, not sub-indexes. Bear is the flat equal-weight mean of all 9 constituents β not the mean of its two unequal sleeves.
- The gauge reads MKT, not the Bear composite. Regime bands are calibrated to priced crash risk. Reading the composite would let regulatory odds trip a crash regime.
Tracked with acceptance criteria in BACKLOG.md. Stated here because a dashboard that hides its holes is worse than one that doesn't:
- China monitor "community mentions" (w=10) is hardcoded
nullβ Reddit and X have no public, key-free, CORS-accessible API for it. - Android chart presence rides a reverse-engineered scrape that breaks every year or two; a SerpApi fallback is specced.
- Some fundamentals fields are hand-updated, and the capex aggregate sums whichever hyperscalers reported in a given quarter β so a 4-filer quarter can sit next to a 5-filer quarter.
- BACKLOG.md β open work with acceptance criteria.
- docs/updates/ β dated change notes.
- readme.txt β the ASCII banner, and a pointer back here. It used to be a second full manual; two hand-maintained copies of one document drift, and these had.
Not investment advice.
This is a measurement instrument, not a recommendation.
Every threshold is published so you can disagree with a number rather than with a black box.