# Dealer positioning methodology

How FlashAlpha derives dealer gamma exposure, what each model assumes, and what it
cannot tell you. Written to be read directly - by a person or by a model - without
needing the rest of the API reference.

Canonical URL: https://flashalpha.com/methodology
API reference: https://flashalpha.com/docs/api

---

## Two views of the same chain

FlashAlpha serves dealer gamma under two independent models, selected with the
`polarity` query parameter on the `/v1/flow/*` endpoints. Neither is a correction of
the other.

| | `polarity=convention` (default) | `polarity=flow` |
|---|---|---|
| Position source | Settled open interest | Session volume, classified by aggressor |
| Sign source | Fixed convention: calls positive, puts negative | `dealer_pos = sells − buys` |
| What it reads as | The **structural** gamma map carried into today | What today's **tape** is doing to that map |
| Window | Prior-night OI snapshot, static during the session | Cumulative from 09:30 ET, starting at zero |
| Availability | All paid tiers | Alpha and above |

**Agreement means** session flow is reinforcing the structural regime.
**Disagreement means** intraday flow is pushing against it. It does **not** mean the
structural sign has flipped: a structural +$8bn against −$1bn of session inventory
transfer is still net long gamma. The flow model measures a change, not a level.

---

## Why the conventional model needs an assumption

Gamma is identical for a call and a put at the same strike and expiry. The sign in a
gamma-exposure calculation therefore cannot come from the option's right - it comes
from who is long and who is short. Long an option is long gamma; short an option is
short gamma, whichever right it is.

Public open interest reports how many contracts exist. It does not report who holds
which side. Any OI-based dealer-gamma figure must therefore assign a position sign by
convention. The standard convention - the one FlashAlpha's `convention` polarity
implements - assigns positive gamma to calls and negative gamma to puts, which is
equivalent to assuming dealers are long calls and short puts.

That assumption is usually reasonable and sometimes wrong. It is stated here rather
than buried because it is the single largest modelling choice in any GEX number,
including everyone else's.

---

## How the tape is classified

The `flow` polarity needs to know which side initiated each trade. FlashAlpha
classifies every option print at ingest:

1. Trades arrive as Databento **TCBBO** records - the trade and the concurrent NBBO in
   one message, so the quote is contemporaneous rather than joined from a separate
   stream.
2. A quote rule assigns the aggressor (Lee-Ready-style):
   - price at or near the **ask** to buyer-initiated
   - price at or near the **bid** to seller-initiated
   - anything in the inner band to **midpoint**
3. Midpoint prints are **excluded** from net flow rather than guessed. There is no
   tick-rule fallback, so this is a quote rule in the Lee-Ready family, not a literal
   implementation of Lee-Ready.
4. Trades with no NBBO at the moment of execution are classified midpoint.
5. Every print is persisted with its side and the bid/ask at execution, so
   classification is reproducible and auditable historically, not only live.

The feed's own carried side flag is deliberately ignored: it reports neutral for
dealer-internalised prints, which is exactly the population that matters.

`net_customer = buys − sells` per contract. `dealer_pos = −net_customer`, i.e. the
passive side of every classified trade is attributed to liquidity providers.

### Futures options

CME/GLBX options use the same quote rule with a stricter band - a print must reach the
posted bid or ask exactly, with no inner tolerance. Futures-options polarity is
therefore slightly more conservative than OPRA polarity and the two are not
numerically comparable. Expect a larger midpoint share on thin roots.

---

## What this does not tell you

Stated plainly, because these limits are load-bearing:

- **Not an absolute dealer book.** `dealer_pos` accumulates from zero at the 09:30 ET
  open. It is what dealers traded into during the session, not their full inventory
  including prior days.
- **Not participant identification.** OPRA carries no participant or origin code.
  FlashAlpha does not distinguish market-maker from retail from institutional flow, and
  cannot from this feed. "Customer" throughout means "the aggressing side" - nothing
  more. Trade size (`is_block`) is a size threshold, not an identity.
- **Not open/close status.** The public tape does not say whether a trade opened or
  closed a position. Where FlashAlpha reports an opening or closing bias it is an
  inference from simulated intraday OI, carries an explicit confidence, and directional
  intent is withheld entirely on a closing bias.
- **Inference, not exchange accounting.** The dealer side is derived from market data
  under the assumptions above.

---

## Coverage diagnostics

Every `polarity=flow` response carries a `flow_coverage` block, so an all-zero profile
is never ambiguous:

| Field | Meaning |
|-------|---------|
| `contracts_total` | Distinct contract identities in the chain slice |
| `contracts_with_flow` | Identities with at least one classified print this session |
| `twin_contracts_merged` | Contracts whose flow could not be attributed to a specific OPRA root, because an AM-settled and a PM-settled root share an expiry, strike and right. Non-zero only on SPX third Fridays today; `0` for every ordinary symbol |
| `flow_source_ok` | `false` only when the classified-flow source could not be **read**: unreachable, or a response that is not a valid flow envelope (wrong symbol echoed, missing contracts collection, an error payload). A response echoing the requested symbol with an explicit empty contracts list is a quiet tape and reports `true` |

The distinction matters in both directions, and the flag is deliberately built to survive
both failure modes:

- Pre-market, on an untraded expiry, or on a thin name, the flow map is legitimately empty
  while the source is perfectly healthy. Availability is reported by the fetching client
  rather than inferred from map emptiness, so the flag does not fire on every quiet chain.
- An error payload that happens to parse must not read as a flat market. Deserialising
  without throwing is not enough evidence, so the envelope is validated: the symbol must
  match the request and the contracts collection must be present. An empty collection is
  still valid - that is precisely the quiet tape.

A reading with `flow_source_ok: false` should not be traded or charted: an all-zero
profile there means *unknown*, not *flat*.

---

## The intraday OI simulator

Separately from polarity, FlashAlpha estimates intraday open interest so exposure can
be computed against a chain that has moved since the overnight snapshot.

Each classified trade contributes `±0.43 × size` to simulated OI (buy positive, sell
negative, midpoint zero). The 0.43 weight is calibrated, not assumed: it was raised
from 0.40 after reconciling 525,000 contract-days across nine symbols against
settled EOD open interest, and residuals are tracked continuously.

`effective_oi = settled OI + simulated intraday delta`, with `oi_delta_confidence`
returned alongside so callers can weight it.

This path is independent of `polarity` and is keyed per OPRA root, so it is unaffected
by the twin-root limitation above.

---

## Point-in-time integrity

Historical replay via `?at=` recomputes from the state known at that timestamp. Settled
open interest is the start-of-day vintage, so a replayed reading does not incorporate
open interest that had not yet been published. This is what makes the dataset usable
for backtests without look-ahead.

---

## Field reference

| Field | Definition |
|-------|------------|
| `net_customer` | `buys − sells` in contracts, from classified prints; midpoint excluded |
| `dealer_pos` | `−net_customer`; the inferred liquidity-provider side |
| `live_net_gex` | Σ `dealer_pos × gamma × multiplier × spot² × 0.01` across the slice |
| `live_net_gex_label` | `positive` when the session left dealers longer gamma (stabilising), `negative` when shorter (destabilising) |
| `live_gamma_flip` | Strike where the signed profile crosses zero |
| `live_call_wall` / `live_put_wall` | Strikes of peak signed call / put gamma concentration |
| `live_max_pain` | OI-based, not gamma-signed; identical under both polarities |
