# Changelog Source: https://docs.backquant.com/api/v2/discovery/changelog GET /changelog Returns the v2 release log plus the lifecycle status of v1. Customers integrating against either version use this to decide when to schedule re-integration work — `breaking: true` on any release is the signal to test, and `v1_status` flips to `deprecated` / `sunset` long before v1 is ever turned off. Response is static — no I/O, sub-millisecond — so polling it from a CI job daily is free. Static release log with per-version `breaking` flags. Customers integrating against the API use this to decide when to schedule re-integration work - `breaking: true` on any release is the signal to test. # Endpoint catalog Source: https://docs.backquant.com/api/v2/discovery/endpoints GET /endpoints Introspects the running FastAPI sub-app and returns one entry per route with path, HTTP methods, tag, summary, description, and the parameter list. Each parameter carries its name, location (`query` / `path` / `header`), required flag, type, default, numeric bounds (if any), and `enum` array when the parameter is Literal-typed. Derived from in-memory route metadata — no Redis or DB I/O. Requires `X-API-Key`. For totally anonymous discovery use `/v2/openapi.json` (FastAPI built-in). Live introspection of every registered route in the API. For each endpoint you get the path, HTTP methods, tag, summary, description, and the full parameter list - including the `enum` array for any parameter that's Literal-typed (so you can validate user input client-side against exactly the same values the server accepts). The catalog is derived from the running FastAPI app at request time, not hard-coded. New routes appear here automatically. Use it as a lighter alternative to `/v2/openapi.json` when you want a flat catalog rather than the full OpenAPI 3 schema. ## See also # Active expiries Source: https://docs.backquant.com/api/v2/discovery/expiries GET /expiries Currently-tradeable expiry tokens for a symbol with DTE and an ISO date when the token is parseable. Use this to populate filter UIs without pulling the heavier /options/expiry-summary. Currently-tradeable expiry tokens for a symbol with DTE and ISO date. Use this to populate filter UIs without parsing /options/expiry-summary. ## See also # API metadata Source: https://docs.backquant.com/api/v2/discovery/meta GET /meta Static metadata about the v2 API. Supported symbol and exchange catalogs, the full error code list with HTTP status mapping, the auth scheme, the rate-limit ladder, the response envelope shape, and links to the OpenAPI spec / Swagger UI / ReDoc / endpoint catalog. Cheap (in-memory, no I/O). Requires `X-API-Key` like every other v2 route — anonymous discovery is via `/v2/openapi.json` (FastAPI standard, no auth). Static metadata about the API itself: name, version, the auth scheme, the rate-limit ladder, the response envelope shape, the full error code catalog with HTTP status mapping, and the supported symbol + exchange catalogs. No I/O - pure in-memory dict, sub-millisecond. Use this when you want a single programmatic source of truth for client-side validation: the supported-symbols list, the error-code table, and the auth requirements all come from one call so your client doesn't drift from the API contract. ## See also # Service status Source: https://docs.backquant.com/api/v2/discovery/status GET /status Operational status. Each (symbol × category) cell is classified as `healthy`, `degraded`, `unhealthy`, or `unavailable` based on cache age. The `overall_status` is the worst case across the grid. Thresholds in the response reflect the underlying compute cadence. Operational status: per-symbol-per-category freshness with healthy/degraded/unhealthy classification + an overall aggregate. ## See also # Symbol catalog Source: https://docs.backquant.com/api/v2/discovery/symbols GET /symbols Lists every symbol covered by v2 with the data categories that are currently live, the freshness of each category, the spot price, the count of active expiries, and the full set of supported endpoints. Recommended first call for new integrations. The first call most integrators make. Returns every symbol the API supports with the categories of data currently live for each, the spot price, the count of active expiries, and the full set of endpoint paths that accept a `symbol` parameter. Use it to: * Discover which symbols are supported (`BTCUSDT`, `ETHUSDT`, `SOLUSDT`, `HYPEUSDT`) * Check at a glance whether a symbol has live cache data before hitting downstream endpoints * Build a dynamic UI that lists supported endpoints without hard-coding paths Powered by a single Redis `MGET` round-trip - sub-millisecond when warm. ## See also # Dealer flow (time series) Source: https://docs.backquant.com/api/v2/gex/dealer-flow GET /gex/dealer-flow Estimated **dealer** hedging pressure across successive chain snapshots: gamma, charm (and vanna when available), total, and cumulative. ## Formula (summary) * `gamma_flow` = spot move (%) × prior net GEX * `charm_flow` = −elapsed hours × prior net charm * `total_flow` = sum of components * `cumulative_flow` = running sum of `total_flow` Positive ≈ dealers buying the underlying to re-hedge. Negative ≈ selling. Uses `?positioning=flow|std` (default **flow**). Needs at least two chain snapshots; otherwise 404. This is **not** customer tape delta. For that, use [delta flow](/api/v2/gex/delta-flow). Full model notes: [Positioning](/concepts/positioning). ## See also # Dealer flow by level Source: https://docs.backquant.com/api/v2/gex/dealer-flow-by-level GET /gex/dealer-flow/by-level Expected cumulative dealer hedge dollars if spot walks from the current price to each strike (dealer-flow ladder). Built from the signed strike GEX profile. Positive at a strike ≈ expected dealer **buying** if spot reaches that level. Negative ≈ selling. Default `positioning=flow`. See [Positioning](/concepts/positioning). ## See also # Delta flow (customer) Source: https://docs.backquant.com/api/v2/gex/delta-flow GET /gex/delta-flow Time-bucketed **customer** signed Black-Scholes delta notional from the options tape (HIRO-style delta line), with cumulative net. * Coins: **BTC**, **ETH** * BS inputs use high-quality prints (Deribit for unit and IV consistency) * Distinct from [dealer-flow](/api/v2/gex/dealer-flow) (chain × price moves) Positive net ≈ customer buy-side delta. Negative ≈ customer sell-side delta. For multi-venue premium quadrants (not delta), use [tape imbalance](/api/v2/tape/imbalance) with `weight=flow`. ## See also # GEX by expiry Source: https://docs.backquant.com/api/v2/gex/expiry-profile GET /gex/expiry-profile Net / call / put gamma per expiration date with days-to-expiry. Useful for spotting where dealer exposure rolls off. DTE-axis filters: * `dte_min` / `dte_max` — bound the DTE window. * `weekly_only=true` — exclude monthly + quarterly anchors (defined as the last Friday of the month). Tokens we cannot parse are kept rather than silently dropped. Net / call / put gamma per expiration date with DTE. `?weekly_only=true` excludes monthly + quarterly anchors. Default **`positioning=flow`**. See [Positioning](/concepts/positioning). ## See also # Gamma flow Source: https://docs.backquant.com/api/v2/gex/gamma-flow GET /gex/gamma-flow Dealer gamma hedge series only: each point is `ΔS% × net_GEX` under the selected positioning model, plus a cumulative sum of those increments. Positive ≈ dealer buying pressure from gamma hedging on the move. Negative ≈ selling. Default `positioning=flow`. See [Positioning](/concepts/positioning). ## See also # GEX history Source: https://docs.backquant.com/api/v2/gex/history GET /gex/history Time-series of total GEX (and net-local-5pct, call/put walls when available). Use `?limit` (≤1000) to cap the page size and `?before=` to walk backwards into older history. Cursor-paginated time-series of total GEX, net-local-5pct, call wall, put wall. **Positioning is std only.** Stored history was written under the textbook model. Live levels default to flow. See [Positioning](/concepts/positioning). ## See also # GEX levels Source: https://docs.backquant.com/api/v2/gex/levels GET /gex/levels All-expiry HVL/walls plus 0DTE-specific levels in a single call. Use `?include=` to opt in to optional sections — `spot,candles,zones,max_pain,expected_move,ranked`. Filter by exchange with `?exchanges=deribit,bybit,okx,binance`. Composable HVL / call wall / put support / 0DTE levels in one call. Use `?include=` to opt into max-pain, expected move, ranked top-10, gamma flip zones, spot, candles. Default **`positioning=flow`**. Pass `?positioning=std` for textbook call/put OI signing. See [Positioning](/concepts/positioning). ## See also # Max pain Source: https://docs.backquant.com/api/v2/gex/max-pain GET /gex/max-pain The strike at which option writers profit most at expiry. Use `?expiry=0dte` for the cheapest path (read straight from the 0DTE levels cache), `?expiry=all` for a per-expiry table covering every active expiration, or pass a specific expiry token (e.g. `28MAR25`) to also receive the full pain curve by strike. The strike where option writers profit most at expiry. Use `?expiry=0dte` for the cheapest path, `?expiry=all` for a per-expiry table, or pass a specific expiry token to also receive the full pain curve. ## See also # Stress history (Postgres) Source: https://docs.backquant.com/api/v2/gex/stress-history GET /gex/stress-history Persistent record of HVL / call wall / put wall / net GEX, suitable for backtests or regime analysis. Window is bounded to 30 days (720 hours) so a query never blows out the read pool. Long-window record of HVL / call wall / put wall / net GEX from the persistent store. Suitable for backtests or regime work. Bounded to 30 days. **Positioning is std only** (same as GEX history). Live REST levels default to flow. ## See also # Strike × expiry heatmap Source: https://docs.backquant.com/api/v2/gex/strike-expiry-heatmap GET /gex/strike-expiry-heatmap Rows are strikes, columns are expiries (sorted ascending DTE). Filters apply BEFORE downsampling so the cap parameters operate on the already-narrowed grid: * `dte_min` / `dte_max` — drop expiries outside the DTE window. * `moneyness_min` / `moneyness_max` — drop strikes outside the moneyness window (e.g. `0.8..1.2` keeps ATM ±20%). * `max_strikes` — cap on strikes returned (kept closest to spot). * `max_expiries` — cap on expiries (kept nearest by DTE). 2D matrix of net GEX with rows=strikes, cols=expiries (sorted ascending DTE). `?max_strikes` and `?max_expiries` downsample around spot / front of curve. Default **`positioning=flow`**. See [Positioning](/concepts/positioning). ## See also # GEX by strike Source: https://docs.backquant.com/api/v2/gex/strike-profile GET /gex/strike-profile Gamma exposure aggregated by strike. `?expiry=all` (default) covers every expiry, `?expiry=0dte` filters to today's expiry only, and an explicit expiry token (e.g. `28MAR25`) returns just that expiration. Strike-axis filters (intersected when more than one is set): * `strike_min` / `strike_max` — absolute bounds. * `moneyness_min` / `moneyness_max` — strike/spot ratio bounds, e.g. `0.9..1.1` keeps the ATM ±10% slab. * `spot_window_pct` — symmetric window in percent (e.g. `5` for ±5% of spot). Combined by intersection with the others. When any strike filter narrows the set, `ranked_top_10` is rebuilt from the surviving strikes — the cached pre-filter ranking is not reused. Gamma exposure aggregated by strike. Filter by `?expiry=all|0dte|` and by strike window (`strike_min/max`, `moneyness_min/max`, `spot_window_pct`). Default **`positioning=flow`**. See [Positioning](/concepts/positioning). ## See also # Strike × time heatmap Source: https://docs.backquant.com/api/v2/gex/time-heatmap GET /gex/time-heatmap Time-series of greek exposures by strike. Pick the greek (`gex|dex|vex|charm|vanna`) and bound by DTE / OI / IV. Without filters this serves the pre-computed cache (cheap); with filters it reconstructs from raw snapshots (more work, but still bounded to the last ~48 ticks). Time-series of greek exposures by strike. Pick the greek (`gex|dex|vex|charm|vanna`), bound by DTE / OI / IV. Default **`positioning=flow`**. See [Positioning](/concepts/positioning). ## See also # Health check Source: https://docs.backquant.com/api/v2/health GET /health Lightweight liveness/readiness probe for external monitors. Reports overall status plus per-subsystem (redis, db) flags. Auth-free — uses the same `check_connection_health()` Fly.io's load balancer hits at `/health`. Rate limit: **60/minute per IP**. Status values: `ok` (all subsystems healthy), `degraded` (one subsystem failing), `unhealthy` (cache + DB both unreachable). Auth-free liveness probe for monitors. Returns subsystem status (Redis + Postgres). ## See also # Liquidation distribution Source: https://docs.backquant.com/api/v2/liquidation/distribution GET /liquidation/distribution Per-price-level long and short liquidation volumes, with a leverage tier estimate based on distance from current price and cumulative-volume series for visualizing concentration. Per-price-level long and short liquidation volumes with a leverage-tier estimate based on distance from spot. Use it as a histogram visualisation or as input to position-sizing logic - clusters of leverage near current price are where stop-cascades typically trigger. Leverage tiers are estimated from distance to spot: | Distance from spot | Tier | | ------------------ | ----------------- | | 0–1% | 100x (high) | | 1–2% | 50x (medium-high) | | 2–4% | 25x (medium) | | 4%+ | 10x (low) | Updated every 5 minutes server-side. ## See also # Liquidation heatmap Source: https://docs.backquant.com/api/v2/liquidation/heatmap GET /liquidation/heatmap Estimated liquidation levels normalized to a 0–100 scale, gridded by price bin and time bucket. Includes the OHLC candles plotted alongside so the heatmap can be rendered standalone. Estimated liquidation levels normalized to a 0–100 scale, gridded by price bin and time bucket. Includes OHLC candles for standalone rendering. # Multi-symbol GEX levels Source: https://docs.backquant.com/api/v2/multi/gex-levels GET /multi/gex/levels Multi-symbol GEX levels in one round-trip. Returns a `results` map keyed by symbol with the same shape per-entry as /v2/gex/levels. Symbols with no current data return null in `results` and appear in `missing` rather than failing the whole request. Up to 8 symbols per request. Includes: `ranked` (top-10 strikes by |GEX|), `max_pain` (0DTE only), `expected_move` (1σ / 2σ implied range), `zones` (gamma flip zones). Bundled multi-symbol read. Up to 8 symbols per request. Returns per-symbol entries plus a `served` / `missing` / `invalid` breakdown. Default **`positioning=flow`** (with the same per-symbol fallbacks as single-symbol levels). See [Positioning](/concepts/positioning). ## See also # Options chain Source: https://docs.backquant.com/api/v2/options/chain GET /options/chain Per-contract data (greeks, OI, IV, bid/ask) plus aggregate stats. Filters supported: * `expiries` — CSV of tokens (e.g. `28MAR25,25APR25`). * `dte_min` / `dte_max` — days-to-expiry bounds. * `oi_min` / `oi_max` — open interest bounds. * `delta_min` / `delta_max` — applied to |delta| (0..1). * `strike_min` / `strike_max` — absolute strike bounds. * `moneyness_min` / `moneyness_max` — strike / spot ratio bounds, e.g. `0.9..1.1` keeps strikes within ±10% of spot. Useful for cross-symbol screens since it normalises the strike grid. * `option_type` — `call`, `put`, or `both`. Two layers of projection: * `fields` (top-level): comma-sep subset of `aggregates,available_expiries,contracts` — drops whole sections of the response, e.g. `?fields=contracts` to skip aggregates. * `include` (per-contract): comma-sep subset of `bid_ask,gex,greeks,iv,oi,volume` — keeps only the named sub-sections of each contract dict. Core identity fields (`strike,expiry,dte,type`) are always present. Default is every section, matching v1 shape. Payload is capped at `?max_contracts=` (default 2000, hard ceiling 5000). On overflow the response is truncated to the closest-to-spot subset and `truncated=true` is set with `total_matching_count` so the client knows to narrow the filter. Response is cached for 30s keyed by the exact filter combination, so repeated calls with identical filters are sub-millisecond. Filtered chain with two-layer projection (`?fields=` top-level, `?include=` per-contract) and a payload cap (`?max_contracts`). Cached 30s per filter combination. ## See also # Expected move Source: https://docs.backquant.com/api/v2/options/expected-move GET /options/expected-move ATM-straddle-derived 1- and 2-sigma expected move (1d horizon). ATM-straddle-derived 1- and 2-sigma expected move (1d horizon). ## See also # Per-expiry summary Source: https://docs.backquant.com/api/v2/options/expiry-summary GET /options/expiry-summary One row per active expiry with the metrics most useful for chain-wide screening: **Pre-computed every 30s** — ATM IV, 25Δ skew, put/call 25Δ IVs, PCR, call/put/total OI, net GEX. **Added by v2 (OPEX enrichment, on-the-fly):** * `type` — `daily | weekly | monthly | quarterly` (Deribit token parsed; quarterlies are last Fridays of Mar/Jun/Sep/Dec). * `is_anchor` — true for monthly + quarterly anchors. * `expiry_date` — ISO date when the token parses cleanly. * `notional_oi_usd` — `total_oi × spot_price` (each contract is 1 unit of underlying on Deribit-style chains). * `max_pain` — `{strike, value}` from a per-expiry max-pain pass over the latest chain snapshot. Cached 30s, shared with `/v2/gex/max-pain` and `/v2/options/opex`. One row per active expiry with ATM IV, 25Δ skew, PCR, OI, net GEX, plus OPEX enrichment (type, is\_anchor, expiry\_date, notional\_oi\_usd, max\_pain). ## See also # Futures term structure Source: https://docs.backquant.com/api/v2/options/futures/term-structure GET /options/futures/term-structure Deribit-listed dated futures annualized basis vs spot, by maturity. Useful as a complement to the IV term structure for assessing carry in the calendar. Deribit-listed dated futures annualized basis vs spot, by maturity. Useful as a complement to the IV term structure for assessing carry. ## See also # 3D greek surface Source: https://docs.backquant.com/api/v2/options/greeks/3d-surface GET /options/greeks/3d/surface Z = sum(greek × OI) per (strike, expiry) cell. Useful for 3D visualizations or for spotting where a particular greek is concentrated across the term structure. Z = sum(greek × OI) per (strike, expiry) cell for any greek. Useful for 3D visualizations or for spotting where exposure concentrates across the term structure. ## See also # Greek profile Source: https://docs.backquant.com/api/v2/options/greeks/by-greek GET /options/greeks/{greek} Net greek exposure per strike. `greek` is one of `delta` (DEX), `theta`, `vanna`, `charm`, `vega`. Filters: * `exchanges` — comma-separated venue filter. * `moneyness_min` / `moneyness_max` — strike/spot ratio bounds, e.g. `0.9..1.1` keeps ATM ±10% exposure only. * `dte_max` — re-aggregates from the raw chain to limit contributing contracts to `DTE ≤ dte_max`. The default cached greeks profile spans every active expiry; this lets you isolate near-term exposure. Cached 30s per `(symbol, greek, dte_max, exchanges)` so repeated requests stay cheap. Net greek exposure per strike. `greek` is one of `delta` (DEX), `theta`, `vanna`, `charm`, `vega`. Optional `?dte_max` recomputes from the raw chain limited to short-DTE contracts. Default **`positioning=flow`**. See [Positioning](/concepts/positioning). ## See also # Greek surface (strike × time) Source: https://docs.backquant.com/api/v2/options/greeks/surfaces GET /options/greeks/surfaces/{greek} Time-evolving surface for `charm` or `vega` — strike on one axis, time on the other. Already pre-computed for the pro terminal; exposed here for quant tooling that wants the same view. Time-evolving surface for `charm` or `vega`. Already pre-computed for the BackQuant terminal; exposed here for quant tooling. ## See also # Greek TRACE - forward (time × price) projection Source: https://docs.backquant.com/api/v2/options/greeks/trace GET /options/greeks/trace/{greek} Black-Scholes forward projection of dealer **gamma**, **charm**, or **vanna** across a (time × price) grid, holding today's chain constant and decaying τ toward each contract's expiry. Returns a 2D `field` of cell values, the `time_axis_ms` and `price_axis` it's keyed on, and the contracts it consumed. Intended for desk-grade dealer-positioning visualisations (the BackQuant Pro Terminal's TRACE panel runs the same math client-side). **Sign convention:** +1 for calls, −1 for puts — positive cell = call book dominates the put book at that (time, price). **Units:** * `gamma` → `$ per 1% spot move` (γ × S² × OI × 0.01). * `charm` → 1/year (∂Δ/∂τ × OI). * `vanna` → per IV unit (∂Δ/∂σ × OI). **Performance:** the field is computed on cache miss in ~50–100 ms for typical grids. Cached for 30 s per `(symbol, full-param-hash)` so identical repeated calls are essentially free. **What-if vol shifts:** `vol_shift_pct` multiplies every contract's σ by `1 + vol_shift_pct/100` before evaluation. Lets you build vol-up / vol-down surfaces without re-fetching. Forward Black-Scholes projection of dealer **gamma**, **charm**, **vanna**, or **delta\_change** across a `(time × price)` grid. Same view as BackQuant Pro TRACE, as raw data for your own renderer. Holding today's chain constant, the endpoint walks each grid point (future timestamp + future spot), recomputes per-contract τ and the requested greek, sums across the chain with positioning-weighted size, and returns the field. Default **`positioning=flow`**. Pass `?positioning=std` for textbook OI sign. See [Positioning](/concepts/positioning). ## Why use it A point-in-time greek profile only shows exposure *now*. TRACE shows how walls develop, decay, or migrate as time and price evolve. Common uses: * **Intraday flow planning** - `greek=gamma&expiry=0dte` shows where 0DTE dealer hedging concentrates as the session progresses. * **Charm bleed** - `greek=charm` exposes passive delta drift from time alone, even with spot frozen. * **Delta change** - `greek=delta_change` maps how dealer delta shifts relative to spot/now as the grid walks (useful for hedge path views). * **Vol-shock surfaces** - `vol_shift_pct=±10` for instant vol up/down without a re-fetch. * **Multi-day overlays** - `expiry=all&horizon_hours=168` across the next week's term structure. For pin zones / acceleration pockets without the full grid, use [TRACE summary](/api/v2/options/greeks/trace-summary). ## Sign convention Under **std**, calls contribute positive size and puts negative (same as the rest of the stack). Under **flow**, size is aggressor sold minus bought per side. A positive cell means the signed book leans supportive at that `(time, price)`; negative means the reverse. ## Units | `greek` | Unit | Formula per cell (std qty = OI; flow qty = sold−bought) | | -------------- | ------------------- | ------------------------------------------------------------ | | `gamma` | \$ per 1% spot move | `Σ sign × γ × S² × qty × 0.01` | | `charm` | per year | `Σ sign × ∂Δ/∂τ × qty` | | `vanna` | per IV unit | `Σ sign × ∂Δ/∂σ × qty` | | `delta_change` | delta units | change in summed signed delta vs reference (spot now, τ now) | `field_units` is echoed on every response so renderers need not hardcode this. ## Cells, axes, and shape ``` field[t][p] ← cell value at time_axis_ms[t], price_axis[p] ``` `time_axis_ms` runs from "now" to `now + horizon_hours`. `price_axis` runs from `spot × (1 − range/100)` to `spot × (1 + range/100)`. Both are evenly spaced. `abs_field_max = max(|field_min|, |field_max|)` supports symmetric colour scales: `value / abs_field_max` clamped to `[−1, +1]`. ## What-if vol shifts `vol_shift_pct` adds a flat percent shift to every contract's IV. Example: `vol_shift_pct=5` multiplies each σ by `1.05` before evaluation. ## Performance Cached for **30 seconds** per `(symbol, full-param-hash)`. Cold compute is typically **50–100 ms** for default grids; payload is often **30–80 KB** of JSON. ## Caps | Param | Min | Max | Default | | ----------------- | --- | --- | ------- | | `time_steps` | 2 | 200 | 60 | | `price_steps` | 2 | 200 | 80 | | `price_range_pct` | >0 | 50 | 8 | | `horizon_hours` | >0 | 720 | 24 | | `vol_shift_pct` | −50 | +50 | 0 | | `min_oi` | 0 | - | 0 | Going past the caps returns a `VALIDATION_ERROR` with a 422. ## See also # Greek TRACE summary Source: https://docs.backquant.com/api/v2/options/greeks/trace-summary GET /options/greeks/trace/{greek}/summary Same forward projection as [TRACE](/api/v2/options/greeks/trace), but returns an interpretability summary instead of the full 2D field: * pin zones (high mean absolute field) * near-spot acceleration pockets * late vs early lean along spot * zero-contour samples at the horizon Supports `gamma`, `charm`, `vanna`, and `delta_change`. Default `positioning=flow`. ## See also # All-expiry smile curves Source: https://docs.backquant.com/api/v2/options/iv/curves GET /options/iv/curves Merged-IV smile (OTM puts below spot, OTM calls above) for every active expiry, sorted by DTE. Full merged-IV smile (OTM puts below spot, OTM calls above) for every active expiry, sorted by DTE. ## See also # IV vs realized vol history Source: https://docs.backquant.com/api/v2/options/iv/iv-rv GET /options/iv/iv-rv Daily implied (DVOL/ATM-IV) and 30d realized vol with the spread. Daily implied (DVOL/ATM-IV) and 30d realized vol with the spread. ## See also # Skew (25Δ / 10Δ + butterfly) Source: https://docs.backquant.com/api/v2/options/iv/skew GET /options/iv/skew Delta-interpolated risk-reversal and butterfly per active expiry. `skew_25d` = 25Δ put IV − 25Δ call IV (positive = put premium, fear bid); `butterfly_25d` = (25Δ put IV + 25Δ call IV) / 2 − ATM IV (smile curvature / wing pricing). Computed from real delta-bracketed options, not strike proxies. Delta-interpolated risk-reversal and butterfly per active expiry. `skew_25d` = 25Δ put IV − 25Δ call IV. ## See also # Single-expiry smile Source: https://docs.backquant.com/api/v2/options/iv/smile GET /options/iv/smile Implied volatility by strike for one expiry — the cheap path when you only need a single tenor. Pass `?expiry=` as a Deribit-style token (e.g. `28MAR25`); omit it to receive the default front-month smile from `iv:smile:{symbol}:default`. The lookup is case-insensitive on the token; if neither cased form exists, this returns 404 with the available tokens hinted in the error. IV by strike for one expiry. Lighter than `/iv/curves` when you only need one tenor. ## See also # IV surface Source: https://docs.backquant.com/api/v2/options/iv/surface GET /options/iv/surface 2D implied-volatility grid: rows are strikes, columns are expiries (sorted by ascending DTE). Useful for vol-surface visualisations and identifying skew/term-structure shape at a glance. 2D implied-volatility grid: rows are strikes, columns are expiries (sorted by ascending DTE). ## See also # Term structure Source: https://docs.backquant.com/api/v2/options/iv/term-structure GET /options/iv/term-structure At-the-money implied volatility for each active expiration. Filters: * `exchanges` — comma-separated venue filter. * `dte_max` — drop tenors with DTE > dte_max (focus on the front of the curve). * `historical_compare_days=N` — also include the constant-maturity ATM IV from N days ago, sourced from the `iv_history` Postgres table. Returned as a separate `historical` block under `compare`. Different shape from the live curve — historical is by DTE-bucket (7/14/30/60/90/180/365) since that's how the worker stores it; the client can interpolate onto the live tenor grid for visualisation. ATM IV per active expiration. Filter `?dte_max` for front-of-curve, or `?historical_compare_days=N` for today vs N days ago overlay. ## See also # OI by expiry Source: https://docs.backquant.com/api/v2/options/oi/by-expiry GET /options/oi/by-expiry Open interest aggregated by expiration with calls and puts split. DTE-axis filters: * `dte_min` / `dte_max` — bound the DTE window. * `weekly_only=true` — exclude monthly + quarterly anchors (last-Friday-of-month). Useful when monthly OI dominates the picture and you want to see the weekly distribution alone. Open interest aggregated by expiration with calls and puts split. `?weekly_only=true` excludes monthly + quarterly anchors. ## See also # OI history Source: https://docs.backquant.com/api/v2/options/oi/history GET /options/oi/history Time-series of total / call / put open interest. Use `?limit` to cap page size and `?before=` to walk backwards into older history (the cursor is the timestamp of the oldest row in the previous page, returned as `next_cursor`). Cursor-paginated time-series of total / call / put open interest for the underlying. Use cursor pagination (`?limit` + `?before=`) to walk backwards through history without missing rows or double-counting at page boundaries. The response carries `next_cursor` - pass it as `?before=` on the next call to fetch the previous page. Useful for: * Detecting OI build-ups before major expirations * Backtest data feeds that need contiguous history * Spotting unwinds (sudden OI drops) coincident with price moves ## See also # OPEX calendar Source: https://docs.backquant.com/api/v2/options/opex GET /options/opex Curated calendar of upcoming options expirations with everything needed for OPEX-day workflows in one call: * Token + ISO date + DTE + classification (daily / weekly / monthly / quarterly) + `is_anchor` flag. * OI block: `call_oi`, `put_oi`, `total_oi`, `pcr`, `notional_oi_usd` (= total_oi × spot, since each crypto options contract is 1 unit of underlying). * IV block: `atm_iv`, `skew_25d`, `put_25d_iv`, `call_25d_iv`. * Gamma block: `net_gex`, `call_resistance`, `put_support` (computed per expiry from the strike × expiry heatmap), and `max_pain` (`{strike, value}` from the shared per-expiry cache used by `/v2/gex/max-pain` and `/v2/options/expiry- summary`). `next_anchor` points at the closest monthly/quarterly. Filter with `?types=monthly,quarterly` for the institutional-roll view or `?horizon=` to bound the look-ahead window. Curated calendar of upcoming expirations with type classification, anchor flag, OI, IV, gamma blocks (walls + max-pain), and `next_anchor` pointer. ## See also # Put / call ratio Source: https://docs.backquant.com/api/v2/options/pcr GET /options/pcr OI-weighted put/call ratio. `?granularity=intraday` returns the rolling intraday series; `daily` returns one value per UTC day. OI-weighted put/call ratio - the most-asked-for single sentiment indicator from options data. PCR > 1 means more put OI than call OI (typically defensive / fear bid); PCR \< 1 means call-heavy (typically risk-on). Two granularities: * **`?granularity=intraday`** - rolling intraday series, refreshed every 30s. Use for live dashboards. * **`?granularity=daily`** - one value per UTC day, sourced from the historical store. Use for time-series studies and backtests. Filter the daily mode with `?days=N` (1–365) to control window length. ## See also # Premium tide Source: https://docs.backquant.com/api/v2/options/premium-tide GET /options/premium-tide Net premium and notional volume from Deribit-side trades, split into call and put flow. Use `?horizon=0dte` for today's tape or `weekly` for the rolling weekly aggregate. Net premium and notional volume from Deribit-side trades, split into call and put flow. `?horizon=0dte|weekly` selects today's tape vs the rolling weekly aggregate. # Probability density Source: https://docs.backquant.com/api/v2/options/probability/density GET /options/probability/density Breeden-Litzenberger probability density derived from option prices. Returns PDF, normalized PDF, CDF (above and below spot), and distribution stats (mean, std, mode, skew, kurtosis, 25/50/75 percentile prices) per expiry. Filters: * `expiry` — slice to a single expiry token (case-insensitive). * `dte_max` — drop expiries with DTE > dte_max so near-term clients can avoid carrying the long tail. * `confidence_band` — when set to `0.68` or `0.95` (the most common choices), each expiry payload gets a `confidence_band` object with `lower` / `upper` price levels containing that much implied probability mass, computed by interpolating the CDF. Risk-neutral PDF/CDF derived from option prices via Breeden-Litzenberger. Optional `?confidence_band=0.68|0.95` returns interpolated price bands per expiry. ## See also # Probability surface Source: https://docs.backquant.com/api/v2/options/probability/surface GET /options/probability/surface Risk-neutral probability density across the full strike × expiry grid as a single matrix, suitable for 3D visualisations or surface-fit applications. Built from the same Breeden-Litzenberger extraction as /options/probability/density but pre-aligned onto a unified strike axis so cross-expiry comparisons are direct. Risk-neutral PDF across the full strike × expiry grid as a single matrix, suitable for 3D visualizations. ## See also # Volatility risk premium Source: https://docs.backquant.com/api/v2/options/vrp GET /options/vrp Difference between implied and realized vol; positive = options expensive. Difference between implied and realized vol. Positive = options expensive vs realized; sustained `vrp < 0` is usually a buy-vol signal. ## See also # Tape imbalance Source: https://docs.backquant.com/api/v2/tape/imbalance GET /tape/imbalance Server-side time-bucketed rollup of the options tape. One row per bucket so long windows stay small. | `weight` | Meaning | | ---------------- | ---------------------------------------------------------------------------- | | `flow` (default) | Multi-venue aggressive premium by call/put × buy/sell, plus passive | | `delta` | Customer BS delta notional (HIRO-style). Deribit-quality prints for IV/units | Each point includes `net` and `cumulative_net` so you can plot a running line without another pass. Coins: BTC, ETH. ## See also # Tape overview Source: https://docs.backquant.com/api/v2/tape/overview Multi-venue crypto options trade tape: REST history, live WebSocket, and rollup analytics across Deribit, Bybit, OKX and Binance. The **Options Tape API** is BackQuant's multi-venue, real-time and historical record of every options trade we observe. The same canonical payload powers cursor-paginated REST history and a live WebSocket stream. Use it for scoring methodologies, flow dashboards, and execution analytics. ## What's in it * **Every trade**, normalised: venue / coin / instrument / strike / expiry / direction / amount / price / index price / IV (when present) / premium in USD / block-trade flag / ms timestamp. * **4 venues** ingested continuously (see [Venue coverage](#venue-coverage)). * **5-year retention** in the persistence layer. Backfill of history before `2026-05-17` is available on request via our archive partner. * **Sub-second freshness** on the WebSocket. REST `/recent` reflects the latest trade as soon as it lands in Postgres (\~200ms after the venue WS emits it). * **Filters** for venue, instrument, option type, strike range, premium minimum, and derived tags (`0dte`, `atm`, `whale`). * **Analytics helpers**: time-bucket imbalance, strike heat, whale prints. ## Venue coverage | Venue | Source | Latency to tape | Coverage | | ----------- | ------------------------------------------------------------------------------------- | --------------- | --------------------------------------------------------------- | | **Deribit** | Native public WebSocket | \< 200 ms | Full BTC + ETH chains, every trade | | **Bybit** | Native public WebSocket (`publicTrade.{coin}.option`) | \< 200 ms | Full BTC + ETH option chains | | **OKX** | Native public WebSocket (`option-trades` channel, `BTC-USD` + `ETH-USD` instFamilies) | \< 200 ms | Every trade on listed BTC + ETH instruments | | **Binance** | REST polling: top-30 most-active BTC + ETH contracts every 30 s | \~ 30 s | Top-volume strikes only (\~95% of total Binance options volume) | Binance polls the most-active contracts rather than every instrument because the Binance options public WS endpoint is not reachable from our infrastructure. The hot-contract list refreshes every 5 minutes by 24-hour volume. ## Coins * `BTC` and `ETH` are fully covered across all four venues. * `SOL` and `HYPE` are accepted by the API schema (some venues are starting to list them) but coverage is currently thin to non-existent. Filter for those at your own risk. ## Tape vs derived GEX | Surface | Live? | History? | Positioning | | ------------------------------------- | ------------------ | ------------------------ | ------------------------------------------- | | **Tape** (this section) | Yes, multi-venue | Yes, 5y cursor-paginated | N/A (raw trades) | | **Live GEX / TRACE / greek profiles** | Yes, \~30s refresh | Rolling Redis window | **flow** default; `?positioning=std` opt-in | | **GEX history / stress-history** | N/A | Stored series | **std** only | | **WS `gex.levels.*`** | Yes (\~30s) | No | **std** snapshot | See [Positioning](/concepts/positioning) for flow vs std. If you need deep history of GEX, IV or levels (not the raw tape), contact us for a custom engagement using replayed chain snapshots. ## Quick start ```bash curl - recent trades theme={null} curl https://api.backquant.com/v2/tape/recent?symbol=BTCUSDT&limit=5 \ -H "X-API-Key: $BQ_API_KEY" ``` ```python Python - WebSocket theme={null} import asyncio, json, os, websockets URL = "wss://api.backquant.com/v2/ws/options?api_key=" + os.environ["BQ_API_KEY"] async def main(): async with websockets.connect(URL) as ws: welcome = json.loads(await ws.recv()) print("welcome:", welcome["subscription_tier"]) await ws.send(json.dumps({ "action": "subscribe", "channels": ["tape.BTC.agg"], })) async for raw in ws: msg = json.loads(raw) if msg.get("event") == "trade": t = msg["data"] print(t["venue"], t["instrument"], t["direction"], "$", t["premium_usd"]) asyncio.run(main()) ``` Need a working demo client? Grab [`ws_tape_demo.py`](https://github.com/backquant/backquant-terminal/blob/main/scripts/v2_ws_demo/ws_tape_demo.py) from the repo. Single file; needs `pip install websockets`. ## See also # Tape — recent trades Source: https://docs.backquant.com/api/v2/tape/recent GET /tape/recent Shorthand for the latest N trades across the requested venues, sorted newest-first. Equivalent to `GET /v2/tape?limit=N&order=desc`, with no cursor state to manage. Use this when you just want a sanity sample or a "what's been happening" dump. Use [`/v2/tape`](/api/v2/tape/tape) when you need filters or pagination. ## See also # Tape — aggregate stats Source: https://docs.backquant.com/api/v2/tape/stats GET /tape/stats Aggregate roll-up over a configurable window (up to 7 days). Returns overall + per-venue totals: trade count, total premium in USD, contract volume, call/put split, and buy-side vs sell-side premium. Use this when you don't need raw trades — just the headline numbers for a scoring model, dashboard, or alert. ## Example response shape ```json theme={null} { "success": true, "data": { "window_hours": 24, "overall": { "trades": 59145, "premium_usd": 65332823.53, "contracts": 59627.75, "calls": 31431, "puts": 27714, "buy_premium_usd": 31252458.99, "sell_premium_usd": 34080364.54 }, "per_venue": { "deribit": { "trades": 14610, "premium_usd": 51521299.5, ... }, "bybit": { "trades": 44406, "premium_usd": 5759987.3, ... }, "okx": { "trades": 123, "premium_usd": 8051378.5, ... }, "binance": { "trades": 6, "premium_usd": 158.0, ... } } }, "meta": { "version": "2.0", "computed_at": "...", "source": [...] } } ``` ## See also # Tape strike heat Source: https://docs.backquant.com/api/v2/tape/strike-heat GET /tape/strike-heat Aggressor premium by strike over a lookback window: buy, sell, net, total, and trade count. Ranked by absolute net premium. Coins: BTC, ETH. Optional venue filter and minimum premium. ## See also # Tape — full filterable history Source: https://docs.backquant.com/api/v2/tape/tape GET /tape Multi-venue options trade tape with cursor pagination. All filters compose with AND. Returns up to 5000 trades per page; pass the response's `next_cursor` back as `?before=` to walk older history. ## How pagination works * **`order=desc`** (default) — newest first. The returned `next_cursor` is the **oldest** trade's timestamp in the page. Pass it as `?before=` on the next call to walk backward. * **`order=asc`** — oldest first. The `next_cursor` is the **newest** trade's timestamp in the page. Pass it as `?after=` to walk forward. * When `has_more` is `false`, you've reached the end. Cursors are emitted with a `Z` (UTC) suffix so they survive being copied straight back into a URL query string. ## Derived tags The `tags` parameter accepts a CSV of: * **`0dte`** — `expiry_date` equals today (UTC). * **`whale`** — trades with `premium_usd` ≥ \$250 000 by default. Override with `premium_min_usd`; the larger of the two wins. * **`atm`** — strike within ±2 % of the trade's `index_price`. Trades without an index price are excluded. ## Coverage See [Tape overview → Venue coverage](/api/v2/tape/overview#venue-coverage) for per-venue freshness and listing notes. ## See also # Tape — live WebSocket Source: https://docs.backquant.com/api/v2/tape/websocket Channel-multiplexed live stream of every options trade across Deribit, Bybit, OKX and Binance, with API-key auth, per-tier connection caps, and slow-consumer protection. `wss://api.backquant.com/v2/ws/options` Single connection, multiple subscriptions. The same canonical trade payload from the REST tape, pushed sub-second after the upstream venue emits it. ## Authentication Pass your API key on the WebSocket handshake — either of: * **Query parameter** — `wss://api.backquant.com/v2/ws/options?api_key=bq_live_...` (easiest from browsers). * **`Authorization: Bearer` header** — preferred from server-to-server clients. * **`X-API-Key` header** — accepted as a fallback for clients that can set arbitrary handshake headers. The handshake validates the key, subscription status and tier eligibility. Failures close the connection with a `1008` policy-violation code and a human-readable reason. ## Channel inventory | Channel | Payload | Frequency | | --------------------- | -------------------------------------------------------------- | ------------------------------- | | `tape.{coin}.{venue}` | One trade per frame | Per trade (\~10–100/s in burst) | | `tape.{coin}.agg` | Same trade payload, aggregated across all venues for that coin | Per trade | | `gex.levels.{SYMBOL}` | GEX levels snapshot (HVL, walls, spot) | When levels update (\~30 s) | | `status.heartbeat` | Server liveness + per-client queue stats | Every 15 s | `{coin}` is `BTC` or `ETH`. `{venue}` is `deribit`, `bybit`, `okx`, or `binance` (case-insensitive on subscribe; the server normalises to lowercase). `{SYMBOL}` is `BTCUSDT`, `ETHUSDT`, `SOLUSDT`, or `HYPEUSDT`. See [Tape overview → Venue coverage](/api/v2/tape/overview#venue-coverage) for what each venue ships. **Levels caveat:** `gex.levels.*` pushes a **std** (textbook) levels snapshot. REST [`/v2/gex/levels`](/api/v2/gex/levels) defaults to **flow**. See [Positioning](/concepts/positioning). Max **48 channels per connection**. ## Protocol ### Server → client envelope Every server frame is a JSON object with an `event` field: ```json theme={null} { "event": "welcome", "ts": "...", "subscription_tier": "enterprise", "client_id": "abc...", "max_channels": 48, "available_channels": [...] } { "event": "subscribed", "ts": "...", "channels": [...], "added": [...] } { "event": "unsubscribed", "ts": "...", "channels": [...], "removed": [...] } { "event": "trade", "ts": "...", "channel": "tape.BTC.deribit", "data": { ...trade payload... } } { "event": "levels", "ts": "...", "channel": "gex.levels.BTCUSDT", "data": { "symbol": "BTCUSDT", "hvl": ..., "call_resistance": ..., "put_support": ..., "spot_price": ..., "positioning_note": "..." } } { "event": "heartbeat", "ts": "...", "channel": "status.heartbeat", "queue": { "size": 7, "capacity": 1000 }, "channels": ["tape.BTC.agg", ...] } { "event": "pong", "ts": "..." } { "event": "error", "ts": "...", "detail": "...", "invalid": [...], "available_channels": [...] } { "event": "slow_consumer", "ts": "...", "detail": "Queue full for >5s ..." } ``` ### Client → server commands ```json theme={null} { "action": "subscribe", "channels": ["tape.BTC.agg", "gex.levels.BTCUSDT"] } { "action": "unsubscribe", "channels": ["tape.BTC.deribit"] } { "action": "ping" } ``` Each command receives a corresponding ack frame. ### Trade payload ```json theme={null} { "venue": "deribit", // "deribit" | "bybit" | "okx" | "binance" "coin": "BTC", // "BTC" | "ETH" "instrument": "BTC-30JUN26-100000-C", "trade_id": "12345", "direction": "buy", // "buy" | "sell" (taker side) "option_type": "call", // "call" | "put" "strike": 100000.0, "expiry_date": "2026-06-30", "amount": 1.5, // contracts "price": 0.0525, // venue-quoted (BTC for Deribit/OKX, USDT for Bybit/Binance) "mark_price": 0.0530, // venue mark at trade time (when published) "index_price": 67234.10, // spot index at trade time (when published) "iv": 65.4, // implied vol % (when published) "premium_usd": 5293.60, // canonical USD premium = price × amount × (index_price OR 1) "is_block_trade": false, "ts_ms": 1748480000000 } ``` `premium_usd` is the field to rank, filter or score by — venue quotation differences are already normalised away. ## Connection lifecycle 1. **Connect** with API key → server validates → counts connection against your per-tier cap. 2. **Receive `welcome`** → sanity-check `subscription_tier` and `available_channels`. 3. **Send `subscribe`** → server replies with `subscribed` ack + immediately starts streaming matching trades. 4. **Stream** — trades arrive on their channel; `status.heartbeat` arrives every 15 s if subscribed. 5. **Heartbeat liveness** — the server expects at least one client frame (subscribe / unsubscribe / ping) within 60 s. Otherwise it closes with code `1001`. 6. **Slow consumer** — each client has a 1000-message send buffer. If full for > 5 s the server sends a `slow_consumer` event then closes with code `1013`. Reduce your subscription set or process frames faster. 7. **Disconnect** → connection counter decrements; reconnect any time. ## Per-tier connection caps | Tier | Concurrent WS connections per API key | | ----------- | ------------------------------------- | | API Monthly | 2 | | API Yearly | 4 | | Enterprise | 10 (customisable) | Exceeding the cap on connect returns close code `1008` with `Connection cap reached for tier 'X' (max N)`. ## Reconnect template The server has no replay — `$` cursor is used on the underlying Redis stream, so a reconnect resumes with whatever's live, not whatever you missed. For zero-gap consumers, also poll [`/v2/tape?after=`](/api/v2/tape/tape) on reconnect to fetch trades that arrived during the gap. ```python theme={null} import asyncio, json, os, random, websockets URL = "wss://api.backquant.com/v2/ws/options?api_key=" + os.environ["BQ_API_KEY"] CHANNELS = ["tape.BTC.agg", "status.heartbeat"] async def run(): attempt = 0 while True: try: async with websockets.connect(URL, ping_interval=20) as ws: await ws.recv() # welcome await ws.send(json.dumps({"action": "subscribe", "channels": CHANNELS})) attempt = 0 async for raw in ws: msg = json.loads(raw) if msg.get("event") == "trade": handle(msg["data"]) except (websockets.ConnectionClosed, ConnectionError) as e: attempt += 1 backoff = min(60, 2 ** attempt) * (1 + random.uniform(-0.2, 0.2)) print(f"reconnect in {backoff:.1f}s ({e})") await asyncio.sleep(backoff) def handle(trade): print(trade["venue"], trade["instrument"], trade["direction"], f"${trade['premium_usd']:,.0f}") asyncio.run(run()) ``` Full working client: [`scripts/v2_ws_demo/ws_tape_demo.py`](https://github.com/backquant/backquant-terminal/blob/main/scripts/v2_ws_demo/ws_tape_demo.py). ## See also # Whale prints Source: https://docs.backquant.com/api/v2/tape/whale GET /tape/whale Largest options prints by `premium_usd` in the window. Default floor \$100k. Returns full trade fields (venue, instrument, direction, strike, expiry, IV). Coins: BTC, ETH. ## See also # Authentication Source: https://docs.backquant.com/authentication Get an API key and authenticate your requests Every BackQuant API request requires an API key passed in the `X-API-Key` header. Keys are tied to your subscription tier and control your [rate limits](/concepts/rate-limits). ## Get an API key Sign up or sign in at **backquant.com/api-access** to create and manage your keys. ## Pass your API key Include your key in the `X-API-Key` header on every request. ```bash curl theme={null} curl https://api.backquant.com/v2/gex/levels?symbol=BTCUSDT \ -H "X-API-Key: bq_live_your_api_key_here" ``` ```python Python theme={null} import requests resp = requests.get( "https://api.backquant.com/v2/gex/levels", params={"symbol": "BTCUSDT"}, headers={"X-API-Key": "bq_live_your_api_key_here"}, ) data = resp.json() ``` ```typescript TypeScript theme={null} const resp = await fetch( "https://api.backquant.com/v2/gex/levels?symbol=BTCUSDT", { headers: { "X-API-Key": "bq_live_your_api_key_here" } }, ); const data = await resp.json(); ``` API keys follow this format: ```text theme={null} bq_live_<32-character-token> ``` Keep your API key secret. Do not commit it to source control or expose it in client-side code. If your key is compromised, rotate it immediately at [backquant.com/api-access](https://backquant.com/api-access). ## Endpoints that don't require auth A small set of endpoints are intentionally public so you can introspect the API before signing up: | Endpoint | Purpose | | ------------------ | -------------------------------------- | | `/v2/openapi.json` | Full machine-readable OpenAPI 3 spec | | `/v2/docs` | Swagger UI — interactive playground | | `/v2/redoc` | ReDoc — alternative reference renderer | | `/v2/health` | Liveness/readiness probe for monitors | Every other route requires `X-API-Key`. ## Authentication errors A missing or invalid API key returns a `401` response with the `UNAUTHORIZED` error code: ```json theme={null} { "success": false, "error": { "code": "UNAUTHORIZED", "message": "Missing X-API-Key header" }, "meta": { "version": "2.0", "timestamp": "2026-04-30T12:00:00Z", "request_id": "req_..." } } ``` Common causes: * The `X-API-Key` header is missing from the request. * The key value has a leading/trailing space (common when copy-pasting). * The key was revoked at [backquant.com/api-access](https://backquant.com/api-access). * Your subscription lapsed (`FORBIDDEN` rather than `UNAUTHORIZED` in this case). The `request_id` field in the `meta` block is the value of the `X-Request-ID` header — include it when reporting auth issues so we can trace the specific call in our logs. ## Next steps Make your first API call. Python, TypeScript, and curl recipes. Per-tier limits and backoff strategy. Every error code with handling examples. # Data freshness Source: https://docs.backquant.com/concepts/data-freshness How current the data is — and how to tell from the response Every v2 response carries `computed_at` and `freshness_seconds` in the `meta` block. This page explains what they mean and the cadence behind them. ## Refresh cadences | Category | Refresh | What's in it | | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------- | ------------------------------------------------------------------- | | **GEX** (levels, strike profile, expiry profile, max-pain, expiry summary, time heatmap, history) | **30 seconds** | Pulled from Deribit / Bybit / OKX / Binance, computed by our worker | | **Options** (chain, IV surface / term / skew / smile / curves, greeks, probability density, OI by expiry, expected move) | **30 seconds** | Same compute pipeline as GEX — they share the snapshot | | **Premium tide / OI history** | **30 seconds** | Append-only history of options trade flow | | **Liquidation heatmap** | **5 minutes** | Lower cadence; perp liquidations don't move that fast | | **IV-RV / VRP history** | **Daily** (after UTC midnight) | Daily snapshots, not intraday | | **Stress history** (`/v2/gex/stress-history`) | **Continuous** (Postgres-backed) | Persistent log of HVL / walls over time | ## Reading freshness from a response Every successful response includes: ```json theme={null} { "meta": { "computed_at": "2026-04-30T11:59:48.000Z", "freshness_seconds": 12.0, ... } } ``` * `computed_at` — wall-clock when the underlying cache was last written * `freshness_seconds` — `now - computed_at`, in seconds For a healthy 30s endpoint, `freshness_seconds` should be in the range 0–60. Values consistently > 90 mean the worker is having trouble refreshing — check `/v2/status` for confirmation. ## Operational status [`/v2/status`](/api/v2/discovery/status) gives you a per-symbol per-category health grid: ```json theme={null} { "service": "BackQuant API v2", "version": "2.0", "current_time": "2026-04-30T12:00:00Z", "overall_status": "healthy", "symbols": { "BTCUSDT": { "gex": { "status": "healthy", "freshness_seconds": 12 }, "options": { "status": "healthy", "freshness_seconds": 18 }, "chain": { "status": "healthy", "freshness_seconds": 22 }, "liquidation": { "status": "healthy", "freshness_seconds": 280 } }, ... }, "thresholds": { "gex": { "healthy_max_seconds": 60, "degraded_max_seconds": 300 }, ... } } ``` Each cell is one of: * `healthy` — within the expected refresh window * `degraded` — behind schedule but still usable * `unhealthy` — dangerously stale; worker is having trouble * `unavailable` — no cache at all (cold start or extended outage) The `overall_status` is the worst case across the grid — pessimistic on purpose so monitoring alerts trip on the first real problem. ## When you don't care about freshness For one-off historical queries (`/v2/gex/stress-history`, `/v2/options/iv/iv-rv`, `/v2/options/vrp`), the data is daily/historical and `computed_at` reflects when the historical row was written. Don't panic if `freshness_seconds` is huge — that's normal for time-series. ## Public liveness probe [`/v2/health`](/api/v2/health) is a no-auth liveness probe for external monitors (UptimeRobot, Datadog synthetic, etc.). Returns `status: ok | degraded | unhealthy` based on Redis + Postgres reachability. Always HTTP 200 — the body has the truth, not the status code, so the envelope contract stays clean. ## Related concepts Every meta field documented in detail. Cadence considerations for polling — don't poll faster than the refresh. # Errors Source: https://docs.backquant.com/concepts/errors Every error code, when it fires, and how to handle it When a request fails, the API returns a non-2xx HTTP status and an error envelope with a machine-readable `code` field, a human-readable `message`, and (sometimes) a structured `details` payload. ```json theme={null} { "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Invalid request parameters", "details": { "errors": [ { "type": "literal_error", "loc": ["query", "symbol"], "msg": "Input should be 'BTCUSDT', 'ETHUSDT', 'SOLUSDT' or 'HYPEUSDT'" } ] } }, "meta": { "version": "2.0", "timestamp": "2026-04-30T12:00:00Z", "request_id": "req_a1b2c3..." } } ``` ## All error codes | Code | HTTP status | When it fires | | --------------------- | --------------- | ----------------------------------------------------------------------------------------- | | `UNAUTHORIZED` | 401 | Missing, invalid, or revoked `X-API-Key` header. | | `FORBIDDEN` | 403 | Key is valid but the subscription tier doesn't allow this resource. | | `NOT_FOUND` | 404 | Symbol, expiry, or other resource not present in cache or DB. | | `VALIDATION_ERROR` | 400 / 422 | Bad query parameter — out of range, wrong type, or unknown enum value. | | `RATE_LIMIT_EXCEEDED` | 429 | Per-tier rate limit hit. Retry after `meta.rate_limit.reset` or the `Retry-After` header. | | `UPSTREAM_ERROR` | 502 / 503 / 504 | Cache (Redis) or database (Postgres) temporarily unreachable. Retry with backoff. | | `INTERNAL_ERROR` | 500 | Unexpected server-side failure. Include `meta.request_id` when reporting. | The full list is also available programmatically at [`/v2/meta`](/api/v2/discovery/meta) — useful if you want to keep your client's error map in sync with the API. ## Handling errors in code Always check `success` before reading `data`. Use `error.code` to branch your error handling. ```python Python theme={null} import requests import time def get_gex_levels(api_key, symbol="BTCUSDT", max_retries=3): for attempt in range(max_retries): resp = requests.get( "https://api.backquant.com/v2/gex/levels", params={"symbol": symbol}, headers={"X-API-Key": api_key}, ) body = resp.json() if body["success"]: return body["data"] code = body["error"]["code"] if code == "UNAUTHORIZED": raise ValueError( "Invalid API key. Get/rotate at https://backquant.com/api-access" ) if code == "RATE_LIMIT_EXCEEDED": retry_after = int(resp.headers.get("Retry-After", "60")) print(f"Rate limited; sleeping {retry_after}s") time.sleep(retry_after) continue if code == "NOT_FOUND": # 404 = no cache for this symbol/expiry. Don't retry — handle as no-data. return None if code == "UPSTREAM_ERROR": # Redis/DB blip — exponential backoff sleep = 2 ** attempt print(f"Upstream error; sleeping {sleep}s") time.sleep(sleep) continue if code == "VALIDATION_ERROR": raise ValueError(f"Invalid parameters: {body['error']}") raise RuntimeError( f"Unexpected error: {body['error']} " f"(request_id={body['meta'].get('request_id')})" ) raise RuntimeError("Max retries exceeded") ``` ```typescript TypeScript theme={null} async function getGexLevels( apiKey: string, symbol = "BTCUSDT", maxRetries = 3, ): Promise { for (let attempt = 0; attempt < maxRetries; attempt++) { const resp = await fetch( `https://api.backquant.com/v2/gex/levels?symbol=${symbol}`, { headers: { "X-API-Key": apiKey } }, ); const body = await resp.json(); if (body.success) return body.data; const code = body.error.code; if (code === "UNAUTHORIZED") { throw new Error( "Invalid API key. Get/rotate at https://backquant.com/api-access", ); } if (code === "RATE_LIMIT_EXCEEDED") { const retryAfter = parseInt(resp.headers.get("Retry-After") ?? "60", 10); console.log(`Rate limited; sleeping ${retryAfter}s`); await new Promise((r) => setTimeout(r, retryAfter * 1000)); continue; } if (code === "NOT_FOUND") { return null; // no data, don't retry } if (code === "UPSTREAM_ERROR") { const sleep = 2 ** attempt; console.log(`Upstream error; sleeping ${sleep}s`); await new Promise((r) => setTimeout(r, sleep * 1000)); continue; } if (code === "VALIDATION_ERROR") { throw new Error(`Invalid parameters: ${JSON.stringify(body.error)}`); } throw new Error( `Unexpected error: ${body.error.message} ` + `(request_id=${body.meta?.request_id})`, ); } throw new Error("Max retries exceeded"); } ``` ## Guidance per code **`UNAUTHORIZED`** — Check the `X-API-Key` header is present, the value has no leading/trailing space, and the key hasn't been revoked at [backquant.com/api-access](https://backquant.com/api-access). **`FORBIDDEN`** — Your subscription doesn't allow this resource. Usually a sign you've downgraded a paid tier or your subscription lapsed. **`NOT_FOUND`** — Don't retry. Either the symbol/expiry doesn't have cache yet (cold start, or unsupported symbol), or the path is wrong. **`VALIDATION_ERROR`** — Read `error.details.errors` for the failing parameter list. Common causes: passing `BTC` instead of `BTCUSDT`, unsupported `?greek=` value, out-of-range numeric param. **`RATE_LIMIT_EXCEEDED`** — Read `Retry-After` header (or `meta.rate_limit.reset`) and wait. The [`/v2/options/chain`](/api/v2/options/chain) is the most expensive single endpoint — if you're polling it aggressively, batch with [multi-symbol bundles](/concepts/multi-symbol) or pull only the contracts you need with `?max_contracts`. **`UPSTREAM_ERROR`** — Transient. Exponential backoff (2s, 4s, 8s) usually clears it within \~30s. If it persists past a minute, check [`/v2/health`](/api/v2/health). **`INTERNAL_ERROR`** — Always include `meta.request_id` when reporting to [dev@backquant.com](mailto:dev@backquant.com). We trace by it. ## Always-present headers Even on errors, the response carries: * `X-RateLimit-Limit` — your tier's per-minute cap * `X-RateLimit-Remaining` — calls left in the current window * `X-RateLimit-Reset` — Unix timestamp when the window resets * `Retry-After` — seconds until safe to retry (on 429 only) * `X-Request-ID` — the same UUID as `meta.request_id` ## See also Per-tier limits and how to think about backoff. Avoiding `UNAUTHORIZED` errors. What `NOT_FOUND` actually means per endpoint. # FAQ Source: https://docs.backquant.com/concepts/faq Common gotchas, integration questions, and operational tips ## Data and freshness ### Why is `freshness_seconds` 30+ in my response? That's normal. The compute pipeline refreshes the cache on a 30-second cadence for GEX / IV / chain data, and 5 minutes for liquidation. A `freshness_seconds` value between 0 and 60 on a 30s-cadence endpoint is healthy. See [Data freshness](/concepts/data-freshness) for the full cadence table. ### Polling once a second is hammering the rate limit. What's the right frequency? Polling faster than **30 seconds** is wasted work — you'll see the same `computed_at` 30 times in a row. For dashboard panels, 30s is the sweet spot. For OPEX-day or volatile windows where you want every refresh, you could go to 15s, but anything below that returns duplicate data. See [Rate limits](/concepts/rate-limits) for tier budgeting. ### My `meta.freshness_seconds` is showing huge values (hours / days). Bug? Not a bug. The historical endpoints (`/v2/gex/stress-history`, `/v2/options/iv/iv-rv`, `/v2/options/vrp`) return *historical* time series — the `computed_at` reflects when each historical row was written, which can be days or weeks ago. For these endpoints, freshness isn't a staleness signal; it's a retention signal. ## Errors and gotchas ### I'm getting 404 on a symbol that should have data. What's wrong? A 404 on an authed v2 endpoint means "no cache available for this symbol/expiry/window right now," not "endpoint doesn't exist." Possible causes: 1. The symbol is supported but cold — call [`/v2/symbols`](/api/v2/discovery/symbols) and check `categories_live`. If your category isn't in the list, the worker hasn't populated it yet. 2. The symbol is unsupported. The supported set is `BTCUSDT`, `ETHUSDT`, `SOLUSDT`, `HYPEUSDT`. Anything else returns a `VALIDATION_ERROR` (422), not a 404. 3. You asked for a specific expiry token that doesn't exist. Call [`/v2/expiries?symbol=...`](/api/v2/discovery/expiries) for the active token list. ### Why does my client see `success: false` even when the data looks fine? You're probably reading `data` before checking `success`. The envelope **always** has `success` first — branch on it before touching `data` or `error`. See the [response format](/concepts/response-format). ### I just hit `RATE_LIMIT_EXCEEDED`. What now? Check the `Retry-After` response header (or the `error.details.retry_after_seconds` field in the body). Wait that many seconds, then retry. If it happens repeatedly, your polling cadence is too aggressive — see the rate-limits page or consider upgrading your tier at [backquant.com/api-access](https://backquant.com/api-access). ### What does `UPSTREAM_ERROR` mean? A transient failure reaching our cache or database — usually clears within 30 seconds. Use exponential backoff (2s, 4s, 8s) and your client should recover automatically. If it persists past a minute, hit [`/v2/health`](/api/v2/health) for the current subsystem status. ## Positioning (flow vs std) ### Why did my GEX levels change without a big move in spot? Live GEX defaults to **`positioning=flow`**: dealer size is aggressor sold minus bought from the multi-venue options tape, not textbook OI sign. Walls and HVL can shift when flow rotates even if spot is quiet. Use `?positioning=std` for the older call-positive / put-negative model. See [Positioning](/concepts/positioning). ### SOL or HYPE returned `positioning: "std"` even though I asked for flow? Those coins do not have a full multi-venue aggressor tape yet. The API falls back to std and sets `meta.extra.positioning_fallback_reason` (for example `no_aggressor_tape_for_symbol`). BTC and ETH also fall back if the tape is empty. ### Is `/v2/gex/history` on the same model as live levels? No. History and stress-history are stored **std** series. Live levels, strike profile, TRACE, and greek profiles default to **flow**. Do not mix them in one chart without noting the model. ### Dealer-flow vs delta-flow: which is HIRO-style customer flow? * **`/v2/gex/delta-flow`** and tape **`weight=delta`**: customer signed delta notional from the options tape. * **`/v2/gex/dealer-flow`** / **`gamma-flow`**: estimated **dealer** hedge from successive chain snapshots (spot moves × prior GEX/charm). Positive dealer-flow ≈ dealers buying the underlying to re-hedge. Positive delta-flow ≈ net customer buy-side delta. ### WebSocket `gex.levels.BTCUSDT` vs REST `/v2/gex/levels`? REST rebuilds with default **flow** (unless you pass `positioning=std`). The WebSocket levels channel pushes a **std** levels snapshot when it updates (\~30s). Use REST when you need flow; use WS for a light std pulse. ## Pagination, filters, and projection ### How does cursor pagination work? History endpoints (`/v2/gex/history`, `/v2/options/oi/history`) accept `?limit` (page size) and `?before=` (cursor). Each response includes `next_cursor` — pass it as the next call's `?before` to walk backwards. Iterate until `timestamps` comes back empty. ```python theme={null} cursor = None while True: params = {"symbol": "BTCUSDT", "limit": 200} if cursor: params["before"] = cursor r = get("/v2/gex/history", params).json() if not r["data"]["timestamps"]: break process(r["data"]) cursor = r["data"]["next_cursor"] ``` ### What's the difference between `?fields=` and `?include=` on `/v2/options/chain`? * **`?fields=`** is **top-level** projection. It drops whole sections of the response: `?fields=contracts` returns just contracts and skips `aggregates` and `available_expiries`. * **`?include=`** is **per-row** projection on contracts. It keeps only the named sub-sections of each contract dict: `?include=oi,iv` returns `strike, expiry, dte, type, oi, iv` per row and drops the greeks, bid/ask, etc. You can use both in the same call. ### How do I filter by strike around spot without knowing spot price? Use **moneyness bounds** (`?moneyness_min=0.9&moneyness_max=1.1`) — the server resolves them against current spot for you. Or `?spot_window_pct=5` for a symmetric ±5% window. Both work on every endpoint that accepts strike-axis filters. ## OPEX, expiries, dates ### Why are expiry tokens like `28MAR25` and not ISO dates? That's the convention the underlying exchange (Deribit) uses, so it's what the worker stores natively. Every endpoint that returns an expiry token also includes the parsed `expiry_date` (ISO YYYY-MM-DD) when it parses cleanly, so you can render either format in your UI. ### How do I find the next OPEX day? Hit [`/v2/options/opex?symbol=BTCUSDT`](/api/v2/options/opex) and read `data.next_anchor`. It points at the closest monthly or quarterly expiration with its expiry token, ISO date, and DTE. ### Are weekly / monthly / quarterly classifications consistent across symbols? Yes — the classification is calendar-based (last Friday of month → monthly; last Friday of Mar/Jun/Sep/Dec → quarterly), not symbol-specific. So `28MAR25` is `quarterly` for BTC, ETH, SOL, and HYPE. ## Caching and SDKs ### Can I cache responses on my end? Yes, and you should. Server-side data refreshes every 30s anyway — caching responses for 15-30s on your end is standard practice and saves you rate-limit budget. Use the `meta.computed_at` field as your cache key so you can detect refreshes. ### Do you ship official SDKs? Not yet. The OpenAPI 3 spec at [`/v2/openapi.json`](/api/v2/discovery/endpoints) is comprehensive enough that codegen tools produce a usable client in any language. See [curl recipes](/examples/curl) for the codegen one-liners. ### How do I report a bug? Email **[dev@backquant.com](mailto:dev@backquant.com)** with: 1. The endpoint URL you hit 2. The `meta.request_id` from the response (lets us trace the exact call) 3. What you expected vs what you saw We trace by request ID so the more specific the better. ## Subscription and access ### How do I get an API key? [backquant.com/api-access](https://backquant.com/api-access) — sign up or sign in, create a key, copy. Same key works across all v2 endpoints; tier determines your rate limit. ### How do I upgrade my tier? Same place. Upgrades take effect immediately; the new rate limit applies on your next API call. ### Is my data redistributable? No — personal-use plans don't permit redistribution. If you're building a product on top of the API that resells the data, contact **[dev@backquant.com](mailto:dev@backquant.com)** for an enterprise discussion. See [Terms of Service](https://www.backquant.com/terms). # What is GEX? Source: https://docs.backquant.com/concepts/gex Gamma exposure, dealer positioning, walls and support levels — explained **Gamma exposure (GEX)** measures how much underlying delta dealers are forced to hedge for every \$1 the underlying price moves. It's the single most useful concept for understanding why crypto sometimes pins to a strike, sometimes accelerates through it, and where the next reversal is likely to happen. ## The intuition in one paragraph When you buy an option, somebody else (a dealer / market maker) is the counterparty. To stay delta-neutral, the dealer hedges in the underlying. **Gamma** is the rate of change of delta with respect to the underlying price — so as price moves, the dealer's delta hedge has to be adjusted. **Net dealer gamma** at a given strike tells you how much hedging *pressure* exists at that level. Positive net gamma = dealers buy on dips and sell on rallies (stabilizing). Negative net gamma = dealers sell on dips and buy on rallies (amplifying). ## What we compute for you For every active option contract across Deribit, Bybit, OKX, and Binance, we compute the dollar gamma exposure (USD impact per 1% spot move) and aggregate it per strike, per expiry, and per venue. You get the aggregated values directly; no reconstruction needed on your side. **Signing the book (positioning).** Live GEX defaults to **flow**: dealer size per contract is multi-venue aggressor **sold minus bought** from the options tape. Pass `?positioning=std` for the textbook model (call OI positive, put OI negative). Full rules, fallbacks, and history caveats: [Dealer positioning: flow vs std](/concepts/positioning). The [`/v2/gex/strike-profile`](/api/v2/gex/strike-profile) endpoint is the strike-level aggregation; [`/v2/gex/levels`](/api/v2/gex/levels) distils that further into the trader-actionable levels below. ## Key levels you'll see in the API The [`/v2/gex/levels`](/api/v2/gex/levels) endpoint gives you the trader-actionable levels distilled from the full strike profile: | Field | What it means | How traders use it | | ----------------------------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | | **HVL** (Hedging Volume Level) | The strike where cumulative net gamma crosses zero closest to spot | The "gamma flip" — above HVL dealers stabilize, below HVL they amplify. Treat as a regime line. | | **call\_resistance** | The strike with the largest *positive* gamma above spot | The wall dealers will defend by selling into rallies. Common reversal zone. | | **call\_wall\_2 / call\_wall\_3** | Second / third strongest call walls above spot | Where price *might* pin if it breaks call\_resistance. | | **put\_support** | The strike with the largest positive gamma below spot | Dealers buy on dips into this level. Common bounce zone. | | **put\_wall\_2 / put\_wall\_3** | Second / third strongest put supports below spot | Successive support levels if put\_support breaks. | | **odte\_hvl / odte\_call\_resistance / odte\_put\_support** | Same three levels but computed from 0DTE-only options | Intraday-magnet levels; matters most in the last few hours of the trading day. | ## How to read them in practice **Spot above HVL with strong call\_resistance overhead** — expect mean-reverting price action, dampened vol, dealers selling into rallies toward the call wall. Good environment for premium selling. **Spot below HVL with strong put\_support below** — expect amplified moves on the way down, but a snap-back if price reaches put\_support. Dealers will start buying. **Spot crossing HVL** — regime change. The stabilizing/destabilizing balance flips. Watch for vol expansion right at the cross. **0DTE levels different from all-expiry levels** — common in the last hour before close. The 0DTE levels usually win for the close itself; the all-expiry levels matter more for the next session. ## What our `/v2/gex/levels` endpoint returns Out of the box, you get all-expiry walls + 0DTE walls: ```json theme={null} { "all_expiry": { "hvl": 67000, "call_resistance": 70000, "call_wall_2": 72500, "call_wall_3": 75000, "put_support": 64000, "put_wall_2": 60000, "put_wall_3": 58000 }, "odte": { "hvl": 67500, "call_resistance": 68000, "put_support": 66500 } } ``` Add `?include=ranked,max_pain,expected_move,zones` to also get: * `ranked` — top 10 strikes by absolute GEX (where the action concentrates) * `max_pain` — see the [max pain page](/concepts/max-pain) * `expected_move` — 1σ / 2σ implied range from ATM straddle * `gamma_flip_zones` — multiple-zero-crossing detail when the chain has more than one regime line ## Filtering by venue Aggregating across all four venues smooths noise but can also hide venue-specific positioning (e.g. Deribit dealers vs retail-heavy Binance options). Use `?exchanges=deribit` (or any subset) to recompute levels from a specific venue's chain only. ## Filtering by strike window Far-OTM strikes can have huge GEX from a small cluster of contracts and distort the picture. Use `?moneyness_min=0.9&moneyness_max=1.1` to clamp the analysis to ATM ±10%, or `?spot_window_pct=5` for a symmetric ±5% window. Both work on [`/v2/gex/strike-profile`](/api/v2/gex/strike-profile) and [`/v2/gex/strike-expiry-heatmap`](/api/v2/gex/strike-expiry-heatmap). ## Related concepts How live GEX and TRACE sign dealer size, and when history stays std. The strike where option writers profit most. Different metric from GEX walls but often near them. Vanna, charm, vega: what they measure and how to use them alongside GEX. Estimated hedge pressure across chain snapshots (gamma, charm, total). Why monthly/quarterly expirations dominate the GEX picture for a few days each month. Get GEX levels for BTC + ETH + SOL + HYPE in a single call. # Glossary Source: https://docs.backquant.com/concepts/glossary Quick-reference for every term used across the docs Alphabetical. If you're new to options analytics, start with [What is GEX?](/concepts/gex) and the [IV suite](/concepts/iv-suite) overviews instead — those build the mental model. Use this page as a lookup once concepts land. ## A **Anchor expiry** — A monthly or quarterly options expiration. Heavier OI than weeklies; institutional positioning concentrates here. See [OPEX calendar](/concepts/opex). **ATM IV** — At-the-money implied volatility. The IV of options whose strike equals current spot. Used as the "headline" vol for an expiry. ## B **Backwardation (vol)** — Term structure where front-month IV is higher than back-month IV. Common during stress. See [IV suite](/concepts/iv-suite). **Breeden-Litzenberger** — The technique that extracts a probability distribution from option prices. The basis of [`/v2/options/probability/density`](/api/v2/options/probability/density). **Butterfly** — `(25Δ put IV + 25Δ call IV) / 2 − ATM IV`. Measures the curvature of the smile / how richly priced the wings are. ## C **Call resistance** — The strike with the largest positive net gamma above spot. Often a reversal level — dealers sell into rallies toward this strike. See [What is GEX?](/concepts/gex). **Call wall** — Synonym for call resistance. `call_wall_2` and `call_wall_3` are the second and third strongest above spot. **Cardle (candle)** — OHLCV bar. The terminal renders 30m candles by default; the API ships them alongside GEX levels when `?include=candles` is set. **Charm** — `∂Delta/∂Time`. Drives end-of-day and pre-OPEX delta hedging flows. See [Greeks beyond delta](/concepts/greeks). **`computed_at`** — ISO timestamp in `meta` showing when the underlying cache was last written by our worker. Pair with `freshness_seconds`. **Confidence band** — A price range containing X% of implied probability mass for an expiry. Returned by [`/v2/options/probability/density?confidence_band=0.68|0.95`](/api/v2/options/probability/density). **Contango (vol)** — Term structure where back-month IV is higher than front-month IV. The "normal" regime in low-vol periods. ## D **Dealer flow** — Estimated dealer hedge pressure across successive chain snapshots (gamma, charm, optional vanna components + cumulative). See [`/v2/gex/dealer-flow`](/api/v2/gex/dealer-flow). Distinct from customer **delta flow**. **Delta flow** — Customer signed Black-Scholes delta notional from the options tape (HIRO-style customer line). See [`/v2/gex/delta-flow`](/api/v2/gex/delta-flow). **DEX** — Dollar delta exposure. Net delta dealers carry per strike, in USD terms. See [Greeks beyond delta](/concepts/greeks). **DTE** — Days to expiration. An integer count. ## E **Expected move** — The 1σ implied price range for the next 24 hours, derived from ATM straddle pricing. Returned by [`/v2/options/expected-move`](/api/v2/options/expected-move) or as part of `/v2/gex/levels?include=expected_move`. **Expiry token** — Deribit-style date encoding, e.g. `28MAR25` = March 28 2025. Every endpoint that returns one also returns `expiry_date` (ISO) when parseable. ## F **Flow positioning** — Dealer size signed from multi-venue aggressor tape (sold minus bought per call/put side). Default on live GEX and TRACE. See [Positioning](/concepts/positioning). **`freshness_seconds`** — `now − computed_at`. Useful for staleness alarms. See [Data freshness](/concepts/data-freshness). ## G **Gamma** — Rate of change of delta with respect to spot. The greek that drives dealer hedging behaviour around walls. **Gamma flip** — The strike where cumulative net dealer gamma crosses zero, closest to spot. Above the flip, dealers stabilise; below, they amplify. Reported as `hvl` in the levels response. **GEX** — Gamma exposure. The flagship analytic. See [What is GEX?](/concepts/gex). ## H **HVL** — Hedging Volume Level. Same as the gamma flip — the regime line for dealer behaviour. **HYPEUSDT** — Hyperliquid's HYPE token. One of the four supported symbols (`BTCUSDT`, `ETHUSDT`, `SOLUSDT`, `HYPEUSDT`). ## I **IV** — Implied volatility. The volatility level the market is pricing into options. **IV rank** — Where current IV sits between its 52-week low and high. Not currently exposed in v2 (planned). **IV-RV spread** — `IV − RV`. Positive = options expensive vs realised move; negative = cheap. Returned by [`/v2/options/iv/iv-rv`](/api/v2/options/iv/iv-rv). ## M **Max pain** — The strike where option *writers* profit most at expiry. See [Max pain](/concepts/max-pain). **Moneyness** — `strike / spot`. `0.9` = 10% OTM (for calls) / 10% ITM (for puts). Used as a strike filter on most endpoints (`?moneyness_min=0.9&moneyness_max=1.1`). ## N **Net GEX** — Sum of call GEX and put GEX at a strike (or aggregated across the chain). Positive net = stabilising; negative = amplifying. **Notional OI (USD)** — `total_oi × spot_price`. The dollar size of the position behind the OI count. Surfaced on every OPEX expiration. ## O **0DTE** — Zero days to expiration. Options expiring today. Many endpoints have a 0DTE variant of their levels (`odte_hvl`, `odte_call_resistance`, etc.). **OI** — Open interest. The number of contracts outstanding. **OPEX** — Options expiration. See [OPEX calendar](/concepts/opex). ## P **PCR** — Put / call ratio. OI-weighted ratio of put OI to call OI. PCR > 1 = defensive; PCR \< 1 = call-heavy. See [`/v2/options/pcr`](/api/v2/options/pcr). **PDF (probability density function)** — The implied probability distribution of the underlying at a future expiry. See [Probability density](/concepts/probability-density). **Pin risk** — The likelihood of price pinning to a strike on expiry day. Highest when max pain and gamma walls cluster near spot. **Positioning** — How live GEX/TRACE sign dealer size: `flow` (default) or `std`. See [Positioning](/concepts/positioning). **Premium tide** — Net options premium and notional volume tilt. See [`/v2/options/premium-tide`](/api/v2/options/premium-tide). **Put support** — The strike with the largest positive net gamma below spot. Often a bounce level — dealers buy on dips into this strike. **Put wall** — Synonym for put support. `put_wall_2` and `put_wall_3` are the second and third strongest below spot. ## Q **Quarterly expiry** — Last Friday of March, June, September, or December. The heaviest OI bucket of the year. ## R **Risk reversal** — `25Δ call IV − 25Δ put IV`. Positive = call premium; negative (the more common case) = put premium / fear bid. Surfaced on [`/v2/options/iv/skew`](/api/v2/options/iv/skew). **RV** — Realised volatility. The vol that actually happened over a trailing window (typically 30 days). ## S **Skew** — The asymmetry between OTM put IV and OTM call IV. Crypto typically prices puts richer (positive skew). See [IV suite](/concepts/iv-suite). **Smile** — The shape of IV when plotted by strike. ATM is the trough; OTM puts and OTM calls are the wings. **Source** — `meta.source` lists the upstream venues each response touched (`["deribit", "bybit", "okx", "binance"]`). Provenance. **`spot_price`** — Current spot price for the symbol, snapshotted at the time of computation. Returned in `meta` on every endpoint. **Stress history** — Long-window record of HVL and walls from the persistent store. Used for backtests. [`/v2/gex/stress-history`](/api/v2/gex/stress-history). ## T **Term structure** — The curve of ATM IV plotted against expiry tenor. Slope tells you the market's vol forecast across time. [`/v2/options/iv/term-structure`](/api/v2/options/iv/term-structure). **Theta** — Time decay. Premium that bleeds out of an option as time passes. Useful as a rough estimate of dealer income from selling premium. See [Greeks beyond delta](/concepts/greeks). **TRACE** — Forward (time × price) projection of dealer greek exposure from today's chain. Greeks: `gamma`, `charm`, `vanna`, `delta_change`. See [`/v2/options/greeks/trace`](/api/v2/options/greeks/trace). ## V **Vanna** — `∂Delta/∂IV`. Drives "vol-rallies-the-tape" hedging flows. See [Greeks beyond delta](/concepts/greeks). **Vega** — Sensitivity to IV moves. Where dealer P\&L is concentrated when vol shifts. **VRP** — Volatility risk premium. The persistent gap by which implied vol exceeds realised vol. See [`/v2/options/vrp`](/api/v2/options/vrp). ## W **Whale print** — Large options trade by `premium_usd`. REST helper: [`/v2/tape/whale`](/api/v2/tape/whale). **Weekly expiry** — A Friday expiration that is *not* the last Friday of the month. Lighter OI than monthlies, more responsive to short-term positioning. # Greeks beyond delta Source: https://docs.backquant.com/concepts/greeks DEX, theta, vanna, charm, vega — what they measure and how to use them The greek profile endpoints in v2 give you the **dealer-aggregated greek exposure per strike** for the four underlyings. Same idea as [GEX](/concepts/gex) but for the higher-order greeks that drive hedging flows the gamma profile alone misses. Profiles default to **`positioning=flow`** (tape sold−bought size). Pass `?positioning=std` for textbook call/put OI sign. Forward (time × price) maps live under [TRACE](/api/v2/options/greeks/trace). ## The five exposure profiles | Greek | Endpoint | What dealer holds | What it drives | | --------------- | ------------------------------------------------------------- | ----------------------------------- | --------------------------------------- | | **Delta (DEX)** | [`/v2/options/greeks/delta`](/api/v2/options/greeks/by-greek) | Net directional exposure per strike | Forced delta hedging in the underlying | | **Theta** | [`/v2/options/greeks/theta`](/api/v2/options/greeks/by-greek) | Time-decay USD/day per strike | The premium dealers earn passively | | **Vega** | [`/v2/options/greeks/vega`](/api/v2/options/greeks/by-greek) | IV sensitivity per strike | P\&L impact of vol moves | | **Vanna** | [`/v2/options/greeks/vanna`](/api/v2/options/greeks/by-greek) | ∂Delta/∂IV per strike | Delta hedging adjustment when vol moves | | **Charm** | [`/v2/options/greeks/charm`](/api/v2/options/greeks/by-greek) | ∂Delta/∂Time per strike | EOD / pre-expiry delta drift | All five share the same shape: ```json theme={null} { "greek": "vanna", "strikes": [60000, 62500, 65000, 67500, 70000, ...], "net_exposure": [-12000, -8000, 5000, 22000, 14000, ...], "call_exposure": [...], "put_exposure": [...], "total_exposure": 81000 } ``` ## DEX — the workhorse Net delta exposure tells you how much underlying dealers must hold *right now* to be neutral at each strike. Large positive DEX above spot \= dealers long deltas there = they'll sell into rallies. Large negative DEX below spot = they're short = they'll buy on dips. DEX is essentially the *integral* of GEX across price. Use both: **GEX** for "where will dealers turn the boat?", **DEX** for "how big is their current position?" ## Vanna — the vol-rallies-the-tape effect Vanna is **dDelta/dVol**. Positive vanna means: when IV rises, dealer delta increases (they need to buy underlying); when IV falls, dealer delta decreases (they sell). The classic "vol crush rallies the underlying" pattern is vanna in action. In crypto, vanna concentrates around 25Δ strikes (where vega is biggest). Watch for cliffs in the vanna profile near current spot — those are levels where a vol move triggers hedging flows that move price. ## Charm — the time-bleed delta drift Charm is **dDelta/dTime**. As an option ages with no underlying move, its delta drifts (calls toward 0 or 1, puts toward 0 or -1). Dealers hedge that drift, which creates predictable end-of-day flows. Watch this in the last hour before close (especially before OPEX days) — large charm at strikes near spot means dealers will be adjusting deltas just from time passing, even if price doesn't move. ## Vega — vol P\&L geography Where dealers will gain or lose if IV moves. Useful as a complement to the [IV surface](/concepts/iv-suite) — high vega at a strike means a vol move there has outsized impact on the dealer book. ## Filtering the greek profiles Each greek endpoint accepts: * `?exchanges=deribit,bybit,okx,binance` — venue filter * `?moneyness_min=0.9&moneyness_max=1.1` — focus on ATM ±10% * `?dte_max=7` — recompute from raw chain limited to short-DTE contracts (cached 30s per `(symbol, greek, dte_max)` so repeated requests stay sub-millisecond) The `?dte_max` filter is particularly useful — the cached profiles span *every* expiry, but for intraday workflows you usually only care about 0DTE / weekly contributions. ## Surfaces — strike × time Two endpoints expose the *evolution* of exposure over time: * [`/v2/options/greeks/surfaces/charm`](/api/v2/options/greeks/surfaces) — strike × time charm surface * [`/v2/options/greeks/surfaces/vega`](/api/v2/options/greeks/surfaces) — strike × time vega surface These are useful when you want to see where exposure is *building* over the day, not just the current snapshot. ## 3D — strike × expiry for any greek [`/v2/options/greeks/3d/surface?greek=gamma`](/api/v2/options/greeks/3d-surface) returns a `Z` matrix of `Σ(greek × OI)` per `(strike, expiry)` cell for any greek. Best for 3D visualisation; the matrix is downsampled around spot when the cell count exceeds 5000 to keep payload bounded. ## Related concepts Gamma is the special case. Start there before reading the higher order greeks. How profiles and TRACE sign dealer size. Forward time × price projection of dealer greeks. Vanna and charm shape how the implied PDF evolves. Charm matters most pre-OPEX as time-decay flows accelerate. # The IV suite Source: https://docs.backquant.com/concepts/iv-suite Surface, term structure, skew, smile, IV-RV, VRP — what each one tells you The implied-volatility (IV) endpoints in v2 give you every standard slice and aggregate of the IV surface. Each one answers a different question. ## At a glance | Endpoint | What it shows | Use it for | | -------------------------------------------------------------------- | ----------------------------------------- | --------------------------------------------------- | | [`/v2/options/iv/surface`](/api/v2/options/iv/surface) | Full 2D IV grid (strike × expiry) | 3D vol-surface viz | | [`/v2/options/iv/term-structure`](/api/v2/options/iv/term-structure) | ATM IV per expiry | Calendar / term carry, contango vs backwardation | | [`/v2/options/iv/skew`](/api/v2/options/iv/skew) | 25Δ / 10Δ skew + 25Δ butterfly per expiry | Risk-reversal, fear gauge | | [`/v2/options/iv/curves`](/api/v2/options/iv/curves) | Full smile per expiry, multi-expiry | Vol surface comparison across tenors | | [`/v2/options/iv/smile`](/api/v2/options/iv/smile) | Single-expiry smile | Cheaper than `/curves` when you only need one tenor | | [`/v2/options/iv/iv-rv`](/api/v2/options/iv/iv-rv) | Daily IV vs realised vol history | Vol-selling timing | | [`/v2/options/vrp`](/api/v2/options/vrp) | Volatility risk premium (IV − RV history) | Mean-reversion signal on premium richness | ## Surface The full strike × expiry grid of implied volatilities. Best rendered as a 3D surface or contour plot. Returns: * `x_grid` — strikes * `y_grid` — expiry tenors (DTE) * `z_grid` — IV values at each (strike, expiry) cell * `min_iv` / `max_iv` — for color scaling Most useful when you can see the whole shape — local "wings" up means fat-tail pricing, "smirk" with low call-side IV means upside-skew positioning. ## Term structure ATM implied vol per expiry, sorted by DTE. The shape tells you the market's vol forecast across time: * **Upward-sloping (contango)** — far-month vol higher than front-month. Normal for low-vol regimes; the market is pricing in eventual normalisation. * **Downward-sloping (backwardation)** — front higher than back. Common during stress, expirations or regime breaks. The market is pricing in *now* being noisier than *later*. * **Hump near anchor expirations** — higher IV at the upcoming monthly/quarterly than at adjacent weeklies, because of OPEX positioning. Filter `?dte_max=60` to focus on the front of the curve, or pass `?historical_compare_days=30` to also receive the constant-maturity ATM IV from 30 days ago — useful for showing "term structure shifted right" in dashboards. ## Skew `/v2/options/iv/skew` gives you per-expiry: * `skew_25d` = 25Δ put IV − 25Δ call IV (positive = downside premium) * `skew_10d` = 10Δ put IV − 10Δ call IV (tail skew) * `butterfly_25d` = (25Δ put IV + 25Δ call IV) / 2 − ATM IV (smile curvature) These are computed from real delta-bracketed options (interpolated to exact 25Δ / 10Δ), not strike proxies. Crypto skews are often more extreme than equities — `skew_25d` of +5–10 IV points on a near-term expiry is normal in jittery regimes, > 15 is signalling crash hedging. A *flattening* skew over time (positive going to zero) often precedes a local low. A *steepening* skew is fear bid. ## Curves and Smile [`/v2/options/iv/curves`](/api/v2/options/iv/curves) returns the full merged-IV smile (OTM puts below spot, OTM calls above) for *every* active expiry, sorted by DTE. Best for plotting all smiles on a single chart for visual comparison. [`/v2/options/iv/smile`](/api/v2/options/iv/smile) returns the same data for *one* expiry. Lighter payload; use this if you only need the front month or a specific tenor. ## IV-RV history `/v2/options/iv/iv-rv` returns the daily history of: * `iv` — at-the-money implied volatility * `rv` — realised volatility (typically 30d) * `spread` — IV − RV Use the spread to decide when implied vol is rich relative to what's actually been realised. Sustained `spread > 0` is a sell-vol signal; sustained `spread < 0` is a buy-vol signal. ## VRP — the volatility risk premium `/v2/options/vrp` is the same as `iv-rv` but normalised. The vol risk premium is the *excess* IV traders demand over realised — historically positive on average (vol writers earn a premium). Sharp drops below zero are usually short-vol unwinds and worth tracking. ## Filtering knobs across the suite * `?days=30/90/365` on history endpoints (`/iv-rv`, `/vrp`) — window length * `?exchanges=deribit,bybit,okx,binance` — venue filter (term structure recomputes from filtered breakdown) * `?dte_max=N` — front-of-curve focus * `?historical_compare_days=N` — overlay today's term structure with N days ago ## Related concepts The Breeden-Litzenberger PDF derived from the IV surface. GEX is computed using the same per-contract greeks as IV — the suites are complementary. Term-structure humps near anchor expirations are an OPEX signal. # Max pain Source: https://docs.backquant.com/concepts/max-pain The strike where option writers profit most — and how to read it **Max pain** is the strike at which the largest dollar amount of options expires worthless. It's the single number that summarizes "where do option *writers* (dealers + sellers) want price to be at expiry?" ## The intuition For each candidate strike, calculate the total dollar payout option *buyers* would receive if the underlying settled there at expiry. **Max pain is the strike that minimises that total payout** — the price where the most contracts expire worthless and writers keep the most premium. Dealers who sold those options have an interest in hedging price toward that strike as expiration approaches. Whether they actually have the muscle to drive price there is a separate question (usually no for liquid underlyings, sometimes yes for thin OPEX days near the strike), but the level itself is a meaningful magnet. ## The endpoints ### Quick: 0DTE max-pain [`/v2/gex/max-pain?symbol=BTCUSDT&expiry=0dte`](/api/v2/gex/max-pain) — returns the max-pain strike for today's expiry from the pre-computed 0DTE cache. Sub-millisecond response. ```json theme={null} { "expiry": "0dte", "max_pain": { "strike": 67500, "value": 12500000.0 } } ``` `value` is the total option-buyer payout at the max-pain strike, in USD. ### Per-expiry table [`/v2/gex/max-pain?symbol=BTCUSDT&expiry=all`](/api/v2/gex/max-pain) — the per-expiry max-pain table for every active expiration. Sorted by DTE. ```json theme={null} { "expiry": "all", "per_expiry": [ { "expiry": "30APR26", "dte": 0, "max_pain": { "strike": 67500, "value": 5e7 } }, { "expiry": "02MAY26", "dte": 2, "max_pain": { "strike": 67000, "value": 8e7 } }, { "expiry": "30MAY26", "dte": 30, "max_pain": { "strike": 65000, "value": 4e8 } }, ... ] } ``` Cached for 30s — repeated calls are sub-millisecond. ### Specific expiry with the full pain curve [`/v2/gex/max-pain?symbol=BTCUSDT&expiry=30MAY26`](/api/v2/gex/max-pain) — returns the max-pain strike for that expiry **plus** the full `pain_curve` (one entry per strike), so you can plot the pain function as a U-shape and visually see how steep the magnet is. ```json theme={null} { "expiry": "30MAY26", "dte": 30, "max_pain": { "strike": 65000, "value": 4e8 }, "pain_curve": [ { "strike": 60000, "pain": 7.2e8 }, { "strike": 62500, "pain": 5.5e8 }, { "strike": 65000, "pain": 4.0e8 }, { "strike": 67500, "pain": 4.3e8 }, { "strike": 70000, "pain": 5.1e8 }, ... ] } ``` A flat curve (similar pain values across many strikes) means weak magnet — dealers have no strong preference. A sharp U (one strike with much lower pain than its neighbours) means a strong magnet — that's the strike to watch into expiry. ## Where else max-pain shows up in v2 * **`/v2/options/opex`** — every expiration in the horizon comes with its `max_pain` block under `gamma`. * **`/v2/options/expiry-summary`** — every row carries `max_pain` for that expiry. * **`/v2/gex/levels?include=max_pain`** — the 0DTE max-pain alongside the gamma walls. All three share the same per-expiry max-pain cache (30s TTL) so the snapshot scan happens once per refresh cycle regardless of how many endpoints query it. ## How traders use it **Day before OPEX**: combine max-pain with [GEX levels](/concepts/gex). When max\_pain is between call\_resistance and put\_support, the levels reinforce each other and the pin is high-probability. **OPEX day**: the closer to expiry, the tighter the pin tends to get. Calculate the distance from spot to max\_pain and the implied move; if the implied move is smaller than the distance, the chain is pricing in a successful pin. **Cross-expiry**: an upcoming monthly's max\_pain that's far from spot is bullish/bearish positioning. The market is signalling where it expects to be at the next anchor. **Avoid**: treating max-pain as a forecast on illiquid expiries. Distant or thinly-traded expirations have noisy max-pain — the curve is flat, no real magnet exists. ## Related concepts The other major source of expiry-day price magnets. Anchor expirations have the strongest max-pain signal. The market's full implied price distribution for each expiry. # Multi-symbol bundling Source: https://docs.backquant.com/concepts/multi-symbol Get the universe in one round-trip via /v2/multi/gex/levels Desk dashboards routinely watch the entire crypto-options universe at once: BTC + ETH + SOL + HYPE. Hitting `/v2/gex/levels` four times means four HTTP round-trips. The multi endpoint collapses that into one. Default **`positioning=flow`**, with the same per-symbol fallbacks as single-symbol levels (SOL/HYPE usually fall back to std). See [Positioning](/concepts/positioning). ## The endpoint [`/v2/multi/gex/levels`](/api/v2/multi/gex-levels) takes a CSV of symbols (up to 8) and returns a `results` map keyed by symbol: ```bash theme={null} curl "https://api.backquant.com/v2/multi/gex/levels?\ symbols=BTCUSDT,ETHUSDT,SOLUSDT,HYPEUSDT\ &include=ranked,max_pain,expected_move,zones" \ -H "X-API-Key: YOUR_API_KEY" ``` ```json theme={null} { "results": { "BTCUSDT": { "all_expiry": { "hvl": 67000, "call_resistance": 70000, ... }, "odte": { "hvl": 67500, ... }, "spot_price": 67213.5, "computed_at": "2026-04-30T11:59:48Z", "ranked": { "all_expiry_top_10": [...], "odte_top_10": [...] }, "max_pain": { "strike": 67500, "value": 5e7 }, "expected_move": { "upper_1sd": 70000, "lower_1sd": 64000, ... } }, "ETHUSDT": { ... }, "SOLUSDT": { ... }, "HYPEUSDT": { ... } }, "requested": ["BTCUSDT", "ETHUSDT", "SOLUSDT", "HYPEUSDT"], "served": ["BTCUSDT", "ETHUSDT", "SOLUSDT", "HYPEUSDT"], "missing": [], "invalid": [] } ``` ## Why it's faster than N calls Server-side, the bundle endpoint batches every lookup into a single operation rather than serving them sequentially. The result: one HTTP round-trip instead of N, and lower upstream load on our side. For a desk dashboard polling on a 30s loop, that's a 4× reduction in round-trips with no client-side change beyond switching to the bundle endpoint. ## Partial results If one of the requested symbols has no live data (cold cache), it appears in `missing` and its `results[symbol]` is `null` — but the other symbols still come back. **The whole call doesn't fail** because of one stale symbol. ```json theme={null} { "results": { "BTCUSDT": { ... }, "ETHUSDT": { ... }, "SOLUSDT": null, "HYPEUSDT": { ... } }, "requested": ["BTCUSDT", "ETHUSDT", "SOLUSDT", "HYPEUSDT"], "served": ["BTCUSDT", "ETHUSDT", "HYPEUSDT"], "missing": ["SOLUSDT"], "invalid": [] } ``` `invalid` lists symbols that aren't in the supported set (BTCUSDT/ETHUSDT/SOLUSDT/HYPEUSDT) — your client got the symbol name wrong. ## Filters * `?exchanges=deribit,bybit,okx,binance` — same venue filter as single-symbol; applies to every symbol in the bundle. * `?include=ranked,max_pain,expected_move,zones` — same composable includes. The single-symbol `spot` and `candles` includes are intentionally absent here to keep the bundled payload bounded (candles arrays would multiply payload by \~10×). ## Limits * **Up to 8 symbols** per request (you have 4 today; the cap leaves room for future expansion). * Duplicate symbols in the request are de-duplicated while preserving caller order. ## When to use it **Use it for**: dashboard refreshes, multi-symbol overlays, screeners that scan the universe. **Don't use it for**: single-symbol drilldowns. The per-symbol endpoints support more includes (`spot`, `candles`) that the multi endpoint omits. ## Related concepts The data this bundles is the same as `/v2/gex/levels` per symbol. The shared cache that makes the bundle cheap. # OPEX calendar Source: https://docs.backquant.com/concepts/opex Daily, weekly, monthly, quarterly — and why anchor expirations matter **OPEX** (options expiration) is the day options contracts expire. In crypto, contracts expire on multiple cadences: * **Daily expirations** — BTC and ETH have daily 0DTE contracts on Deribit * **Weekly expirations** — every Friday, smaller OI * **Monthly expirations** — the **last Friday** of each month, much larger OI * **Quarterly expirations** — last Friday of March, June, September, December — the heaviest of all The monthly and quarterly expirations are **anchor expiries** — institutional positioning concentrates in them, GEX clusters around their strikes, and they often dictate price action in the days leading up to expiry. ## Why this distinction matters Monthly/quarterly expirations have: * **5–10× the OI** of weeklies in the same week * **Roll activity** in the 3–5 days before — rolling positions to the next expiry can move spot * **Pin risk** from large dealer hedges unwinding on expiry day * **Vol regime shifts** as the GEX picture redistributes after the expiry-day delta unwind Weekly expirations matter for short-term gamma but have nowhere near the institutional weight. ## The classification BackQuant returns Every expiration in our responses comes pre-classified with a `type` field — you don't have to parse Deribit-style tokens or detect last-Friday-of-month rules client-side: | Type | Definition | Example | | ----------- | ------------------------------------ | --------------------------------------------------------------------------------- | | `quarterly` | Last Friday of Mar / Jun / Sep / Dec | `28MAR25`, `27JUN25`, `26SEP25`, `26DEC25` | | `monthly` | Last Friday of any other month | `25APR25` (April 30 is Wednesday → last Friday is the 25th), `30MAY25`, `25JUL25` | | `weekly` | Any other Friday | `07MAR25`, `14MAR25`, `21MAR25`, `04APR25`, `11APR25` | | `daily` | Any non-Friday weekday | `26MAR25` (Wednesday), `27MAR25` (Thursday) | `is_anchor: true` is shorthand for `monthly` or `quarterly` — the two that institutions watch. Tokens that don't parse cleanly are returned as `unknown` and never silently filtered out. ## The `/v2/options/opex` endpoint [`/v2/options/opex`](/api/v2/options/opex) bundles everything you need for an OPEX-day workflow into one call: ```bash theme={null} curl "https://api.backquant.com/v2/options/opex?symbol=BTCUSDT&horizon=30" \ -H "X-API-Key: YOUR_API_KEY" ``` For each upcoming expiration within the horizon, you get: * **Identification**: token, ISO date, DTE, type, `is_anchor` flag * **OI block**: `call_oi`, `put_oi`, `total_oi`, `pcr`, **`notional_oi_usd`** (= total\_oi × spot) * **IV block**: `atm_iv`, `skew_25d`, `put_25d_iv`, `call_25d_iv` * **Gamma block**: `net_gex`, `call_resistance`, `put_support` (computed per-expiry from the strike × expiry heatmap), `max_pain` Plus a `next_anchor` pointer at the closest monthly/quarterly so you can highlight it in your UI. ## Filtering for the institutional view Want only the heavyweight expirations? Filter to anchors: ```bash theme={null} curl "https://api.backquant.com/v2/options/opex?\ symbol=BTCUSDT\ &horizon=90\ &types=monthly,quarterly" \ -H "X-API-Key: YOUR_API_KEY" ``` You'll get back just the monthlies + quarterlies in the next 90 days, sorted by DTE. ## Same enrichment in `/v2/options/expiry-summary` If you already use [`/v2/options/expiry-summary`](/api/v2/options/expiry-summary), each row is enriched with the same `type`, `is_anchor`, `expiry_date`, `notional_oi_usd`, and `max_pain` fields. Plus top-level `anchor_count` and `type_counts` so you can render summary stats without iterating. ## How to use this in production **Pre-OPEX setup (T-5 to T-1)**: filter to next anchor, watch the gamma walls move as positions unwind/roll. Big call\_resistance moves upward = bullish positioning rolling forward. **OPEX day**: the `max_pain` strike on the anchor expiry is a magnet. Combined with [GEX levels](/concepts/gex), you get a tight pin prediction window for the close. **Post-OPEX**: the next anchor's expected\_move and OI tell you whether positioning has compressed (low IV, low OI = sell-vol environment) or expanded (high IV, high OI = vol expansion likely). ## Related concepts The pinning strike on each expiry — a key OPEX-day signal. The walls / HVL / support levels OPEX positioning concentrates in. Term structure flattening / steepening around OPEX is a big signal. # Dealer positioning: flow vs std Source: https://docs.backquant.com/concepts/positioning How GEX and TRACE sign dealer exposure, multi-venue aggregation, and when each model applies Dealer exposure needs a **sign** at every contract: is the desk long or short that option's gamma? Two models are available on the live GEX and TRACE surfaces. ## Models | Mode | Meaning | When to use | | -------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | | **`flow`** (default) | Dealer size = aggressor **sold minus bought** per call/put side from the multi-venue options tape | Best match to how price behaves when tape exists (BTC, ETH) | | **`std`** | Textbook convention: call OI positive, put OI negative | Backtests, comparisons to public GEX, or symbols without tape | Pass `?positioning=flow` or `?positioning=std`. Omit the param to get **flow**. Every response that rebuilds live exposure reports the effective model in: * `data.positioning` when present * `meta.extra.positioning` and `meta.extra.positioning_requested` * `meta.extra.positioning_fallback_reason` when flow was requested but std ran instead ## Aggregation Live signed surfaces use: 1. **Chain** - multi-venue options chain (Deribit, Bybit, OKX, Binance) for greeks and OI. 2. **Tape (flow only)** - multi-venue aggressor trades for the same coins. Size is coin-normalized so venues can be summed. GEX dollar unit is unchanged: **γ × S² × qty × 0.01** (USD impact per 1% spot move), where `qty` is flow sold-bought or std signed OI. Details also appear under `meta.extra.aggregation` (`chain_venues`, `flow_venues`, `flow_method`, `gex_scale`, `note`). ## Coin coverage | Symbol | Flow tape | Default behaviour | | ----------------- | --------- | --------------------------------------------------------- | | BTCUSDT, ETHUSDT | Yes | Flow when tape is non-empty | | SOLUSDT, HYPEUSDT | No | Auto-fallback to **std** (`no_aggressor_tape_for_symbol`) | If flow is empty for BTC/ETH, the API also falls back to std and sets a reason such as `flow_tape_empty` or `flow_qty_all_zero`. ## Live vs history | Surface | Positioning | | ------------------------------------------------------------------------------------- | --------------------------------------------------- | | Levels, strike profile, expiry profile, heatmaps, TRACE, multi levels, greek profiles | **flow** default | | `/v2/gex/history`, `/v2/gex/stress-history` | **std** only (stored series) | | WebSocket `gex.levels.{SYMBOL}` | **std** snapshot (REST levels stay flow by default) | | Max pain, IV, PCR, OI, expected move, probability | Not a signed dealer model | ## Customer flow vs dealer hedge flow Do not mix these up: | Endpoint family | Side | Source | | ------------------------------------------- | -------------------------------------------- | ------------- | | `/v2/gex/delta-flow`, tape `weight=delta` | **Customer** signed delta from trades | Options tape | | `/v2/gex/gamma-flow`, `/v2/gex/dealer-flow` | **Dealer** hedge estimate from chain × moves | GEX snapshots | Positive on dealer-flow ≈ estimated dealer **buying** the underlying to re-hedge. Positive on delta-flow ≈ net **customer** buy-side delta notional. ## Related # Probability density Source: https://docs.backquant.com/concepts/probability-density Risk-neutral PDF / CDF via Breeden-Litzenberger — the market's implied price distribution **Risk-neutral probability density** is the full implied probability distribution of the underlying's price at a future expiry, extracted from option prices. It tells you not just where the market expects price to land (the mean / mode) but the full *shape* of expectations — including skew toward downside crashes or upside squeezes. ## The intuition Breeden and Litzenberger (1978) showed that the shape of an option chain — specifically the way call prices respond to strike — implicitly encodes the market's probability distribution for the underlying at expiry. We do the heavy lifting on our side and return a clean, normalised PDF over the strike grid for every active expiry. You get the distribution directly. No surface-fitting, no numerical differentiation, no boundary-handling. Plot it, integrate it, sample percentiles from it. ## What you get from `/v2/options/probability/density` For every active expiry, the [endpoint](/api/v2/options/probability/density) returns: ```json theme={null} { "expiries": { "30MAY26": { "strikes": [60000, 62500, 65000, 67500, 70000, 72500, 75000, ...], "pdf": [0.001, 0.003, 0.012, 0.025, 0.018, 0.008, 0.002, ...], "pdf_normalized": [...], "cdf_below": [0.05, 0.12, 0.30, 0.55, 0.78, 0.92, 0.99, ...], "cdf_above": [0.95, 0.88, 0.70, 0.45, 0.22, 0.08, 0.01, ...], "mean": 67200, "mode": 67500, "std": 4100, "skewness": -0.18, "kurtosis": 3.6, "prob_25": 64500, "prob_50": 67500, "prob_75": 70200 }, ... }, "spot_price": 67213.5 } ``` | Field | Meaning | | ------------------- | ------------------------------------------------------------------------- | | `pdf` | Probability density per strike (un-normalized — sum × strike spacing ≈ 1) | | `pdf_normalized` | PDF divided by total mass, sums to 1 over the strike grid | | `cdf_below` | Cumulative probability `P(price < strike)` — monotone non-decreasing | | `cdf_above` | Survival function `P(price > strike)` = 1 − cdf\_below | | `mean` | Expected price at expiry under the risk-neutral measure | | `mode` | Most likely single price (peak of the PDF) | | `std` | Standard deviation in price units | | `skewness` | Distribution skew — negative = downside-skewed (crash risk priced in) | | `kurtosis` | Tail-fatness — > 3 means fat tails, \< 3 means thin | | `prob_25 / 50 / 75` | The 25th / 50th / 75th percentile prices | ## Confidence bands — the easiest way to use this Pass `?confidence_band=0.68` (≈ 1σ) or `?confidence_band=0.95` (≈ 2σ) and each expiry payload gets an extra block: ```json theme={null} { "30MAY26": { ..., "confidence_band": { "confidence": 0.68, "lower": 63100, "upper": 71200, "lower_percentile": 0.16, "upper_percentile": 0.84 } } } ``` `lower` and `upper` are the price levels containing 68% (or 95%) of the implied probability mass, computed by interpolating the CDF. Use these directly as range bounds in dashboards or mean-reversion strategies. ## Filtering * `?expiry=28MAR25` — slice to a single expiry token (case-insensitive) * `?dte_max=30` — drop expiries beyond N days, focus on the front ## How traders use it **Identifying skew**: a `skewness` of -0.5 on the next monthly means the market is paying up for downside protection. Often a contrarian buy signal when extreme. **Sizing risk**: a `confidence_band` at 95% gives you the implied 2σ range. Position sizing that assumes a tighter range is taking a vol view; sizing that assumes wider is hedged. **Strike selection**: looking at the PDF for the next monthly, you can see where the "shoulders" of the distribution are — strikes with elevated PDF density that aren't the mode. These are often the strikes where dealer hedging clusters and worth knowing for execution. **Comparing expiries**: the term structure of `std` (per-expiry vol) and `skewness` (per-expiry crash risk) tells you whether the market is pricing volatility expansion or compression over the next few weeks. ## What it isn't * **Not a forecast.** Risk-neutral probabilities differ from real-world probabilities by the risk premium. A 70% RN probability of upside doesn't mean a 70% real-world chance. * **Not infinitely smooth.** The underlying chain can be sparse for far-OTM strikes, so the tails of the PDF are noisier than the body. Use the body for sizing decisions; treat the tails as informational. * **Not free.** It's computationally heavy. The endpoint is cached at the worker layer — refreshed every 30s, not real-time. ## See also * **3D surface**: [`/v2/options/probability/surface`](/api/v2/options/probability/surface) returns the same PDF aligned across all expiries on a unified strike grid, suitable for 3D visualisation. ## Related concepts The IV surface that underpins this density extraction. A simpler scalar measure of "where price wants to be" at expiry. Vanna and charm shape how the PDF evolves between now and expiry. # Rate limits Source: https://docs.backquant.com/concepts/rate-limits Per-tier limits, response headers, and backoff strategy Rate limits are enforced per API key based on your subscription tier. Exceeding your limit returns a `429` response with the `RATE_LIMIT_EXCEEDED` error code and a `Retry-After` header. ## Limits by tier | Tier | Requests per minute | Annual savings | | -------------- | ------------------- | ---------------------- | | **Monthly** | 60 | — | | **Yearly** | 120 | \~15% off monthly × 12 | | **Enterprise** | Unlimited | Custom | [Get or upgrade your key at backquant.com/api-access](https://backquant.com/api-access). ## Rate limit headers Every response — success **or** error — includes: | Header | Description | | ----------------------- | ----------------------------------------- | | `X-RateLimit-Limit` | Your per-minute cap | | `X-RateLimit-Remaining` | Calls left in the current window | | `X-RateLimit-Reset` | Unix timestamp when the window resets | | `Retry-After` | Seconds until safe to retry (on 429 only) | The same triple is also echoed inside `meta.rate_limit` in the response body, so browser-based dashboards can read it without inspecting headers. ```bash theme={null} curl -I "https://api.backquant.com/v2/gex/levels?symbol=BTCUSDT" \ -H "X-API-Key: YOUR_API_KEY" ``` ```text theme={null} HTTP/2 200 X-RateLimit-Limit: 120 X-RateLimit-Remaining: 87 X-RateLimit-Reset: 1714478160 ``` ## What happens at the limit 1. The 121st request in a minute (on the Yearly tier) returns: ```json theme={null} { "success": false, "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded. Try again in 32 seconds.", "details": { "retry_after_seconds": 32, "limit": "120 per 1 minute" } }, "meta": { ... } } ``` 2. HTTP status is `429`. 3. `Retry-After: 32` header tells you exactly how long to wait. 4. `X-RateLimit-Remaining: 0` confirms you're out for the window. ## Strategy: how to think about throttling **Cache aggressively at your end.** The data refreshes every 30s server-side anyway. If you're showing GEX levels in a dashboard, polling once every 30 seconds (= 2 req/min per panel) gives you the freshest possible data without burning quota. Polling every second is wasted — you'd see the same `computed_at` 30 times in a row. **Use multi-symbol bundling.** Hitting [`/v2/multi/gex/levels`](/api/v2/multi/gex-levels) once for all four symbols counts as **one** request, not four. For desk dashboards watching the universe, this is the difference between 240/min (4 symbols × 60s) and 60/min on the Monthly tier. **Use composable `?include=`.** `/v2/gex/levels?include=ranked,max_pain, expected_move,zones` returns four blocks of data in one request that would otherwise take four separate calls. **Project payloads with `?fields` and `?include`.** Heavy endpoints like `/v2/options/chain` support both top-level (`?fields=contracts`) and per-row (`?include=oi,iv`) projection. Lighter responses = faster parsing client-side and lower egress on our side. **Implement exponential backoff on 429.** Wait `Retry-After` seconds, then if you 429 again, wait 2× as long. Don't hammer. ```python theme={null} import time, requests def call_with_backoff(url, headers, max_attempts=4): for i in range(max_attempts): r = requests.get(url, headers=headers) if r.status_code != 429: return r delay = int(r.headers.get("Retry-After", 60)) time.sleep(delay * (2 ** i)) # 60s, 120s, 240s, 480s raise RuntimeError("rate-limit retries exhausted") ``` **Monitor `X-RateLimit-Remaining`.** When it drops below \~10% of your limit, throttle yourself rather than waiting for a 429. Better to slow down voluntarily than to handle errors after the fact. ## Endpoint cost notes Most endpoints are pure cache reads (\~1ms server-side). A handful do on-the-fly computation: * `/v2/options/chain` with novel filter combos — first call computes, subsequent calls (same filters) hit a 30s cache * `/v2/gex/max-pain?expiry=all` — computes per-expiry max-pain across the chain, cached 30s * `/v2/options/greeks/{greek}?dte_max=N` — recomputes from raw chain for the DTE-filtered slice, cached 30s * `/v2/options/greeks/3d/surface` — strike × expiry matrix per greek, cached 30s All four count as one request against your rate limit, regardless of whether they hit cold or warm cache. ## Need more throughput? If 120 req/min isn't enough — typically when you're powering a paid product downstream, or running >100 concurrent dashboards — contact **[dev@backquant.com](mailto:dev@backquant.com)** about Enterprise. Enterprise removes the per-minute limit and includes: * Dedicated capacity * Custom symbol additions (e.g. SUI, AVAX) * Historical bulk export (CSV / Parquet) * SLA + priority support ## See also Full error code list with recommended retry strategies. The cheapest way to watch the universe. Why polling faster than 30s wastes quota. # Response format Source: https://docs.backquant.com/concepts/response-format The standard envelope every BackQuant API response uses Every response from the BackQuant API uses the same envelope structure, whether the request succeeds or fails. **Always check the `success` field before reading `data` or `error`.** ## Success response When a request succeeds, `success` is `true` and the result is in `data`. The `meta` block carries everything you need to know about the provenance and freshness of the data. ```json theme={null} { "success": true, "data": { "all_expiry": { "hvl": 67000, "call_resistance": 70000, "put_support": 64000 }, "odte": { "hvl": 67500, "call_resistance": 68000, "put_support": 66500 } }, "meta": { "version": "2.0", "timestamp": "2026-04-30T12:00:00.123Z", "request_id": "req_a1b2c3d4...", "symbol": "BTCUSDT", "spot_price": 67213.5, "computed_at": "2026-04-30T11:59:48.000Z", "freshness_seconds": 12.0, "source": ["deribit", "bybit", "okx", "binance"] } } ``` ## Error response When a request fails, `success` is `false` and the reason is in `error`. The `meta` block keeps the same shape so logging code doesn't have to special-case errors. ```json theme={null} { "success": false, "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded. Try again in 60 seconds.", "details": { "retry_after_seconds": 60, "limit": "120 per 1 minute" } }, "meta": { "version": "2.0", "timestamp": "2026-04-30T12:00:00.123Z", "request_id": "req_a1b2c3d4..." } } ``` See [Errors](/concepts/errors) for the full list of error codes. ## Fields `true` if the request succeeded, `false` if it failed. **Always check this field first.** The response payload. Present when `success` is `true`. The shape varies by endpoint — see the [API reference](/api/v2/gex/levels) for each endpoint's schema. The top 10 most-traffic endpoints have Pydantic-typed schemas in the OpenAPI spec; the rest are documented with examples. Present when `success` is `false`. Machine-readable error code. See [Errors](/concepts/errors). Human-readable description. Optional structured payload — e.g. `RATE_LIMIT_EXCEEDED` returns `retry_after_seconds`; `VALIDATION_ERROR` returns the failing field list. Metadata included in **every** response — success or error. API version that served the response. Always `"2.0"` for v2 routes. ISO 8601 wall-clock when the response was built. Per-request UUID, propagated from `X-Request-ID` header. Include this when reporting issues — we trace by it in our logs and Sentry. Symbol the response is about, when applicable (e.g. `BTCUSDT`). Current spot price for the symbol at the time of computation. Useful for normalising returns or sizing. ISO 8601 of when the underlying cache value was last written by our worker. `now - computed_at` in seconds. Useful for staleness alarms in your code. Upstream venue list the data flowed through, e.g. `["deribit", "bybit", "okx", "binance"]`. For provenance. Echo of the `?exchanges=` filter when one was applied. Confirms what your filter actually narrowed to. `{limit, remaining, reset}` echo of the rate-limit headers. Echoed in body too so browser-based dashboards can read it without inspecting headers. Open dict for endpoint-specific metadata (e.g. `filter_hash` on `/options/chain`, `expiry_count` on `/options/opex`). ## Pydantic schemas The 10 most-traffic v2 endpoints have full Pydantic response schemas in the OpenAPI spec. Use them with codegen tools (`openapi-python-client`, `openapi-typescript-codegen`, etc.) to get typed client SDKs: * `GexLevelsResponse`, `GexStrikeProfileResponse`, `MaxPainResponse` * `ChainResponse`, `ExpirySummaryResponse`, `OpexResponse` * `IvTermStructureResponse`, `IvSkewResponse`, `ProbabilityDensityResponse` * `MultiGexLevelsResponse` * Plus `ChangelogResponse` The other 28 endpoints are documented with examples and the same envelope shape. ## See also Every error code with HTTP status mapping. Rate-limit headers and tier-based throttling. What `computed_at` and `freshness_seconds` mean per endpoint. # Venue coverage Source: https://docs.backquant.com/concepts/venues Which exchanges BackQuant ingests for options chains, GEX, IV, levels and the tape — plus where each venue's data shows up in the API. BackQuant ingests crypto options data from four exchanges and folds them into one canonical surface. This page describes which venues feed which endpoints, and how to filter by venue when relevant. ## Venues at a glance | Venue | Tape | Chain / IV / GEX inputs | Source | | ----------- | ----------- | ------------------------------- | --------------------------------------------------------------------------------------------------------- | | **Deribit** | ✅ live WS | ✅ primary | Public WebSocket + REST chain. The deepest BTC + ETH books in the market. | | **Bybit** | ✅ live WS | ✅ multi-venue OI roll-up | Public V5 WebSocket (`publicTrade.{coin}.option`) + chain REST. | | **OKX** | ✅ live WS | ✅ multi-venue OI roll-up | Public V5 WebSocket (`option-trades`, `instFamily=BTC-USD` / `ETH-USD`). | | **Binance** | ✅ REST poll | Limited (no live WS on options) | `eapi.binance.com/eapi/v1/trades` polled for the top-30 most-active BTC + ETH contracts every 30 seconds. | ### Coins `BTC` and `ETH` are fully covered across all four venues. `SOL` and `HYPE` are valid symbols in the API schema (some venues have started listing them) but live coverage is currently thin. ## Where venues show up | Endpoint family | Per-venue filter? | Default behaviour | | ---------------------------------------------------------------- | ------------------------------------------------ | -------------------------------------------- | | [Tape REST](/api/v2/tape/overview) | ✅ `?venues=deribit,bybit,...` | All four venues unioned | | [Tape WebSocket](/api/v2/tape/websocket) | ✅ per-channel subscription `tape.{coin}.{venue}` | Subscribe to `tape.{coin}.agg` for the union | | [GEX](/api/v2/gex/levels) — strike profile / levels / heatmaps | Deribit-primary; multi-venue OI roll-up | Multi-venue blended view | | [IV](/api/v2/options/iv/surface) — surface / term / skew / smile | Deribit-derived | Single, Deribit-rooted view | | [Greeks profiles](/api/v2/options/greeks/by-greek) | Deribit-derived | Deribit-rooted | | [Open interest](/api/v2/options/oi/by-expiry) | Multi-venue blended | Union of all venues | When a metric is documented as "multi-venue blended," it means OI from every venue that lists the coin is rolled into one weighted view. Bid/ask depth is Deribit-only — the other venues don't make the chain pricing usable at retail latency. ## Why Binance isn't on the live WS The public Binance options WebSocket host (`nbstream.binance.com/eoptions/...`) is not reachable from our infrastructure, despite the REST chain on `eapi.binance.com` working normally. We therefore poll Binance's REST trade endpoint every 30 seconds for the most-actively-traded BTC and ETH contracts (hot list refreshed every 5 minutes by 24-hour volume). In practice this captures \~95 % of Binance options premium because volume concentrates in the top 20-30 strikes. Tail strikes that print rarely will be missed. We're tracking the issue and will move Binance to the live WS as soon as the upstream path becomes usable. ## See also # curl recipes Source: https://docs.backquant.com/examples/curl Single-shot curl one-liners for the most common v2 workflows Set your key once and reuse the variable across requests: ```bash theme={null} export BACKQUANT_API_KEY=bq_live_your_api_key_here ``` All examples below assume that variable is set. Pipe through `jq` if you want pretty JSON. ## Health probe (no auth) ```bash theme={null} curl -s https://api.backquant.com/v2/health | jq ``` ## API metadata + supported symbols ```bash theme={null} curl -s https://api.backquant.com/v2/meta \ -H "X-API-Key: $BACKQUANT_API_KEY" | jq '.data | {name, version, supported_symbols, rate_limits}' ``` ## GEX levels with everything One call, all the optional sections: ```bash theme={null} curl -s "https://api.backquant.com/v2/gex/levels?\ symbol=BTCUSDT\ &include=ranked,max_pain,expected_move,zones,spot,candles" \ -H "X-API-Key: $BACKQUANT_API_KEY" | jq '.data | {hvl: .all_expiry.hvl, max_pain, expected_move}' ``` ## OPEX calendar — anchors only, next 90 days ```bash theme={null} curl -s "https://api.backquant.com/v2/options/opex?\ symbol=BTCUSDT\ &horizon=90\ &types=monthly,quarterly" \ -H "X-API-Key: $BACKQUANT_API_KEY" \ | jq '.data.expirations[] | {expiry, type, dte, oi: .oi.total_oi, max_pain: .gamma.max_pain.strike}' ``` ## Multi-symbol GEX in one call ```bash theme={null} curl -s "https://api.backquant.com/v2/multi/gex/levels?\ symbols=BTCUSDT,ETHUSDT,SOLUSDT,HYPEUSDT\ &include=ranked,max_pain,expected_move" \ -H "X-API-Key: $BACKQUANT_API_KEY" \ | jq '.data.results | to_entries[] | {symbol: .key, hvl: .value.all_expiry.hvl, max_pain: .value.max_pain.strike}' ``` ## Filter chain to ATM ±10% with greeks only ```bash theme={null} curl -s "https://api.backquant.com/v2/options/chain?\ symbol=BTCUSDT\ &moneyness_min=0.9\ &moneyness_max=1.1\ &include=oi,iv,greeks\ &max_contracts=200" \ -H "X-API-Key: $BACKQUANT_API_KEY" \ | jq '{contracts: (.data.contracts | length), truncated: .data.truncated, total: .data.total_matching_count}' ``` ## Per-expiry max-pain ```bash theme={null} curl -s "https://api.backquant.com/v2/gex/max-pain?symbol=BTCUSDT&expiry=all" \ -H "X-API-Key: $BACKQUANT_API_KEY" \ | jq '.data.per_expiry' ``` ## Probability density with 1σ band ```bash theme={null} curl -s "https://api.backquant.com/v2/options/probability/density?\ symbol=BTCUSDT\ &confidence_band=0.68" \ -H "X-API-Key: $BACKQUANT_API_KEY" \ | jq '.data.expiries | to_entries[] | {expiry: .key, lower: .value.confidence_band.lower, upper: .value.confidence_band.upper}' ``` ## IV term structure with 30-day historical overlay ```bash theme={null} curl -s "https://api.backquant.com/v2/options/iv/term-structure?\ symbol=BTCUSDT\ &historical_compare_days=30" \ -H "X-API-Key: $BACKQUANT_API_KEY" \ | jq '{current: .data, compare_30d_ago: .data.compare}' ``` ## 30-day stress history (Postgres-backed) ```bash theme={null} curl -s "https://api.backquant.com/v2/gex/stress-history?\ symbol=BTCUSDT\ &days=30" \ -H "X-API-Key: $BACKQUANT_API_KEY" \ | jq '{points: (.data.timestamps | length), latest_hvl: .data.hvl[-1]}' ``` ## Watch rate-limit headers ```bash theme={null} curl -s -i "https://api.backquant.com/v2/gex/levels?symbol=BTCUSDT" \ -H "X-API-Key: $BACKQUANT_API_KEY" \ | grep -i "x-ratelimit" ``` ```text theme={null} x-ratelimit-limit: 120 x-ratelimit-remaining: 87 x-ratelimit-reset: 1714478160 ``` ## Save the OpenAPI spec for SDK codegen ```bash theme={null} curl -s https://api.backquant.com/v2/openapi.json -o backquant-v2-openapi.json # Generate a Python client (requires `openapi-python-client`): openapi-python-client generate --path backquant-v2-openapi.json # Generate a TypeScript client (requires `openapi-typescript-codegen`): openapi --input backquant-v2-openapi.json --output ./src/api-client ``` ## See also The same patterns wired through `requests`. The same patterns wired through `fetch`. Step-by-step from zero to first call. # Python recipes Source: https://docs.backquant.com/examples/python Copy-paste Python snippets for the most common v2 workflows These recipes use only the standard library + `requests`. No SDK required. Set your key in an env var so it doesn't get committed: ```bash theme={null} export BACKQUANT_API_KEY=bq_live_your_api_key_here ``` ```python theme={null} import os import requests API_KEY = os.environ["BACKQUANT_API_KEY"] BASE = "https://api.backquant.com" def get(path: str, params: dict | None = None) -> dict: """Tiny wrapper around requests with auth + error handling.""" r = requests.get( f"{BASE}{path}", params=params or {}, headers={"X-API-Key": API_KEY}, timeout=15, ) body = r.json() if not body["success"]: raise RuntimeError( f"{body['error']['code']}: {body['error']['message']} " f"(request_id={body['meta'].get('request_id')})" ) return body ``` ## Watch GEX levels for the universe Single call, four symbols, all the includes: ```python theme={null} resp = get("/v2/multi/gex/levels", { "symbols": "BTCUSDT,ETHUSDT,SOLUSDT,HYPEUSDT", "include": "ranked,max_pain,expected_move,zones", }) for sym, data in resp["data"]["results"].items(): if data is None: print(f"{sym}: no data") continue print(f"{sym}: HVL={data['all_expiry']['hvl']} " f"Call wall={data['all_expiry']['call_resistance']} " f"Put support={data['all_expiry']['put_support']} " f"Max pain={data['max_pain']['strike']}") ``` ## Build the OPEX calendar widget Show the next 30 days of expirations with their OI and walls, sorted by DTE: ```python theme={null} resp = get("/v2/options/opex", {"symbol": "BTCUSDT", "horizon": 30}) print(f"{'EXPIRY':<12} {'TYPE':<10} {'DTE':>4} {'OI':>10} {'NOTIONAL($M)':>14} {'CALL WALL':>10} {'PUT SUPP':>10}") for e in resp["data"]["expirations"]: notional_m = (e["oi"]["notional_oi_usd"] or 0) / 1e6 print( f"{e['expiry']:<12} {e['type']:<10} {e['dte']:>4} " f"{e['oi']['total_oi']:>10.0f} {notional_m:>14.1f} " f"{e['gamma']['call_resistance'] or 0:>10.0f} " f"{e['gamma']['put_support'] or 0:>10.0f}" ) next_anchor = resp["data"]["next_anchor"] if next_anchor: print(f"\nNext anchor: {next_anchor['expiry']} ({next_anchor['type']}, " f"{next_anchor['dte']}d away)") ``` ## Filter the chain to ATM ±10% with greeks only Cuts payload by \~80% vs the full chain: ```python theme={null} resp = get("/v2/options/chain", { "symbol": "BTCUSDT", "moneyness_min": 0.9, "moneyness_max": 1.1, "option_type": "both", "include": "greeks,iv,oi", # per-row projection "max_contracts": 200, }) for c in resp["data"]["contracts"]: print(f"{c['strike']:>7} {c['expiry']:>10} {c['type']:>5} " f"OI={c['oi']:>5} IV={c['iv']:>5.1f} Δ={c['delta']:>+.2f} " f"Γ={c['gamma']:>+.4f}") if resp["data"]["truncated"]: print(f"\n⚠ Result truncated: matched {resp['data']['total_matching_count']}, " f"returned {len(resp['data']['contracts'])}.") ``` ## Implied price band for risk sizing Get the 1σ price band for the next monthly OPEX expiry: ```python theme={null} # Find the next monthly anchor opex = get("/v2/options/opex", { "symbol": "BTCUSDT", "horizon": 60, "types": "monthly,quarterly", }) next_monthly = next((e for e in opex["data"]["expirations"] if e["is_anchor"]), None) if next_monthly: expiry = next_monthly["expiry"] # Pull the probability band for that expiry pdf = get("/v2/options/probability/density", { "symbol": "BTCUSDT", "expiry": expiry, "confidence_band": 0.68, }) band = pdf["data"]["expiries"][expiry]["confidence_band"] print(f"{expiry} 1σ implied range: ${band['lower']:.0f} – ${band['upper']:.0f}") ``` ## Paginate the GEX history Walk backwards through the time-series using cursor pagination: ```python theme={null} all_points = [] cursor = None for _ in range(10): # safety cap params = {"symbol": "BTCUSDT", "limit": 200} if cursor: params["before"] = cursor resp = get("/v2/gex/history", params) page = resp["data"] all_points.extend(zip(page["timestamps"], page["total_gex"])) if not page["timestamps"]: break cursor = page["next_cursor"] print(f"Pulled {len(all_points)} history points, oldest={all_points[-1][0]}") ``` ## Backtest setup — historical levels Pull 30 days of HVL / walls / net GEX for a regime study: ```python theme={null} resp = get("/v2/gex/stress-history", {"symbol": "BTCUSDT", "days": 30}) import pandas as pd df = pd.DataFrame({ "ts": pd.to_datetime(resp["data"]["timestamps"]), "spot": resp["data"]["spot_price"], "hvl": resp["data"]["hvl"], "call_wall": resp["data"]["call_wall"], "put_wall": resp["data"]["put_wall"], "net_gex": resp["data"]["net_gex"], }) df["above_hvl"] = df["spot"] > df["hvl"] df["distance_to_call_wall_pct"] = (df["call_wall"] - df["spot"]) / df["spot"] * 100 print(df.tail()) print(f"\n% of time above HVL: {df['above_hvl'].mean():.1%}") ``` ## Health check before each polling cycle For long-running dashboards: ```python theme={null} import time def health_ok() -> bool: """Auth-free health probe.""" r = requests.get(f"{BASE}/v2/health", timeout=5) return r.json()["data"]["status"] in ("ok", "degraded") while True: if not health_ok(): print("API unhealthy, sleeping 30s") time.sleep(30) continue # do your work levels = get("/v2/gex/levels", {"symbol": "BTCUSDT", "include": "max_pain,expected_move"}) # ... render dashboard ... time.sleep(30) ``` ## See also Same patterns in TypeScript / Node.js. Pure-curl one-liners for each common workflow. Robust error handling with backoff. Stay under your tier without hitting 429. # TypeScript recipes Source: https://docs.backquant.com/examples/typescript Copy-paste TypeScript / Node.js snippets for the most common v2 workflows These recipes use the standard `fetch` API. No SDK required. Set your key in an env var: ```bash theme={null} export BACKQUANT_API_KEY=bq_live_your_api_key_here ``` ```typescript theme={null} const API_KEY = process.env.BACKQUANT_API_KEY!; const BASE = "https://api.backquant.com"; interface ApiResponse { success: boolean; data?: T; error?: { code: string; message: string; details?: unknown }; meta: { version: string; request_id?: string; [k: string]: unknown }; } async function get( path: string, params: Record = {}, ): Promise> { const qs = new URLSearchParams( Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)])), ); const url = `${BASE}${path}${qs.toString() ? `?${qs}` : ""}`; const r = await fetch(url, { headers: { "X-API-Key": API_KEY } }); const body: ApiResponse = await r.json(); if (!body.success) { throw new Error( `${body.error!.code}: ${body.error!.message} ` + `(request_id=${body.meta.request_id})`, ); } return body; } ``` ## Watch GEX levels for the universe ```typescript theme={null} const resp = await get("/v2/multi/gex/levels", { symbols: "BTCUSDT,ETHUSDT,SOLUSDT,HYPEUSDT", include: "ranked,max_pain,expected_move,zones", }); const results = (resp.data as { results: Record }).results; for (const [sym, data] of Object.entries(results)) { if (data === null) { console.log(`${sym}: no data`); continue; } console.log( `${sym}: HVL=${data.all_expiry.hvl} ` + `Call wall=${data.all_expiry.call_resistance} ` + `Put support=${data.all_expiry.put_support} ` + `Max pain=${data.max_pain.strike}`, ); } ``` ## OPEX calendar widget ```typescript theme={null} const resp = await get("/v2/options/opex", { symbol: "BTCUSDT", horizon: 30 }); const data = resp.data as { expirations: any[]; next_anchor: any }; console.table( data.expirations.map((e) => ({ expiry: e.expiry, type: e.type, dte: e.dte, oi: e.oi.total_oi, notional_m: ((e.oi.notional_oi_usd ?? 0) / 1e6).toFixed(1), call_wall: e.gamma.call_resistance, put_support: e.gamma.put_support, })), ); if (data.next_anchor) { console.log( `Next anchor: ${data.next_anchor.expiry} (${data.next_anchor.type}, ` + `${data.next_anchor.dte}d away)`, ); } ``` ## Filter the chain to ATM ±10% with greeks only ```typescript theme={null} const resp = await get("/v2/options/chain", { symbol: "BTCUSDT", moneyness_min: 0.9, moneyness_max: 1.1, option_type: "both", include: "greeks,iv,oi", max_contracts: 200, }); const data = resp.data as { contracts: any[]; truncated: boolean; total_matching_count: number; }; console.table(data.contracts); if (data.truncated) { console.warn( `Result truncated: matched ${data.total_matching_count}, ` + `returned ${data.contracts.length}.`, ); } ``` ## 1σ implied price band for the next monthly OPEX ```typescript theme={null} const opex = await get("/v2/options/opex", { symbol: "BTCUSDT", horizon: 60, types: "monthly,quarterly", }); const expirations = (opex.data as any).expirations; const nextAnchor = expirations.find((e: any) => e.is_anchor); if (nextAnchor) { const pdf = await get("/v2/options/probability/density", { symbol: "BTCUSDT", expiry: nextAnchor.expiry, confidence_band: 0.68, }); const band = (pdf.data as any).expiries[nextAnchor.expiry].confidence_band; console.log( `${nextAnchor.expiry} 1σ implied range: $${band.lower.toFixed(0)} – $${band.upper.toFixed(0)}`, ); } ``` ## Paginate GEX history ```typescript theme={null} const allPoints: [string, number][] = []; let cursor: string | undefined; for (let i = 0; i < 10; i++) { const params: Record = { symbol: "BTCUSDT", limit: 200, }; if (cursor) params.before = cursor; const resp = await get("/v2/gex/history", params); const page = resp.data as { timestamps: string[]; total_gex: number[]; next_cursor: string | null }; page.timestamps.forEach((t, idx) => allPoints.push([t, page.total_gex[idx]])); if (page.timestamps.length === 0) break; cursor = page.next_cursor ?? undefined; } console.log(`Pulled ${allPoints.length} history points`); ``` ## Backoff on rate-limit errors ```typescript theme={null} async function getWithBackoff(path: string, params = {}, maxAttempts = 4): Promise> { for (let attempt = 0; attempt < maxAttempts; attempt++) { try { return await get(path, params); } catch (e: any) { if (e.message.startsWith("RATE_LIMIT_EXCEEDED")) { const wait = 60 * 2 ** attempt; // 60s, 120s, 240s, 480s console.warn(`Rate limited, sleeping ${wait}s`); await new Promise((r) => setTimeout(r, wait * 1000)); continue; } throw e; } } throw new Error("Max rate-limit retries exceeded"); } ``` ## See also Same patterns in Python. Pure-curl one-liners. Robust error handling. Per-tier limits + backoff strategy. # Introduction Source: https://docs.backquant.com/introduction Professional-grade options + GEX analytics for crypto markets BackQuant is a professional market-data API focused on **crypto options analytics**: gamma exposure (GEX), implied volatility surfaces, dealer positioning, max-pain, OPEX calendar, and risk-neutral probability distributions. The same data that powers the BackQuant terminal is available programmatically, ready to plug into your trading systems, research workflows, and dashboards. Sign up at **backquant.com/api-access**. This API is for personal-use plans. No redistribution is permitted — see our [Terms of Service](https://www.backquant.com/terms) for details. Custom and Enterprise plans are available; contact [dev@backquant.com](mailto:dev@backquant.com). ## Base URL ```text theme={null} https://api.backquant.com/v2/ ``` ## Get started Get your API key and authenticate your first request. Make your first API call in under five minutes. Browse the full endpoint set with interactive try-it-out. Python, TypeScript, and curl recipes for common workflows. ## What's covered The API is focused on the data crypto options traders actually trade against. Everything is computed by us from raw exchange chains (Deribit + Bybit + OKX + Binance) and refreshed every 30 seconds. | Category | What's in it | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **GEX** | Composable levels (HVL / call wall / put support / max-pain / expected move / gamma flip zones), strike + expiry profiles, heatmaps, history, stress history, dealer/gamma/delta flow | | **Options chain** | Filtered chain with two-layer projection (`?fields` + `?include`), expiry summary table, OPEX calendar with daily/weekly/monthly/quarterly classification | | **IV analytics** | Surface, term structure, 25Δ/10Δ skew, butterfly, full smile per expiry, single-expiry smile, IV-RV history, VRP | | **Greeks** | DEX, theta, vanna, charm, vega: per-strike profiles, strike × time surfaces, 3D surface, TRACE field + summary | | **Tape** | Multi-venue REST + WebSocket, imbalance, strike heat, whale prints, optional live GEX levels channel | | **Probability** | Breeden-Litzenberger PDF/CDF + 3D probability surface + confidence-band interpolation | | **Open interest** | By expiry, time-series with cursor pagination, put/call ratio (intraday + daily) | | **Other** | Premium tide (0DTE + weekly), dated-futures basis, liquidation heatmap + distribution, multi-symbol GEX bundle | Live dealer exposure defaults to **flow** positioning (`?positioning=std` for textbook OI sign). See [Positioning](/concepts/positioning). ## Concepts before reference If you're new to options analytics or just want a refresher on what this data means in trader terms: Gamma exposure, dealer positioning, walls and support levels. How live GEX and TRACE sign dealer size. Why monthly and quarterly expirations matter. The strike where option writers profit most. Breeden-Litzenberger explained for traders. Surface, term structure, skew, smile. DEX, vanna, charm, vega — what they measure. # Quick start Source: https://docs.backquant.com/quickstart Make your first v2 API call in under five minutes This guide takes you from "I have an account" to "I have a working integration" in five steps. Sign in at [**backquant.com/api-access**](https://backquant.com/api-access) and create a key. Your key looks like: ```text theme={null} bq_live_<32-character-token> ``` Hit the composable GEX levels endpoint for BTC. Replace `YOUR_API_KEY` with your actual key: ```bash curl theme={null} curl "https://api.backquant.com/v2/gex/levels?symbol=BTCUSDT" \ -H "X-API-Key: YOUR_API_KEY" ``` ```python Python theme={null} import requests resp = requests.get( "https://api.backquant.com/v2/gex/levels", params={"symbol": "BTCUSDT"}, headers={"X-API-Key": "YOUR_API_KEY"}, ) print(resp.json()) ``` ```typescript TypeScript theme={null} const resp = await fetch( "https://api.backquant.com/v2/gex/levels?symbol=BTCUSDT", { headers: { "X-API-Key": "YOUR_API_KEY" } }, ); console.log(await resp.json()); ``` Every response uses the same envelope. A successful call returns: ```json theme={null} { "success": true, "data": { "all_expiry": { "hvl": 67000, "call_resistance": 70000, "call_wall_2": 72500, "call_wall_3": 75000, "put_support": 64000, "put_wall_2": 60000, "put_wall_3": 58000 }, "odte": { "hvl": 67500, "call_resistance": 68000, "put_support": 66500 } }, "meta": { "version": "2.0", "timestamp": "2026-04-30T12:00:00.123Z", "request_id": "req_a1b2c3...", "symbol": "BTCUSDT", "spot_price": 67213.5, "computed_at": "2026-04-30T11:59:48.000Z", "freshness_seconds": 12.0, "source": ["deribit", "bybit", "okx", "binance"] } } ``` Always check `success` before reading `data`. On failure you get `success: false` and an `error` object instead — see [Errors](/concepts/errors) for the full code list. The `meta` block tells you how stale the data is (`freshness_seconds`), which venues it came from (`source`), and the spot price at the time of computation (`spot_price`). Use `request_id` when reporting issues. Most endpoints support filters. Here's the same call with the composable `?include=` parameter and an exchange filter: ```bash theme={null} curl "https://api.backquant.com/v2/gex/levels?\ symbol=BTCUSDT\ &exchanges=deribit,bybit\ &include=ranked,max_pain,expected_move,zones" \ -H "X-API-Key: YOUR_API_KEY" ``` Now the response includes the top-10 ranked strikes by |GEX|, the 0DTE max-pain strike, the 1σ/2σ expected move from ATM straddles, and the gamma flip zones — in one call. Common filters you'll use across the API: * `?symbol=BTCUSDT|ETHUSDT|SOLUSDT|HYPEUSDT` - supported symbols * `?exchanges=deribit,bybit,okx,binance` - venue filter * `?expiry=all|0dte|` - single expiry slice * `?dte_min` / `?dte_max` / `?weekly_only=true` - DTE-axis filter * `?moneyness_min` / `?moneyness_max` - strike/spot ratio bounds * `?positioning=flow|std` - dealer signing (default flow on live GEX) * `?include=...` - opt-in to optional response sections * `?fields=...` - top-level projection on heavy endpoints Live GEX defaults to flow positioning. Details: [Positioning](/concepts/positioning). For a more product-y example, get the next 30 days of options expirations with their classification, OI, walls, and max-pain in one call: ```bash theme={null} curl "https://api.backquant.com/v2/options/opex?symbol=BTCUSDT&horizon=30" \ -H "X-API-Key: YOUR_API_KEY" ``` The response gives you a ranked calendar of upcoming expirations, each tagged `daily | weekly | monthly | quarterly`, with the next monthly/quarterly anchor highlighted under `next_anchor`. See [the OPEX concept page](/concepts/opex) for what these mean and how to use them. ## What to read next The single most-asked concept question. Read this before integrating. Full envelope schema, every meta field explained. Per-tier limits, response headers, retry strategy. Python, TypeScript, curl recipes for common workflows. Daily/weekly/monthly/quarterly classification, anchor expirations. All 38 endpoints, with interactive try-it-out. # Long-Term Trend & Valuation Model Source: https://docs.backquant.com/tradingview/long-term-trend-valuation A two-layer framework that separates 'where we are in the cycle' from 'what to do about it' - built for long-horizon decisions on assets with strong boom-bust structures. Long-Term Trend & Valuation Model Open the Long-Term Trend & Valuation Model on TradingView. Invite-only. ## Overview This invite-only tool is a **two-layer framework** designed to simplify long-horizon decision-making: * **Valuation Engine** - measures how extended price is relative to its own regime. Outputs a single oscillator centered on zero, bounded by configurable overbought / oversold thresholds. * **Trend Model** - aggregates several independent long-term subsystems into a composite **strength score**, mapped to a long / cash / short stance. The separation of "where we are in the cycle" from "what to do about it" makes this model particularly suited to assets with strong boom-bust structures (like crypto). Each layer answers one question, and they're designed to be read **together**, not as standalone signals. ## Valuation Engine The valuation oscillator is a **weighted blend of six standardized components**. Each component measures a different facet of "extension" relative to the asset's own historical regime, then gets normalized so that all signals sit on the same scale before they're combined. ### How it is built | Stage | What happens | | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Standardization** | Each component is converted to a z-score over a configurable lookback (default `1100` bars), so different scales become comparable. | | **Weighted blend** | Components are combined with user-controllable weights so you can emphasize the lenses you trust most. | | **Optional smoothing** | A moving average (`SMA`, `EMA`, `RMA`, `HULL`, `WMA`, `DEMA`, `TEMA`, `LINREG`, `ALMA`, `T3`) can be applied to filter whipsaws. Default is `RMA` over `45` bars. | | **Bounded scaling** | A `tanh`-based compression keeps the oscillator readable across cycles - extreme outliers don't break the chart. | | **Volatility-aware re-expansion** | After compression, the signal is re-expanded by rolling standard deviation, so "overbought / oversold" stays meaningful when volatility regimes change. | | **Threshold logic** | Thresholds can be **fixed** (default ±1.5) or **dynamic** (k·σ bands around the rolling mean or around zero). | ### Reading the oscillator | State | Meaning | | ---------------------- | ---------------------------------------------------------- | | **Above 0** | Supportive regime - backdrop favors continuation. | | **Below 0** | Deterioration / risk aversion regime. | | **Tagging upper band** | Optimistic stretch - late-cycle risk, take-profit context. | | **Tagging lower band** | Pessimistic stretch - washouts, early-base context. | ### Configurable inputs | Input | Default | What it changes | | ---------------------------- | ------------- | ------------------------------------------------------------------------------------ | | **Calculation Source** | `close` | Price source used everywhere downstream. | | **Z-Score Lookback** | `1100` | The master regime lookback. Longer = smoother / longer-term framing. | | **Per-component lookbacks** | various | Each component has its own length - defaults are tuned for daily/weekly horizons. | | **Per-component weights** | `1.0`–`1.5` | Relative importance of each component in the final score. Set any to `0` to disable. | | **Smooth Valuation** | on | Apply a moving-average filter to the final composite. | | **MA Type / Period / Sigma** | RMA / 45 / 2 | Smoothing kernel and length (Sigma only used by ALMA). | | **Normalization Length** | `90` | Window used by the volatility-aware compression. | | **Re-expand Cap** | `2.0` σ units | Maximum width of the post-compression swing. | | **Alpha mode** | `Manual` | Compression strength - set manually, or auto-mapped so 1σ→0.76, 2σ→0.95, or 3σ→0.99. | | **Scale by Volatility** | on | Toggles the compression / re-expansion stage. | ### Threshold modes * **Fixed thresholds** - default ±`1.5`, configurable. * **Dynamic (`k · σ`)** - thresholds become `mean ± k · σ` over a rolling window (default `100` bars, `k = 1.5`). Optionally **center at zero** to use `±k · σ` regardless of mean drift. ### Built-in summary table A right-corner summary table prints each component's current z-score plus a 🟢 / 🔴 status emoji, the **Final Score**, and the overall valuation emoji. Toggle on/off via *Show Summary Table*. The table works in light or dark mode (controlled by the *Light or Dark Mode* input). ## Trend Model The trend layer combines **seven independent long-term subsystems**. Each subsystem produces its own long / short / neutral vote on the prevailing regime, and the votes are averaged into a single composite score in the range `[-1, +1]`. The subsystems are deliberately diverse - each looks at trend from a different mathematical lens (price-anchor alignment, persistence checks, macro regime confirmation, higher-timeframe overlays, etc.). You can toggle any subsystem on or off independently. ### Subsystem toggles | Toggle | Default | | ------------------- | ------- | | Use Component One | on | | Use Component Two | on | | Use Component Three | on | | Use Component Four | on | | Use Component Five | on | | Use Component Six | on | | Use Component Seven | on | Disabling a subsystem reweights the composite among the remaining active subsystems - so you can trim the model down to only the lenses you trust without throwing the score off scale. ### Interpretation | Composite (`tpi`) | Stance | | ----------------------------------------- | -------------- | | Above **Long Threshold** *(default `0`)* | Long | | Below **Short Threshold** *(default `0`)* | Short | | Between thresholds | Neutral / Cash | A colored signal line is plotted on the **price chart** (overlay), optional bar coloring is available, and a right-corner table reports the current strength (%), rate of change, and position state. ### Configurable inputs | Input | Default | What it changes | | --------------------------------- | ------------------ | ------------------------------------------------------ | | **Show Signal Line** | on | Plots a colored MA on price reflecting current regime. | | **Show Trend Table** | on | Right-corner table with strength %, RoC, and position. | | **Show Background Coloring** | off | Tints the chart background by regime. | | **Show Bar Color** | off | Repaints candles by regime. | | **Long / Short Thresholds** | `0` / `0` | Where the composite must cross to flip stance. | | **Long / Short / Neutral Colors** | green / red / gray | Visual palette. | ## How to Use ### Cycle framing Read **valuation** and **trend** as a 2×2 matrix - the combination tells you what stage of the cycle the asset is in. | Valuation | Trend Composite | Cycle Context | | ----------------- | --------------- | ----------------------------------------------------------- | | Deep negative | Neutral / Short | **Accumulation context** - bases are built here. | | Positive | Strong Long | **Participate in expansion** - trend is doing the work. | | Extended positive | Weakening | **Late cycle caution** - start trimming, tighten risk. | | Negative | Short | **Distribution / unwind** - risk-off, capital preservation. | ### Workflow examples * **Regime allocation** - increase exposure when trend is Long *and* valuation is rising; reduce when trend is Short *and* valuation is falling. * **Signal gating** - only run shorter-term entry systems in the composite trend's direction. * **Sizing overlay** - scale smaller near upper-band stretches; scale larger after valuation resets near zero. * **DCA context** - accumulate while valuation is negative but *stabilizing* (slope flattening), not while it's still falling. * **Cross-asset rotation** - compare valuations across multiple assets on the daily timeframe and rotate to where conditions are most favorable. ## Alerts Built-in alert conditions: ### Valuation alerts | Alert | Fires when | | --------------------------- | -------------------------------------------- | | **Overbought Enter / Exit** | Valuation crosses into / out of the OB zone. | | **Oversold Enter / Exit** | Valuation crosses into / out of the OS zone. | | **Bullish Zero Cross** | Valuation crosses above 0. | | **Bearish Zero Cross** | Valuation crosses below 0. | ### Trend alerts | Alert | Fires when | | ------------------------- | ------------------------------------------------------ | | **Long-Term Trend Long** | Composite crosses above the long threshold. | | **Long-Term Trend Short** | Composite crosses below the short threshold. | | **Long-Term Trend Flip** | Composite crosses the short threshold (regime change). | ## Strengths * **Separates cycle context from trend stance** - two questions, two answers, no conflation. * **Multi-component voting** - both layers use diverse subsystems so no single component can drag the model off course. * **Volatility-aware scaling** - keeps "extreme" meaningful across changing regimes. * **Per-component toggles + weights** - you can tune the model to your preferred lenses without rewriting the script. * **Clear visuals and alerts** - encourages long-horizon discipline rather than minute-to-minute reactivity. ## Final Notes The Long-Term Trend & Valuation Model is **not designed to catch every swing**. It is built to keep you aligned with the dominant trend, help manage risk around cycle extremes, and provide a consistent language for allocation decisions across timeframes and assets. # Oscillator Suite Source: https://docs.backquant.com/tradingview/oscillator-suite A coordinated set of momentum, money flow, confluence, divergence, and reversal modules - built to turn live market noise into a readable sequence of conditions. Oscillator Suite Open the Oscillator Suite on TradingView. ## What This Suite Does Oscillator Suite is built for one job: **turn live market noise into a readable sequence of conditions.** Not "one signal." Not "one oscillator." A coordinated set of modules that track momentum, money pressure, agreement between them, and exhaustion - so you can see when moves have real backing, when they're fading, and when reversal conditions are worth treating as a serious event. The suite ships with **six interlocking modules**, all in one pane: 1. **Momentum Ribbon** - the core oscillator + signal line + crossover events. 2. **Modified Money Flow Index (MFI)** - pressure overlay with adaptive thresholds. 3. **Confluence Zones** - bands that fill when momentum and money flow agree. 4. **Reversal Signals** - Major (`ℝ`) and Minor (cross) reversal events, filtered by volume. 5. **Divergences** - auto-drawn divergence lines on extended momentum. 6. **Momentum Velocity** - a slower, structural momentum lens. 7. **Bar Coloring** - projects the suite's read directly onto candles. ## Why This Suite Feels Different in Live Markets This suite is designed to show the *why* behind the candle: * Is the move being **driven** or just **drifting**? * Is participation **accumulating** or **exiting**? * Are components telling the **same story**, or is the market split? * When a reversal appears, is it a **real shift** or a random wiggle? When components **converge**, you get higher clarity. When they **diverge**, you get a warning before price makes it obvious. ## How to Read the Suite in Order If you want the indicator to feel "alive" instead of confusing, use this order of operations: 1. **Money Flow** - for pressure and participation 2. **Momentum Ribbon** - for direction and shift timing 3. **Confluence** - to measure agreement and regime quality 4. **Reversals** - to mark turning points inside those regimes 5. **Divergences** - for early "engine weakness" warnings 6. **Bar Coloring** - to project the whole read onto price ## Core Modules ### Momentum Ribbon - Timing Engine The Momentum Ribbon is the oscillator at the center of the suite. It's a normalized momentum read with two visible lines: the raw **signal** (`sig`) and a smoothed **signal-of-signal** (`sgD`). The fill between them flips color on every cross, and circle markers print at every crossover. **Controls you'll touch:** | Input | Default | What it changes | | --------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | **Calculation Period** | `7` | Lookback for the oscillator base. Higher = smoother / fewer signals. Lower = faster / more signals. | | **Signal Line type** | `SMA` | Smoothing kernel applied to the ribbon's signal-of-signal. Choose between `SMA`, `EMA`, `WMA`, `LINREG`, `T3`, `ALMA`, `DEMA`, `TEMA`, `RMA`. | | **Smoothing length** | `3` | The smoothing length for the signal-line kernel. Higher = cleaner shifts. Lower = earlier shifts. | | **Positive / Negative colors + transparency** | green / red / 40 | The ribbon fill colors and opacity. | **What you'll see on chart:** * A **filled ribbon** above and below zero, colored bullish or bearish. * **Circle markers** at every momentum crossover (signal vs signal-of-signal). * **Black inner-line outlines** so the ribbon is readable on any background. **In live markets:** This is what you watch when price is chopping. The ribbon will often show momentum actually resolving even if price looks messy for a few candles. ### Modified Money Flow Index (MFI) - Pressure Layer A modified Money Flow Index that uses an **adaptive threshold** rather than fixed 80/20 levels. The script tracks rolling averages of bullish and bearish MFI readings, so "strong pressure" is judged relative to the asset's own recent participation - not a textbook constant. **Controls:** | Input | Default | What it changes | | ---------------------- | ----------- | -------------------------------------------------------- | | **Calculation Period** | `35` | MFI lookback. Higher = smoother. Lower = more reactive. | | **Smoothing Period** | `6` | Extra smoothing applied to the modified MFI. | | **Money Flow Colors** | green / red | Colors used in the MFI fill and inside confluence zones. | **What you'll see on chart:** * A line plotted between the ribbon and the zero level, colored by sign. * **Stronger fills** (less transparent) where MFI exceeds its own bullish or bearish running average - the script highlights *when pressure is actually elevated*, not just present. * A faint fill between MFI and zero when readings are weak, so you don't mistake "barely positive" for accumulation. The Money Flow wave is your context filter. It exists to separate: | Condition | What it means | | --------------------------- | ----------------------------------------------- | | Real buy-side pressure | A push that has genuine participation behind it | | Weak participation | A move that looks bullish but lacks support | | Heavy distribution | A selloff with real selling pressure | | Selling running out of fuel | A dip where pressure is fading | Money Flow accumulation ### Confluence Zones - Regime Detection The bands at ±50 / ±60 fill with color based on how Momentum and MFI agree. You pick the fill style: | Zone Type | What it shows | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | **None** | Bands plotted in gray, no fills. | | **Confluence Zones** *(default)* | Both bands fill bullish when momentum > 0 and MFI > 0; both fill bearish when both \< 0; muted/transparent when split. | | **Overbought / Oversold Strength** | Band opacity scales with how extreme momentum is - the more stretched the read, the more saturated the fill. | **Reading it:** | Zone state | Meaning | | ---------------------- | --------------------------------------------------------------- | | **Bullish Confluence** | Momentum is constructive **and** pressure supports it | | **Bearish Confluence** | Momentum is bearish **and** selling pressure supports it | | **Mixed / faded** | Components disagree - expect chop, fakeouts, low follow-through | **This is how you stop forcing trades.** When confluence is strong, you can hold with more confidence. When confluence fades, you tighten expectations and demand better structure or confirmation. ### Reversal Signals - Turning Points Reversal events are filtered by **volume expansion** combined with momentum and money flow conditions - they're not raw oscillator extremes. The suite prints two tiers: | Tier | Marker | Triggered when | | --------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Major** | `ℝ` label at ±65 + soft background tint | Volume expands strongly **and** momentum is past the reversal threshold **and** money flow agrees with the direction (above bullish-MFI average / below bearish-MFI average). | | **Minor** | Cross marker at ±65 | Lighter volume expansion **and** momentum past ±20 **and** the volume's own RSI confirms direction. | **Reversal Factor (`1`–`10`, default `4`)** scales the strictness: * **Lower** → more reversal events, more sensitive. * **Higher** → fewer events, but each is more strongly filtered. **Live-Market Mindset:** * A reversal print during **heavy opposing pressure** is often just a pause. * A reversal print when **money flow pressure is weakening** or shifting is a different animal. * A reversal print as **confluence transitions** is where dips and tops become actionable ideas. Strong reversal examples Reversals catching dips ### Divergences - Early Warning Divergences here only evaluate **when momentum is past the divergence threshold** (default `20`). That filter is intentional - it stops the indicator from spamming low-quality divergence lines in mid-range chop. **Controls:** | Input | Default | What it changes | | --------------------------------- | ----------- | ---------------------------------------------------------------------------------- | | **Divergence Threshold** | `20` | Lower = more (shorter-term) divergences. Higher = fewer (longer-term) divergences. | | **Show Divergences** | on | Toggle the auto-drawn lines. | | **Bull / Bear Divergence Colors** | green / red | Line colors. | **What gets drawn:** when the ribbon is in extended territory and crosses its own signal line, the script compares the most recent momentum extreme against the previous extreme (in the same regime). If price has made a new extreme but momentum hasn't, a line is drawn between the two. **What divergence is used for here:** * Spot **engine weakness** when price attempts to extend but momentum does not match. * Warn you early so you can manage risk **before** the obvious reversal candle shows up. * Help you identify when a trend is losing quality, especially when confluence begins fading. **Divergence → Confirmation Sequence:** 1. Momentum ribbon shifts 2. Money flow eases or flips 3. Confluence transitions 4. Reversal marker appears ### Momentum Velocity - Structural Layer A second momentum lens that's slower and more "structural." It's a weighted-price for-loop momentum read normalized over a longer lookback (default `100` bars) - useful for confirming whether the broader momentum environment is actually supporting what the ribbon is doing. **Visual feedback:** plotted as columns at the bottom of the pane in a **graduated color scale** - light tints at low magnitude, vivid green or red as readings get extreme. So you can read background regime strength at a glance without reading numbers. **How traders use this in practice:** * As a **permission layer** to avoid fighting stronger background pressure. * To confirm when momentum shifts are **likely to hold**, not just flip for a bar. * To spot when short-term momentum is turning inside a larger supportive environment. Momentum Velocity ### Bar Coloring - Speed Layer Bar coloring projects the suite's current read directly onto candles so you can process conditions without staring at the panel. Pick the mode that matches how you trade: | Mode | Bars colored when… | | -------------------------------------- | --------------------------------------------------------------------------------------------------- | | **None** | No bar coloring. | | **Momentum direction** | Ribbon signal is above / below its own smoothed signal. | | **Momentum above/below midline** | Ribbon signal is above / below zero. | | **MFI above/below midline** | Money flow is above / below zero. | | **Confluence (Mom + MFI)** | Both momentum and MFI agree (both bullish or both bearish). | | **Strong Confluence Only** *(default)* | Confluence **plus** money flow exceeds its own running bullish/bearish average. The strictest mode. | | **Momentum Velocity** | Bars colored by the structural-layer reading. | Bar coloring methods ## Practical Playbooks Without chasing. * Start with confluence - get aggressive only when agreement is present * Use the ribbon to time entries on momentum shifts * Confirm with money flow pressure * When confluence fades, manage tighter That is not blind. * Let price pull back while you watch money flow * Heavy selling = you are early * Easing pressure = you are getting close * Best dips show up during confluence transitions Without guessing. * Watch for momentum weakening while price extends * Divergence is your first warning * Confluence fading + money flow shifting = high-interest reversal * This sequence catches "strong reversals" ## Settings That Matter ### Calculation Period (Momentum) | Lower | Higher | | ----------------------------------- | ---------------------------------------- | | Faster, more reactive, more signals | Smoother, fewer signals, cleaner regimes | ### Signal Line Type + Smoothing Pick a kernel that matches asset behavior - `SMA` / `EMA` for general use, `LINREG` or `T3` for cleaner regime tracking, `ALMA` / `DEMA` / `TEMA` for faster response without raw whipsaw. | More smoothing | Less smoothing | | -------------------------- | ----------------------------- | | Cleaner shifts, less noise | Earlier shifts, more activity | ### MFI Calculation + Smoothing | Lower | Higher | | -------------------- | ------------------------------------------- | | Faster pressure read | Clearer accumulation/distribution structure | ### Divergence Threshold | Lower | Higher | | ------------------------------------- | ------------------------------------------ | | More divergence events (shorter-term) | Fewer events (more selective, longer-term) | ### Reversal Factor | Lower | Higher | | -------------------- | ------------------------------------------------ | | More reversal events | Fewer, stronger events through heavier filtering | ## Alerts Built-in alert conditions you can wire into TradingView: | Alert | Fires when | | ---------------------------------- | --------------------------------------------------------------- | | **Ribbon Long / Short** | Momentum signal crosses above / below its smoothed signal-line. | | **Ribbon Cross Up / Down Midline** | Momentum signal crosses zero in either direction. | | **Money Flow Long / Short** | Modified MFI crosses zero in either direction. | | **Reversal Major Long / Short** | A major (`ℝ`) reversal prints. | | **Reversal Minor Long / Short** | A minor (cross-marker) reversal prints. | ## How to Know You Are Reading It Right When you get comfortable, you will start noticing the suite produces **states**, not random prints: | State | What it looks like | | --------------------- | ------------------------------------------------------------- | | **Strong Bullish** | Momentum drives, pressure supports, candles align | | **Bullish Weakening** | Momentum begins fading, pressure cools, divergence may warn | | **Mixed** | More fakeouts, fewer clean runs, demand stronger confirmation | | **Transition** | Where the best reversals and dip catches often appear | | **Strong Bearish** | Downside pressure is real, short-side regimes behave cleaner | If you trade based on **states** instead of isolated signals, the suite stops being "an indicator" and becomes a **live market interpreter**. # TradingView Indicators Source: https://docs.backquant.com/tradingview/overview BackQuant's suite of invite-only TradingView indicators for discretionary execution and study. BackQuant ships a curated set of TradingView indicators built around the same philosophy as the terminal API: **read conditions, not isolated signals.** Each script is designed to work standalone, but the real edge comes from stacking them so that momentum, pressure, structure, and volatility tell a coherent story. All BackQuant TradingView indicators are **invite-only** and require an active **yearly BackQuant subscription** for access. Once subscribed, invites are granted to your TradingView username and the scripts appear under *Indicators - Invite-only scripts* inside TradingView. ## Available Indicators Coordinated momentum, money flow, confluence, divergence, reversal, and bar-coloring modules in a single pane. Modular execution overlay: trend, impulse, stop loss, RSI screener, market structure, FVGs, volumetric order blocks, S/R, and reversal bands. Two-layer cycle framework: a valuation oscillator plus a composite long-term trend score for allocation decisions. See every published BackQuant script directly on TradingView. ## Getting Access Pick up a yearly plan at [backquant.com](https://backquant.com/). The yearly tier is what unlocks the TradingView indicator bundle. Enter your TradingView username in your BackQuant account settings. Access is **auto-granted** as soon as it's saved - no manual approval or email needed. The indicators will be visible under *Indicators - Invite-only scripts* in your TradingView chart. ## How These Docs Are Written Indicator pages are written for **live-market use**, not as a list of inputs. Every page covers the same five things: The single job the indicator was built for, stated up front. What every component reads from the tape, and what it shows on chart. How to interpret outputs as a sequence of conditions, not a checklist of signals. The inputs that genuinely change behavior, with the direction each one pushes the indicator. A **Practical Playbooks** section also appears on each indicator page, showing the trade scenarios the script is designed for - trend participation, dip catching, top selling, exhaustion fading, and so on. ## Support Questions about access, invites, or how to use a specific indicator on a specific market? Reach the team at **[dev@backquant.com](mailto:dev@backquant.com)** or in the BackQuant Discord. # Trading Module Source: https://docs.backquant.com/tradingview/trading-module A modular overlay combining Trend, Impulse, Stop Loss, RSI screener, market structure, FVGs, volumetric order blocks, volumetric S/R, and reversal bands. Built for discretionary execution and study. Trading Module Open the Trading Module on TradingView. Current version: **v2.0.1**. ## What This Script Is The Trading Module is a single overlay that bundles the components of a real discretionary process: a directional **bias filter**, a **timing engine**, a **risk framework**, a **multi-symbol watchlist**, and a **price-action layer**. Every module is independently switchable, so you can run it as a clean trend-follower, a structure-driven execution chart, or a watchlist scanner - without juggling multiple scripts. It is intentionally **not** a "buy/sell arrow" tool. The script's job is to keep five questions answered at all times: 1. What direction am I biased toward? *(Trend)* 2. Is now a moment worth engaging? *(Impulse)* 3. Where is the idea wrong? *(Stop Loss)* 4. What else in my universe is doing the same thing? *(Screener)* 5. Where on the chart actually matters? *(Structure, FVGs, Order Blocks, S/R)* ## Module Map Direction filter - 5 selectable engines from a composite multi-factor model down to a simple EMA cross. Timing engine - flags expansion / pressure events with `𝕃` (engage) and `ℂ` (cash) labels. Three structural risk frameworks - volatility, fixed-percent, or bar-to-bar invalidation. 10-slot multi-symbol multi-timeframe RSI watchlist, gradient-colored by strength. Independent swing and internal pivot tracking with BOS / MSB / MSB+ tagging. Pivot-anchored OB zones with internal buy/sell volume split and relevance %. Multi-timeframe imbalance boxes with mid-line and right-extension. High-volume pivot-based support/resistance with touch counting and auto-cleanup on break. Volatility-and-percentile bands for top finding, dip hunting, and exhaustion detection. ## Trend Models The Trend Model is your bias filter. The script ships five engines so you can pick the one whose behavior matches the asset and timeframe you trade. Only one is active at a time. | Model | What It Reads | Best For | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | **Universal Trend+** | A composite of five families - RSI regime, smoothed Rate-of-Change, fast/slow EMA spread, a normalized T3 oscillator, and a DEMA-ATR band - each emitting its own long/short/neutral vote, then averaged. | The default if you want one trend read that is hard to fake out by any single indicator failing. | | **EMA Cross** | Fast vs slow EMA. Color flips with the cross. | Simple, responsive, but expects you to handle range-bound chop yourself. | | **DEMA ATR** | A double-EMA midline with an ATR envelope; the line only shifts color when it actually breaks structure, not on every wiggle. | Cleaner than a basic cross in choppy assets. | | **Relative Strength Overlay** | A "for-loop" RSI scoring system that ranks the current RSI against a band of historical readings and flips on persistent strength shifts. | A "strength state" trend rather than a moving-average state. | | **Hull Trend** | A long-period Hull moving average, painted by its own slope. | Smooth, lag-reduced trend backdrop for higher timeframes. | Treat the trend model as a **regime filter**, not a signal. Long bias = filter shorts, look for impulse longs. Short bias = the inverse. ## Impulse Models Impulse is the timing layer. It is decoupled from trend on purpose - a market can be trending without currently *impulsing*, and impulses can occur counter-trend (where they're useful as warnings rather than entries). Both impulse engines share the same scoring core: a current oscillator value is **compared against many of its own historical bars at once** and counted into a single score. The score crosses a long threshold to print `𝕃`, and crosses a short threshold to print `ℂ` (cash). This makes them more selective than a raw oscillator cross. | Model | Underlying Read | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | **BBPct FL Impulse** | Bollinger %B normalized into the for-loop score. Reacts to where price sits inside its volatility envelope, not just its slope. | | **DM Impulse Enhanced** | Built on Directional Movement. Cleaner read on directional pressure, less reactive to single bar volatility spikes. | **Labels you'll see:** * `𝕃` (long) - the impulse score flipped above the long threshold. * `ℂ` (cash) - the impulse score crossed under the short threshold; treat as exit / stand-aside, not necessarily an active short. Counter-trend impulses are not invalid signals - they're early-warning events. Use them to take profits or tighten stops, not to fight the trend model. ## Stop Loss Frameworks Three independent risk-overlay styles. None of them are "the" stop - they're visual frameworks for structuring invalidation. | Mode | What It Plots | When To Use | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | | **Dynamic** | Two volatility bands above and below price, scaled by RMA of True Range × 1.5. Expands in volatile regimes, tightens when range contracts. | Default for most trend or impulse setups - it adapts. | | **Fixed** | Three pairs of percentage bands at ±1%, ±2.5%, and ±4% from the bar's reference price. | Rule-based / mechanical risk sizing where you want flat percentages instead of vol-aware ones. | | **Bar-to-Bar** | Marks the prior bar's low (on up bars) or high (on down bars) as the immediate invalidation. | Tight intra-trend management - "if last bar gets violated, the move is broken." | Stops belong where the **trade idea is wrong**, not where you start to feel pain. If you take entries from impulses, your stop has to be wide enough to survive impulse-volatility, not just last-bar noise. ## RSI Screener A built-in multi-symbol, multi-timeframe RSI watchlist that draws as a stacked overlay on the right side of your chart - no extra panes, no second tab. **What you control:** * **10 symbol slots** with sensible crypto majors as defaults; each slot can be swapped to any TradingView ticker. * **Per-slot timeframe** (empty = current chart timeframe). Mix HTF and LTF reads in the same screener. * **RSI length** and a configurable **midline threshold** (default 50) used as the bull/bear divider. * **Gradient coloring**: when enabled, each row interpolates between long and short colors based on its RSI value (20 → 80 range), so you read strength by hue rather than just reading numbers. * **Label size** and X/Y offsets for fitting the screener cleanly into your layout. **How to read it:** * Read the **midline crosses**, not the OB/OS extremes - RSI > midline = bull regime, \< midline = bear regime. * Stack a higher-timeframe slot on top for regime, lower-timeframe slots below for timing within that regime. * Use the screener as **context**, not a trade trigger. Each slot is a `request.security()` call. Heavy use on lower-end machines may impact load time - disable slots you don't actively watch. ## Market Structure Tracks pivots and prints **structural events** as labels on the chart. Two independent layers: **Swing** (slower, macro) and **Internal** (faster, intra-trend). Each has its own lookback. **Events plotted:** | Event | Meaning | | ---------------------------------- | -------------------------------------------------------------------------------------- | | **BOS** *(Break of Structure)* | A continuation event - trend extends past the most recent swing in the same direction. | | **MSB** *(Market Structure Break)* | A directional shift - the structure breaks against the prior trend. | | **MSB+** | A higher-confidence MSB filter for users who want fewer, stronger shift signals. | **Configurable per layer:** * Display mode: `All` / `MSB` / `MSB+` / `BOS` / `None` - show only what you care about. * Independent lookback: swing default `50`, internal default `5`. * Independent bull/bear colors and line styles (Solid / Dashed / Dotted). **Use it for:** * **Swing layer** → confirm the larger regime (does the trend model align with structural reality?). * **Internal layer** → time entries inside the swing regime. ## Volumetric Order Blocks Order Blocks (OBs) are pivot-anchored zones that mark where the last opposite-direction candle pushed the move that created a structural pivot. The Trading Module's OB engine adds **volume context** so you can tell apart "a textbook OB" from "an OB the market actually traded heavily through." **Anatomy of each OB box:** | Component | What it shows | | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Top / Bottom of zone** | The OB price range itself. | | **Mid-line** | A horizontal mid-price reference for partial reactions. | | **Internal Buy/Sell metric (`Internal Buy/Sell Activity`)** | Two stacked sub-zones inside each OB box that grow as later bars print up vs down inside the zone. Lets you see at a glance whether buyers or sellers are actually engaging it. | | **Volume label + relevance %** | Total volume that built the OB, plus what percentage of the visible OB stack that volume represents. The OB carrying more weight is doing more work. | | **Right-extension box** | The OB projected forward so you can see future tests at a glance. | **Detection & filtering:** * **Filtering** - restrict OB creation to break events of a specific type (`None`, `BOS`, `MSB`, `MSB+`). * **Mitigation** - choose how an OB gets considered "used up": `Absolute` (full body breach) or `Middle` (mid-line breach). * **Hide Overlap** - when two OBs overlap, the engine keeps either the more recent or earlier one (default: keep previous), so the chart doesn't clutter with stacked duplicates. * **OB count** - how many active OBs to keep on chart at once. * **Swing OBs** - optional separate set of OBs anchored to swing structure rather than internal structure. * **Grayscale mode** - desaturate the OBs if you'd rather use color for other modules. An OB with a high relevance % and an internal metric that **agrees** with the OB direction (buy metric dominant inside a bull OB) is doing real work. An OB whose internal metric disagrees with its direction is already losing its edge before price even returns. ## Fair Value Gaps (FVGs) Three-bar imbalance boxes - places where price moved fast enough that the candle in the middle did not overlap with its neighbors. The script draws them as zones and adds a mid-line for partial fills. **Controls:** * **Enable** - toggle FVGs on/off (off by default). * **Show Last** - cap of how many recent FVGs to display (default 5). * **Timeframe** - detect FVGs on a different timeframe than your chart (e.g. mark 1H FVGs on a 5m chart). Empty = chart timeframe. * **Extend** - how many bars to project the FVG box into the future. * **Bull/Bear color** - both default to a soft tint of your long color so the chart doesn't fight your other modules; you can override. **Interpretation:** * An FVG is an **area of interest**, not a trade by itself. Many traders use them as targets (price likes to revisit unfilled imbalances) or as decision zones for entry/rejection. * The engine **auto-removes** an FVG once price closes through it from the wrong side - so what's on the chart is always still active. ## Volumetric Support & Resistance A pivot-based S/R engine that only respects pivots **confirmed by elevated volume**. Plain pivots are ignored - the level has to be earned. **Detection logic:** | Input | Effect | | --------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | **Detection Sensitivity** *(default 5)* | The pivot lookback. Lower = more pivots, higher = stricter. | | **Volume Multiplier** *(default 1.0×)* | The pivot's volume must be at least this multiple of average volume to qualify as a level. | | **Analysis Period** *(default 100)* | Bars used for the volume baseline. | | **Min Distance %** *(default 0.5%)* | Levels closer to an existing one are skipped, so the chart doesn't fill with near-duplicates. | | **Max Levels** *(default 15)* | Hard cap on active levels. | | **Remove Broken** | Auto-deletes levels once price closes through them by more than \~0.3 × ATR - keeps only "still active" zones. | **What each box shows:** * A **box around the level** with thickness scaled to ATR (so the zone is visually proportionate to volatility). * **Border thickness** - high-volume levels get a thicker border, so the chart highlights the zones built on the most participation. * **Touch counter** - every time price comes back into the zone, the touch count goes up. Repeat tests are more meaningful than one-and-done levels. * **Volume text** - optionally displayed inside the box (or beside it). * **Right-extend** - toggle whether levels project into future bars. Pair volumetric S/R with the impulse model: an `𝕃` printing **at** a high-volume support that has multiple touches is much higher quality than the same `𝕃` printing in open air. ## Reversal Bands NEW in v2.0.1 A volatility-aware band system designed for **top finding, dip hunting, and local exhaustion**. It combines two ideas: 1. **A percentile envelope** - the script tracks where the source has historically traded within a configurable lookback (default 200 bars, 95th percentile) and uses that as a "stretched" anchor. 2. **A volatility-multiplied deviation band** - built off a baseline length (default 50) and a volatility length (default 53), then scaled by a multiplier (default 3.1×). Together, they highlight **price behaving extremely relative to its own recent regime** - not just "above an SMA." **Inputs you'll touch most:** | Input | What it changes | | ---------------------------------- | ----------------------------------------------------------------------------------------------------- | | **Percentile Lookback / Level** | How far back the percentile is measured, and how extreme a reading must be (95 = top 5% / bottom 5%). | | **Baseline Length** | The smoothing of the central reference. Lower = faster, higher = cleaner. | | **Volatility Length & Multiplier** | The width of the deviation envelope. Higher multiplier = fewer signals, more selective. | | **Show Reversal Signals** | Toggle the in-band Dip / Top reversal signal markers on or off. | **How to read it:** * Price tagging the **upper band** in conditions of strong trend = late-cycle caution / take-profit zone, not a blind short. * Price tagging the **lower band** with weakening down-impulse = high-quality dip-hunt context. * Use it as a **filter on top of impulse signals**: an `ℂ` near the upper band is more meaningful than an `ℂ` mid-range. ## Core Philosophy This indicator is not "one model to rule them all." It exists to let you build a process where each layer answers exactly one question: | Layer | Decides | | ------------------------------ | ---------- | | **Trend** | bias | | **Impulse** | timing | | **Structure / OB / FVG / S/R** | location | | **Stop Loss** | risk | | **Screener** | focus | | **Reversal Bands** | exhaustion | If you only use one layer, you're discarding most of the edge the script is designed to build. The strength is in **confluence and filtering** - multiple modules agreeing on the same idea at the same place. ## Suggested Presets * Trend: Universal Trend+ or DEMA ATR * Impulse: BBPct FL or DM Enhanced * Stop Loss: Dynamic * Structure / FVG / OB: Off * Screener: On (high TF) * Trend: Hull Trend or Universal Trend+ * Impulse: On * Market Structure: Swing + Internal * FVG + Volumetric Order Blocks: On * Stop Loss: Dynamic or Bar-to-Bar * Screener: On (10 slots, mixed TFs) * Minimal chart overlays * Reversal Bands: On for context * Drill into individual charts when alignment shows up ## Quick Input Map | Group | Key inputs | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | **Main / Models** | Trend, Impulse, Stop Loss model selectors • Show Screener • Long/Short colors • Reversal Bands toggles | | **Screener** | 10 symbol slots • per-slot timeframe + toggle • RSI length & midline • label size + offsets • gradient coloring | | **Reversal Bands** | Percentile source, lookback, level • Deviation source, baseline length, volatility length & multiplier | | **Volume Order Blocks** | Show on chart • OB count • Internal buy/sell metric • Swing OBs • Filtering (None / BOS / MSB / MSB+) • Mitigation (Absolute / Middle) • Grayscale | | **Market Structure** | Swing & Internal mode (All / MSB / MSB+ / BOS / None) • per-layer lookback • bull/bear colors • line styles | | **Fair Value Gaps** | Enable • Show Last (count) • Timeframe • Extend bars • bull/bear colors | | **Volumetric S/R** | Sensitivity • Volume multiplier • Analysis period • Max levels • Min distance % • Remove broken • Right-extend • Volume text inside | ## Version History ### v2.0.1 - Latest * Added **Reversal Bands** with volatility-adjusted percentile calculations. * New reversal signals (Green for Dip, Red for Top). * 7 new inputs for band sensitivity calibration. * Bug fixes for calculation errors. * Larger lookback without losing calculation time. ### v1.1.0 * Added **Hull Trend** model. * Static screener color option (inverse of chart background). * Volumetric S/R on/off toggle. * Improved Market Structure inputs. * Enhanced Order Block plotting with internal buy/sell volume metric. * Significant runtime improvement. * Temporary removal of `alerts()`. ## Final Notes This is a full visual decision-support overlay for discretionary traders who want trend, timing, structure, and watchlist scanning in one place. Use it to build a repeatable process, then validate that process with proper testing and journaling before risking real capital. This is a heavy script when many modules are enabled - it draws live objects and requests multiple symbols. Not every combination is optimal for every market or timeframe; the modules are deliberately independent so you can disable what you're not using.