DEV Community

Cover image for Effective Open Interest: How to Estimate Live OI From OPRA Flow
tomasz dobrowolski
tomasz dobrowolski

Posted on • Originally published at flashalpha.com

Effective Open Interest: How to Estimate Live OI From OPRA Flow

If you have ever wondered why one vendor's "live GEX" differs from another's even though both read the same OPRA feed, the answer is almost always how they handle open interest.

OPRA broadcasts settled OI once per session, usually before the open. Everything past 09:30 ET is built on top of an OI estimate derived from trade flow. The estimate is rarely disclosed. Most vendors mix settled OI with their own intraday adjustments and call the result "live data" without describing how the adjustment is made.

The choice of estimator changes the GEX number. It changes the call wall. It sometimes changes the gamma flip. For anyone wiring this into a model, the methodology is the product.

This post walks through how we do it at FlashAlpha: the six OI values we expose, the flow simulator, the 0.43 confidence weight and how it was calibrated, and how the whole thing feeds live exposure.

The problem with official OI

Open interest is the count of outstanding contracts at a strike and expiration. It moves when positions are opened or closed. Aggregate exposure (GEX, DEX, VEX, charm) is linear in OI: wrong OI, wrong exposure.

Official OI comes from the OCC's nightly clearing run and is broadcast by OPRA once per ET trading day, typically before the open. It is the only OI that has cleared. It is also stale through every minute of the trading day after the morning broadcast.

For a sleepy index in a quiet week, that approximation is fine. For SPY on a Fed afternoon, QQQ on an NVDA-news day, or any single name on earnings, official OI is hours behind the actual book.

0DTE is the extreme case: the entire OI lifecycle from creation to expiration happens inside one trading day. By the time tomorrow's settled OI arrives, those contracts have already cleared. If you want any GEX signal from 0DTE flow, you have to estimate OI from the tape in real time.

The six OI values we expose

Every Exposure API response includes six named OI fields per chain. Mixing them up produces silently wrong analytics, so each one has a precise meaning.

Field What it is
official_oi Last settled OI from OPRA (StatType=9). Conservative, accurate, stale.
intraday_oi_delta Signed running estimate of position changes since the morning broadcast. Resets on a new ET trading day.
intraday_oi_delta_x10 Same delta in fixed-point form (one decimal preserved) for integer-only pipelines.
simulated_oi official_oi + intraday_oi_delta, unclamped. May go negative on contracts where sell flow exceeds the morning baseline. Diagnostic only.
effective_oi max(0, simulated_oi) per contract. The non-negative, intraday-updated OI surface that feeds live GEX/DEX/VEX/CHEX.
oi_day The ET trading day this state belongs to. Critical for reconnect-replays and weekend boundaries.

If you want the textbook official number, read official_oi. If you want to see how aggressive intraday flow has been, watch intraday_oi_delta. If you want the number that actually drives live GEX, read effective_oi.

The side-classified flow simulator

The intraday delta comes from a per-trade accumulator running continuously against OPRA. Each trade is side-classified (buy / sell / midpoint), weighted, and added to the running delta:

ΔOI_t = size_t × Confidence × sign(side_t)
Enter fullscreen mode Exit fullscreen mode

Where sign(buy) = +1, sign(sell) = -1, sign(mid) = 0. Midpoint trades contribute zero because they cannot be reliably classified as opening or closing flow. That removes some signal but eliminates a large bias source in tight-spread names where midpoint prints are common.

The aggregator runs at OPRA-trade resolution: a single contract's OI estimate updates the instant a print hits. Chain-level effective_oi is the per-strike sum.

Side classification is the upstream weakness. Tick-rule and quote-rule heuristics get most trades right, but rapid quote updates around news events can flip classification on individual prints. We monitor the aggregate impact in the residual reconcile rather than trying to make every classification perfect.

The 0.43 confidence weight

The confidence weight is the one knob that has to come from data, not theory. There is no first-principles derivation of "43% of volume opens new positions": the right value depends on participant mix, side-classification accuracy, time of day, and ticker. The only honest calibration is empirical.

The reconcile loop runs daily.

16:30 ET snapshot. After the MOC auction settles, the calibration service walks every contract with non-zero intraday delta and writes a prediction row: today's morning settled OI, the accumulated intraday delta, the predicted end-of-day OI, and the confidence weight in use.

09:35 ET reconcile. The next morning, after the new day's settled-OI broadcast, the service looks up the actual settled value for each contract and writes a residual row: actual - predicted. Predictions are matched to the previous trading day: Monday's reconcile compares against Friday's predictions, not Sunday's.

Aggregating residuals over a week or two tells you three things no single trade can: whether the weight is right on average, whether the bias is symmetric or tilted, and whether the error varies by ticker, side, or trade size.

The current 0.43 came out of a calibration run in May 2026. The original setting was 0.40, calibrated when the pipeline first shipped. Residual analysis showed that 0.40 systematically under-predicted EOD OI by 4 to 10 percent on liquid names, with the bias largest on names with heavy retail flow. Raising the weight to 0.43 brought the median residual close to zero across the liquidity distribution.

The tuning itself is not automated. The service writes residuals; humans look at the data and decide whether the heuristic needs adjusting. Automating the loop would risk over-fitting to a particular liquidity regime. Treating it as a quarterly engineering review preserves the option to ignore the data when something else is going on in the market.

Feeding live GEX

The aggregate GEX calculation does not care about the OI source. It multiplies gamma by OI by 100 by spot squared, summed across the chain:

GEX_live = Σ Γ_i × effective_oi_i × 100 × S²
Enter fullscreen mode Exit fullscreen mode

Swap the OI input and the GEX number changes mechanically. Using official_oi gives a morning-stale GEX. Using simulated_oi would feed a negative-OI artifact into the sum on contracts where the heuristic overshot. The per-contract max(0, simulated) clamp is the small, important step that makes chain-level GEX sensible even when individual contract estimates are noisy.

Same effective OI feeds DEX, VEX, charm exposure, and every per-strike profile. They are all linear in OI, so they all inherit the calibration of the underlying flow simulator.

A useful diagnostic

Aggregate intraday_oi_delta is the simulator's direct signed sum, never reconstructed from (effective - official). Reconstructing from clamped values loses magnitude on contracts where the clamp fired. The accumulator is the source of truth for net intraday flow.

A large gap between unclamped simulated_oi and clamped effective_oi on a chain means per-contract clamps fired somewhere and the heuristic is straining. That is your "trust the live GEX less right now" signal.

When the heuristic breaks

The simulator is a heuristic, not a position-tracking ledger. Three patterns produce predictable bias.

Dealer-internalised crosses. Large institutional trades that print through dealer-internal crossing networks show up on the tape but do not create new OI. The 0.43 weight accounts for some fraction of this through calibration, but bursts of internalised flow can locally inflate the estimate.

Roll activity. Around OPEX, large multi-leg rolls produce flurries of trades that side-classifiers read as one-directional. The simulator counts them as new positions when they are, in aggregate, position-neutral.

Midpoint density. In tight-spread names, a large fraction of prints clear at the midpoint and contribute zero. Genuine positioning that prints at the midpoint is missed. Bias is toward under-estimating OI changes in liquid, tight-spread names.

None of these break the methodology on aggregate. They show up as residual variance in the daily reconcile and inform the next calibration pass.

Using it

import requests

resp = requests.get(
    "https://lab.flashalpha.com/v1/exposure/gex/SPY",
    headers={"X-Api-Key": "YOUR_KEY"}
)
data = resp.json()
print(f"Official OI:    {data['oi']['official_oi']:,}")
print(f"Simulated OI:   {data['oi']['simulated_oi']:,}")
print(f"Effective OI:   {data['oi']['effective_oi']:,}")
print(f"Intraday Delta: {data['oi']['intraday_oi_delta']:+,}")
print(f"Confidence:     {data['oi']['oi_delta_confidence']}")
print(f"Live GEX:       {data['live_gex']:,.0f}")
Enter fullscreen mode Exit fullscreen mode

Free tier and docs at flashalpha.com. Full methodology details, calibration history, and the four-question vendor checklist are in the original article.

Top comments (0)