US
SPYVIXS&P 500NasdaqRussellGoldBTCCrudeSPYVIXS&P 500NasdaqRussellGoldBTCCrude
Data integrity

How we handle missing data

Every market data feed has holes in it. What separates them is what they put in the hole. A vendor that hands you a zero where it has no measurement has not given you data, it has given you a number that will survive every sanity check you write and be wrong. Here is what we do instead, rule by rule, with the mechanism named so you can check it.

The one rule the rest come from

Absent is not empty. “We did not measure this” and “we measured this and it was nothing” are completely different claims about the world, and a feed that renders them identically has destroyed the difference before your code ever sees it. Every rule below is that one rule applied somewhere specific.

What a zero-fill gives you { "gamma": 0, "oi": 0, "pct": 0 }
What we give you { "gamma": null, "oi": null, "pct": null, "n": 3 }

The rules

Rule 01

Nothing is zero-filled. Ever.

The ninety-coin cross-section ranks coins on one measure, on one clock. A coin we are missing a reading for is left out of the ranking, not padded with a zero and sorted to the bottom. That distinction is the whole product: a padded zero sorts the coins we know least about straight to one end of the screen, which is the opposite of what a screener is for.

This one is not a policy, it is a test. scripts/crypto-screen-check.js runs on every deploy against a synthetic snapshot with deliberate holes in it, and it carries a selftest that runs the same assertions against a zero-filling implementation to prove they fail. A check that cannot fail is decoration.

Rule 02

A percentile arrives with its population and its window

“IV is in the 8th percentile” is not a fact until you know of what, and since when. The eighth percentile of two years and the eighth percentile of thirty-six years are different statements, and a number without its denominator lets you confuse them.

So every percentile ships beside n, the first date in the series, and the window it was measured over. Check it yourself:

curl -s https://novo-options.trade/api/vol | jq '.series.VIX | {n, first, pct_all, pct_2y}' { "n": 9280, "first": "1990-01-02", "pct_all": 30.6, "pct_2y": 8.9 }

That sample is a real response from 2026-09-21 and it will not match yours: n grows by one every session and both percentiles move with it. Run the command. On a page about not showing you stale numbers as if they were live ones, a frozen example has to say so.

Rule 03

Too thin to rank means no rank, not a guess

Below eight samples a percentile is not published at all. The field comes back null and the analyst says why rather than ranking three observations and calling it a reading. A thin sample does not make the number uncertain, it makes it meaningless, and rounding meaninglessness to a plausible-looking integer is how a data product starts lying politely.

Rule 04

A quiet collector is a 503, never a calm market

When a collector stops publishing, the honest answer is that we are broken. So the endpoint returns 503 with reason: "collector_quiet" and a Retry-After header. It does not return 200 with an empty book.

The comment on that line in our own source says it better than marketing copy would: a 200 with zeroes would render a dead publisher as a calm book. A flat market and a dead feed look identical in a zeroed payload, and the difference is the entire trade.

Rule 05

“Nothing yet” is an answer, and it says which kind of nothing

Ask for today's gamma profile before the session's first pass and you get 200 with banked: false and a sentence explaining that the key is scoped to one Eastern trading day, so this is empty before the first pass rather than a claim that gamma was flat.

A 404 there would say your request was wrong. An empty array would say the session was featureless. Both are claims we have no basis for. The same applies to a ticker with no logged session history: logged: false, plus a note saying that is a statement about our record, not about the ticker.

Rule 06

The caveat travels inside the payload

Dealer gamma describes the size of moves and the location of levels. It does not predict direction. That is not a disclaimer at the bottom of a webpage where an agent will never read it, it is a field in the response:

"not_a_signal": "Where dealer gamma built and drained through the session. Gamma describes the SIZE of moves and the LOCATION of levels, never the direction price will take."

The history endpoints carry not_a_forecast for the same reason: counts and medians over sessions we logged describe what happened, and an agent that drops the note turns a description into a prediction. Putting it in the payload means the warning survives the trip into someone else's model.

Rule 07

Every response tells you how old it is

Freshness is a number, not an adjective. Every documented endpoint answers with the same three fields, so you can branch on the age rather than trust the word “live” in our marketing:

"as_of": "2026-09-21T15:12:12Z" // when, ISO 8601 "age_seconds": 187 // how old, already subtracted for you "as_of_kind": "measured" // WHAT KIND of time that is

The third field is the one that matters, and almost nobody ships it. A timestamp with no provenance reads as a measurement whether it is one or not, so ours says which:

  • measured — the data carries this stamp. When the world was observed. The dealer map computing its ladder, the CFTC printing its weekly report, the volatility record closing a session.
  • retrieved — we fetched it live for this request. When we got it, not when it was produced. Honest for a passthrough, and materially weaker. We say so rather than letting you assume the stronger one.
  • unknown — we could not source a real timestamp, so as_of is null. Never the request clock.

That last rule is the whole discipline, and we failed it until 2026-09-21. Four endpoints were stamping the moment the JSON was assembled and presenting it as the age of the data. Three of them answered an upstream failure with { updated: now, events: [] } — a fetch error rendered as “checked just now, nothing scheduled”. A fourth put updated: now on CFTC positioning, which is published weekly and can honestly be six days old. That is a zero-fill wearing a clock, and it is precisely what this page argues we do not do. It is fixed, and there is now a deploy gate that fails the build if the shape comes back.

An upstream failure now nulls the collection rather than emptying it, so events: null and events: [] can never be confused — one means the fetch broke, the other would mean the week is genuinely clear.

curl -s https://novo-options.trade/api/vol | jq '{as_of, age_seconds, as_of_kind}' curl -s https://novo-options.trade/api/positioning | jq '{as_of, age_seconds, as_of_kind}' # /api/quotes is a MAP keyed by ticker, so a top-level as_of key would be # iterated as an instrument. Its age ships in headers instead. curl -sD- -o/dev/null https://novo-options.trade/api/quotes | grep -i 'x-novo-'

Why we publish this

Because it is checkable, and because most of the industry fails it. Zero-filling is not usually malice, it is a schema that had no way to say “unknown” and a pipeline that needed a number. The result reaches you as a measurement, and there is nothing in the payload to tell you it was never measured.

We would rather hand you a null, a 503 and a sentence about why than a clean-looking row you cannot trust. It makes our responses slightly harder to consume and considerably harder to be wrong with. If you find a place where we break one of these rules, that is a bug and we want to hear about ittell us and we will fix it or document why it is not what it looks like.

Related: the error contract · live status · the MCP & API · how the map is built

Home · Trader · Crypto · AI · MCP & APIs · Plans · Market Data · SPY gamma · QQQ gamma · IWM gammaFutures positioningThe Daily StrikeCongress tradesVolatility record
Options 101 · 0DTE Guide · Basics & the Greeks · 0DTE options · Gamma & dealer positioning · Volatility & structure · Risk management · The Journal · Glossary · TradingView script
Expected Move · Max Pain · Position Size · Options P&L · Greeks · Economic Calendar · Market Holidays · Free API endpoints
Best 0DTE Tools · Best GEX Tools · vs SpotGamma · vs MenthorQ · vs Unusual Whales · vs Option Alpha
About · FAQ · Help · Getting Started · Security · Status · Data integrity · Error contract · Read Archive · Methodology · The Week Ahead · What’s new
Terms · Risk · Refunds · Privacy
Discord · Reddit · StockTwits · X · Contact

Market data on this page is delayed and provided for general information only. It is not financial advice or a recommendation to trade. VIX/VXN/RVX are ~15-minute delayed (CBOE); index values use E-mini futures. Options trading involves significant risk of loss. © 2026 NoVo Options Trading LLC, d.b.a. NoVo Crypto Trading.