Detect New Positioning: Day-Over-Day Open Interest Changes by Strike | FlashAlpha

Detect New Positioning: Day-Over-Day Open Interest Changes by Strike

Volume is noise; open interest is the position that survived. The FlashAlpha OI-diff endpoint returns day-over-day open-interest changes per contract (today minus the prior trading day), the top strikes ranked by absolute change, and call/put aggregate totals, so you can see where dealers added or unwound overnight. Growth plan.

T
Tomasz Dobrowolski Quant Engineer
Jun 13, 2026
26 min read
OpenInterest DealerPositioning OptionsFlow API DeveloperGuide Python

If you are looking for an open interest change API, a way to detect new options positioning, a day-over-day OI delta feed, or a signal for where dealers added or unwound exposure, this is the reference. The FlashAlpha GET /v1/exposure/oi-diff/{symbol} endpoint returns per-contract OI deltas (today minus the prior trading day), the top-N strikes by absolute magnitude, and call/put aggregate change totals. It is on the Growth plan.

Read this first. The diff needs a prior-day snapshot to compare against. A nightly OI snapshot service writes the prior day's open interest; until at least one prior trading day exists for a symbol (a freshly added ticker, or the very first session of coverage), prior_snapshot_available comes back false, the aggregate totals are 0, and top_oi_changes is empty. For an exact diff between two specific dates regardless of warm-up, drive two ?at= calls against historical.flashalpha.com (covered at the end). The JSON below is an illustrative shape with placeholder numbers.


Why OI Change, Not Volume

Volume double-counts: a contract opened and closed the same day shows volume but leaves no position. Open interest is the net of what was opened minus what was closed, the standing inventory dealers actually have to hedge. So the question that matters for positioning is not "what traded today" but "what is open now that was not open yesterday." That is the OI diff:

  • A positive OI change at a strike means net new contracts were opened there, fresh positioning that adds to dealer exposure at that strike.
  • A negative OI change means contracts were closed or expired, positioning coming off.
  • The call-versus-put split of the aggregate change tells you the directional lean of the new positioning, independent of price.

This is the read behind "someone opened 10,000 of the 600 calls overnight": a large positive OI change concentrated at one strike is the fingerprint of a deliberate, surviving position.

See where positioning actually moved overnight, by strike

Per-contract OI deltas, top builds and unwinds, call/put totals. Growth plan.

Get API Access

Quick Start

Path is the underlying symbol; the optional topN (default 10, clamped 1 to 100) caps how many ranked strike changes come back.

from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_API_KEY")

d = fa.exposure_oi_diff("SPY", top_n=5)

if not d["prior_snapshot_available"]:
    print("No prior-day snapshot yet for this symbol; deltas are zero.")
else:
    print(f"Net call OI change: {d['total_call_oi_change']:+,}")
    print(f"Net put OI change:  {d['total_put_oi_change']:+,}")
    for r in d["top_oi_changes"]:
        print(f"  {r['type']} {r['strike']} {r['expiry']}: "
              f"{r['oi_change']:+,}  (today {r['today_oi']:,} vs prior {r['prior_oi']:,})")
import { FlashAlpha } from 'flashalpha';

const fa = new FlashAlpha('YOUR_API_KEY');

const d = await fa.exposureOiDiff('SPY', { topN: 5 });

if (!d.prior_snapshot_available) {
    console.log('No prior-day snapshot yet for this symbol; deltas are zero.');
} else {
    console.log(`Net call OI change: ${d.total_call_oi_change.toLocaleString()}`);
    for (const r of d.top_oi_changes) {
        console.log(`  ${r.type} ${r.strike} ${r.expiry}: ${r.oi_change.toLocaleString()}`);
    }
}
using FlashAlpha;

var client = new FlashAlphaClient("YOUR_API_KEY");

var d = await client.ExposureOiDiffAsync("SPY", topN: 5);

if (!d.GetProperty("prior_snapshot_available").GetBoolean())
    Console.WriteLine("No prior-day snapshot yet for this symbol; deltas are zero.");
else
    Console.WriteLine($"Net call OI change: {d.GetProperty("total_call_oi_change").GetInt64():N0}");
package main

import (
    "context"
    "fmt"

    flashalpha "github.com/FlashAlpha-lab/flashalpha-go"
)

func main() {
    fa := flashalpha.NewClient("YOUR_API_KEY")

    d, err := fa.ExposureOiDiff(context.Background(), "SPY",
        flashalpha.WithOiDiffTopN(5))
    if err != nil {
        panic(err)
    }

    fmt.Printf("prior available: %v  net call OI change: %v\n",
        d["prior_snapshot_available"], d["total_call_oi_change"])
}
curl -H "X-Api-Key: YOUR_KEY" \
  "https://lab.flashalpha.com/v1/exposure/oi-diff/SPY?topN=5"
$ pip install flashalpha  |  npm install flashalpha  |  dotnet add package FlashAlpha  |  go get github.com/FlashAlpha-lab/flashalpha-go

Response Shape

An illustrative shape with placeholder numbers, snake_case fields:

{
  "symbol": "SPY",
  "underlying_price": 597.50,
  "as_of": "2026-06-13T15:30:00Z",
  "prior_snapshot_available": true,
  "total_call_oi_change": 124000,
  "total_put_oi_change": -38000,
  "top_oi_changes": [
    { "strike": 600.0, "type": "C", "expiry": "2026-06-18", "today_oi": 35000, "prior_oi": 30000, "oi_change": 5000 }
  ]
}
FieldWhat it means
prior_snapshot_availablefalse when no prior-day OI snapshot exists yet for the symbol. The totals are then 0 and top_oi_changes is empty. Always branch on this first.
total_call_oi_change / total_put_oi_changeSum of per-contract deltas across all call (or put) contracts that have a prior-day match. The directional lean of new positioning.
top_oi_changes[]Sorted by |oi_change| descending, capped by topN. Each row carries the contract's strike, type, expiry, today's OI, prior OI, and the signed delta.

Contracts present today with no prior-day entry are excluded from top_oi_changes, because no true delta is computable for them. So the ranked set is limited to contracts where a real day-over-day change exists.

How To: Read an Overnight Build

The first read is the call-versus-put aggregate: a large positive total_call_oi_change against a flat or negative put change is bullish new positioning, and vice versa. The second read is concentration, the top row in top_oi_changes. New OI spread evenly across many strikes is portfolio churn; new OI piled into one strike is a deliberate position with a level attached. The strike, expiry, and sign of that top row tell you the trade: a big positive call build at the 600 strike expiring next Friday is a different signal from a put build at 595 in three months.

How To: Confirm a Gamma Wall Is Real

A gamma wall on a GEX chart can be a genuine concentration of fresh positioning, or a stale artifact of a same-day repricing. The OI diff disambiguates: pair a wall strike with its OI change. If the wall sits on a strike that shows a real positive oi_change overnight, the positioning that built it is fresh and the level has conviction. If the wall strike shows little or no OI change, the gamma there is old inventory, and the wall is softer than it looks.

# Cross OI builds against GEX to grade wall conviction
oi  = fa.exposure_oi_diff("SPY", top_n=20)
gex = fa.gex("SPY")                       # per-strike gamma exposure

if oi["prior_snapshot_available"]:
    builds = {(r["strike"], r["type"]): r["oi_change"] for r in oi["top_oi_changes"]}
    # For each notable GEX strike, check whether fresh OI backs it
    for s in gex["strikes"][:10]:
        delta = builds.get((s["strike"], "C"), 0) + builds.get((s["strike"], "P"), 0)
        tag = "FRESH build" if delta > 0 else "stale / unwinding" if delta < 0 else "no change"
        print(f"  strike {s['strike']:>7}: GEX {s['net_gex']:>14,}  OI change {delta:+,}  -> {tag}")

For Exact Two-Date Diffs: the Historical Path

The live endpoint compares today against the most recent prior snapshot. When you need the exact OI change between two specific dates (for a backtest, or to study positioning around a past event), drive two point-in-time calls against historical.flashalpha.com with an ?at= timestamp each, then diff the per-strike open interest yourself. The exposure sheet aggregates open interest per strike (separate call_oi and put_oi fields), which is strike-level rather than the live endpoint's per-contract granularity, but it is the cleanest point-in-time OI source:

import requests

BASE = "https://historical.flashalpha.com"
HEADERS = {"X-Api-Key": "YOUR_KEY"}

def strike_oi(symbol, at):
    r = requests.get(f"{BASE}/v1/exposure/sheet/{symbol}", headers=HEADERS, params={"at": at})
    r.raise_for_status()
    # the sheet returns one row per strike with separate call_oi / put_oi
    return {row["strike"]: (row["call_oi"], row["put_oi"]) for row in r.json()["strikes"]}

today = strike_oi("SPY", "2026-06-13T20:00:00Z")
prior = strike_oi("SPY", "2026-06-12T20:00:00Z")

changes = []
for strike, (c_now, p_now) in today.items():
    c_prev, p_prev = prior.get(strike, (0, 0))
    changes.append((strike, c_now - c_prev, p_now - p_prev))

# Strikes with the biggest call-OI change between the two dates
for strike, d_call, d_put in sorted(changes, key=lambda r: abs(r[1]), reverse=True)[:10]:
    if d_call or d_put:
        print(f"  {strike}: calls {d_call:+,}  puts {d_put:+,}")

This gives an exact, reproducible diff between any two dates the historical service covers, with no dependence on the live snapshot warm-up. The historical service is on the Alpha plan.

API Access and Pricing

The OI-diff endpoint is on the Growth plan and higher.

PlanPriceOI diffRate Limit
Free$0No5 req/day
Basicfrom $63/moNo100 req/day
Growthfrom $239/moYes2,500 req/day
Alphafrom $1,199/moYesUnlimited

Try the shape in the browser via the OI-diff endpoint docs (live "Try It" widget) or the interactive API playground. SDKs cover Python, JavaScript, C#, Go, and Java.

Spot the strikes where positioning actually changed

Day-over-day OI deltas, ranked. Growth plan. Pairs with GEX to grade wall conviction.

Get API Access

Frequently Asked Questions

Yes. GET /v1/exposure/oi-diff/{symbol} returns per-contract open-interest deltas (today minus the prior trading day), the top strikes ranked by absolute change, and call/put aggregate totals. It is on the Growth plan. The diff needs a prior-day snapshot to exist for the symbol; a nightly OI snapshot service provides it.
It means there is no prior-day OI snapshot to diff against yet for that symbol, typically a freshly added ticker or the very first session of coverage. The totals come back 0 and top_oi_changes is empty. Once a prior trading day has been snapshotted, it flips to true and the deltas populate with no client change. For an exact diff between two specific dates regardless of warm-up, use the historical two-call method described above.
Volume counts every contract traded, including positions opened and closed the same day that leave no standing exposure. Open interest is the net of opened minus closed, the inventory that survives to the next session. The day-over-day OI change isolates genuinely new positioning, which is what drives dealer hedging, where volume alone cannot distinguish a real build from round-trip churn.
Yes. The live endpoint always compares today against the most recent prior snapshot. For an exact diff between two chosen dates, make two point-in-time calls against historical.flashalpha.com with an ?at= timestamp on each (for example the exposure sheet, which carries per-strike call_oi and put_oi), then subtract the open interest yourself. The historical service is on the Alpha plan.
The OI-diff endpoint is on the Growth plan (from $239/mo, 2,500 req/day) and the Alpha plan (from $1,199/mo, unlimited). It is not available on Free or Basic. The two-date historical diff method uses historical.flashalpha.com, which is on the Alpha plan.

Live Market Pulse

Get tick-by-tick visibility into market shifts with full-chain analytics streaming in real time.

Intelligent Screening

Screen millions of option pairs per second using your custom EV rules, filters, and setups.

Execution-Ready

Instantly send structured orders to Interactive Brokers right from your scan results.

Join the Community

Discord

Engage in real time conversations with us!

Twitter / X

Follow us for real-time updates and insights!

GitHub

Explore our open-source SDK, examples, and analytics resources!