MasscrestMasscrest
Documentation · 01

Data dictionary

Field-level reference for every table Masscrest delivers.

Download .md

Masscrest covers US-listed single stocks and ETFs. OTC-traded names and index products (SPX, VIX, and similar) are out of scope.

For every listed-option contract in scope, Masscrest tracks buy, sell and net flows across three investor cohorts:

CohortDescription
InstitutionalLong-only, long/short, market-neutral, long-biased, multi-strat and other equity hedge funds; mutual funds, sovereign and endowment funds, family offices and other asset managers; proprietary trading desks, banks and dealers trading on behalf of customers.
RetailGlobal self-directed retail traders (not investment advisors or private-banking clients).
Interdealer / market maker~99% of trades take place against a market maker, so this cohort sits opposite to Institutional and Retail flows. Interdealer trades are between dealers, typically to offset greek exposure.

What we provide

Three product surfaces sit on top of the same underlying inference layer:

  1. Option flow by contract — buy, sell, and net trading imbalance for every listed contract, expressed in shares, premium, and greek-adjusted notional (delta, gamma, vega). Query with contract-level filters (dte, abs_delta, callput). Tables: option_flows_01_day and option_flows_10_min.
  2. Option flow by underlying ticker — the same trading imbalance aggregated to the underlying root symbol × callput. Pre-aggregated at ingest; use when you don't need contract-level filters. Available at the underlying grain (daily + 10-min) and rolled up to sector / industry / ETF / ADR / single-stock / all cohorts (daily only). Tables: underlying_option_flows_01_day, underlying_option_flows_10_min, and grouped_flow_signal_01_day (the grouped table carries the raw flow alongside the z-scored signal — see below).
  3. Signal — proprietary indicator of abnormal net options positioning (delta, gamma, vega exposure) driven by institutional investors on the underlying. Historically correlated with subsequent excess returns; used to surface conviction at the ticker, industry, or sector level. Tables: underlying_flow_signal_01_day (per underlying) and grouped_flow_signal_01_day (aggregated to sector / industry / ETF / ADR / single-stock / all).
SurfaceGrainDaily10-min
Option flow by contract(date, figi, callput, strike, expiration)
Option flow by underlying ticker(date, figi, callput)
Option flow — grouped (sector / industry / …)(date, group_key, callput)
Signal — per underlying(date, figi, callput)
Signal — grouped (sector / industry / …)(date, group_key, callput)

Typical uses

  • Follow institutional flow. See where hedge funds and asset managers are building or unwinding positions.
  • Spot retail-vs-institutional divergences. Identify names where the two cohorts sit on opposite sides.
  • Trade the optionality dimension. Separate directional bets from vega and gamma positioning; know whether a move is premium-driven or forced by dealer hedging.
  • Layer market narrative on price action. Explain why a name moved when the tape alone doesn't tell you: accumulation ahead of a break, capitulation into a bottom.
  • Control for flow in factor models. A signal orthogonal to price and volume. Wire it in as a regression control alongside conventional risk factors.

Delivery & availability

Daily datasets are keyed on US-equity trading dates and delivered one calendar day later (Friday's data lands Saturday). Real-time notification is available via webhook; a polling fallback is documented below.

Cadence

  • Frequency: T+1 — data for trading day T is delivered on calendar day T+1 (Friday's data lands Saturday). Data only exists for US equity trading days; delivery cadence itself is calendar-day, not trading-day.
  • Publish window: 05:00 – 09:00 UTC on the calendar day after the trading date.

Webhook (recommended)

Register an HTTPS endpoint via POST /v1/webhooks (returns HTTP 201) with your API key — no admin gesture required, no ticket to open. When each day's data lands, we push a signed JSON payload. Wire the delivery handler to your flat-files sync job and the day's parquets pull automatically the moment they're published, without polling the manifest.

{
  "event_type": "daily_options_flow.available",
  "trading_date": "2026-07-24",
  "published_at": "2026-07-25T04:37:12Z",
  "workflow_run_id": "aa1cb0c2-5b47-46f1-9c9a-8c9d1c2b0e33"
}

Field semantics:

  • trading_date: the US equity session the data covers, in YYYY-MM-DD (UTC-derived, but represents the exchange trading session, not the write time).
  • published_at: ISO-8601 UTC timestamp of when the notification was dispatched. Data is fully written before this fires, so it is safe to start reading immediately.
  • workflow_run_id: unique id for the publishing run. Use as your idempotency key: dedupe on this if you receive the same message twice.
  • event_type: always daily_options_flow.available today. See "Supported events" below.

Verifying the signature

Every request carries two headers:

  • X-Masscrest-Timestamp: <unix-seconds>
  • X-Masscrest-Signature: sha256=<hex>

Compute HMAC-SHA256 over {timestamp}.{raw_body} using your webhook secret. Reject requests where the timestamp is more than 300 seconds from your clock to block replay attacks.

The secret returned at registration is a 64-character hex string. Decode it to bytes before using it as the HMAC key; UTF-8-encoding the hex string produces the wrong signature.

Python:

import hmac, hashlib, time

def verify(secret_hex: str, timestamp: str, raw_body: bytes, received_sig: str) -> bool:
    # Reject stale requests (replay protection).
    if abs(int(time.time()) - int(timestamp)) > 300:
        return False
    key = bytes.fromhex(secret_hex)                                # NOT secret_hex.encode()
    expected = hmac.new(key, f"{timestamp}.".encode() + raw_body, hashlib.sha256).hexdigest()
    # Signature header format: "sha256=<hex>[,sha256=<hex>]" (comma-separated during rotation grace)
    for sig in received_sig.split(","):
        if hmac.compare_digest(f"sha256={expected}", sig.strip()):
            return True
    return False

Node.js:

const crypto = require("crypto");

function verify(secretHex, timestamp, rawBody, receivedSig) {
  if (Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) > 300) return false;
  const key = Buffer.from(secretHex, "hex");                       // NOT Buffer.from(secretHex)
  const expected = crypto
    .createHmac("sha256", key)
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest("hex");
  // Header may contain two signatures separated by "," during the 24h rotation grace window.
  return receivedSig.split(",").some((sig) =>
    crypto.timingSafeEqual(Buffer.from(`sha256=${expected}`), Buffer.from(sig.trim()))
  );
}

Retry policy

Retry on any non-2xx response or timeout after 10 seconds. One initial delivery plus up to six retries (seven POSTs worst case), spaced 30s, 2m, 10m, 1h, 6h, 24h. Use workflow_run_id as your idempotency key. After the sixth failed retry we disable the webhook and email you.

Supported events

One event type is live today:

Event typeFires when
daily_options_flow.availableThe daily options-flow pipeline completes for a session (T+1), between 05:00 and 09:00 UTC on the calendar day after the trading date (Friday's data fires Saturday).

Additional events will be listed here as we add them. Register only for events you handle; an unrecognized event type is rejected at registration (HTTP 422).

Managing webhooks

Self-service via your API key — every endpoint below authenticates with the same X-API-Key you use for /v0/* reads, and each key only sees and manages its own webhooks. Trial accounts may register up to 2 webhooks; paid accounts are unlimited.

MethodPathPurpose
POST/v1/webhooksRegister a URL. Returns webhook_id + secret (secret shown once).
GET/v1/webhooksList your webhooks (secrets redacted).
GET/v1/webhooks/{id}Fetch one webhook by id (secret redacted).
DELETE/v1/webhooks/{id}Remove a webhook.
POST/v1/webhooks/{id}/rotate-secretGenerate a new secret. Old secret stays valid for 24h.
POST/v1/webhooks/{id}/send-testFire a synthetic delivery to the URL for integration testing. Add ?with_retries=true to engage the retry ladder on 5xx for failure-handling validation.

Polling (alternative)

Each daily publish lands a manifest file in the flat files area of the customer bucket; poll it programmatically between 05:00 and 11:00 UTC on the day after each session to detect delivery:

gs://prod-masscrest/v0/_manifests/year=YYYY/date=YYYY-MM-DD/manifest.json

The manifest is written as the very last step of the daily publish, after every parquet and every downstream ingest is in place. Its existence is the delivery-complete signal; once visible it is safe to start reading immediately.

Sample payload:

{
  "workflow_run_id": "aa1cb0c2-5b47-46f1-9c9a-8c9d1c2b0e33",
  "trading_date": "2026-07-31",
  "published_at": "2026-08-03T01:42:22Z",
  "files": [
    { "path": "gs://prod-masscrest/v0/option_flows/01_day/year=2026/date=2026-07-31/000000000000.parquet", "byte_count": 24898886 },
    { "path": "gs://prod-masscrest/v0/underlying_option_flows/01_day/year=2026/date=2026-07-31/000000000000.parquet", "byte_count": 1479423 },
    { "path": "gs://prod-masscrest/v0/underlying_flow_signal/01_day/year=2026/date=2026-07-31/000000000000.parquet", "byte_count": 3512032 },
    "…"
  ]
}

Data corrections

When we detect an error in already-published data, we notify the client's primary technical contact and communicate the fix within 48 hours.

Corrections are written in place, keyed on the same row grain (date, figi, and contract_id where applicable). Re-sync the affected partition to pick up corrected values.

Trial vs. paid tier

Trial accounts share the paid-tier schemas and endpoints — nothing about the shape of the data changes — but usage is capped so a trial can validate integration and evaluate signal quality without pulling the full historical universe. Paid accounts have none of these limits.

SurfaceTrialPaid
REST API — per-figi endpoints (/v0/option_flows/*, /v0/underlying_option_flows/01_day, /v0/underlying_flow_signals/01_day)25 unique FIGIs / UTC day, cumulative across every per-figi call. The 26th distinct FIGI in a UTC day returns HTTP 403 with a structured JSON body: {"error": "trial_figi_cap_exceeded", "cap": 25, "resets_at": "<iso8601>", "touched": [{"figi": "...", "symbol": "..."}, ...], "requested_new": [{"figi": "...", "symbol": "..."}, ...]}. Cap resets at 00:00 UTC. Must supply an explicit figi or underlying_ticker / traded_underlying filter — a broad sector / industry-only sweep returns HTTP 400.Full universe, no per-figi cap. Broad stock_metadata filters (sector / industry / is_etf / is_adr) work as documented.
REST API — per-figi snapshots (/v0/underlying_option_flows/01_day/snapshot, /v0/underlying_flow_signals/01_day/snapshot)HTTP 403. A wide snapshot would charge every returned FIGI against the 25-figi cap in a single call, so snapshot endpoints are blocked outright on trial. Use the ranged per-figi endpoints with an explicit figi filter, or /v0/grouped_flow_signal/01_day/snapshot (not figi-capped) for broad cross-sectional exploration.Full snapshot access; stock_metadata filters (sector / industry / is_etf / is_adr) work as documented.
Data grainAggregate rollups only: per underlying × day, per (underlying, callput) × day, and grouped rollups (sector / industry / ETF / ADR / single-stock). Per-contract data is not available.Same aggregate rollups plus per-contract data through the flat-files bucket.
REST API — intraday (/v0/*/10_min, including snapshots)HTTP 403. Daily grain only.Full intraday access.
REST API — cohort rollups (/v0/grouped_flow_signal/01_day(+/snapshot)) and metadata (/v0/stock_mapping)Not figi-capped. Sector / industry / ETF / ADR / single-stock rollups and metadata browsing are free.Same.
REST API — universeFull — any FIGI Masscrest tracks is queryable (subject to the 25-figi cap).Full.
REST API — daily history depth730 days.Full history from 2020-01-02.
REST API — daily call cap100 calls / UTC day (HTTP 429 on the 101st call).1,000 calls / UTC day (contact sales for higher).
MCP serverAll tools remain callable. API 403s / 400s (25-figi cap, intraday block, broad-filter block) surface as tool errors — the assistant sees a plain-language message and stops retrying. No MCP-side role gate.All tools, no cap.
Flat filesRequires the programmatic entitlement (opt-in per account; contact sales). Enabled trials get HMAC keys against gs://prod-masscrest-trial — an aggregates-only, daily-only, 180-day-lagged subset with fresh reference metadata. See the Flat files section for the exact bucket layout. Trials without programmatic see HTTP 403 on any flat-files call.Full production bucket gs://prod-masscrest — contract-level and intraday parquets, no lag.
Webhooks (/v1/webhooks)Self-service registration with your API key, capped at 2 registered webhooks per account.Self-service registration with your API key, unlimited webhooks.
Portfolio savesUnlimited.Unlimited.

Trial-to-paid upgrade

On upgrade, the API-tier changes take effect immediately (up to a 5-minute per-instance principal-cache TTL). The Clerk webhook revokes the trial HMAC key at the moment of upgrade, so any local ~/.boto or gsutil configuration pointing at gs://prod-masscrest-trial becomes invalid. Call POST /v0/flat-files/credentials/rotate (or the create endpoint) to receive new keys against the production bucket gs://prod-masscrest and re-point your pipeline.

option_flows_01_day

Per-contract daily option-flow rows, split by participant class (institutional _i, retail _r, market-maker _m). One row per unique option contract that traded on the session, uniquely identified by (date, figi, symbol, callput, strikeprice, expirationdate).

  • Grain: (date, figi, symbol, callput, strikeprice, expirationdate)
  • History: 2020-01-02 →
  • Timing: T+1 (published between 05:00 and 09:00 UTC on the calendar day after the trading date — Friday's data lands Saturday)

*_qty fields are raw traded contract counts (integer). *_premium fields are dollar premium (qty × traded price × adj_shares_deliverable). *_delta / *_gamma / *_vega fields are the traded contracts as greek-weighted USD notional: dollar notional multiplied by the respective per-contract greek, then scaled by adj_shares_deliverable. Delta-weighted notionals are naturally signed by option type (calls +, puts −); net_* = buy_* − sell_* and inherits the sign. The _i + _r + _m ≈ 0 accounting identity holds row-by-row on the notional columns (up to rounding). floor_* is exchange-floor / broker-committed volume, held in its own columns because Masscrest does not track these trades and their side (buy vs sell) is not available. Additional per-contract context columns carry the last-observed underlying spot, forward, IV and greeks for the session (useful as an end-of-day mark).

Fields

field_nametypeunitgraindescriptionprovenancetiming_laghistory_startnull_semanticsexample
dateDatetrading day (UTC)(date, figi, symbol, callput, strikeprice, expirationdate)Session date. US equity trading calendar.exchangeT+12020-01-02never null2026-07-24
figiString(12)identifier(date, figi, symbol, callput, strikeprice, expirationdate)OpenFIGI composite FIGI for the underlying. Stable across ticker changes; join key to stock_metadata, px_01_day, split_factors.exchangen/a2020-01-02never nullBBG000MM2P62
symbolStringticker(date, figi, symbol, callput, strikeprice, expirationdate)OCC OPRA root of the option (no-dot form: BRKB, not BRK.B). Match on this key when joining back to raw OCC / OPRA feeds. Use traded_underlying for equity-side joins.exchangeT+12020-01-02never nullAAPL
traded_underlyingStringticker(date, figi, symbol, callput, strikeprice, expirationdate)Point-in-time listed equity ticker for the deliverable underlying on date. Dotted dual-class form (BRK.B). Join key to px_01_day, stock_metadata, dividends.exchangeT+12020-01-02never nullAAPL
callputString(1)option type(date, figi, symbol, callput, strikeprice, expirationdate)'C' (call) or 'P' (put).exchangeT+12020-01-02never nullC
strikepriceFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Contract strike price, in the deliverable equity's price units. Not split-adjusted at this grain; use cumulative_split_factor to convert to a current-share-equivalent strike.exchangeT+12020-01-02never null215.00
expirationdateDateboundary(date, figi, symbol, callput, strikeprice, expirationdate)Contract expiration date (US equity calendar).exchangeT+12020-01-02never null2026-08-15
dteInt64days(date, figi, symbol, callput, strikeprice, expirationdate)Days to expiry: expirationdate − date (calendar days). 0 on the expiry-day trading session; can be negative on late prints of already-expired contracts (rare).derivedT+12020-01-02never null22
contract_idStringidentifier(date, figi, symbol, callput, strikeprice, expirationdate)Masscrest-built contract identifier that tracks a contract's economic identity across corporate actions (splits, reverse splits, M&A, deliverable adjustments). Standard OCC / OSI symbols mint a new identifier every time the deliverable changes; contract_id deliberately does not, so a single value follows the same economic contract through its lifetime. Built as a deterministic string from (root_symbol, expiration, callput, root-strike × 1000, corporate-action epoch tag); root_symbol and root-strike are the OCC-root form pre-action, and the E<YYYYMMDD> epoch tag disambiguates recycled OPRA symbols that share the same expiry/strike across pre- and post-action cohorts. Joins to underlying_option_flows_01_day (per underlying) via figi but is unique at the contract grain.derivedT+12020-01-02null on trades that pre-date a required OCC memo for a not-yet-mapped corporate-action epoch (rare, <0.01% of rows)AAPL 260815C00215000_E00000000
adj_shares_deliverableFloat64shares per contract(date, figi, symbol, callput, strikeprice, expirationdate)Shares conversion adjustment for OCC contract adjustments such as reverse split, M&A, and other corporate actions. Standard equity option delivers 100 shares → adj_shares_deliverable = 100. Post-adjustment values (fractional deliverables, cash + share basket primary legs) are the OCC-declared per-contract share count.exchangeT+12020-01-02never null100.0
cumulative_split_factorFloat64ratio(date, figi, symbol, callput, strikeprice, expirationdate)Cumulative equity split factor in effect on date (same source as split_factors.cum_split_factor). Divide strikeprice by this factor to convert to a current-shares-equivalent strike. 1.0 when no split applies on or after date.derivedT+12020-01-02never null1.0
is_indexBoolflag(date, figi, symbol, callput, strikeprice, expirationdate)Whether the underlying is a cash-settled index (SPX, NDX, RUT, VIX, XSP, MRUT, NANOS, XND). false for every single-stock and ETF contract. Currently always false in the served surface (Masscrest does not sell index-option data). Retained for schema stability.exchangeT+12020-01-02never nullfalse
last_underpriceFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Underlying spot price observed at the last option trade of the session. Not split-adjusted (contemporaneous mark).exchangeT+12020-01-02null when the contract has no trades in the session224.31
last_fwd_underpriceFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Forward price on the underlying, adjusted for dividends and the risk-free rate. Not adjusted for splits.derivedT+12020-01-02null when the contract has no trades in the session224.68
last_priceFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Last traded option price on the session. Per-contract price (not scaled by adj_shares_deliverable).exchangeT+12020-01-02null when the contract has no trades in the session9.42
last_ivFloat64annualised vol (0.30 = 30%)(date, figi, symbol, callput, strikeprice, expirationdate)Implied volatility at the last trade of the session, fit against last_fwd_underprice and last_price.derivedT+12020-01-02null when the contract has no trades / IV inversion fails0.2418
last_deltaFloat64delta per contract(date, figi, symbol, callput, strikeprice, expirationdate)Contract delta at the last trade of the session. Signed by option type (calls +, puts −).derivedT+12020-01-02null when the contract has no trades in the session0.5620
last_gammaFloat64gamma per contract(date, figi, symbol, callput, strikeprice, expirationdate)Contract gamma at the last trade of the session.derivedT+12020-01-02null when the contract has no trades in the session0.0184
last_vegaFloat64vega per contract(date, figi, symbol, callput, strikeprice, expirationdate)Contract vega at the last trade of the session (dollar P&L per 1 vol point change).derivedT+12020-01-02null when the contract has no trades in the session0.2712
qtyInt64contracts(date, figi, symbol, callput, strikeprice, expirationdate)Total unique traded contract volume for the session (each print contributes its quantity once; m_qty is not double-counted).exchangeT+12020-01-020 on no-volume rows (present only when other fields exist)1_842
buy_r_qty / sell_r_qty / net_r_qtyInt64contracts(date, figi, symbol, callput, strikeprice, expirationdate)Retail buy / sell / net contract count. net = buy − sell.modelT+12020-01-020 when no qualifying retail flow241
buy_r_premium / sell_r_premium / net_r_premiumFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Retail dollar premium paid on buys, received on sells, and their net. Signed on net. Scaled by adj_shares_deliverable (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying retail flow312_040.0
buy_r_delta / sell_r_delta / net_r_deltaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Retail buy / sell / net flow as USD notional × delta (deliverable-adjusted). Naturally signed by option type (calls +, puts −); net = buy − sell. Positive net = net long-delta demand from retail on this contract.modelT+12020-01-020.0 when no qualifying retail flow-38_720.55
buy_r_gamma / sell_r_gamma / net_r_gammaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Retail buy / sell / net flow as USD-notional-converted gamma (dollar notional multiplied by the per-contract gamma, deliverable-adjusted).modelT+12020-01-020.0 when no qualifying retail flow12_450.30
buy_r_vega / sell_r_vega / net_r_vegaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Retail buy / sell / net flow as USD-notional-converted vega (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying retail flow88_012.40
buy_i_qty / sell_i_qty / net_i_qtyInt64contracts(date, figi, symbol, callput, strikeprice, expirationdate)Institutional buy / sell / net contract count. Same convention as retail.modelT+12020-01-020 when no qualifying institutional flow987
buy_i_premium / sell_i_premium / net_i_premiumFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Institutional dollar premium buy / sell / net (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying institutional flow1_120_338.55
buy_i_delta / sell_i_delta / net_i_deltaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Institutional buy / sell / net flow as USD notional × delta (deliverable-adjusted). Naturally signed; positive net = net long-delta demand from the institutional class on this contract.modelT+12020-01-020.0 when no qualifying institutional flow892_310.00
buy_i_gamma / sell_i_gamma / net_i_gammaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Institutional buy / sell / net flow as USD-notional-converted gamma (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying institutional flow41_882.70
buy_i_vega / sell_i_vega / net_i_vegaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Institutional buy / sell / net flow as USD-notional-converted vega (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying institutional flow312_048.80
buy_m_qty / sell_m_qty / net_m_qtyInt64contracts(date, figi, symbol, callput, strikeprice, expirationdate)Market-maker buy / sell / net contract count. Includes the residual absorption of customer (retail + institutional) net imbalance on the opposite side, so the row-level accounting identity _i + _r + _m ≈ 0 holds by construction on the notionals.modelT+12020-01-020 when no qualifying market-maker flow-1_098
buy_m_premium / sell_m_premium / net_m_premiumFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Market-maker dollar premium buy / sell / net (deliverable-adjusted). Mirror of _i + _r on the same row up to floor / rounding.modelT+12020-01-020.0 when no qualifying market-maker flow-1_432_378.65
buy_m_delta / sell_m_delta / net_m_deltaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Market-maker buy / sell / net flow as USD notional × delta (deliverable-adjusted). Mirror of _i + _r on the same row.modelT+12020-01-020.0 when no qualifying market-maker flow-853_590.00
buy_m_gamma / sell_m_gamma / net_m_gammaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Market-maker buy / sell / net flow as USD-notional-converted gamma (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying market-maker flow-54_333.00
buy_m_vega / sell_m_vega / net_m_vegaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Market-maker buy / sell / net flow as USD-notional-converted vega (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying market-maker flow-400_172.00
floor_qtyInt64contracts(date, figi, symbol, callput, strikeprice, expirationdate)Total floor-executed contract count (direct-negotiation outcry trades on the exchange floor). No directional classification provided; reported for volume completeness.exchangeT+12020-01-020 when no floor prints24
floor_premiumFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Total floor-executed dollar premium (deliverable-adjusted). No directional classification provided; reported for volume completeness.exchangeT+12020-01-020.0 when no floor prints24_120.0
floor_deltaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Total floor-executed USD notional × delta (deliverable-adjusted). No directional classification provided; reported for volume completeness. Naturally signed by option type.exchangeT+12020-01-020.0 when no floor prints13_492.20
floor_gammaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Total floor-executed USD-notional-converted gamma (deliverable-adjusted). No directional classification provided; reported for volume completeness.exchangeT+12020-01-020.0 when no floor prints1_842.10
floor_vegaFloat64USD(date, figi, symbol, callput, strikeprice, expirationdate)Total floor-executed USD-notional-converted vega (deliverable-adjusted). No directional classification provided; reported for volume completeness.exchangeT+12020-01-020.0 when no floor prints9_820.60

Join hints

For per-underlying daily aggregates (rolled up across every contract on the same (date, figi)), use underlying_option_flows_01_day. For underlying prices and turnover, join to px_01_day on (date, figi) and self-adjust using split_factors. For contract-level metadata (deliverable per contract, corporate-action epochs) the values in this table are already point-in-time; no secondary join is needed.

Gross-volume accounting

Each trade has a customer side (buy_{r,i}_qty / sell_{r,i}_qty) and a market-maker counterparty (buy_m_qty / sell_m_qty, rebuilt to absorb the customer imbalance so the row-level _i + _r + _m ≈ 0 identity holds on notionals).

DO: aggregate on qty — one entry per trade.

DON'T: sum the cohort legs (buy_r_qty + sell_r_qty + buy_i_qty + sell_i_qty + buy_m_qty + sell_m_qty + floor_qty) — inflates volume by ~30-35% because the market-maker leg gets added on top of the customer legs.

option_flows_10_min

10-minute-bucketed sibling of option_flows_01_day. Per-contract option-flow rows at a 10-minute cadence: one row per unique option contract that traded within the bucket, keyed on (date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate).

  • Grain: (date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)
  • History: 2020-01-02 →
  • Timing: T+1 (published between 05:00 and 09:00 UTC on the calendar day after the trading date — Friday's data lands Saturday)

Request window is capped at 30 days per call on the REST endpoint. Omitted dates default to the most recent 30. For multi-year intraday history use flat files at gs://prod-masscrest/v0/option_flows/10_min/; the REST cap keeps response sizes bounded.

Bucket boundary convention

ten_min_timeframe is the end of the 10-minute bucket, in New York wall-clock time stored as a naive DateTime. Bucket covers (ten_min_timeframe − 10 min, ten_min_timeframe]. First RTH bucket ends at 09:40:00, last at 16:00:00 (39 buckets per full RTH session). Join to px_10_min on (date, ten_min_timeframe, figi); both sides use the same NY-wall-clock convention.

Field semantics match option_flows_01_day exactly: the same buy / sell / net triples across the participant classes, greek-weighted USD notionals, the accounting identity _i + _r + _m ≈ 0 (row-by-row), and the same last_* end-of-bucket context columns. The only structural difference is the added ten_min_timeframe dimension.

Fields

field_nametypeunitgraindescriptionprovenancetiming_laghistory_startnull_semanticsexample
dateDatetrading day (UTC)(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Session date. US equity trading calendar.exchangeT+12020-01-02never null2026-07-24
ten_min_timeframeDateTimeNY wall-clock, bucket close(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)End timestamp of the 10-min bucket (naive NY-time). Bucket covers (ten_min_timeframe − 10 min, ten_min_timeframe].exchangeT+12020-01-02never null2026-07-24 09:40:00
figiString(12)identifier(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)OpenFIGI composite FIGI for the underlying. Stable across ticker changes; join key to stock_metadata, px_10_min, split_factors.exchangen/a2020-01-02never nullBBG000MM2P62
symbolStringticker(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)OCC OPRA root of the option (no-dot form: BRKB, not BRK.B). Match on this key when joining back to raw OCC / OPRA feeds. Use traded_underlying for equity-side joins.exchangeT+12020-01-02never nullAAPL
traded_underlyingStringticker(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Point-in-time listed equity ticker for the deliverable underlying on date. Dotted dual-class form (BRK.B). Join key to px_10_min, stock_metadata, dividends.exchangeT+12020-01-02never nullAAPL
callputString(1)option type(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)'C' (call) or 'P' (put).exchangeT+12020-01-02never nullC
strikepriceFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Contract strike price, in the deliverable equity's price units. Not split-adjusted at this grain; use cumulative_split_factor to convert to a current-share-equivalent strike.exchangeT+12020-01-02never null215.00
expirationdateDateboundary(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Contract expiration date (US equity calendar).exchangeT+12020-01-02never null2026-08-15
dteInt64days(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Days to expiry: expirationdate − date (calendar days). 0 on the expiry-day trading session.derivedT+12020-01-02never null22
contract_idStringidentifier(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Masscrest-built contract identifier that tracks a contract's economic identity across corporate actions (splits, reverse splits, M&A, deliverable adjustments), unlike standard OCC / OSI symbols which mint a new identifier every time the deliverable changes. Built as a deterministic string from (root_symbol, expiration, callput, root-strike × 1000, corporate-action epoch tag); the trailing E<YYYYMMDD> epoch tag disambiguates recycled OPRA symbols that share expiry/strike across pre- and post-action cohorts. Same value across every 10-min bucket of the contract's session and across option_flows_01_day.derivedT+12020-01-02null on trades that pre-date a required OCC memo for a not-yet-mapped corporate-action epoch (rare)AAPL 260815C00215000_E00000000
adj_shares_deliverableFloat64shares per contract(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Shares conversion adjustment for OCC contract adjustments such as reverse split, M&A, and other corporate actions. Standard equity option delivers 100 shares → adj_shares_deliverable = 100.exchangeT+12020-01-02never null100.0
cumulative_split_factorFloat64ratio(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Cumulative equity split factor in effect on date (same source as split_factors.cum_split_factor).derivedT+12020-01-02never null1.0
is_indexBoolflag(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Whether the underlying is a cash-settled index. Currently always false in the served surface.exchangeT+12020-01-02never nullfalse
last_underpriceFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Underlying spot at the last option trade within the bucket. Not split-adjusted.exchangeT+12020-01-02null when the contract has no trades in the bucket224.10
last_fwd_underpriceFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Forward price on the underlying, adjusted for dividends and the risk-free rate. Not adjusted for splits.derivedT+12020-01-02null when the contract has no trades in the bucket224.48
last_priceFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Last traded option price in the bucket. Per-contract price (not scaled by adj_shares_deliverable).exchangeT+12020-01-02null when the contract has no trades in the bucket9.38
last_ivFloat64annualised vol (0.30 = 30%)(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Implied volatility at the last trade of the bucket.derivedT+12020-01-02null when the contract has no trades / IV inversion fails0.2418
last_deltaFloat64delta per contract(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Contract delta at the last trade of the bucket. Signed by option type.derivedT+12020-01-02null when the contract has no trades in the bucket0.5620
last_gammaFloat64gamma per contract(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Contract gamma at the last trade of the bucket.derivedT+12020-01-02null when the contract has no trades in the bucket0.0184
last_vegaFloat64vega per contract(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Contract vega at the last trade of the bucket.derivedT+12020-01-02null when the contract has no trades in the bucket0.2712
qtyInt64contracts(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Total unique traded contract volume in the bucket.exchangeT+12020-01-020 on no-volume rows184
buy_r_qty / sell_r_qty / net_r_qtyInt64contracts(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Retail buy / sell / net contract count within the bucket.modelT+12020-01-020 when no qualifying retail flow24
buy_r_premium / sell_r_premium / net_r_premiumFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Retail dollar premium buy / sell / net for the bucket (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying retail flow31_204.0
buy_r_delta / sell_r_delta / net_r_deltaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Retail buy / sell / net flow as USD notional × delta (deliverable-adjusted). Naturally signed by option type; positive net = net long-delta demand from retail on this contract in the bucket.modelT+12020-01-020.0 when no qualifying retail flow-3_872.55
buy_r_gamma / sell_r_gamma / net_r_gammaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Retail buy / sell / net flow as USD-notional-converted gamma (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying retail flow1_245.30
buy_r_vega / sell_r_vega / net_r_vegaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Retail buy / sell / net flow as USD-notional-converted vega (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying retail flow8_801.40
buy_i_qty / sell_i_qty / net_i_qtyInt64contracts(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Institutional buy / sell / net contract count for the bucket.modelT+12020-01-020 when no qualifying institutional flow98
buy_i_premium / sell_i_premium / net_i_premiumFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Institutional dollar premium buy / sell / net (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying institutional flow112_034.55
buy_i_delta / sell_i_delta / net_i_deltaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Institutional buy / sell / net flow as USD notional × delta (deliverable-adjusted). Positive net = net long-delta demand from the institutional class on this contract in the bucket.modelT+12020-01-020.0 when no qualifying institutional flow89_231.00
buy_i_gamma / sell_i_gamma / net_i_gammaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Institutional buy / sell / net flow as USD-notional-converted gamma (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying institutional flow4_188.70
buy_i_vega / sell_i_vega / net_i_vegaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Institutional buy / sell / net flow as USD-notional-converted vega (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying institutional flow31_204.80
buy_m_qty / sell_m_qty / net_m_qtyInt64contracts(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Market-maker buy / sell / net contract count for the bucket. Includes the residual absorption of customer (retail + institutional) net imbalance on the opposite side.modelT+12020-01-020 when no qualifying market-maker flow-108
buy_m_premium / sell_m_premium / net_m_premiumFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Market-maker dollar premium buy / sell / net (deliverable-adjusted). Mirror of _i + _r on the same row up to floor / rounding.modelT+12020-01-020.0 when no qualifying market-maker flow-143_237.65
buy_m_delta / sell_m_delta / net_m_deltaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Market-maker buy / sell / net flow as USD notional × delta (deliverable-adjusted). Mirror of _i + _r on the same row.modelT+12020-01-020.0 when no qualifying market-maker flow-85_359.00
buy_m_gamma / sell_m_gamma / net_m_gammaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Market-maker buy / sell / net flow as USD-notional-converted gamma (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying market-maker flow-5_433.30
buy_m_vega / sell_m_vega / net_m_vegaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Market-maker buy / sell / net flow as USD-notional-converted vega (deliverable-adjusted).modelT+12020-01-020.0 when no qualifying market-maker flow-40_017.20
floor_qtyInt64contracts(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Total floor-executed contract count within the bucket (direct-negotiation outcry trades on the exchange floor). No directional classification provided; reported for volume completeness.exchangeT+12020-01-020 when no floor prints4
floor_premiumFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Total floor-executed dollar premium within the bucket (deliverable-adjusted). No directional classification provided; reported for volume completeness.exchangeT+12020-01-020.0 when no floor prints4_120.0
floor_deltaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Total floor-executed USD notional × delta within the bucket (deliverable-adjusted). No directional classification provided; reported for volume completeness. Naturally signed.exchangeT+12020-01-020.0 when no floor prints2_349.20
floor_gammaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Total floor-executed USD-notional-converted gamma within the bucket (deliverable-adjusted). No directional classification provided; reported for volume completeness.exchangeT+12020-01-020.0 when no floor prints184.10
floor_vegaFloat64USD(date, ten_min_timeframe, figi, symbol, callput, strikeprice, expirationdate)Total floor-executed USD-notional-converted vega within the bucket (deliverable-adjusted). No directional classification provided; reported for volume completeness.exchangeT+12020-01-020.0 when no floor prints982.60

Join hints

For per-underlying 10-min aggregates (rolled up across every contract on the same (date, ten_min_timeframe, figi)), use underlying_option_flows_10_min. For underlying prices and turnover, join to px_10_min on (date, ten_min_timeframe, figi) and self-adjust using split_factors. For daily rollups of the same contracts, sum across ten_min_timeframe or use option_flows_01_day directly.

Gross-volume accounting

Each trade has a customer side (buy_{r,i}_qty / sell_{r,i}_qty) and a market-maker counterparty (buy_m_qty / sell_m_qty, rebuilt to absorb the customer imbalance so the row-level _i + _r + _m ≈ 0 identity holds on notionals).

DO: aggregate on qty — one entry per trade per bucket.

DON'T: sum the cohort legs (buy_r_qty + sell_r_qty + buy_i_qty + sell_i_qty + buy_m_qty + sell_m_qty + floor_qty) — inflates per bucket volume by ~30-35% because the market-maker leg gets added on top of the customer legs.

underlying_option_flows_01_day

Per-underlying × callput daily aggregation. Same participant-split option-flow columns as option_flows_01_day, rolled up across every contract on the same (date, figi, callput) grain rather than kept at contract resolution. Every underlying has up to two rows per session: callput = 'C' and callput = 'P'. Adds context columns (contract count, aggregate share count, session-end underlying mark, 30-day ATM IV).

  • Grain: (date, figi, callput)
  • History: 2020-01-02 →
  • Timing: T+1 (published between 05:00 and 09:00 UTC on the calendar day after the trading date — Friday's data lands Saturday)

Underlying price, turnover, and metadata are not baked into this table. Join to px_01_day (adjust locally with split_factors) and stock_metadata on figi.

Difference vs option_flows_01_day

option_flows_01_day is per-contract. underlying_option_flows_01_day collapses every contract on (date, figi, callput) into a single row, preserving the call vs put dimension for cross-sectional or callput-aware research (put-only skew, gross calls-vs-puts turnover, callput-aware Δ). Query with callput=CP to have both rows summed back into one at query time; query with callput=C or callput=P for a single side. No contract-level filters here (no dte / abs_delta axes); this table is pre-aggregated inside the pipeline.

Fields

field_nametypeunitgraindescriptionprovenancetiming_laghistory_startnull_semanticsexample
dateDatetrading day (UTC)(date, figi, callput)Session date. US equity trading calendar.exchangeT+12020-01-02never null2026-07-24
figiString(12)identifier(date, figi, callput)OpenFIGI composite FIGI for the underlying. Join key to stock_metadata, px_01_day, split_factors.exchangen/a2020-01-02never nullBBG000MM2P62
traded_underlyingStringticker(date, figi, callput)Point-in-time listed equity ticker on date. Dotted dual-class form (BRK.B).exchangeT+12020-01-02never nullAAPL
callputString(1)option type(date, figi, callput)'C' (call side) or 'P' (put side). When queried with callput=CP the router sums the two rows and emits the literal 'CP' in this column.exchangeT+12020-01-02never nullC
n_contractsUInt32contracts(date, figi, callput)Distinct option contracts that traded on this underlying × callput on date.exchangeT+12020-01-020 on no-volume days184
total_sharesFloat64delta-adjusted share equivalents(date, figi, callput)Sum of all traded contract volume converted to underlying-share equivalents (qty × adj_shares_deliverable), regardless of side.exchangeT+12020-01-020.0 on no-volume days2_384_100.0
last_underpriceFloat64USD(date, figi, callput)Underlying spot price observed against the last option trade of the session. Not split-adjusted (contemporaneous mark).exchangeT+12020-01-02null on no-volume days224.31
last_fwd_underpriceFloat64USD(date, figi, callput)Forward price on the underlying, adjusted for dividends and the risk-free rate. Not adjusted for splits.derivedT+12020-01-02null on no-volume days224.68
atm_iv_30dFloat64annualised vol (0.30 = 30%)(date, figi, callput)30-day at-the-money implied volatility of the underlying, from the last IV surface fit of the session.derivedT+12020-01-02null on no-volume days0.2418
buy_r_shares / sell_r_shares / net_r_sharesFloat64delta-adjusted share equivalents(date, figi, callput)Retail buy / sell / net share equivalents for the (underlying, callput) row.modelT+12020-01-020.0 when no qualifying volume24_100.0
buy_r_premium / sell_r_premium / net_r_premiumFloat64USD(date, figi, callput)Retail dollar premium buy / sell / net.modelT+12020-01-020.0 when no qualifying volume312_040.0
buy_r_delta / sell_r_delta / net_r_deltaFloat64USD(date, figi, callput)Retail buy / sell / net flow as USD notional × delta for the (underlying, callput) row. Naturally signed (calls +, puts −); positive net = net long-delta demand from retail on this call/put side.modelT+12020-01-020.0 when no qualifying volume-3_872_000.55
buy_r_gamma / sell_r_gamma / net_r_gammaFloat64USD(date, figi, callput)Retail buy / sell / net flow as USD-notional-converted gamma.modelT+12020-01-020.0 when no qualifying volume1_245_030.30
buy_r_vega / sell_r_vega / net_r_vegaFloat64USD(date, figi, callput)Retail buy / sell / net flow as USD-notional-converted vega.modelT+12020-01-020.0 when no qualifying volume8_801_240.40
buy_i_shares / sell_i_shares / net_i_sharesFloat64delta-adjusted share equivalents(date, figi, callput)Institutional buy / sell / net share equivalents.modelT+12020-01-020.0 when no qualifying volume128_450.0
buy_i_premium / sell_i_premium / net_i_premiumFloat64USD(date, figi, callput)Institutional dollar premium buy / sell / net.modelT+12020-01-020.0 when no qualifying volume412_034.55
buy_i_delta / sell_i_delta / net_i_deltaFloat64USD(date, figi, callput)Institutional buy / sell / net flow as USD notional × delta for the (underlying, callput) row. Positive net = net long-delta demand from the institutional class on this call/put side.modelT+12020-01-020.0 when no qualifying volume89_231_000.00
buy_i_gamma / sell_i_gamma / net_i_gammaFloat64USD(date, figi, callput)Institutional buy / sell / net flow as USD-notional-converted gamma.modelT+12020-01-020.0 when no qualifying volume4_188_270.70
buy_i_vega / sell_i_vega / net_i_vegaFloat64USD(date, figi, callput)Institutional buy / sell / net flow as USD-notional-converted vega.modelT+12020-01-020.0 when no qualifying volume31_200_480.80
buy_m_shares / sell_m_shares / net_m_sharesFloat64delta-adjusted share equivalents(date, figi, callput)Market-maker buy / sell / net share equivalents. _i + _r + _m = 0 per row.modelT+12020-01-020.0 when no qualifying volume-130_860.0
buy_m_premium / sell_m_premium / net_m_premiumFloat64USD(date, figi, callput)Market-maker dollar premium buy / sell / net.modelT+12020-01-020.0 when no qualifying volume-824_067.10
buy_m_delta / sell_m_delta / net_m_deltaFloat64USD(date, figi, callput)Market-maker buy / sell / net flow as USD notional × delta. Mirror of _i + _r on the same row (identity _i + _r + _m = 0).modelT+12020-01-020.0 when no qualifying volume-85_359_000.00
buy_m_gamma / sell_m_gamma / net_m_gammaFloat64USD(date, figi, callput)Market-maker buy / sell / net flow as USD-notional-converted gamma.modelT+12020-01-020.0 when no qualifying volume-5_433_300.00
buy_m_vega / sell_m_vega / net_m_vegaFloat64USD(date, figi, callput)Market-maker buy / sell / net flow as USD-notional-converted vega.modelT+12020-01-020.0 when no qualifying volume-40_001_720.00

Gross-volume accounting

Each trade has a customer side (buy_{r,i}_shares / sell_{r,i}_shares) and a market-maker counterparty (buy_m_shares / sell_m_shares, rebuilt to absorb the customer imbalance so the row-level _i + _r + _m ≈ 0 identity holds on notionals). Same convention applies to _premium / _delta / _gamma / _vega.

DO: aggregate on total_shares — one entry per trade.

DON'T: sum the cohort legs (buy_r_shares + sell_r_shares + buy_i_shares + sell_i_shares + buy_m_shares + sell_m_shares) — inflates volume by ~30-35% because the market-maker leg gets added on top of the customer legs.

underlying_option_flows_10_min

10-minute-bucketed sibling of underlying_option_flows_01_day. Per-underlying × callput option-flow aggregation at a 10-minute cadence, with contract-count context and session-end marks.

  • Grain: (date, ten_min_timeframe, figi, callput). Up to two rows per underlying per 10-min bucket ('C' and 'P').
  • History: 2020-01-02 →
  • Timing: T+1 (published between 05:00 and 09:00 UTC on the calendar day after the trading date — Friday's data lands Saturday)

Underlying price, turnover, and metadata are not baked into this table. Join to px_10_min (adjust locally with split_factors) and stock_metadata on figi.

Request window is capped at 30 days per call on the REST endpoint. For multi-year intraday history use flat files at gs://prod-masscrest/v0/underlying_option_flows/10_min/.

Bucket boundary convention

ten_min_timeframe is the end of the 10-minute bucket, NY wall-clock, naive DateTime. Bucket covers (ten_min_timeframe − 10 min, ten_min_timeframe]. First RTH bucket: 09:40:00; last: 16:00:00. Same convention as option_flows_10_min and px_10_min; join directly on (date, ten_min_timeframe, figi).

Difference vs option_flows_10_min

option_flows_10_min is per-contract. underlying_option_flows_10_min collapses every contract on (date, ten_min_timeframe, figi, callput) into a single row, keeping the call vs put dimension and adding contract-count / session-mark / IV context columns. This is the intended surface for cross-sectional or callput-aware research.

buy_m_shares / sell_m_shares are for directional cohort analysis (net market-maker positioning on the row), not for gross volume totals. The same convention applies to the _premium, _delta, _gamma, and _vega cohort columns — sum buy_r + sell_r + buy_i + sell_i + buy_m + sell_m and you double-count the market-maker leg on every unit.

Fields

field_nametypeunitgraindescriptionprovenancetiming_laghistory_startnull_semanticsexample
dateDatetrading day (UTC)(date, ten_min_timeframe, figi, callput)Session date.exchangeT+12020-01-02never null2026-07-24
ten_min_timeframeDateTimeNY wall-clock, bucket close(date, ten_min_timeframe, figi, callput)End timestamp of the 10-min bucket (naive NY-time).exchangeT+12020-01-02never null2026-07-24 09:40:00
figiString(12)identifier(date, ten_min_timeframe, figi, callput)OpenFIGI composite FIGI for the underlying.exchangen/a2020-01-02never nullBBG000MM2P62
traded_underlyingStringticker(date, ten_min_timeframe, figi, callput)Point-in-time listed equity ticker on date. Dotted dual-class form.exchangeT+12020-01-02never nullAAPL
callputString(1)option type(date, ten_min_timeframe, figi, callput)'C' or 'P'. callput=CP on the endpoint sums the two rows at query time.exchangeT+12020-01-02never nullC
n_contractsUInt32contracts(date, ten_min_timeframe, figi, callput)Distinct option contracts traded on this underlying × callput within the bucket.exchangeT+12020-01-020 on no-volume buckets48
total_sharesFloat64delta-adjusted share equivalents(date, ten_min_timeframe, figi, callput)Sum of all traded volume converted to share equivalents within the bucket.exchangeT+12020-01-020.0 on no-volume buckets184_500.0
last_underpriceFloat64USD(date, ten_min_timeframe, figi, callput)Underlying spot at the last option trade within the bucket. Not split-adjusted.exchangeT+12020-01-02null on no-volume buckets224.10
last_fwd_underpriceFloat64USD(date, ten_min_timeframe, figi, callput)Forward price on the underlying, adjusted for dividends and the risk-free rate. Not adjusted for splits.derivedT+12020-01-02null on no-volume buckets224.48
atm_iv_30dFloat64annualised vol(date, ten_min_timeframe, figi, callput)30-day at-the-money implied vol at the last option trade of the bucket.derivedT+12020-01-02null on no-volume buckets0.2418
buy_r_shares / sell_r_shares / net_r_sharesFloat64delta-adjusted share equivalents(date, ten_min_timeframe, figi, callput)Retail buy / sell / net share equivalents within the bucket.modelT+12020-01-020.0 when no qualifying volume2_410.0
buy_r_premium / sell_r_premium / net_r_premiumFloat64USD(date, ten_min_timeframe, figi, callput)Retail dollar premium buy / sell / net.modelT+12020-01-020.0 when no qualifying volume31_204.0
buy_r_delta / sell_r_delta / net_r_deltaFloat64USD(date, ten_min_timeframe, figi, callput)Retail buy / sell / net flow as USD notional × delta for the (underlying, callput) row within the bucket. Naturally signed by option type; positive net = net long-delta demand from retail on this call/put side in the bucket.modelT+12020-01-020.0 when no qualifying volume-387_200.55
buy_r_gamma / sell_r_gamma / net_r_gammaFloat64USD(date, ten_min_timeframe, figi, callput)Retail buy / sell / net flow as USD-notional-converted gamma for the bucket.modelT+12020-01-020.0 when no qualifying volume124_530.30
buy_r_vega / sell_r_vega / net_r_vegaFloat64USD(date, ten_min_timeframe, figi, callput)Retail buy / sell / net flow as USD-notional-converted vega for the bucket.modelT+12020-01-020.0 when no qualifying volume880_140.40
buy_i_shares / sell_i_shares / net_i_sharesFloat64delta-adjusted share equivalents(date, ten_min_timeframe, figi, callput)Institutional buy / sell / net share equivalents. _i + _r + _m = 0 per row.modelT+12020-01-020.0 when no qualifying volume12_845.0
buy_i_premium / sell_i_premium / net_i_premiumFloat64USD(date, ten_min_timeframe, figi, callput)Institutional dollar premium buy / sell / net.modelT+12020-01-020.0 when no qualifying volume41_203.55
buy_i_delta / sell_i_delta / net_i_deltaFloat64USD(date, ten_min_timeframe, figi, callput)Institutional buy / sell / net flow as USD notional × delta for the bucket. Positive net = net long-delta demand from the institutional class on this call/put side in the bucket.modelT+12020-01-020.0 when no qualifying volume8_923_100.00
buy_i_gamma / sell_i_gamma / net_i_gammaFloat64USD(date, ten_min_timeframe, figi, callput)Institutional buy / sell / net flow as USD-notional-converted gamma for the bucket.modelT+12020-01-020.0 when no qualifying volume418_870.70
buy_i_vega / sell_i_vega / net_i_vegaFloat64USD(date, ten_min_timeframe, figi, callput)Institutional buy / sell / net flow as USD-notional-converted vega for the bucket.modelT+12020-01-020.0 when no qualifying volume3_120_080.80
buy_m_shares / sell_m_shares / net_m_sharesFloat64delta-adjusted share equivalents(date, ten_min_timeframe, figi, callput)Market-maker buy / sell / net share equivalents.modelT+12020-01-020.0 when no qualifying volume-13_086.0
buy_m_premium / sell_m_premium / net_m_premiumFloat64USD(date, ten_min_timeframe, figi, callput)Market-maker dollar premium buy / sell / net.modelT+12020-01-020.0 when no qualifying volume-82_406.71
buy_m_delta / sell_m_delta / net_m_deltaFloat64USD(date, ten_min_timeframe, figi, callput)Market-maker buy / sell / net flow as USD notional × delta for the bucket. Mirror of _i + _r on the same row (identity _i + _r + _m = 0).modelT+12020-01-020.0 when no qualifying volume-8_535_900.00
buy_m_gamma / sell_m_gamma / net_m_gammaFloat64USD(date, ten_min_timeframe, figi, callput)Market-maker buy / sell / net flow as USD-notional-converted gamma for the bucket.modelT+12020-01-020.0 when no qualifying volume-543_330.00
buy_m_vega / sell_m_vega / net_m_vegaFloat64USD(date, ten_min_timeframe, figi, callput)Market-maker buy / sell / net flow as USD-notional-converted vega for the bucket.modelT+12020-01-020.0 when no qualifying volume-4_001_720.00

Gross-volume accounting

Each trade has a customer side (buy_{r,i}_shares / sell_{r,i}_shares) and a market-maker counterparty (buy_m_shares / sell_m_shares, rebuilt to absorb the customer imbalance so the row-level _i + _r + _m ≈ 0 identity holds on notionals). Same convention applies to _premium / _delta / _gamma / _vega.

DO: aggregate on total_shares — one entry per trade per bucket.

DON'T: sum the cohort legs (buy_r_shares + sell_r_shares + buy_i_shares + sell_i_shares + buy_m_shares + sell_m_shares) — inflates per bucket volume by ~30-35% because the market-maker leg gets added on top of the customer legs.

underlying_flow_signal_01_day

Daily flow-signal derivatives computed on the underlying-level nets (net_i_*, net_r_*, greeks included) from underlying_option_flows_01_day. Per-(date, figi, callput) grain. All fields are trailing-window transforms (no forward-looking information), so a row at date = D is safe to use for a decision at close of D (or, more conservatively, at open of D+1).

  • Grain: (date, figi, callput) where callput ∈ {'C', 'P', 'CP'}.
  • History: 2020-01-02 →
  • Timing: T+1 (published between 05:00 and 09:00 UTC on the calendar day after the trading date — Friday's data lands Saturday; same publish window as flows).

Signal design

This is an in-house Masscrest signal built to flag options activity that is extreme against a stock's own history. Each raw daily net is summed over a 21-trading-day trailing window and then z-scored against its own 2-year rolling mean and standard deviation, scoped to (figi, callput).

  • _21dma = 21-trading-day rolling SUM of the raw daily net, PARTITION BY (figi, callput). (Historically shipped as a rolling mean; arithmetically equivalent for the z-score, since the sum is just the mean multiplied by 21. The distributed series is now the sum.)
  • z_net_*_21dma = z-score of _21dma against its 2-year (504 trading day) trailing mean and sample standard deviation, computed at query time. Minimum 63 non-null observations in the trailing window required, else null. Rounded to 2 decimals on emit.

The raw rolling μ and σ series are computed on the fly at query time and are not distributed as separate columns — only the resulting z-score ships.

The calculation is deliberately simple. Options activity is not stationary across stocks or over time, and z-scores can reach unrealistic multiples of 3 SD during earnings, M&A, or other catalysts. Masscrest does not cap, winsorise, or otherwise regularise the output: keeping the formula plain avoids overfitting to any specific regime and lets clients replicate the transform end-to-end from underlying_option_flows_01_day if they want to. Validation of the single-name spike predicate lives at /research/systematic-single-stock-signal; the industry-aggregated version and its long-short performance live at /research/industry-flow-signal.

callput semantics

Every row is scoped to one callput bucket:

  • C: calls only. Raw nets and greeks are direct sums of the call-side contracts.
  • P: puts only. Raw nets and greeks are direct sums of the put-side contracts.
  • CP: pre-computed combined view. Nets (shares / premium / delta) are C − P because bullish call flow and bearish put flow both express bullish conviction, so the nets combine sign-flipped. Greeks (gamma / vega) are C + P because they are naturally signed by option type (calls +, puts −) and summing captures total book exposure.

Rolling windows PARTITION BY (figi, callput); each callput row has its own independent trailing 21dma and 2y baseline, so z(C) + z(P) ≠ z(CP). Filter to exactly one callput per query or triple-count.

Warm-up

All fields are computed from 2020-01-02 forward. The 21-day rolling sum needs 21 non-null daily nets; the z-score needs ≥ 63 non-null observations in the trailing 504-session (2-year) window and emits NULL until that threshold is met.

Transform chain: daily net → 21-day trailing sum (_21dma) → z-score against 2y rolling μ/σ of the 21dma (z_*).

Fire semantics live at the consumer

The signal table does not persist signal_* (2σ / 3σ fire) or hp_* (22-day holding-period) columns; derive them at query time from the z-scores. Masscrest's canonical spike predicate is:

WHERE z_net_i_delta_21dma > 2 AND callput = 'CP'    -- 2σ institutional-delta spike
WHERE z_net_i_delta_21dma > 3 AND callput = 'CP'    -- 3σ institutional-delta spike

Both retail (net_r_*) and institutional (net_i_*) transforms ship. By convention Masscrest treats large institutional-side deviations as the primary tradable spike; the retail-side z-scores are available for symmetric analysis (crowded-consensus vs smart-money-vs-retail divergence checks, see the crowding_tag in the taxonomy resource).

Adding liquidity and stock-return context

This table only carries the signal itself, not stock liquidity or stock returns. If you want to filter for liquid names (e.g. "ignore anything under $10M average daily dollar turnover") or overlay stock returns for validation, join px_01_day on (date, figi). Dollar turnover (turnover) can be averaged directly; for split-adjusted returns from close, also join split_factors.

Fields

field_nametypeunitgraindescriptionprovenancetiming_laghistory_startnull_semanticsexample
dateDatetrading day (UTC)(date, figi, callput)Session date. US equity trading calendar.exchangeT+12020-01-02never null2026-07-24
figiString(12)identifier(date, figi, callput)OpenFIGI composite FIGI for the underlying. Stable across ticker changes; join key to stock_metadata, px_01_day, split_factors.exchangen/a2020-01-02never nullBBG000MM2P62
underlying_tickerStringticker(date, figi, callput)Point-in-time listed equity ticker on date. Dotted dual-class form (BRK.B). Named underlying_ticker here rather than traded_underlying; the API SELECT aliases it back to traded_underlying on the wire.exchangeT+12020-01-02never nullAAPL
callputLowCardinality(String)enum(date, figi, callput)One of 'C', 'P', 'CP'. See the callput note above for the CP synthesis rule.derivedT+12020-01-02never nullCP
net_i_sharesFloat64underlying-share equivalents(date, figi, callput)Institutional net share-equivalent flow for the callput bucket. Calculated as sum(net_i_qty × adj_shares_deliverable): the underlying-share equivalent of the traded contracts (standard OCC contract = 100 shares, adjusted for corporate actions). On callput='CP' rows: C − P (bullish-conviction sign convention).modelT+12020-01-020.0 when no qualifying institutional flow18_450.0
net_r_sharesFloat64underlying-share equivalents(date, figi, callput)Retail net share-equivalent flow. Same formula (sum(net_r_qty × adj_shares_deliverable)) and CP-synthesis rule as net_i_shares.modelT+12020-01-020.0 when no qualifying retail flow4_120.0
net_i_premiumFloat64USD(date, figi, callput)Institutional net dollar premium flow. On CP rows: C − P.modelT+12020-01-020.0 when no qualifying institutional flow1_820_038.55
net_r_premiumFloat64USD(date, figi, callput)Retail net dollar premium flow.modelT+12020-01-020.0 when no qualifying retail flow312_040.0
net_i_deltaFloat64USD(date, figi, callput)Institutional net flow as USD notional × delta. Naturally signed; positive = net long-delta demand. On CP rows: C − P.modelT+12020-01-020.0 when no qualifying institutional flow12_530_000.00
net_r_deltaFloat64USD(date, figi, callput)Retail net flow as USD notional × delta.modelT+12020-01-020.0 when no qualifying retail flow-870_000.00
net_i_gammaFloat64USD × gamma(date, figi, callput)Institutional net flow as USD notional × gamma. On CP rows: C + P (gamma is naturally signed by option type). Positive = net gamma demand.modelT+12020-01-020.0 when no qualifying institutional flow84_000.0
net_r_gammaFloat64USD × gamma(date, figi, callput)Retail net gamma flow.modelT+12020-01-020.0 when no qualifying retail flow12_100.0
net_i_vegaFloat64USD × vega(date, figi, callput)Institutional net flow as USD notional × vega. On CP rows: C + P. Positive = net long-vol demand.modelT+12020-01-020.0 when no qualifying institutional flow1_400_000.0
net_r_vegaFloat64USD × vega(date, figi, callput)Retail net vega flow.modelT+12020-01-020.0 when no qualifying retail flow-210_000.0
net_i_shares_21dmaFloat64underlying-share equivalents(date, figi, callput)Rolling sum of net_i_shares over the prior 21 trading days (inclusive of date), scoped to this callput.derivedT+12020-01-02null when the trailing 21-day window has fewer than 21 non-null observations319_284.0
net_r_shares_21dmaFloat64underlying-share equivalents(date, figi, callput)Rolling 21-day sum of net_r_shares.derivedT+12020-01-02null when the trailing 21-day window has fewer than 21 non-null observations68_040.0
net_i_premium_21dmaFloat64USD(date, figi, callput)Rolling 21-day sum of net_i_premium.derivedT+12020-01-02null when the trailing 21-day window has fewer than 21 non-null observations25_294_500.0
net_r_premium_21dmaFloat64USD(date, figi, callput)Rolling 21-day sum of net_r_premium.derivedT+12020-01-02null when the trailing 21-day window has fewer than 21 non-null observations5_040_252.0
net_i_delta_21dmaFloat64USD(date, figi, callput)Rolling 21-day sum of net_i_delta.derivedT+12020-01-02null when the trailing 21-day window has fewer than 21 non-null observations206_220_000.0
net_r_delta_21dmaFloat64USD(date, figi, callput)Rolling 21-day sum of net_r_delta.derivedT+12020-01-02null when the trailing 21-day window has fewer than 21 non-null observations-13_020_000.0
net_i_gamma_21dmaFloat64USD × gamma(date, figi, callput)Rolling 21-day sum of net_i_gamma.derivedT+12020-01-02null when the trailing 21-day window has fewer than 21 non-null observations1_512_000.0
net_r_gamma_21dmaFloat64USD × gamma(date, figi, callput)Rolling 21-day sum of net_r_gamma.derivedT+12020-01-02null when the trailing 21-day window has fewer than 21 non-null observations218_400.0
net_i_vega_21dmaFloat64USD × vega(date, figi, callput)Rolling 21-day sum of net_i_vega.derivedT+12020-01-02null when the trailing 21-day window has fewer than 21 non-null observations24_780_000.0
net_r_vega_21dmaFloat64USD × vega(date, figi, callput)Rolling 21-day sum of net_r_vega.derivedT+12020-01-02null when the trailing 21-day window has fewer than 21 non-null observations-3_780_000.0
z_net_i_shares_21dmaFloat64z-score(date, figi, callput)Z-score of net_i_shares_21dma against its trailing 2-year rolling mean and sample standard deviation, computed at query time. Rounded to 2 decimals on emit.derivedT+12020-01-02null when the trailing 2y window has fewer than 63 non-null observations or its sample stddev is 01.34
z_net_r_shares_21dmaFloat64z-score(date, figi, callput)Same construction as z_net_i_shares_21dma, applied to the retail 21dma.derivedT+12020-01-02null when the trailing 2y window is unwarmed or its sample stddev is 00.42
z_net_i_premium_21dmaFloat64z-score(date, figi, callput)Standardised institutional-premium 21dma vs its 2y baseline.derivedT+12020-01-02null when the trailing 2y window is unwarmed or its sample stddev is 01.18
z_net_r_premium_21dmaFloat64z-score(date, figi, callput)Standardised retail-premium 21dma vs its 2y baseline.derivedT+12020-01-02null when the trailing 2y window is unwarmed or its sample stddev is 0-0.24
z_net_i_delta_21dmaFloat64z-score(date, figi, callput)Standardised institutional-delta 21dma vs its 2y baseline. Masscrest's canonical "spike" predicate is z > 2 (2σ) or z > 3 (3σ) with callput = 'CP'.derivedT+12020-01-02null when the trailing 2y window is unwarmed or its sample stddev is 02.08
z_net_r_delta_21dmaFloat64z-score(date, figi, callput)Standardised retail-delta 21dma vs its 2y baseline.derivedT+12020-01-02null when the trailing 2y window is unwarmed or its sample stddev is 0-0.55
z_net_i_gamma_21dmaFloat64z-score(date, figi, callput)Standardised institutional-gamma 21dma vs its 2y baseline.derivedT+12020-01-02null when the trailing 2y window is unwarmed or its sample stddev is 00.86
z_net_r_gamma_21dmaFloat64z-score(date, figi, callput)Standardised retail-gamma 21dma vs its 2y baseline.derivedT+12020-01-02null when the trailing 2y window is unwarmed or its sample stddev is 00.11
z_net_i_vega_21dmaFloat64z-score(date, figi, callput)Standardised institutional-vega 21dma vs its 2y baseline. Masscrest's ≤ −3σ vega tail is historically followed by ~1.6 vol-point IV declines over 21 days (see research/vega-iv-signal).derivedT+12020-01-02null when the trailing 2y window is unwarmed or its sample stddev is 0-1.42
z_net_r_vega_21dmaFloat64z-score(date, figi, callput)Standardised retail-vega 21dma vs its 2y baseline.derivedT+12020-01-02null when the trailing 2y window is unwarmed or its sample stddev is 00.30

Z-score precision

z_* fields are rounded to 2 decimals on emit. When comparing a client-side derived spike predicate (z > 2) against the rounded value, expect edge cases on low-vol names where the true unrounded z sits just under 2.00 but the rounded value equals 2.00.

CP synthesis is pre-computed, not query-time

The 'CP' rows are materialised at publish time, not derived at query time. Each (figi, callput) bucket has its own independent 21dma and 2y baseline trajectory; z_net_i_delta_21dma on the CP row is derived from CP's own trailing series, not from summing per-side z-scores.

Deriving fire flags and holding periods

The table does not persist pre-computed fire flags (e.g. "2σ spike today = TRUE") or holding-period booleans. Compute them at query time from the z-scores: WHERE z_net_i_delta_21dma > 2 AND callput = 'CP' for the canonical 2σ institutional-delta spike. For a 22-day holding-period rule (fire within the last N days and the same z has not gone negative since), a self-join or a rolling max(z) window on the same series is enough.

grouped_flow_signal_01_day

Group-level daily flow-signal derivatives: sector / industry / etf / adr / single_stock / all rollups of the underlying-level nets and greeks aggregated in underlying_flow_signal_01_day. Same 50-flow-metric shape across 5 layers × 10 metrics. Per-(date, group_key, callput) grain.

  • Grain: (date, group_key, callput)
  • History: 2020-01-02 →
  • Timing: T+1 (published shortly after underlying_flow_signal_01_day on the option-analytics workflow, typically 06:00-11:00 UTC on the calendar day after the trading date — Friday's data lands Saturday).

Response-shape rewrite (2026-08-14)

Every row is keyed by a single group_key STRING column. group_type on the REST endpoint takes the same set of canonical values (case-insensitive on input) — a mixed-case sector name (e.g. "Communication Services"), a mixed-case industry name (e.g. "Semiconductors"), or one of the rollup literals "etf" / "adr" / "single_stock" / "all". The row echoes that value back in group_key.

Group semantics

Query group_typegroup_key shape (on the row)Row count per (date, callput)Notes
sectorCanonical mixed-case sector name (e.g. Technology, Financial Services)~11 distinct valuesExcludes ETFs and ADRs. Figis missing a sector label drop from these rows.
industryCanonical mixed-case industry name (e.g. Semiconductors, Software - Application)~150 distinct values (matches the FMP industry taxonomy)Excludes ETFs and ADRs. Same drop-on-missing rule. Every industry-day is emitted (gap-free time series) with n_figi populated for downstream filtering — see "Thin-industry handling" below.
etfLiteral string etf1Aggregate over all figis with isEtf='true' in the latest-known phase (live or delisted).
adrLiteral string adr1Aggregate over all figis with isAdr='true' in the latest-known phase (live or delisted).
single_stockLiteral string single_stock1Everything else (non-ETF, non-ADR). Figis missing from stock_metadata are treated as single_stock.
allLiteral string all1Every figi in the flow universe, no filter.

Why sector / industry exclude ETFs and ADRs

FMP tags each ETF's sector with the issuer's legal-entity sector (typically Financial Services for the sponsor, not the underlying-exposure sector). Pooling ETFs into sector rollups inflated Financial Services gross flow by 85%+ — meaningless for cohort analysis. Same-shape distortion for ADRs. The 2026-08-11 pipeline change excludes both from sector and industry aggregations; they get their own top-level etf and adr branches. As a consequence, sum(sector) ≠ all — the residual is precisely the etf + adr contribution.

Valid sector values

The ~11 distinct group_key strings emitted when group_type = 'sector'. Case-insensitive on input; canonical mixed-case on the wire. As-of 2026-08-14 snapshot; may drift as figis are reclassified or new sectors are added.

SectorDescription
Basic MaterialsCompanies extracting and processing raw commodities used across the economy: metals and mining, chemicals, forestry, and construction materials. Cyclically exposed to global industrial demand.
Communication ServicesFirms that carry information or entertainment to end users: telecom carriers, media conglomerates, interactive-media platforms, publishers, and gaming. Combines legacy telecom with digital-native content.
Consumer CyclicalBusinesses selling discretionary goods and services whose demand rises and falls with the business cycle: automakers, apparel, restaurants, travel, leisure, and specialty retail. Highly sensitive to consumer confidence.
Consumer DefensiveProducers of everyday essentials that consumers buy regardless of the economy: packaged food, beverages, tobacco, household staples, and discount retailers. Stable revenue through downturns.
EnergyThe oil, gas, coal and related supply chain: exploration and production, refining, midstream infrastructure, and oilfield services. Tied to commodity prices and global demand cycles.
Financial ServicesFirms that intermediate capital: banks, insurers, asset managers, brokerages, exchanges, and credit-services companies. Rate-sensitive and cycle-exposed.
HealthcareBusinesses across the health value chain: drug and biotech developers, medical-device makers, diagnostics labs, healthcare providers, health insurers, and pharma distributors. Mix of defensive demand and secular growth.
IndustrialsCapital goods and services that keep the economy moving: aerospace, defence, machinery, transportation, construction, and logistics. Cyclically exposed to capex spending.
Real EstateProperty-owning and property-servicing companies, dominated by REITs across residential, retail, office, industrial, healthcare, and specialty sub-types. Highly interest-rate-sensitive.
TechnologyHardware and software firms including semiconductors, application and infrastructure software, IT services, computer hardware, and consumer electronics. Growth-biased, with high cyclicality on the semiconductor side.
UtilitiesRegulated and independent providers of electricity, natural gas, water, and renewable power. Defensive cash flows, bond-like return profile.

Valid industry values

The ~150 distinct group_key strings emitted when group_type = 'industry'. Case-insensitive on input; canonical mixed-case on the wire. As-of 2026-08-14 snapshot; may drift as existing figis are reclassified or new industries are added. For the live authoritative list, call the grouped-flow-signal endpoint with group_type=industry and take the distinct group_key values from the response.

Every industry with any flow on a given date is emitted (2026-08-28 revision — the time series is gap-free). Every industry-day carries populated raw daily nets AND populated rolling / z fields; n_figi is exposed on the row so consumers can filter thin-breadth days at query time. See "Thin-industry handling" below for the z-magnitude comparability caveat on structurally-thin industries such as Copper, Uranium, Consulting Services, Regulated Water, Conglomerates, Waste Management, and Railroads.

IndustryDescription
Advertising AgenciesAgencies that plan, create and place advertising across traditional and digital channels for brand and direct-response clients.
Aerospace & DefenseManufacturers of civil aircraft, military platforms, missiles, satellites and defence-electronics systems for governments and commercial airlines.
Agricultural - MachineryProducers of tractors, combines, harvesters and other equipment used in commercial farming.
Agricultural Farm ProductsGrowers, processors and marketers of grains, oilseeds, produce, meat and other primary agricultural output.
Agricultural InputsSuppliers of seeds, fertilisers, pesticides and other inputs consumed by farms.
Airlines, Airports & Air ServicesPassenger and cargo airlines, airport operators, ground-handling firms and other air-transport service providers.
AluminumMiners, smelters and rolled-product manufacturers of aluminium.
Apparel - Footwear & AccessoriesDesigners and makers of shoes, handbags, jewellery, watches and other fashion accessories.
Apparel - ManufacturersCompanies that design and produce clothing lines under owned or licensed brands.
Apparel - RetailRetailers selling clothing and accessories through stores or online, including specialty and off-price chains.
Asset ManagementFirms managing pooled investment vehicles (mutual funds, ETFs, separate accounts) across broad multi-asset strategies.
Asset Management - BondsManagers specialising in fixed-income mutual funds and bond ETFs.
Asset Management - CryptocurrencyManagers of cryptocurrency-linked funds, trusts and ETFs.
Asset Management - GlobalManagers of internationally-diversified equity and multi-asset funds.
Asset Management - IncomeManagers focused on dividend-equity and income-oriented fund products.
Asset Management - LeveragedSponsors of leveraged and inverse ETFs.
Auto - DealershipsRetailers of new and used vehicles, plus related service, parts and financing operations.
Auto - ManufacturersOEMs producing passenger cars, trucks and, increasingly, electric vehicles.
Auto - PartsSuppliers of components, systems and modules used in vehicle assembly and aftermarket repair.
Auto - Recreational VehiclesMakers of RVs, motorcycles, boats and other personal-use motorised recreational vehicles.
Banks - DiversifiedLarge multi-line banks combining consumer, commercial, investment-banking and wealth divisions.
Banks - RegionalBanks concentrated in a specific geographic footprint, focused on retail deposits and commercial lending.
Beverages - AlcoholicBrewers, distillers and vintners producing beer, spirits and packaged alcoholic drinks at scale.
Beverages - Non-AlcoholicProducers of soft drinks, bottled water, juices, energy drinks and other non-alcoholic packaged beverages.
Beverages - Wineries & DistilleriesCraft and specialty wine and spirits producers, generally smaller-scale than the mass-market alcoholic-beverage majors.
BiotechnologyCompanies developing novel therapeutics using biological processes, typically pre-commercial or single-product-focused.
BroadcastingOwners of TV and radio stations, local networks and syndicated content distributors.
Business Equipment & SuppliesManufacturers of office equipment, printers, copiers and workplace supplies.
ChemicalsProducers of commodity petrochemicals, plastics, industrial gases and basic chemical intermediates.
Chemicals - SpecialtyProducers of higher-value differentiated chemicals used in coatings, adhesives, catalysts and formulations.
CoalMiners and marketers of thermal and metallurgical coal.
Communication EquipmentMakers of network hardware, routers, switches and telecom infrastructure gear.
Computer HardwareManufacturers of PCs, servers, storage systems and other computing hardware.
ConglomeratesMulti-industry holding companies spanning several unrelated business lines.
ConstructionGeneral contractors and construction firms building commercial, industrial and infrastructure projects.
Construction MaterialsProducers of cement, aggregates, gypsum, insulation and other bulk building materials.
Consulting ServicesManagement, technology, HR and strategy consulting firms selling professional advisory services.
Consumer ElectronicsMakers of smartphones, tablets, audio devices, wearables and other consumer-facing electronic products.
CopperMiners and processors of copper ore and refined copper products.
Department StoresMulti-category retailers under a single store format spanning apparel, home and accessories.
Discount StoresMass-market retailers competing primarily on price across broad general merchandise.
Diversified UtilitiesUtilities operating across multiple regulated categories (electric, gas, water) rather than a single service.
Drug Manufacturers - GeneralLarge pharmaceutical companies with broad marketed portfolios and multi-therapeutic pipelines.
Drug Manufacturers - Specialty & GenericPharma companies focused on generic drugs, biosimilars or niche specialty therapeutic areas.
Education & Training ServicesFor-profit universities, career schools, tutoring and corporate training providers.
Electrical Equipment & PartsManufacturers of electrical components, motors, transformers, cables and industrial electrical systems.
Electronic Gaming & MultimediaVideo-game publishers, developers, esports operators and interactive-entertainment firms.
Engineering & ConstructionFirms providing engineering-design and heavy-construction services for large infrastructure projects.
EntertainmentFilm, TV and content-production studios and integrated entertainment companies.
Environmental ServicesWaste-water treatment, environmental consulting and remediation service providers.
Financial - Capital MarketsInvestment banks, broker-dealers and trading firms operating in equity, fixed-income and derivatives markets.
Financial - ConglomeratesDiversified financial holding companies spanning several finance subsectors.
Financial - Credit ServicesConsumer-finance companies, credit-card networks, payment processors and buy-now-pay-later providers.
Financial - Data & Stock ExchangesExchange operators, index providers, financial-data vendors and market-infrastructure firms.
Financial - DiversifiedMiscellaneous financial-services companies that do not fit the more specific finance subsectors.
Financial - MortgagesMortgage originators, servicers and secondary-market intermediaries.
Food ConfectionersProducers of chocolate, candy, chewing gum and other confectionery products.
Food DistributionWholesalers distributing food and related goods to restaurants, retailers and institutional customers.
Furnishings, Fixtures & AppliancesMakers of home furniture, bedding, kitchen appliances and household fixtures.
Gambling, Resorts & CasinosOperators of casinos, integrated resorts and online-gambling platforms.
General TransportationDiversified transportation companies that do not fit the more specific rail, trucking or air subsectors.
GoldMiners and refiners of gold ore and physical gold, plus gold-focused streaming and royalty firms.
Grocery StoresTraditional supermarkets and grocery chains selling food and household goods.
Hardware, Equipment & PartsGeneral hardware and industrial-equipment manufacturers and distributors.
Home ImprovementBig-box home-improvement retailers and specialty tool, paint and hardware chains.
Household & Personal ProductsManufacturers of cleaning products, personal-care items and consumer packaged household goods.
Independent Power ProducersNon-utility power generators selling electricity into wholesale or contracted markets.
Industrial - DistributionWholesalers of industrial equipment, parts, fasteners and MRO supplies.
Industrial - Infrastructure OperationsOperators of ports, pipelines, terminals and other industrial-infrastructure assets.
Industrial - MachineryManufacturers of heavy machinery for construction, mining, agriculture and industrial processes.
Industrial - Pollution & Treatment ControlsProviders of air, water and industrial pollution-control equipment and services.
Industrial - SpecialtiesSpecialty industrial firms in niche categories that do not fit broader industrial subsectors.
Industrial MaterialsProducers of steel-alternatives, industrial ceramics, composites and other engineered materials.
Information Technology ServicesIT-consulting, systems-integration, outsourcing and managed-services firms.
Insurance - BrokersInsurance and reinsurance brokers acting as intermediaries between clients and underwriters.
Insurance - DiversifiedMulti-line insurers writing across life, P&C and other coverage types.
Insurance - LifeInsurers focused on individual and group life insurance, plus annuity products.
Insurance - Property & CasualtyInsurers writing property, auto, liability and other short-tail coverage.
Insurance - ReinsuranceFirms providing insurance to primary insurers to cover concentrated or catastrophic risk.
Insurance - SpecialtyInsurers focused on niche or hard-to-place risks (marine, aviation, cyber, professional liability).
Integrated Freight & LogisticsMulti-modal freight and logistics operators spanning trucking, rail, air and ocean.
Internet Content & InformationDigital-content platforms, search engines, social networks and online-information providers.
Investment - Banking & Investment ServicesFull-service investment banks and firms providing M&A, underwriting and advisory services.
LeisureManufacturers of leisure goods, hobby products, toys and personal-recreation equipment.
Luxury GoodsProducers of high-end fashion, jewellery, watches, leather goods and other luxury consumer categories.
Manufacturing - Metal FabricationFirms fabricating metal parts, structures and assemblies for industrial and consumer use.
Manufacturing - MiscellaneousDiversified manufacturers that do not fit more specific industrial subsectors.
Manufacturing - TextilesProducers of yarn, fabric and finished textiles for apparel and industrial applications.
Manufacturing - Tools & AccessoriesMakers of power tools, hand tools and related industrial and consumer tool accessories.
Marine ShippingOcean-freight carriers operating tankers, dry-bulk vessels and container ships.
Media & EntertainmentDiversified media conglomerates spanning television, film, publishing and digital content.
Medical - Care FacilitiesHospital operators, nursing-home chains and specialty care-facility providers.
Medical - DevicesManufacturers of medical implants, surgical tools, monitoring and therapeutic devices.
Medical - Diagnostics & ResearchClinical-diagnostics labs, life-science research tools and diagnostic-imaging providers.
Medical - DistributionWholesale distributors of drugs, medical supplies and healthcare products.
Medical - Equipment & ServicesProviders of medical equipment plus related installation, maintenance and services.
Medical - Healthcare Information ServicesHealth-IT firms providing electronic health records, clinical software and data-analytics platforms.
Medical - Healthcare PlansManaged-care organisations and health-insurance plans covering employer, individual and government populations.
Medical - Instruments & SuppliesManufacturers of medical instruments, consumables and disposable healthcare supplies.
Medical - PharmaceuticalsBroadly-focused pharmaceutical companies not classified under the more specific drug-manufacturer subsectors.
Medical - SpecialtiesSpecialty medical companies operating in niche healthcare categories.
Oil & Gas DrillingContract drilling firms operating onshore and offshore rigs for oil-and-gas producers.
Oil & Gas EnergyDiversified oil-and-gas firms that do not fit the more specific upstream, midstream or downstream categories.
Oil & Gas Equipment & ServicesOilfield-services providers offering drilling, completion and reservoir-management equipment and expertise.
Oil & Gas Exploration & ProductionUpstream producers exploring for and producing crude oil and natural gas.
Oil & Gas IntegratedIntegrated majors operating across upstream, midstream and downstream oil-and-gas businesses.
Oil & Gas MidstreamPipelines, storage terminals and processing operators moving oil and gas from wellhead to market.
Oil & Gas Refining & MarketingDownstream refiners and fuel-marketing companies converting crude into gasoline, diesel and petrochemical feedstocks.
Other Precious MetalsMiners of platinum, palladium and other precious metals not classified under gold or silver.
Packaged FoodsProducers of branded packaged and processed foods sold through retail and foodservice channels.
Packaging & ContainersManufacturers of paper, plastic, glass and metal packaging for consumer and industrial products.
Paper, Lumber & Forest ProductsProducers of pulp, paper, lumber and other wood-based industrial and consumer products.
Personal Products & ServicesPersonal-care product makers, beauty firms and personal-services businesses.
PublishingPublishers of books, newspapers, magazines and other periodical content in print and digital.
REIT - DiversifiedREITs owning property portfolios spanning multiple real-estate categories.
REIT - Healthcare FacilitiesREITs owning hospitals, medical-office buildings, senior housing and other healthcare properties.
REIT - Hotel & MotelREITs owning hotel, motel and hospitality properties.
REIT - IndustrialREITs owning warehouses, logistics facilities and light-industrial properties.
REIT - MortgageMortgage REITs earning spread on residential and commercial mortgage assets rather than owning property directly.
REIT - OfficeREITs owning office buildings across urban and suburban markets.
REIT - ResidentialREITs owning apartment buildings, single-family rentals and manufactured-home communities.
REIT - RetailREITs owning shopping malls, strip centres and standalone retail properties.
REIT - SpecialtyREITs owning niche property types such as data centres, cell towers, self-storage and infrastructure.
RailroadsClass I and short-line freight-rail operators plus passenger-rail companies.
Real Estate - DevelopmentProperty developers building residential, commercial and mixed-use projects for sale or lease.
Real Estate - DiversifiedDiversified real-estate operating companies not structured as REITs.
Real Estate - ServicesReal-estate brokerages, property-management firms and title-and-appraisal services.
Regulated ElectricRate-regulated electric utilities serving retail customers in defined service territories.
Regulated GasRate-regulated natural-gas distribution utilities.
Regulated WaterRate-regulated water and wastewater utilities.
Renewable UtilitiesUtilities and power generators focused on solar, wind, hydro and other renewable-energy assets.
Rental & Leasing ServicesFirms renting equipment, vehicles and other assets to industrial and consumer customers.
Residential ConstructionHomebuilders constructing single-family homes and residential communities for sale.
RestaurantsRestaurant operators and franchisors across quick-service, casual-dining and fine-dining categories.
Security & Protection ServicesProviders of guarding, cash-in-transit, alarm-monitoring and security-technology services.
SemiconductorsDesigners and manufacturers of integrated circuits, memory, logic chips and related semiconductor equipment.
Shell CompaniesPublicly-listed holding entities without significant operations, often SPACs or blank-check vehicles.
SilverMiners and refiners of silver ore and silver products.
Software - ApplicationVendors of packaged and SaaS application software for business, industry-vertical and consumer use.
Software - InfrastructureVendors of infrastructure software including databases, operating systems, security and developer tools.
Software - ServicesSoftware-enabled services firms delivering platform-hosted business solutions.
SolarManufacturers of solar panels, inverters and installers of solar-power systems.
Specialty Business ServicesBusiness-services firms in specialty categories (data processing, marketing services, testing, inspection).
Specialty RetailRetailers focused on specific product categories such as electronics, sporting goods, jewellery or auto parts.
Staffing & Employment ServicesTemporary-staffing, executive-search and human-capital-management firms.
SteelIntegrated and mini-mill producers of carbon and specialty steel products.
Technology DistributorsDistributors of IT hardware, software and networking products to resellers and enterprise buyers.
Telecommunications ServicesWireless and wireline telecom carriers providing voice and data services to consumers and businesses.
TobaccoCigarette manufacturers plus producers of cigars, smokeless tobacco and next-generation nicotine products.
Travel LodgingHotel and lodging operators and franchisors across the value, mid-scale and luxury segments.
Travel ServicesOnline travel agencies, tour operators and travel-booking platforms.
TruckingLong-haul and less-than-truckload freight-trucking companies.
UraniumMiners of uranium ore and producers of nuclear-fuel feedstock.
Waste ManagementSolid-waste collection, disposal, recycling and hazardous-waste management firms.

Signal formula

  • _21dma = 21-trading-day rolling SUM of the raw group-level net, PARTITION BY (group_key, callput). (Historically shipped as a rolling mean; arithmetically equivalent for the z-score, since the sum is just the mean multiplied by 21. The distributed series is now the sum.)
  • z_net_*_21dma = z-score of _21dma against its 2-year (504 trading day) trailing mean and sample standard deviation, computed at query time. Minimum 63 non-null observations in the trailing window required, else null. Rounded to 2 decimals on emit.

The raw rolling μ and σ series are computed on the fly at query time and are not distributed as separate columns — only the resulting z-score ships.

Every industry-day is emitted with populated rolling / z fields. The 21dma and 2y baseline include ALL days regardless of n_figi — thin days contribute their (smaller) raw values to the trailing window, so each industry's z is calibrated against its own historical breadth mix and populates on every day. Cross-industry z magnitudes aren't directly comparable when breadth profiles differ meaningfully — filter on n_figi at query time if comparability matters. See "Thin-industry handling" below.

Aggregate identities

  • single_stock + etf + adr = all per (date, callput), 0 residual.
  • sum(sector) ≈ single_stock per (date, callput); small residual (< 1% of gross flow) attributable to single-stock figis without a sector label.
  • Because sector / industry exclude ETFs and ADRs, sum(sector) ≠ all — the difference is precisely the etf + adr gross flow. Historical rollups produced before 2026-08-11 rolled ETFs+ADRs into sector; those runs have been superseded.
  • sum(industry) ≈ single_stock per (date, callput). Every industry row (thin or full-breadth) contributes its raw flow to the sum, so this identity holds tighter than it did before 2026-08-28.

Thin-industry handling

Every group with any flow on a given date is emitted — the time series is gap-free. Every row (thin or full-breadth) carries populated raw daily nets AND populated rolling / z fields:

  • n_figi is populated with the honest constituent count (visible in the response) — consumers filter thin-breadth days at query time.
  • Raw daily nets (net_i_shares, net_r_shares, …, net_r_vega) are populated with the actual group sum.
  • Rolling / derived columns (_21dma, z_*) are populated on every row. The 21dma and 2y baseline include ALL days regardless of n_figi — thin days contribute their (smaller) raw values to the trailing window and to the 2y μ/σ used to z-score. Each industry's z-score is therefore calibrated against its own historical breadth mix — a chronically-thin industry's baseline is set to its own thin-day scale, so z fires when flow is unusually large for that industry. Cross-industry z magnitudes aren't directly comparable when breadth profiles differ meaningfully (a +2σ on a 5-FIGI industry is not directly comparable to a +2σ on a 50-FIGI industry). Filter on n_figi at query time when magnitude comparability matters.

Sector, etf, adr, single_stock, and all branches always exceed 10 contributing FIGIs so the caveat is a no-op for those branches. Only industries — historically ~46 including Copper, Uranium, Consulting Services, Regulated Water, Conglomerates, Waste Management, Railroads — are materially affected. Before 2026-08-28 those industries were dropped entirely and visible as gaps in the served time series; now they emit continuous rows with populated raw flow AND populated (but caveated) z-scores. Live coverage: 11 sectors + ~150 industries + 4 rollups (all, etf, adr, single_stock) — every group with flow that day appears in the cross-section.

Classification is not strictly PIT

The underlying flow data is point-in-time, but each figi's sector, industry, and ETF / ADR classification uses the most recent labelling rather than the label in force at the time. META, for example, rolls up under Communication Services for its entire history even though it was classified as Technology in 2019. This keeps historical rollups comparable under a single consistent scheme as companies drift between sectors over time.

callput semantics

Every row is scoped to one callput bucket. C = calls only, P = puts only, CP = pre-computed combined view (nets are C − P because bullish call flow and bearish put flow both express bullish conviction, so nets combine sign-flipped; greeks are C + P because they are naturally signed by option type). Rolling windows partition by (group_key, callput); each callput row has its own independent trailing 21dma and 2y baseline, so z(C) + z(P) ≠ z(CP). Filter to exactly one callput per query or triple-count.

Warmup NULL conventions

  • ..._21dma NULL when a (group_key, callput) has < 21 days of history in the trailing 21-row window.
  • z_..._21dma NULL when the 21dma series has < 63 non-null observations in the trailing 504-session (2-year) window, or the window's sample stddev is 0.
  • These are legitimate NULLs; do not fill.

REST endpoints

  • GET /v0/grouped_flow_signal/01_day: ranged. Requires group_type = a single canonical group_key value (sector / industry name or one of the rollup literals etf / adr / single_stock / all; case-insensitive on input, fuzzy-suggestion on unknown values). Returns rows for a date range at that one key.
  • GET /v0/grouped_flow_signal/01_day/snapshot: single-day cross-sectional. All filters optional. date defaults to latest. Omit both category and group_type → every group_key with any flow on the day (up to ~168 rows on the default callput = 'CP'). category (snapshot-only: industry / sector / rollup / all) narrows to a whole family in one call. group_type (single canonical group_key value) narrows to one key. category and group_type are mutually exclusive.

Fields

field_nametypeunitgraindescriptionprovenancetiming_laghistory_startnull_semanticsexample
dateDatetrading day (UTC)(date, group_key, callput)Session date. US equity trading calendar.exchangeT+12020-01-02never null2026-07-24
group_keyLowCardinality(String)textkeyCanonical mixed-case sector or industry name (e.g. Communication Services, Semiconductors); or the literal etf / adr / single_stock / all for the rollup branches. Single key column — the same value you pass in the REST group_type query parameter.derivedT+12020-01-02never nullTechnology
callputLowCardinality(String)enumkeyOne of C, P, CP. See the callput note above.derivedT+12020-01-02never nullCP
n_figiNullable(Int64)count(date, group_key, callput)Number of distinct FIGIs contributing to this group-day. Every row is emitted with raw nets AND rolling / z fields populated; the 21dma and 2y baseline include all days regardless of n_figi, so each industry's z is calibrated against its own historical breadth mix. Consumers filter on n_figi at query time when z-magnitude comparability across breadth matters. Sector, etf, adr, single_stock, and all branches always carry n_figi >> 10; only certain industries can dip below.derivedT+12020-01-02never null in practice24
net_i_sharesFloat64delta-adjusted share equivalents(date, group_key, callput)Institutional net share-equivalent flow, summed across every figi in this grouping. On CP rows: C − P (bullish-conviction sign convention). Populated on every emitted row (even n_figi < 10 days).modelT+12020-01-020.0 when no qualifying institutional flow1_820_000.0
net_r_sharesFloat64delta-adjusted share equivalents(date, group_key, callput)Retail net share-equivalent flow, same CP-synthesis rule.modelT+12020-01-020.0 when no qualifying retail flow412_000.0
net_i_premiumFloat64USD(date, group_key, callput)Institutional net dollar premium flow.modelT+12020-01-020.0 when no qualifying institutional flow182_000_000.0
net_r_premiumFloat64USD(date, group_key, callput)Retail net dollar premium flow.modelT+12020-01-020.0 when no qualifying retail flow31_000_000.0
net_i_deltaFloat64USD(date, group_key, callput)Institutional net USD × delta flow. Positive = net long-delta demand across the grouping.modelT+12020-01-020.0 when no qualifying institutional flow1_250_000_000.0
net_r_deltaFloat64USD(date, group_key, callput)Retail net USD × delta flow.modelT+12020-01-020.0 when no qualifying retail flow-87_000_000.0
net_i_gammaFloat64USD × gamma(date, group_key, callput)Institutional net USD × gamma. On CP rows: C + P.modelT+12020-01-020.0 when no qualifying institutional flow8_400_000.0
net_r_gammaFloat64USD × gamma(date, group_key, callput)Retail net USD × gamma.modelT+12020-01-020.0 when no qualifying retail flow1_210_000.0
net_i_vegaFloat64USD × vega(date, group_key, callput)Institutional net USD × vega. On CP rows: C + P. Positive = net long-vol demand across the grouping.modelT+12020-01-020.0 when no qualifying institutional flow140_000_000.0
net_r_vegaFloat64USD × vega(date, group_key, callput)Retail net USD × vega.modelT+12020-01-020.0 when no qualifying retail flow-21_000_000.0
net_*_21dmaFloat64(matches base metric)(date, group_key, callput)21-trading-day rolling SUM of the corresponding raw net, scoped to this (group_key, callput). Ten of these, one per raw metric.derivedT+12020-01-02null when the trailing 21-day window has fewer than 21 non-null observations(varies by metric)
z_net_*_21dmaFloat64z-score(date, group_key, callput)Z-score of the corresponding 21dma against its trailing 2-year (504-session) rolling mean and sample standard deviation, computed at query time. Rounded to 2 decimals on emit. Masscrest's canonical spike predicate for the group-level table is z_net_i_delta_21dma > 2 AND callput = 'CP' (or > 3 for 3σ), same rule as the underlying-level table. Ten of these.derivedT+12020-01-02null when the trailing 2y window is unwarmed or its sample stddev is 0(varies)

CP synthesis is pre-computed, not query-time

Same rule as underlying_flow_signal_01_day: the 'CP' rows are materialised at publish time, and each (group_key, callput) bucket has its own independent 21dma and 2y baseline trajectory.

Group classification is snapshot-only

Load-bearing, worth repeating: the sector / industry / etf / adr / single_stock labels used to route flow into each row come from each figi's latest-known stock_metadata phase (current phase for live names, last-live phase for delisted names) applied across every historical date. Do NOT use this table for PIT backtests where a stock's historical group membership matters; use underlying_flow_signal_01_day and roll up client-side.

px_01_day

Daily close (unadjusted) for the covered US single-stock and ETF universe, keyed on FIGI.

Access: this table is not directly queryable via the MCP or REST API. Split-adjusted price columns (adj_close, adj_open, adj_volume, total_turnover) are returned as LEFT-JOIN columns on get_underlying_option_flows_daily (MCP) and GET /v0/underlying_option_flows/01_day (REST). Use those endpoints for daily price context — passing traded_underlying=<TICKER> and callput='CP' returns one row per date with prices attached to the flow payload. For bulk historical pulls, the parquet flat-file surface is at gs://prod-masscrest/v0/px_data/01_day/.

  • Grain: (date, figi, symbol) [1]
  • History: 2020-01-02 →
  • Timing: T+1 (published between 05:00 and 09:00 UTC on the calendar day after the trading date — Friday's data lands Saturday)

[1] The underlying storage carries symbol to preserve point-in-time ticker context, but every join to Masscrest surfaces should use figi.

Fields

field_nametypeunitgraindescriptionprovenancetiming_laghistory_startnull_semanticsexample
dateDatetrading day (UTC)(date, figi)Session date. US equity trading calendar.exchangeT+12020-01-02never null2026-07-24
figiString(12)identifier(date, figi)OpenFIGI composite FIGI for the underlying. Stable across ticker changes; join key to every other Masscrest table.exchangen/a2020-01-02never nullBBG000MM2P62
symbolStringticker(date, figi)Point-in-time listed ticker on date. Dotted dual-class form (BRK.B). Preserved for readability; do not use as a join key (the same ticker can be recycled across entities over time).exchangeT+12020-01-02never nullAAPL
openFloat64USD(date, figi)Session opening print (unadjusted, contemporaneous). No split adjustment applied.exchangeT+12020-01-02null on non-trading days for the underlying (delistings, halts)223.85
closeFloat64USD(date, figi)Session closing print (unadjusted, contemporaneous). No split adjustment applied.exchangeT+12020-01-02null on non-trading days for the underlying224.31
volumeFloat64shares(date, figi)Session share volume (unadjusted, contemporaneous). No split adjustment applied (a share count on a pre-split day is a real economic quantity that doesn't rescale post-split).exchangeT+12020-01-02null on non-trading days52_308_400.0
turnoverFloat64USD(date, figi)Dollar turnover of the underlying on date (sum(price × volume) across all intraday prints). Dollar amounts are unit-invariant across splits.exchangeT+12020-01-02null on non-trading days11_724_390_012.0

Unadjusted semantics

Prices and volume in this table are unadjusted (contemporaneous). To compute split-adjusted series (safe returns across split events, share-count normalisation, historical strike comparisons), join to split_factors on figi where date BETWEEN valid_from AND valid_to and apply cum_split_factor per the reconstruction snippet in the flat-files section. The split is deliberate: it lets you reload px_01_day and split_factors independently, and gives you an explicit knob when you need to invert an adjustment.

px_10_min

10-minute OHLCV bars (unadjusted) for the covered US single-stock and ETF universe, keyed on FIGI. Same underlying universe and FIGI join key as px_01_day.

Access: this table is not directly queryable via the MCP or REST API. Split-adjusted price columns (adj_close, adj_open, adj_volume, total_turnover) are returned as LEFT-JOIN columns on get_underlying_option_flows_10min (MCP), intraday_flow_day (MCP fat tool for a single symbol × single day), and GET /v0/underlying_option_flows/10_min (REST). Use those endpoints for intraday price context — the LEFT-join emits one row per (date, ten_min_timeframe, figi) with prices attached to the flow payload. For bulk historical pulls, the parquet flat-file surface is at gs://prod-masscrest/v0/px_data/10_min/.

  • Grain: (date, ten_min_timeframe, figi, symbol)
  • History: 2020-01-02 →
  • Timing: T+1 (published between 05:00 and 09:00 UTC on the calendar day after the trading date — Friday's data lands Saturday)

Bucket boundary convention

ten_min_timeframe is the end of the 10-minute bucket, in New York wall-clock time stored as a naive DateTime. Bucket covers (ten_min_timeframe − 10 min, ten_min_timeframe]. First RTH bucket ends at 09:40:00, last at 16:00:00 (39 buckets per full session). On-boundary 1-minute bars (e.g. 10:00:00 close) land in the closing bucket, not the opening bucket of the next.

Coverage

Regular trading hours only (09:30–16:00 ET). No pre-market, no after-hours. Halted sessions produce no rows for the halted intervals.

Fields

field_nametypeunitgraindescriptionprovenancetiming_laghistory_startnull_semanticsexample
ten_min_timeframeDateTimeNY wall-clock, bucket close(date, ten_min_timeframe, figi)End timestamp of the 10-min bucket (naive NY-time). Bucket covers (ten_min_timeframe − 10 min, ten_min_timeframe].exchangeT+12020-01-02never null2026-07-24 09:40:00
dateDatetrading day (UTC)(date, ten_min_timeframe, figi)Session date. US equity trading calendar.exchangeT+12020-01-02never null2026-07-24
figiString(12)identifier(date, ten_min_timeframe, figi)OpenFIGI composite FIGI for the underlying. Stable across ticker changes.exchangen/a2020-01-02never nullBBG000MM2P62
symbolStringticker(date, ten_min_timeframe, figi)Point-in-time listed ticker on date. Dotted dual-class form. Preserved for readability; do not use as a join key.exchangeT+12020-01-02never nullAAPL
openFloat64USD(date, ten_min_timeframe, figi)Unadjusted price of the first 1-minute bar in the bucket. Note: the source 1-minute feed carries open and close only (no high/low), so bar-internal high/low is not available at this grain.exchangeT+12020-01-02null when the underlying has no trading activity in the bucket223.90
closeFloat64USD(date, ten_min_timeframe, figi)Unadjusted price of the last 1-minute bar in the bucket.exchangeT+12020-01-02null when the underlying has no trading activity in the bucket224.02
volumeFloat64shares(date, ten_min_timeframe, figi)Underlying share volume within the bucket. Unadjusted (contemporaneous).exchangeT+12020-01-02null when the underlying has no trading activity1_204_812.0
turnoverFloat64USD(date, ten_min_timeframe, figi)Dollar turnover of the underlying within the bucket. Dollar amounts are unit-invariant across splits.exchangeT+12020-01-02null when the underlying has no trading activity270_881_400.0

Unadjusted semantics

Prices and volume in this table are unadjusted (contemporaneous). To compute split-adjusted series, join to split_factors on figi where date BETWEEN valid_from AND valid_to and apply cum_split_factor per the reconstruction snippet in the flat-files section. The split is deliberate: it lets you reload px_10_min and split_factors independently, and gives you an explicit knob when you need to invert an adjustment.

Ticker-renamed FIGIs

A very small number of FIGIs carry rows under two different symbol values within a single day (the point-in-time symbol at the moment of the trade). If you aggregate over a universe by figi alone, use sum(turnover) for turnover and argMax(close, turnover) for a representative close per (date, ten_min_timeframe, figi). Don't collapse via any(...) or you'll pick a stale legacy row.

stock_metadata

Point-in-time (PIT) reference table for every underlying Masscrest tracks. Maps FIGI to listed ticker, company name, sector / industry, ETF and ADR flags, and the activity window during which that (ticker, FIGI) pair was live. Historical ticker changes, dual-class shares, and shell-reuse events are all encoded as separate phase rows.

  • Grain: (figi, symbol, phase_start), one row per contiguous period during which a FIGI traded under a given symbol.
  • History: all open phases plus historical phases as far back as the OpenFIGI record supports (many phases start 1900-01-01 as a "no constraint" sentinel).
  • Timing: reference table, rebuilt in full each trading day. Re-sync daily to pick up new phases and any corrections.
  • Typical use: join to any flow / price table on figi with date BETWEEN phase_start AND phase_end to attach a point-in-time symbol, sector, name, ETF flag, etc.

Known quirks

  • For ETFs (isEtf='true', which includes ETCs and ETPs), sector and industry describe the sponsor legal entity, not the fund's underlying exposure. All BlackRock / iShares sector ETFs show sector='Financial Services' because BlackRock is a financial-services firm. Filter isEtf='true' out before aggregating flow by sector, otherwise Financial Services gross flow inflates by 85%+.
  • For ADRs (isAdr='true'), sector and industry describe the underlying foreign company's business. HDB is Financial Services (HDFC Bank), BABA is Consumer Cyclical (Alibaba). Usually the label you want. Filter on isAdr if you need to separate US-domiciled sector totals from foreign ADRs.
  • The served rollup v0.grouped_flow_signal_01_day already excludes ETFs and ADRs from its sector and industry group_keys. ADRs have their own adr group_key and ETFs their own etf. This quirk only matters when joining v0.stock_metadata directly.

Symbol conventions

  • Equity tickers use the dotted dual-class convention (BRK.B, RDS.A), matching every other Masscrest surface. When OPRA options data references the same underlying via its no-dot OCC root (BRKB), the equity join still uses the dotted form. Options tables carry both: traded_symbol (OCC root) for options joins and traded_underlying (dotted equity) for FIGI / equity joins.
  • Open phases use phase_end = 2099-12-31 as the sentinel. Closed phases (renames, delistings, ticker recycles) carry a real end date.
  • terminal_symbol is the current ticker for the same FIGI as of today. Historical phases point forward at their entity's today-identity; join on it for "all history for whatever is called META today," even for the older FB rows.

Multi-phase tickers

~1% of tickers have multiple stock_metadata rows across time — one per FIGI-era — from corporate reorgs (ISIN flip, e.g. XOM's 2026-07-07 Exxon → ExxonMobil Holdings restructure) or ticker recycles after a delisting (PARA was Paramount, then Banzai after 2026-08-07 under a different FIGI).

  • Current era only: WHERE symbol='X' AND phase_end='2099-12-31'
  • Full history across eras: WHERE symbol='X', resolve joins with date BETWEEN phase_start AND phase_end
  • Isolate one era: filter by figi rather than symbol

Flow, price, and split tables are already attributed to the FIGI authoritative on each row's date.

Fields

field_nametypeunitgraindescriptionprovenancetiming_laghistory_startnull_semanticsexample
symbolStringticker(figi, symbol, phase_start)Point-in-time listed ticker during [phase_start, phase_end]. Dotted dual-class form (BRK.B).exchangen/a1900-01-01 (sentinel)never nullFB
figiString(12)identifier(figi, symbol, phase_start)OpenFIGI composite FIGI for the underlying entity. Stable across ticker changes.exchangen/an/anever nullBBG000NDYB67
phase_startDateboundary(figi, symbol, phase_start)First trading date this (symbol, figi) pair was active. 1900-01-01 is a "no constraint" sentinel for entities with unknown pre-history.exchangen/an/anever null2012-05-18
phase_endDateboundary(figi, symbol, phase_start)Last trading date this (symbol, figi) pair was active. 2099-12-31 sentinel means the phase is currently open (still trading under this ticker today).exchangen/an/anever null2022-06-08
terminal_symbolStringticker(figi, symbol, phase_start)Current ticker for the same FIGI. On the FB row you'll see terminal_symbol = 'META'; on the META row it's also 'META'. Use this to fetch the full history of a currently-listed name.exchangedaily refreshn/anever nullMETA
companyNameStringname(figi, symbol, phase_start)Registered legal / operating name for the entity during this phase.exchangedaily refreshn/anull when no vendor record is available for the phaseMeta Platforms, Inc.
sectorStringsector(figi, symbol, phase_start)Sector classification for the entity during this phase. Investor-oriented 11-sector taxonomy (companies grouped by demand-cycle and macro exposure, not by production process).exchangedaily refreshn/anull when unresolvedTechnology
industryStringindustry(figi, symbol, phase_start)Industry classification for the entity during this phase. Investor-oriented 154-industry taxonomy nested under the 11 sectors.exchangedaily refreshn/anull when unresolvedSoftware - Application
isinString(12)identifier(figi, symbol, phase_start)ISIN for the entity. Not a unique join key: roughly 200 ISINs map to two active FIGIs simultaneously (the primary listing and the US OTC F-share composite of the same foreign entity). Join on figi, not isin.exchangedaily refreshn/anull when the vendor lacks an ISIN for the phaseUS30303M1027
countryStringISO country(figi, symbol, phase_start)Country of incorporation.exchangedaily refreshn/anull when the vendor lacks a country for the phaseUS
isEtfStringflag ('true' / 'false')(figi, symbol, phase_start)Whether the underlying is an ETF or ETP. String-typed for compatibility with the customer-facing endpoints.exchangedaily refreshn/a'false' when unresolved (defaults to non-ETF)false
isAdrStringflag ('true' / 'false')(figi, symbol, phase_start)Whether the underlying is an American Depositary Receipt.exchangedaily refreshn/a'false' when unresolvedfalse
isActivelyTradingStringflag ('true' / 'false')(figi, symbol, phase_start)Whether the entity is actively trading today. Falls back to 'true' when the phase is still open (phase_end = 2099-12-31) and the vendor lacks an explicit record, 'false' otherwise.exchangedaily refreshn/anever null (default fallback based on phase_end)true

split_factors

Cumulative split-adjustment factors per FIGI, structured as a slowly-changing dimension (SCD2). Delivered so clients can back-adjust unadjusted historical prices (see px_01_day / px_10_min), or invert a Masscrest split-adjusted price back to the raw contemporaneous print for a specific period.

  • Grain: one row per continuous split-factor phase per figi, (figi, valid_from, valid_to), non-overlapping intervals covering each FIGI's timeline. date BETWEEN valid_from AND valid_to matches exactly one row per (figi, date).
  • History: full history for each covered FIGI, back to the earliest known split. Pre-split-history phases use valid_from = 1900-01-01 as a "no earlier bound" sentinel.
  • Timing: reference table, rebuilt in full each trading day whenever a new split lands. Re-sync daily.
  • Coverage: currently ~30k FIGIs. Historical splits back to 1962 and forward-looking upcoming ex-dates are both covered.

How to use the factor

cum_split_factor on a date X means: to convert a raw close on date X into a current-shares-equivalent price, divide by this factor. It equals 1.0 on and after the most recent split; > 1 before a forward split (e.g. 4.0 before a 4-for-1); < 1 before a reverse split. On or after the latest split within an entity's active period, the factor is 1.0.

Fields

field_nametypeunitgraindescriptionprovenancetiming_laghistory_startnull_semanticsexample
figiString(12)identifier(figi, valid_from, valid_to)OpenFIGI composite FIGI for the underlying entity. Join key to stock_metadata, px_01_day, px_10_min.exchangen/an/anever nullBBG000MM2P62
valid_fromDateboundary (inclusive)(figi, valid_from, valid_to)Start date this cumulative factor applies to. 1900-01-01 for the earliest period.derivedn/a1900-01-01 (sentinel)never null2019-08-30
valid_toDateboundary (inclusive)(figi, valid_from, valid_to)Last date this cumulative factor applies to. Open-ended periods (no future split yet) use the phase's phase_end; entities in a currently-active phase carry 2099-12-31.derivedn/an/anever null2020-08-30
cum_split_factorFloat64ratio(figi, valid_from, valid_to)Cumulative product of all splits from valid_from + 1 day forward within the entity's active phase. Divide a raw close on any date in [valid_from, valid_to] by this factor to get a current-shares-equivalent price. 1.0 for the most recent period (no pending split).derivedn/an/anull for extreme reverse-split cases where the true cumulative factor underflows Float64 precision (~1e-15). Treat null as "no valid split adjustment available for this period" and either skip or fail loudly; never coerce to 0.0.4.0

Precision

Cumulative factors are stored at 12 decimal places to preserve heavy reverse-split stacks (e.g. UVXY has thirteen reverse splits; its true cum factor at 1900-01-01 is ~6.7e-11). Do NOT round to 6 decimals when consuming; that historically truncated ~200 heavy-reverse-splitter FIGIs to 0.0 and silently zeroed downstream price adjustments. For the handful of penny-stock cases where the true cumulative product underflows Float64 precision entirely (e.g. stacked 1-for-50000 × 1-for-19000 × 1-for-10000 ≈ 3.5e-20), we emit NULL rather than 0.0 so consumers can detect and skip rather than silently multiplying prices by zero.

No-split entities

FIGIs that never split carry a single row spanning the entity's full active phase with cum_split_factor = 1.0. Same shape as those that split; no special-case handling needed.

Documentation · 02

REST API

Every endpoint the Masscrest API exposes, with query URL, Python sample, and JSON response inline.

Base URL https://api.masscrest.com

Summary

Every REST endpoint at a glance. HTTP 429 (rate-limit) applies universally; the Errors column lists the endpoint-specific ones an integration engineer should handle. See Trial vs. paid tier on the Data dictionary for the full trial policy.

EndpointGrainFrequencyTrialErrors
GET /v0/option_flows/01_dayPer-underlying daily flow with contract-level filters (dte, delta, callput).DailyYes
Charges the 25-FIGI/day cap. Requires explicit figi / ticker filter.
400 (missing figi/ticker), 403 (25-FIGI cap), 429
GET /v0/option_flows/10_minPer-underlying 10-minute intraday flow with contract-level filters.10-minBlocked (403)
Intraday not available on trial.
400 (>30d window, bad params), 403 (trial intraday), 429
GET /v0/underlying_option_flows/01_dayPer-underlying × callput daily aggregate flow (pre-aggregated).DailyYes
Charges the 25-FIGI/day cap. Requires explicit figi / ticker filter.
400 (missing figi/ticker), 403 (25-FIGI cap), 429
GET /v0/underlying_option_flows/01_day/snapshotCross-sectional single-day snapshot, one row per (figi, callput).SnapshotBlocked (403)
Snapshot endpoints not available on trial.
400 (bad params), 403 (trial snapshot), 429
GET /v0/underlying_option_flows/10_minPer-underlying × callput 10-minute aggregate flow (pre-aggregated).10-minBlocked (403)
Intraday not available on trial.
400 (>30d window, bad params), 403 (trial intraday), 429
GET /v0/underlying_option_flows/10_min/snapshotCross-sectional single-bucket intraday snapshot.SnapshotBlocked (403)
Intraday not available on trial.
400 (bad params), 403 (trial intraday), 429
GET /v0/underlying_flow_signals/01_dayPer-underlying × callput daily z-score signal on aggregate flow.DailyYes
Charges the 25-FIGI/day cap. Requires explicit figi / ticker filter.
400 (missing figi/ticker), 403 (25-FIGI cap), 429
GET /v0/underlying_flow_signals/01_day/snapshotCross-sectional single-day snapshot of signal rows per (figi, callput).SnapshotBlocked (403)
Snapshot endpoints not available on trial.
400 (bad params), 403 (trial snapshot), 429
GET /v0/grouped_flow_signal/01_daySector / industry / ETF / ADR / single-stock cohort-level daily signal.DailyYes
Not FIGI-capped — recommended surface for broad exploration on trial.
400 (bad group_type / category, w/ fuzzy suggestion), 429
GET /v0/grouped_flow_signal/01_day/snapshotCross-sectional snapshot across all group_keys (or filter to one family via category).SnapshotYes
Not FIGI-capped.
400 (bad group_type / category, w/ fuzzy suggestion), 429
GET /v0/stock_mappingTicker → FIGI / sector / industry reference table (~42k rows).SnapshotYes
Not FIGI-capped.
400 (bad filter combo), 429
GET/v0/option_flows/01_day

Option Flows: Daily

Per-underlying daily aggregated option flow joined with the split-adjusted underlying price.

Description

Retrieve daily option-flow rows for a single underlying, aggregated per (date, figi): one row per trading day. Every row combines all contracts on that underlying (after any optional filters) into a single set of signed retail / institutional / dealer aggregates, joined to the split-adjusted underlying price on the same day.

Exactly one of figi or traded_underlying must be supplied. If traded_underlying is used, it is resolved server-side to the corresponding FIGI. Supplying both, or neither, returns HTTP 400. An unknown traded_underlying returns HTTP 404, not an empty [].

Trial tiers cap daily history at 730 days and count each distinct FIGI touched per UTC day against a 25-figi cap. Paid tiers have no per-figi cap and honour the requested history window. See Trial vs. paid tier for the full policy.

Optional filters, applied BEFORE aggregation

callput, dte_min / dte_max, and abs_delta_min / abs_delta_max narrow the contract set inside the underlying-level GROUP BY. The aggregate reflects only contracts matching the filter. dte_min <= dte_max and abs_delta_min <= abs_delta_max are cross-validated (400 on mismatch).

Response headers & errors

Every /v0/* list response carries X-Row-Count: <N>. Unknown query parameters return 400 Bad Request with the list of accepted names in the body.

Aggregation

All fields are summed raw across the contracts passing the filters. No call/put sign flip is applied. For a call-vs-put directional view, query callput=C and callput=P separately and subtract client-side.

  • *_shares: sum(qty × adj_shares_deliverable). Units are shares (contract count × current OPRA deliverable).
  • *_premium: sum(premium). Dollar notional.
  • *_delta / *_gamma / *_vega: summed raw. Delta is naturally signed by option type on the source contract (calls +, puts −), so net_i_delta already carries directional lean.

Prefixes are _r_ (retail), _i_ (institutional), _m_ (dealer). The floor bucket is not exposed via this endpoint.

Parameters

NameTypeDescription
figistringComposite FIGI of the underlying. Mutually exclusive with `traded_underlying`.
traded_underlyingstringStock ticker; resolved to FIGI via the `dict_symbol_to_figi` dictionary. Mutually exclusive with `figi`.
callputC | PRestrict the contract set to Calls (`C`) or Puts (`P`) BEFORE aggregation. Case-insensitive (`c`, `C`, `p`, `P` all accepted). `CP` is rejected here — this endpoint is contract-grain and every contract is either a call or a put; for a summed C+P view use `/v0/underlying_option_flows/*` with `callput=CP`.
dte_minintegerMinimum days-to-expiration (inclusive). Applied per contract before aggregation.
dte_maxintegerMaximum days-to-expiration (inclusive). Applied per contract before aggregation.
abs_delta_minnumberMinimum `|last_delta|` in `[0.0, 1.0]`. Applied per contract before aggregation.
abs_delta_maxnumberMaximum `|last_delta|` in `[0.0, 1.0]`. Applied per contract before aggregation.
start_datestringEarliest trading day to include (inclusive, YYYY-MM-DD). Trial tiers are clamped to a 730-day history window.
end_datestringLatest trading day to include (inclusive, YYYY-MM-DD).
formatjson | parquetResponse format. `json` returns a JSON array (default). `parquet` streams a binary Parquet file (~5–10× smaller).
GET/v0/option_flows/10_min

Option Flows: 10-minute

Per-underlying 10-minute aggregated option flow joined with the intraday split-adjusted underlying price.

Description

Retrieve 10-minute-bucket option-flow rows for a single underlying, aggregated per (date, ten_min_timeframe, figi): one row per 10-minute bucket. Every row combines all contracts on that underlying (after any optional filters) into a single set of signed retail / institutional / dealer aggregates, joined to the split-adjusted underlying price for the same bucket.

Exactly one of figi or traded_underlying must be supplied. If traded_underlying is used, it is resolved server-side to the corresponding FIGI. Supplying both, or neither, returns HTTP 400. An unknown traded_underlying returns HTTP 404, not an empty [].

Trial tier: intraday is not available. All 10-minute endpoints return HTTP 403 for trial accounts. Trials should use the daily variant /v0/option_flows/01_day. See Trial vs. paid tier.

30-day window cap (paid tier). Any single call returns at most 30 days of history. If start_date / end_date are omitted, the response covers the most recent 30 days. Requests spanning more than 30 days return HTTP 400. For multi-year intraday history, use the flat-files bucket gs://prod-masscrest/v0/option_flows/10_min/. For longer-horizon signals, use the daily variant /v0/option_flows/01_day.

Optional filters, applied BEFORE aggregation

callput, dte_min / dte_max, and abs_delta_min / abs_delta_max narrow the contract set inside the bucket-level GROUP BY. The aggregate reflects only contracts matching the filter. dte_min <= dte_max and abs_delta_min <= abs_delta_max are cross-validated (400 on mismatch).

Sign convention

Same as the daily variant. See /v0/option_flows/01_day for the full sign / prefix / unit notes.

Response headers & errors

Every /v0/* list response carries X-Row-Count: <N>. Unknown query parameters return 400 Bad Request with the list of accepted names in the body.

Parameters

NameTypeDescription
figistringComposite FIGI of the underlying. Mutually exclusive with `traded_underlying`.
traded_underlyingstringStock ticker; resolved to FIGI via the `dict_symbol_to_figi` dictionary. Mutually exclusive with `figi`.
callputC | PRestrict the contract set to Calls (`C`) or Puts (`P`) BEFORE aggregation. Case-insensitive (`c`, `C`, `p`, `P` all accepted). `CP` is rejected here — this endpoint is contract-grain and every contract is either a call or a put; for a summed C+P view use `/v0/underlying_option_flows/*` with `callput=CP`.
dte_minintegerMinimum days-to-expiration (inclusive). Applied per contract before aggregation.
dte_maxintegerMaximum days-to-expiration (inclusive). Applied per contract before aggregation.
abs_delta_minnumberMinimum `|last_delta|` in `[0.0, 1.0]`. Applied per contract before aggregation.
abs_delta_maxnumberMaximum `|last_delta|` in `[0.0, 1.0]`. Applied per contract before aggregation.
start_datestringEarliest trading day (inclusive, YYYY-MM-DD). Combined with `end_date` must span at most 30 days; if both are omitted the response covers the most recent 30 days.
end_datestringLatest trading day (inclusive, YYYY-MM-DD). Combined with `start_date` must span at most 30 days.
formatjson | parquetResponse format. `json` returns a JSON array (default). `parquet` streams a binary Parquet file (~5–10× smaller).
GET/v0/underlying_option_flows/01_day

Underlying Option Flows: Daily

Per-underlying × callput daily aggregated option flow (pre-aggregated), joined with adjusted underlying price.

Description

Retrieve daily underlying-level option-flow rows (pre-aggregated across every contract on the same (date, figi, callput)), joined to the PIT stock_metadata row for (figi, date). Rows are already grouped by (date, figi, callput), so unlike /v0/option_flows/01_day no contract-level filters are available. In exchange, cross-underlying scans (see the sibling /snapshot endpoint) are significantly cheaper.

Provide at most one of figi or traded_underlying. If traded_underlying is used, it is resolved server-side to the corresponding FIGI. Supplying both returns HTTP 400. An unknown traded_underlying returns HTTP 404, not an empty []. The underlying selector may be omitted entirely when at least one stock_metadata filter (is_etf / is_adr / industry / sector) is provided; the response then covers every figi matching the metadata predicate for the date range.

Trial tier. Must supply an explicit figi or traded_underlying — a broad sector / industry-only sweep returns HTTP 400 with the message Trial tier requires explicit figi or underlying_ticker filter (up to 25 per UTC day). For broad cohort exploration use programmatic flat-files access. Each distinct FIGI touched counts toward the 25-figi-per-UTC-day cap; the 26th distinct FIGI returns HTTP 403 with the touched list echoed back. Daily history clamped to 730 days. See Trial vs. paid tier.

callput

  • C: calls-only row per bucket (direct scan).
  • P: puts-only row per bucket (direct scan).
  • CP: default; C and P summed at query time. One row per bucket with callput = "CP" in the output.

Case-insensitive on input — c, Cp, CP, p all accepted; the emitted row always carries the canonical upper-case form.

Response columns

date, figi, traded_underlying, callput, n_contracts, total_shares, last_underprice, last_fwd_underprice, atm_iv_30d, the 45 flow aggregates (buy/sell/net × shares/premium/delta/gamma/vega × r/i/m), adj_open / adj_close / adj_volume / total_turnover from the matched underlying, and industry / sector / isAdr / isEtf from stock_metadata (ranged endpoints intentionally omit companyName).

Response headers & errors

Every /v0/* list response carries X-Row-Count: <N>. Unknown query parameters return 400 Bad Request with the list of accepted names in the body.

Parameters

NameTypeDescription
figistringComposite FIGI of the underlying. Mutually exclusive with `traded_underlying`. Optional when a `stock_metadata` filter is supplied.
traded_underlyingstringStock ticker; resolved via the `dict_symbol_to_figi` dictionary. Mutually exclusive with `figi`. Optional when a `stock_metadata` filter is supplied.
callputC | P | CP`C`, `P`, or `CP` (default). `CP` sums the C and P rows at query time.
start_datestringEarliest trading day (inclusive, YYYY-MM-DD). Trial tiers are clamped to a 730-day window.
end_datestringLatest trading day (inclusive, YYYY-MM-DD).
is_etfbooleanFilter to ETFs (`true`) or non-ETFs (`false`). Applied to the joined `stock_metadata` row.
is_adrbooleanFilter to ADRs (`true`) or non-ADRs (`false`).
industrystringFilter on `stock_metadata.industry`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known industry returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown industry: 'semis'. Did you mean 'Semiconductors'?"`).
sectorstringFilter on `stock_metadata.sector`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known sector returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown sector: 'technolgy'. Did you mean 'Technology'?"`).
formatjson | parquet`json` (default) or `parquet` (binary, ~5–10× smaller).
GET/v0/underlying_option_flows/01_day/snapshot

Underlying Option Flows: Daily Snapshot

Single-day cross-sectional snapshot of pre-aggregated option flow for every underlying, joined with adjusted price.

Description

Cross-sectional snapshot: returns one row per (figi, callput) (every underlying with flow on the requested date, C and P kept as separate rows), joined to the split-adjusted underlying close AND to the PIT stock_metadata row for (figi, date).

No underlying selector is required; the response covers the full universe. Optional stock_metadata-backed filters (is_etf / is_adr / industry / sector) narrow the returned figis to those matching the predicate.

Trial tier: snapshot endpoints are not available. This endpoint returns HTTP 403 for trial accounts (same treatment as intraday). A wide snapshot would charge every returned FIGI against the 25-figi-per-UTC-day cap in a single call, so the snapshot surface is blocked outright on trial. Use the ranged /v0/underlying_option_flows/01_day endpoint with an explicit figi / traded_underlying filter for per-underlying pulls, or /v0/grouped_flow_signal/01_day/snapshot (not figi-capped) for broad cross-sectional exploration. See Trial vs. paid tier.

Typical response is ~10,000 rows per trading day (roughly 5,000 underlyings × C+P). Prefer format=parquet for anything larger than a browser preview.

Response columns

Standard flow-aggregate columns plus companyName / industry / sector / isAdr / isEtf from stock_metadata.

Response headers & errors

Every /v0/* list response carries X-Row-Count: <N>. Unknown query parameters return 400 Bad Request with the list of accepted names in the body.

Parameters

* required
NameTypeDescription
date *stringTrading day (YYYY-MM-DD) to snapshot.
figistringOptional comma-separated list of composite FIGIs to restrict the snapshot to those symbols (paid tier only — trials are blocked at the router with HTTP 403 before this filter is evaluated).
is_etfbooleanFilter to ETFs (`true`) or non-ETFs (`false`). Applied to the joined `stock_metadata` row.
is_adrbooleanFilter to ADRs (`true`) or non-ADRs (`false`).
industrystringFilter on `stock_metadata.industry`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known industry returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown industry: 'semis'. Did you mean 'Semiconductors'?"`).
sectorstringFilter on `stock_metadata.sector`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known sector returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown sector: 'technolgy'. Did you mean 'Technology'?"`).
formatjson | parquet`json` (default) or `parquet` (recommended for full-universe pulls).
GET/v0/underlying_option_flows/10_min

Underlying Option Flows: 10-minute

Per-underlying × callput 10-minute aggregated option flow (pre-aggregated), joined with intraday adjusted price.

Description

Retrieve 10-minute-bucket underlying-level option-flow rows (pre-aggregated across every contract on the same (date, ten_min_timeframe, figi, callput)), joined to the PIT stock_metadata row for (figi, date). Rows are already grouped by (date, ten_min_timeframe, figi, callput); no contract-level filters are available on this endpoint.

Provide at most one of figi or traded_underlying. If traded_underlying is used, it is resolved server-side to the corresponding FIGI. Supplying both returns HTTP 400. An unknown traded_underlying returns HTTP 404, not an empty []. The underlying selector may be omitted entirely when at least one stock_metadata filter (is_etf / is_adr / industry / sector) is provided; the response then covers every figi matching the metadata predicate for the date range.

Trial tier: intraday is not available. All 10-minute endpoints return HTTP 403 for trial accounts. Trials should use the daily variant /v0/underlying_option_flows/01_day. See Trial vs. paid tier.

30-day window cap (paid tier). At most 30 days of history per call. If start_date / end_date are omitted, the response covers the most recent 30 days. Requests spanning more than 30 days return HTTP 400. For multi-year intraday history use the flat-files bucket gs://prod-masscrest/v0/option_flows/10_min/.

callput

Same semantics as the daily variant: C, P, or CP (default). CP sums C and P at query time. Case-insensitive on input.

Response columns

Standard flow-aggregate columns plus industry / sector / isAdr / isEtf from stock_metadata (ranged endpoints intentionally omit companyName).

Response headers & errors

Every /v0/* list response carries X-Row-Count: <N>. Unknown query parameters return 400 Bad Request with the list of accepted names in the body.

Parameters

NameTypeDescription
figistringComposite FIGI of the underlying. Mutually exclusive with `traded_underlying`. Optional when a `stock_metadata` filter is supplied.
traded_underlyingstringStock ticker; resolved via the `dict_symbol_to_figi` dictionary. Mutually exclusive with `figi`. Optional when a `stock_metadata` filter is supplied.
callputC | P | CP`C`, `P`, or `CP` (default).
start_datestringEarliest trading day (inclusive, YYYY-MM-DD). Combined with `end_date` must span at most 30 days.
end_datestringLatest trading day (inclusive, YYYY-MM-DD).
is_etfbooleanFilter to ETFs (`true`) or non-ETFs (`false`). Applied to the joined `stock_metadata` row.
is_adrbooleanFilter to ADRs (`true`) or non-ADRs (`false`).
industrystringFilter on `stock_metadata.industry`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known industry returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown industry: 'semis'. Did you mean 'Semiconductors'?"`).
sectorstringFilter on `stock_metadata.sector`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known sector returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown sector: 'technolgy'. Did you mean 'Technology'?"`).
formatjson | parquet`json` (default) or `parquet` (binary, ~5–10× smaller).
GET/v0/underlying_option_flows/10_min/snapshot

Underlying Option Flows: 10-minute Snapshot

Single 10-minute-bucket cross-sectional snapshot of pre-aggregated option flow for every underlying.

Description

Cross-sectional snapshot at a specific 10-minute bucket: returns one row per (figi, callput) (every underlying with flow at the requested (date, ten_min_timeframe), C and P kept as separate rows), joined to the split-adjusted underlying price for the same bucket AND to the PIT stock_metadata row for (figi, date).

No underlying selector is required. Optional stock_metadata-backed filters (is_etf / is_adr / industry / sector) narrow the returned figis to those matching the predicate.

Trial tier: intraday is not available. All 10-minute endpoints return HTTP 403 for trial accounts. See Trial vs. paid tier.

The ten_min_timeframe param must fall on the requested date. Typical response is ~5,000–10,000 rows per bucket. Prefer format=parquet for full-universe pulls.

Response columns

Standard flow-aggregate columns plus companyName / industry / sector / isAdr / isEtf from stock_metadata.

Response headers & errors

Every /v0/* list response carries X-Row-Count: <N>. Unknown query parameters return 400 Bad Request with the list of accepted names in the body.

Parameters

* required
NameTypeDescription
date *stringTrading day (YYYY-MM-DD) the bucket falls on.
ten_min_timeframe *stringBucket start timestamp (`YYYY-MM-DD HH:MM:SS`). Must fall on `date`.
figistringOptional comma-separated list of composite FIGIs to restrict the snapshot to those symbols (paid tier only — trials are blocked at the router with HTTP 403 before this filter is evaluated).
is_etfbooleanFilter to ETFs (`true`) or non-ETFs (`false`). Applied to the joined `stock_metadata` row.
is_adrbooleanFilter to ADRs (`true`) or non-ADRs (`false`).
industrystringFilter on `stock_metadata.industry`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known industry returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown industry: 'semis'. Did you mean 'Semiconductors'?"`).
sectorstringFilter on `stock_metadata.sector`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known sector returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown sector: 'technolgy'. Did you mean 'Technology'?"`).
formatjson | parquet`json` (default) or `parquet` (recommended for full-universe pulls).
GET/v0/underlying_flow_signals/01_day

Underlying Flow Signals: Daily

Per-underlying × callput daily flow signals (raw nets, 21-day rolling sums, 2y z-scores across shares / premium / delta / gamma / vega).

Description

Retrieve daily flow-signal rows from the pre-aggregated underlying_flow_signal_01_day table, joined to the PIT stock_metadata row for (figi, date). One row per (date, figi, callput).

The source table stores 34 columns per row: 4 keys (date, figi, underlying_ticker, callput) plus a 3-layer × 10-metric flow block. The 10 metrics are institutional + retail × shares / premium / delta / gamma / vega. The 3 layers are raw daily nets, _21dma (21-trading-day rolling sums), and z_net_..._21dma (z-scored against a trailing 2-year rolling mean and sample stddev of the 21dma). The raw μ / σ of the 2-year window are computed at query time and are NOT distributed as separate columns. Rolling windows PARTITION BY (figi, callput); each callput row has its own independent trailing stats, so z(C) + z(P) ≠ z(CP).

The callput dimension

Every source row exists in three variants: C (calls only), P (puts only), and CP (synthetic combined view). CP is built at ingest time so consumers do not have to reconstruct call-vs-put semantics themselves:

  • Directional nets (shares, premium) → CP = C − P. Bullish call flow and bearish put flow both express long-delta demand; the sign-flipped difference is the net directional pressure.
  • Naturally-signed greeks (delta, gamma, vega) → CP = C + P. Calls carry positive delta, puts negative; the straight sum through-adds correctly.

The default callput is CP; omitting the filter would triple-count each figi-date across the three variants.

Deriving fire / holding-period predicates

Fire flags and holding-period flags are not persisted on this table; derive them at query time from the z-score column:

  • 2σ institutional-delta fire: WHERE z_net_i_delta_21dma > 2 AND callput = 'CP'
  • 3σ institutional-delta fire: WHERE z_net_i_delta_21dma > 3 AND callput = 'CP'

Holding-period semantics (fire within the last 22 trading days AND same-metric z has not gone negative since) can be reconstructed with a self-lag or a rolling max(z) window function. The pipeline does not persist this because the same rule can be expressed on any of the 10 z-columns × any threshold, and pre-materialising that cross-product would add 60+ columns of low-density flags.

Underlying selector

Provide at most one of figi or traded_underlying. If traded_underlying is used, it is resolved server-side to the corresponding FIGI. Supplying both returns HTTP 400. An unknown traded_underlying returns HTTP 404, not an empty []. The underlying selector may be omitted entirely when at least one stock_metadata filter (is_etf / is_adr / industry / sector) is provided; the response then covers every figi matching the metadata predicate for the date range.

Trial tier. Must supply an explicit figi or traded_underlying — a broad sector / industry-only sweep returns HTTP 400. Each distinct FIGI touched counts toward the 25-figi-per-UTC-day cap; the 26th distinct FIGI returns HTTP 403 with the touched list echoed back. Daily history clamped to 730 days. For broad cohort exploration use /v0/grouped_flow_signal/01_day (not figi-capped). See Trial vs. paid tier.

Response columns

date, figi, traded_underlying, callput, the 10 raw daily nets, 10 _21dma (21-day rolling sums), and 10 z-scores (against the trailing 2-year rolling μ/σ of the 21dma, computed at query time). Plus industry / sector / isAdr / isEtf from stock_metadata (ranged endpoint intentionally omits companyName).

Response headers & errors

Every /v0/* list response carries X-Row-Count: <N>. Unknown query parameters return 400 Bad Request with the list of accepted names in the body.

Parameters

NameTypeDescription
figistringComposite FIGI of the underlying. Mutually exclusive with `traded_underlying`. Optional when a `stock_metadata` filter is supplied.
traded_underlyingstringStock ticker; resolved via the `dict_symbol_to_figi` dictionary. Mutually exclusive with `figi`. Optional when a `stock_metadata` filter is supplied.
callputC | P | CP`C` (calls only), `P` (puts only), or `CP` (default; synthetic combined view). CP is pre-computed: directional nets are `C − P` (bullish call flow + bearish put flow both express bullish conviction, so nets combine sign-flipped); greeks are `C + P` (naturally signed by option type). **The default is `CP` for a reason: every source row triples up on callput and omitting the filter triple-counts.** Case-insensitive on input (`c`, `Cp`, `CP`, `p` all accepted); the emitted row always carries the canonical upper-case form.
start_datestringEarliest trading day (inclusive, YYYY-MM-DD). Trial tiers are clamped to a 730-day window.
end_datestringLatest trading day (inclusive, YYYY-MM-DD).
is_etfbooleanFilter to ETFs (`true`) or non-ETFs (`false`). Applied to the joined `stock_metadata` row.
is_adrbooleanFilter to ADRs (`true`) or non-ADRs (`false`).
industrystringFilter on `stock_metadata.industry`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known industry returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown industry: 'semis'. Did you mean 'Semiconductors'?"`).
sectorstringFilter on `stock_metadata.sector`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known sector returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown sector: 'technolgy'. Did you mean 'Technology'?"`).
formatjson | parquet`json` (default) or `parquet` (binary, ~5–10× smaller).
GET/v0/underlying_flow_signals/01_day/snapshot

Underlying Flow Signals: Daily Snapshot

Single-day × callput cross-sectional snapshot of daily flow signals for every underlying with a row on `date`.

Description

Cross-sectional snapshot: returns one row per (figi, callput) with a signal-table row on the requested date, joined to the PIT stock_metadata row for (figi, date).

No underlying selector is required; the response covers the full universe on the requested callput (default CP). Optional stock_metadata-backed filters (is_etf / is_adr / industry / sector) narrow the returned figis.

Trial tier: snapshot endpoints are not available. This endpoint returns HTTP 403 for trial accounts (same treatment as intraday). A wide snapshot would charge every returned FIGI against the 25-figi-per-UTC-day cap in a single call, so the snapshot surface is blocked outright on trial. Use the ranged /v0/underlying_flow_signals/01_day endpoint with an explicit figi / traded_underlying filter for per-underlying signal pulls, or /v0/grouped_flow_signal/01_day/snapshot (not figi-capped) for broad screening. See Trial vs. paid tier.

Typical unfiltered response is ~4,000–5,000 rows per trading day per callput variant. Adding a z-score filter (e.g. WHERE z_net_i_delta_21dma > 2) is left to the client; the endpoint returns the full universe row-set and clients filter downstream. Prefer format=parquet for anything larger than a browser preview.

Callput semantics

Same as the ranged endpoint: C, P, or CP. Default is CP when the callput parameter is omitted. Case-insensitive on input. See /v0/underlying_flow_signals/01_day for the C−P / C+P synthesis convention. Omitting the callput filter would triple-count; the endpoint enforces the default.

Deriving fire predicates

The endpoint returns the full universe row-set for the requested date × callput; filter downstream on the emitted z-score columns:

rows = requests.get(...).json()
fires = [r for r in rows if r["z_net_i_delta_21dma"] and r["z_net_i_delta_21dma"] > 2]

Response columns

The 34 signal-table columns (4 keys + 30 flow — 10 raw nets, 10 21-day rolling sums, 10 z-scores against the trailing 2-year μ/σ of the 21dma) plus companyName / industry / sector / isAdr / isEtf from stock_metadata.

Response headers & errors

Every /v0/* list response carries X-Row-Count: <N>. Unknown query parameters return 400 Bad Request with the list of accepted names in the body.

Parameters

* required
NameTypeDescription
date *stringTrading day (YYYY-MM-DD) to snapshot.
callputC | P | CP`C` (calls only), `P` (puts only), or `CP` (default; synthetic combined view). CP is pre-computed: directional nets are `C − P` (bullish call flow + bearish put flow both express bullish conviction, so nets combine sign-flipped); greeks are `C + P` (naturally signed by option type). **The default is `CP` for a reason: every source row triples up on callput and omitting the filter triple-counts.** Case-insensitive on input (`c`, `Cp`, `CP`, `p` all accepted); the emitted row always carries the canonical upper-case form.
figistringOptional comma-separated list of composite FIGIs to restrict the snapshot to those symbols (paid tier only — trials are blocked at the router with HTTP 403 before this filter is evaluated).
is_etfbooleanFilter to ETFs (`true`) or non-ETFs (`false`). Applied to the joined `stock_metadata` row.
is_adrbooleanFilter to ADRs (`true`) or non-ADRs (`false`).
industrystringFilter on `stock_metadata.industry`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known industry returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown industry: 'semis'. Did you mean 'Semiconductors'?"`).
sectorstringFilter on `stock_metadata.sector`. Case-insensitive on input; the emitted row carries the canonical mixed-case form. A value that doesn't match any known sector returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. `"Unknown sector: 'technolgy'. Did you mean 'Technology'?"`).
formatjson | parquet`json` (default) or `parquet` (recommended for full-universe pulls).
GET/v0/grouped_flow_signal/01_day

Grouped Flow Signal: Daily

Group-level (sector / industry / etf / adr / single_stock / all) × callput daily flow-signal rollups.

Description

Retrieve group-level daily flow-signal rows from grouped_flow_signal_01_day. One row per (date, group_key, callput). No stock_metadata join; the group_key ARE the classification.

The source table sums the underlying_flow_signal_01_day raw nets across every figi in the selected bucket, then re-runs the 21-day rolling sum + 2-year z-score cascade on the group-level series. Row shape mirrors underlying_flow_signal_01_day (10 raw nets + 10 _21dma + 10 z-scores; the raw μ / σ of the trailing 2-year window are computed at query time and are NOT distributed as separate columns). Callput semantics are identical (default CP, C−P for nets, C+P for greeks).

Single-axis contract

Query with one group_type value — either a canonical mixed-case sector or industry name (e.g. "Technology", "Software—Application") or one of the rollup literals "etf" / "adr" / "single_stock" / "all". There is no paired second parameter — the family (sector vs industry vs rollup) is inferred from the value itself. The response row echoes the same value in the group_key STRING column; matching is case-insensitive on input and the wire always carries the canonical mixed-case form.

Signal formula

  • _21dma = 21-trading-day rolling SUM of the group-level raw net, scoped to (group_key, callput).
  • z_net_*_21dma = z-score of _21dma against its trailing 2-year (504 trading day) rolling mean and sample standard deviation, computed at query time. Minimum 63 non-null observations in the trailing window required, else null. Rounded to 2dp on emit. The raw μ / σ are not distributed as separate columns.

Every industry-day is emitted with populated rolling-window fields (21dma, z_*) and n_figi exposed on the row. The 21dma and 2y baseline include ALL days regardless of n_figi — thin days contribute their (smaller) raw values to the trailing window, so each industry's z is calibrated against its own historical breadth mix and populates on every day. Cross-industry z magnitudes aren't directly comparable when breadth profiles differ meaningfully — filter on n_figi at query time when comparability matters. Sector, etf, adr, single_stock, and all branches always clear the 10-FIGI mark and are effectively unchanged; only industries (Conglomerates, Regulated Water, Uranium, etc.) are materially affected.

⚠ Not point-in-time for group classification

The sector, industry, ETF, and ADR labels used to bucket each figi come from the CURRENT stock_metadata phase (row with phase_end = 2099-12-31), applied uniformly to every historical date. Sector / industry labels churn (10–20 reclassifications per year across the covered universe); clients expect historical rollups to use today's classification for consistency with the current portfolio view.

The signal rolling window itself is per bucket; only the group-membership assignment is applied retroactively.

If you need a fully point-in-time sector rollup, bucket at query time against stock_metadata filtered by date BETWEEN phase_start AND phase_end, using the underlying_flow_signal_01_day table directly.

Group-type branches

  • sector: one row per distinct sector name per (date, callput). group_key is the mixed-case sector string (~11 distinct values). ETFs and ADRs are excluded from sector rollups — they have their own top-level etf and adr branches, and their FMP-reported "issuer sector" is typically the legal-entity domicile (Financial Services for most ETFs), not the underlying-exposure sector.
  • industry: one row per distinct industry name per (date, callput). group_key is the mixed-case industry string (~150 distinct values matching the FMP taxonomy). Same ETF/ADR exclusion. n_figi is exposed on every row so consumers can filter thin-breadth days at query time — see "Thin-industry handling" below.
  • etf: every figi with isEtf = 'true' on the current phase, aggregated. group_key = literal "etf".
  • adr: every figi with isAdr = 'true' on the current phase, aggregated. group_key = literal "adr".
  • single_stock: every figi that is neither ETF nor ADR (figis missing from stock_metadata default here). group_key = literal "single_stock".
  • all: every figi in the underlying flow table. group_key = literal "all".

Aggregate identities

single_stock + etf + adr = all exactly per (date, callput). sum(sector) ≈ single_stock per (date, callput) within < 1% of gross flow (residual = single-stock figis without a current sector label). Note: because ETFs and ADRs no longer contribute to sector / industry rollups, sum(sector) ≠ all — the diff is the etf + adr contribution. sum(industry) ≈ single_stock too — every industry with any flow now emits a row (raw nets populated), and the honest sum lines up within the same < 1% single-stock-without-industry residual.

Thin-industry handling

Every industry-day is emitted with populated raw daily nets AND populated rolling / z fields. n_figi is exposed on every row so consumers can filter thin-breadth days at query time. The 21dma and 2y baseline include ALL days regardless of n_figi — thin days contribute their (smaller) raw values to the trailing window and to the 2y μ/σ used to z-score. Each industry's z-score is therefore calibrated against its own historical breadth mix — a chronically-thin industry's baseline is set to its own thin-day scale, so z fires when flow is unusually large for that industry. Cross-industry z magnitudes aren't directly comparable when breadth profiles differ meaningfully. Structurally-thin industries (historically ~46, including Copper, Uranium, Consulting Services, Regulated Water, Conglomerates, Waste Management, Railroads) now emit continuous rows with populated raw flow AND populated z-scores rather than being dropped entirely. Sector, etf, adr, single_stock, and all branches always exceed the threshold and the caveat is a no-op for them.

Valid group_key: sector (~11 total)

Sector matching is case-insensitive on input; the row-emitted group_key is always the canonical mixed-case form. For a one-line description of each sector, see the Data dictionary → grouped_flow_signal_01_day section.

Basic Materials, Communication Services, Consumer Cyclical, Consumer Defensive, Energy, Financial Services, Healthcare, Industrials, Real Estate, Technology, Utilities.

Valid group_key: industry (~150 total)

Case-insensitive on input; canonical mixed-case on the wire. Full enumerated list in the Data dictionary → grouped_flow_signal_01_day section (with a one-line description of each). Passing a group_type value that doesn't match any known sector / industry name (or rollup literal) returns HTTP 400 with a fuzzy-match suggestion in the error body (e.g. requesting "Semi" returns "Did you mean 'Semiconductors'?").

Roughly 46 named industries are structurally thin (never clear the 10-FIGI mark). They appear in the response every day with raw daily nets AND rolling / z fields populated; the z magnitude on those rows is not directly comparable to full-breadth days (see the thin-industry handling section above — filter on n_figi at query time when comparability matters). The sector / industry enumerations may drift as existing figis are reclassified or new labels are added. For the live authoritative list, call the snapshot endpoint with category=sector (or industry) and take the distinct group_key values from the response.

Response columns

date, group_key, callput, n_figi, plus the 30 group-level flow columns (10 raw nets, 10 _21dma rolling sums, 10 z-scores; same layer shape as underlying_flow_signal_01_day; no metadata columns, none needed).

Response headers & errors

Every /v0/* list response carries X-Row-Count: <N>. Unknown query parameters return 400 Bad Request with the list of accepted names in the body.

Parameters

* required
NameTypeDescription
group_type *stringA single canonical `group_key` value. Either a mixed-case sector name (e.g. `Technology`, `Financial Services`), a mixed-case industry name (e.g. `Software—Application`, `Semiconductors`), or one of the rollup literals `etf` / `adr` / `single_stock` / `all`. Case-insensitive on input; the row-emitted `group_key` is always the canonical mixed-case form. A value that doesn't match any known name returns HTTP 400 with a fuzzy-match suggestion in the body (e.g. requesting `Semi` returns `Did you mean 'Semiconductors'?`). See the endpoint description for the full enumerated lists (~11 sectors, ~150 industries, plus 4 rollup literals). Time series is gap-free — industries with <10 contributing FIGIs on the day still emit rows with raw nets populated but rolling / z fields NULL. To sweep a whole family in one call, use the snapshot endpoint's `category` parameter instead.
callputC | P | CP`C` (calls only), `P` (puts only), or `CP` (default; synthetic combined view). CP is pre-computed: directional nets are `C − P` (bullish call flow + bearish put flow both express bullish conviction, so nets combine sign-flipped); greeks are `C + P` (naturally signed by option type). **The default is `CP` for a reason: every source row triples up on callput and omitting the filter triple-counts.** Case-insensitive on input (`c`, `Cp`, `CP`, `p` all accepted); the emitted row always carries the canonical upper-case form.
start_datestringEarliest trading day (inclusive, YYYY-MM-DD). Trial tiers are clamped to a 730-day window; this endpoint is not figi-capped, so it is the recommended surface for broad cohort exploration on the trial tier.
end_datestringLatest trading day (inclusive, YYYY-MM-DD).
formatjson | parquet`json` (default) or `parquet` (recommended for full-history sector sweeps).
GET/v0/grouped_flow_signal/01_day/snapshot

Grouped Flow Signal: Daily Snapshot

Single-day cross-sectional snapshot of group-level daily flow-signal rows. Omit both filters to return every group_key (~168 rows / callput / day across ~11 sectors, ~150 industries, and 4 rollup literals).

Description

Cross-sectional snapshot of grouped_flow_signal_01_day on a single trading day. Every input parameter is optional; with no filters the endpoint returns every group_key with any flow on the latest available day (~168 rows on the default callput = 'CP').

  • date omitted → latest available trading day.
  • Neither category nor group_type set → every group_key with flow returned in the same payload (~11 sectors + up to ~150 industries + etf + adr + single_stock + all, on the default callput = 'CP'). Industry rows with fewer than 10 contributing FIGIs on the requested date carry populated raw nets but NULL rolling / z fields (see "Thin-industry handling" below) — they are NOT dropped.
  • category set → restrict the payload to a whole family: industry returns every industry row (up to ~150), sector returns just the ~11 sector rows, rollup returns the 4 rollup rows, all returns every family. Snapshot-only parameter.
  • group_type set → restrict to a single group_key value (a mixed-case sector / industry name or one of the rollup literals etf / adr / single_stock / all). Case-insensitive on input; a value that doesn't match any known name returns HTTP 400 with a fuzzy-match suggestion in the body.
  • category and group_type are mutually exclusive. Pass one or the other, never both.

Response shape is identical to the ranged endpoint: single group_key STRING column + date + callput + 30 flow columns per row (10 raw nets, 10 _21dma rolling sums, 10 z-scores against the trailing 2-year μ/σ of the 21dma), no stock_metadata join. See the ranged endpoint for the full sector / industry enumeration, the CP synthesis rule, and the z-score formula.

⚠ Not point-in-time for group classification

Same load-bearing caveat as the ranged endpoint: the sector / industry / ETF / ADR labels used to bucket each figi come from the CURRENT stock_metadata phase (row with phase_end = 2099-12-31), applied uniformly to every historical date. The signal rolling window (21dma + 2y baseline) is per bucket; only the group-membership assignment is applied retroactively.

For strict-PIT sector rollups, use the underlying_flow_signals/01_day/snapshot endpoint and bucket client-side against a PIT stock_metadata snapshot.

ETF / ADR exclusion from sector & industry

Sector and industry aggregations EXCLUDE ETFs and ADRs (which get their own top-level etf / adr rollups). FMP tags ETFs with the issuer's legal-entity sector (typically Financial Services), which is meaningless for underlying exposure — so pooling ETFs into sector rollups would inflate Financial Services by 85%+ of gross flow. The exclusion is applied at the underlying-figi filter step, not on the emitted rows.

Thin-industry handling

Every industry-day is emitted with populated raw daily nets AND populated rolling / z fields (21dma, z_*). n_figi is exposed on every row so consumers can filter thin-breadth days at query time. The 21dma and 2y baseline include ALL days regardless of n_figi, so each industry's z is calibrated against its own historical breadth mix and populates on every day. Cross-industry z magnitudes aren't directly comparable when breadth profiles differ meaningfully — filter on n_figi at query time when comparability matters. Structurally-thin industries (historically ~46, including Copper, Uranium, Consulting Services, Regulated Water, Conglomerates, Waste Management, Railroads) now emit continuous rows with populated raw flow AND populated z-scores rather than being absent. Sector, etf, adr, single_stock, and all branches always exceed the 10-FIGI mark and the caveat is a no-op for them.

Response headers & errors

Every /v0/* list response carries X-Row-Count: <N>. Unknown query parameters return 400 Bad Request with the list of accepted names in the body.

Parameters

NameTypeDescription
datestringTrading day (YYYY-MM-DD) to snapshot. Defaults to the latest available day.
categoryindustry | sector | rollup | allFamily-level filter. One of `industry` / `sector` / `rollup` / `all`. Restricts the snapshot to the intended grouping family — `industry` returns every industry row with flow on the day (up to ~150), `sector` returns just the ~11 sector rows, `rollup` returns the `etf` / `adr` / `single_stock` / `all` rollup rows, `all` returns every family. Mutually exclusive with `group_type` — pass one or the other, not both. Snapshot-only; not accepted on the ranged endpoint.
group_typestringSingle-key filter — a canonical `group_key` value (mixed-case sector or industry name, or one of the rollup literals `etf` / `adr` / `single_stock` / `all`). Case-insensitive on input; the row-emitted `group_key` is always the canonical mixed-case form. A value that doesn't match any known name returns HTTP 400 with a fuzzy-match suggestion in the body. Mutually exclusive with `category` — use `group_type` when you want a single specific `group_key`, `category` when you want a whole family.
callputC | P | CP`C` (calls only), `P` (puts only), or `CP` (default; synthetic combined view). CP is pre-computed: directional nets are `C − P` (bullish call flow + bearish put flow both express bullish conviction, so nets combine sign-flipped); greeks are `C + P` (naturally signed by option type). **The default is `CP` for a reason: every source row triples up on callput and omitting the filter triple-counts.** Case-insensitive on input (`c`, `Cp`, `CP`, `p` all accepted); the emitted row always carries the canonical upper-case form.
formatjson | parquet`json` (default) or `parquet` (binary, ~5–10× smaller).
GET/v0/stock_mapping

Stock Mapping

Ticker → FIGI / sector / industry reference table (~42k rows).

Description

Retrieve the stock_mapping reference table, which maps every ticker Masscrest tracks to its identifiers and company metadata (FIGI, ISIN, sector, industry, ETF/ADR/fund flags) and includes historical ticker changes via phase_start / phase_end windows. Filters are optional and combine with AND for exact match.

The full table is ~42,000 rows. Default limit is 10 rows to keep interactive docs responsive; pass limit=0 for the full ~42k-row table. When the default limit clipped the rowset, the response carries X-Truncated: true (absent when the full result fit).

Response headers (shared with every /v0/* list endpoint)

  • X-Row-Count: <N> — number of rows in the response, regardless of format.
  • X-Truncated: truestock_mapping only: default limit=10 clipped the rowset.

Error responses

  • 400 Bad Request — unknown query parameter. The response body enumerates the accepted names for this endpoint.

Parameters

NameTypeDescription
terminal_symbolstringCurrent / most recent ticker for the company.
figistringComposite FIGI identifier.
isinstringISIN code.
sectorstringSector classification.
industrystringIndustry classification.
limitintegerMaximum number of rows to return. `0` means no cap (full table). Defaults to `10`.
formatjson | parquetResponse format. `json` returns a JSON array (default). `parquet` streams a binary Parquet file (~5–10× smaller, faster to parse).
Documentation · 03

Flat files

Read-only bucket access for systematic pipelines — warehouse integrations (recommended) or HMAC key pairs.

Warehouse integrations

BUCKETgs://prod-masscrest/v0

Flat-files delivery is for clients whose corporate environment cannot make outbound calls to api.masscrest.com. Your pipeline reads Parquet exports directly from gs://prod-masscrest/v0/. The recommended path is a warehouse integration: bind a Google service account minted by Snowflake, Databricks, BigQuery, or your own GCP runtime and Masscrest grants read-only IAM against it — no HMAC pair in the loop. HMAC keys remain available as a fallback for Python / CLI users (section 5 below).

Warehouse integrations not enabled

Bucket delivery is a separate entitlement from the REST API. Contact us at sales@masscrest.com to enable it on your account.

Trial-tier bucket

Different bucket on trial

Flat-files delivery on the trial tier requires the programmatic entitlement (opt-in per account; contact sales). Enabled trials get HMAC keys against gs://prod-masscrest-trial not gs://prod-masscrest. The trial bucket is an aggregates-only, daily-only mirror lagged 180 days behind current, with fresh reference metadata. Trials without programmatic receive HTTP 403 on any flat-files call.

See Trial vs. paid tier on the Data dictionary for the full policy.

Bucket layout

All flat files land under gs://prod-masscrest/v0/, hive-partitioned by year=YYYY/date=YYYY-MM-DD/ where date is the trading date. New partitions for date = T-1 land in the 05:00–09:00 UTC window on calendar day T. The reference tables (stock_mapping/) are full-snapshot reloads: same filename pattern each day, no partition hierarchy. _manifests/year=YYYY/date=YYYY-MM-DD/manifest.json is written as the very last step of the daily publish and is the delivery-complete signal for polling clients (see the Data dictionary overview for the payload schema).

gs://prod-masscrest/v0/
├── _manifests/
│   └── year=YYYY/date=YYYY-MM-DD/manifest.json    # delivery-complete signal, written last
├── stock_mapping/
│   ├── stock_metadata_*.parquet          # full snapshot, rewritten daily
│   └── split_factors_*.parquet           # full snapshot, rewritten daily
├── px_data/
│   ├── 01_day/year=YYYY/date=YYYY-MM-DD/*.parquet
│   └── 10_min/year=YYYY/date=YYYY-MM-DD/*.parquet
├── option_flows/
│   ├── 01_day/year=YYYY/date=YYYY-MM-DD/*.parquet
│   └── 10_min/year=YYYY/date=YYYY-MM-DD/*.parquet
├── underlying_option_flows/              # underlying-level rollups
│   ├── 01_day/year=YYYY/date=YYYY-MM-DD/*.parquet
│   └── 10_min/year=YYYY/date=YYYY-MM-DD/*.parquet
├── underlying_flow_signal/               # underlying-level flow-signal derivatives
│   └── 01_day/year=YYYY/date=YYYY-MM-DD/*.parquet
└── grouped_flow_signal/
    └── data-*.parquet                    # full snapshot, rewritten daily

Trial-tier bucket

Trial accounts with the programmatic entitlement land on gs://prod-masscrest-trial instead of the production bucket. The tree is a strict subset: aggregate flow tables only, daily grain only, and every partition is 180 days behind the current session.

gs://prod-masscrest-trial/v0/
├── stock_mapping/
│   ├── stock_metadata_*.parquet          # fresh; full mirror of production
│   └── split_factors_*.parquet           # fresh; full mirror of production
├── underlying_option_flows/
│   └── 01_day/year=YYYY/date=YYYY-MM-DD/*.parquet     # T-180d lag
├── underlying_flow_signal/
│   └── 01_day/year=YYYY/date=YYYY-MM-DD/*.parquet     # T-180d lag
├── grouped_flow_signal/
│   └── data-*.parquet                    # fresh; full mirror (not figi-capped)
└── px_data/
    └── 01_day/year=YYYY/date=YYYY-MM-DD/*.parquet     # T-180d lag

The trial bucket does not carry contract-level option_flows/*, 10-minute intraday *_10_min/* partitions, or any positioning data. A daily append at 04:00 UTC extends the lagged tables by one partition (T-180d). Reference data (stock_mapping/ and grouped_flow_signal/) refreshes on the same daily cadence as the production bucket. On upgrade to a paid account, your trial HMAC key is revoked at the moment of upgrade — call POST /v0/flat-files/credentials/rotate to mint a new key against gs://prod-masscrest, then re-point your ~/.boto / gsutil config to the production bucket.

Split-adjusted price and volume: local reconstruction

px_data/01_day and px_data/10_min ship raw unadjusted close and volume per (date, figi, symbol). Split-adjustment is decoupled: stock_mapping/split_factors_*.parquet (a full snapshot of split_factors) is the authoritative source, refreshed daily. This lets you keep raw prints for audit and derive adj_close / adj_volume at query time.

split_factors schema

One row per (figi, valid_from, valid_to), non-overlapping intervals covering the FIGI's timeline. The cum_split_factor column holds the cumulative product of every split from valid_from + 1 day forward within the entity's active phase. To convert a raw close on date X into a current-shares-equivalent price, divide by cum_split_factor; to convert raw volume, multiply. The most recent interval always carries 1.0. No-split entities carry a single row spanning their full active phase with cum_split_factor = 1.0; same shape, no special case.

Ticker context

For time series where the ticker has changed (FB → META, TWTR → X, etc.), join on figi (stable), never on symbol (point-in-time and recyclable). stock_metadata provides (figi, symbol, phase_start, phase_end, terminal_symbol, ...); join with date BETWEEN phase_start AND phase_end to attach the correct symbol / sector / ETF flag for the session.

Snippet A: pandas + pyarrow

import pandas as pd
import s3fs
# requires: pip install pandas pyarrow s3fs
# s3fs signs with the HMAC pair against the S3-compatible GCS endpoint.
# Do not use gcsfs or google-cloud-storage here — those libraries
# authenticate via OAuth / ADC and reject HMAC credentials.
fs = s3fs.S3FileSystem(
    key="<your HMAC access key>",
    secret="<your HMAC secret>",
    client_kwargs={"endpoint_url": "https://storage.googleapis.com"},
)

d = "2026-07-24"
px = pd.read_parquet(
    f"s3://prod-masscrest/v0/px_data/01_day/year=2026/date={d}/",
    filesystem=fs,
)
sh_files = ["s3://" + p for p in fs.glob("prod-masscrest/v0/stock_mapping/split_factors_*.parquet")]
md_files = ["s3://" + p for p in fs.glob("prod-masscrest/v0/stock_mapping/stock_metadata_*.parquet")]
sh = pd.read_parquet(sh_files, filesystem=fs,
                     columns=["figi", "valid_from", "valid_to", "cum_split_factor"])
md = pd.read_parquet(md_files, filesystem=fs,
                     columns=["figi", "symbol", "phase_start", "phase_end"])

px["date"] = pd.to_datetime(px["date"]).dt.date
sh["valid_from"] = pd.to_datetime(sh["valid_from"]).dt.date
sh["valid_to"] = pd.to_datetime(sh["valid_to"]).dt.date

merged = px.merge(sh, on="figi", how="left")
in_range = (merged["date"] >= merged["valid_from"]) & (merged["date"] <= merged["valid_to"])
merged = merged[in_range].copy()
merged["adj_close"] = merged["close"] / merged["cum_split_factor"]
merged["adj_volume"] = merged["volume"] * merged["cum_split_factor"]

# Optional: attach point-in-time symbol from stock_metadata.
merged = merged.merge(md, on="figi", how="left", suffixes=("", "_md"))
merged = merged[(merged["date"] >= merged["phase_start"]) & (merged["date"] <= merged["phase_end"])]
out = merged[["figi", "date", "symbol", "close", "volume", "adj_close", "adj_volume"]]

Snippet B: DuckDB

INSTALL httpfs;
LOAD httpfs;
SET s3_endpoint='storage.googleapis.com';
SET s3_url_style='path';
SET s3_access_key_id='<your HMAC access key>';
SET s3_secret_access_key='<your HMAC secret>';

WITH px AS (
  SELECT date, figi, symbol, close, volume
  FROM read_parquet('s3://prod-masscrest/v0/px_data/01_day/year=2026/date=2026-07-24/*.parquet')
),
sh AS (
  SELECT figi, valid_from, valid_to, cum_split_factor
  FROM read_parquet('s3://prod-masscrest/v0/stock_mapping/split_factors_*.parquet')
),
md AS (
  SELECT figi, symbol AS pit_symbol, phase_start, phase_end
  FROM read_parquet('s3://prod-masscrest/v0/stock_mapping/stock_metadata_*.parquet')
)
SELECT
    px.figi,
    px.date,
    COALESCE(md.pit_symbol, px.symbol) AS symbol,
    px.close,
    px.volume,
    px.close  / sh.cum_split_factor AS adj_close,
    px.volume * sh.cum_split_factor AS adj_volume
FROM px
LEFT JOIN sh USING (figi)
LEFT JOIN md USING (figi)
WHERE px.date BETWEEN sh.valid_from AND sh.valid_to
  AND px.date BETWEEN md.phase_start AND md.phase_end;

Precision

cum_split_factor is stored at 12 decimal places to preserve heavy reverse-split stacks (e.g. UVXY's true factor at 1900-01-01 is ~6.7e-11). Do not round to 6 decimals; that historically truncated ~200 heavy-reverse-splitter FIGIs to 0.0 and silently zeroed downstream adjustments. Also treat cum_split_factor IS NULL as "no valid adjustment for this period"; never coerce to 0.0, as the extreme tail of penny-stock reverse-split chains underflows Float64 and is emitted as NULL by design.

Applying the same adjustment to flows

option_flows_01_day and its 10-minute sibling already carry a split-adjusted underlying price via the joined adj_close field; clients using the provided column need no local derivation. To overlay flows on your own price series, or compute ratios like flow_per_shares_outstanding where the denominator is your own split-adjusted volume, apply the same close / cum_split_factor and volume * cum_split_factor logic from Section 3 against the raw px files. Join keys are identical: figi for the row, (valid_from, valid_to) on split_factors for the factor. See the dictionary entries for option_flows_01_day and px_01_day for the full column contracts.

HMAC access keys (Python / CLI users)

For local scripts, notebooks, and edge runtimes that can't use a warehouse integration, Masscrest also issues HMAC key pairs that authenticate against the GCS S3-compatible endpoint. Provision or rotate the pair from the HMAC access key card on the Flat files section of the docs page. This path is a fallback — for production pipelines, prefer the warehouse integration in section 1 (no secret material, shorter blast radius on rotation, native to the SQL engine you're already running).

S3-compatible access

GCS speaks the S3 protocol at https://storage.googleapis.com. HMAC keys authenticate against the bucket the same way native S3 keys do. Bucket name is prod-masscrest (drop the gs:// prefix); every other path segment above is a key prefix. Trial accounts substitute prod-masscrest-trial throughout the snippets below — the client library, endpoint URL, and signing flow are identical.

import boto3
from datetime import date, timedelta

s3 = boto3.client(
    "s3",
    endpoint_url="https://storage.googleapis.com",
    aws_access_key_id="<your HMAC access key>",
    aws_secret_access_key="<your HMAC secret>",
    region_name="auto",
)

d = date.today() - timedelta(days=1)
prefix = f"v0/px_data/01_day/year={d.year}/date={d.isoformat()}/"
for obj in s3.list_objects_v2(Bucket="prod-masscrest", Prefix=prefix).get("Contents", []):
    print(obj["Key"], obj["Size"])

From AWS (Lambda, EC2, ECS, Glue)

The same boto3 client works unchanged from any AWS runtime. No GCP setup is required; the Masscrest HMAC key is just another set of S3 credentials, and the --endpoint-url override reroutes the request to storage.googleapis.com. The recommended pattern is to store your Masscrest HMAC pair in AWS Secrets Manager (or SSM Parameter Store) and fetch it at runtime:

import boto3, json

secrets = boto3.client("secretsmanager", region_name="us-east-1")
creds = json.loads(secrets.get_secret_value(SecretId="masscrest/hmac")["SecretString"])

s3 = boto3.client(
    "s3",
    endpoint_url="https://storage.googleapis.com",
    aws_access_key_id=creds["access_key"],
    aws_secret_access_key=creds["secret"],
    region_name="auto",
)

The AWS CLI works with the same override:

aws s3 --endpoint-url https://storage.googleapis.com ls s3://prod-masscrest/v0/
aws s3 --endpoint-url https://storage.googleapis.com cp \
    s3://prod-masscrest/v0/px_data/01_day/year=2026/date=2026-07-24/ ./local/ --recursive

Common downstream patterns from AWS:

  • Sync into your own S3 for Athena or Redshift Spectrum. Schedule a small EC2, Lambda, or Glue job that copies each day's partition into a bucket you own, then query the parquet directly. No format translation, no re-shaping needed.
  • External stages in Snowflake or Databricks on AWS. Both support S3-compatible sources; point the external stage at s3://prod-masscrest/... with the --endpoint-url override and load daily.
  • Step Functions or Glue Workflows. Poll s3://prod-masscrest/v0/_manifests/year=YYYY/date=YYYY-MM-DD/manifest.json as the "day is ready" signal; the manifest also enumerates every parquet the run wrote (fields: workflow_run_id, trading_date, published_at, files: [{path, byte_count}]), so it doubles as the file list for your job.
Not enabled

Flat-files access not enabled. Bucket delivery is a separate entitlement from the REST API. Contact us at sales@masscrest.com to enable it on your account.

Notes

  • Credentials are read-only and list-only. They cannot upload, delete, or overwrite objects.
  • Disabling flat-files access on the account revokes the key server-side immediately.
  • For high-volume downloads, run your workload in the same region as the bucket to avoid egress charges.
Documentation · 04

MCP server

Query Masscrest data conversationally from Claude, ChatGPT, and other MCP-aware clients.

MCPhttps://mcp.masscrest.com/mcp

What is the Masscrest MCP server?

MCP (Model Context Protocol) is an open standard that lets an LLM call external tools and pull in curated context inside a chat. The Masscrest MCP server exposes our proprietary options-flow and price datasets as tools an assistant like Claude or ChatGPT can invoke on your behalf, so you can ask questions in natural language and get back live, sourced answers grounded in Masscrest data.

The server does two things. First, it exposes 16 data tools that pull live from the Masscrest API: flow signals, price history, portfolio snapshots, grouped/sector rollups, and research search. Second, it ships curated context: analyst playbooks (starting with investigate-flow-event), a cohort-behaviour priors resource (retail, institutional, and dealer behavioural facts sourced from Masscrest research), a canonical event taxonomy, and every published Masscrest research piece. Your assistant gets the data plus Masscrest's editorial framing on how to read it.

Authentication uses your existing Masscrest account. When your client first connects, it opens a browser window for you to sign in. No separate API key to paste, no extra credential to rotate.

What you can ask it

A few starter prompts to try after connecting:

  • "What did institutional flow look like on NVDA over the last 30 days?"
  • "Investigate the unusual call flow on TSLA yesterday."
  • "Show me the top 20 stocks in my portfolio by 5-day institutional net-flow z-score."
  • "Which sectors saw the largest institutional-delta z-score moves this week?"

The assistant chains tool calls automatically. A portfolio question typically routes through list_my_portfoliosportfolio_signal_snapshotsummarize_flow without you having to name the tools.

How to connect it

Pick your client below. First connect opens a browser sign-in.

Claude (claude.ai)

  1. Go to claude.ai/settings/connectors (or open Settings → Connectors from the sidebar).
  2. Click Add custom connector at the bottom of the page.
  3. Give it a name (Masscrest) and paste https://mcp.masscrest.com/mcp as the Remote MCP server URL.
  4. Click Add, then sign in with your Masscrest account when the browser window opens.

Claude Desktop

  1. Open Claude Desktop → SettingsConnectors.
  2. Click Add MCP server (or Add custom connector on newer builds).
  3. Enter Masscrest as the name and https://mcp.masscrest.com/mcp as the URL, then confirm.
  4. Complete the browser sign-in flow when it launches.

ChatGPT

Verified on the free tier — Pro / Team / Enterprise may differ.

  1. Open ChatGPT and go to Settings → Connectors.
  2. Click the + button in the top-right of the Connectors page.
  3. Choose Add custom connection.
  4. Enter the Masscrest MCP URL: https://mcp.masscrest.com/mcp.
  5. Click Create.
  6. Open the new connection and enable the individual tools (or toggle "always allow") — tool access is limited by default.

Use the full URL https://mcp.masscrest.com/mcp — the bare host https://mcp.masscrest.com returns 404, the /mcp suffix is required.

GitHub Copilot Chat (VS Code, Agent mode) reads the same .vscode/mcp.json config as the VS Code snippet in the sidebar, so Copilot is supported out of the box on VS Code; the JetBrains and Visual Studio plug-ins don't expose MCP yet.

What's inside

The MCP surface groups into:

  • Flow signals: daily and intraday institutional, retail, and dealer net-flow reads on any covered underlying.
  • Portfolio snapshots: the same signal snapshots served on your /portfolios/* pages, callable by name.
  • Grouped & sector rollups: flow aggregated to industry and sector levels.
  • Guided investigation: the investigate-flow-event playbook, which walks the assistant through the same cohort-behaviour checks a Masscrest analyst runs when an unusual flow event fires.

For per-endpoint field definitions, see the REST API and Data dictionary sections above. The MCP tools return the same schemas.

Notes

  • Access to individual tools mirrors your account entitlements. If a dataset isn't enabled on your subscription, the corresponding tool returns a permission error instead of data.
  • Trial accounts share the same tool surface as paid accounts. The REST API limits described in the Trial vs. paid tier table apply here too — the 25-figi-per-UTC-day cap, the intraday-endpoint 403, and the "must supply an explicit FIGI / underlying" rule surface as tool errors with a clear message, and the assistant will move on rather than retry. Cohort rollups (industry / sector / etf / adr / single_stock / all) are not figi-capped and are the natural surface for broad exploration on the trial tier.
  • Portfolio management (create, rename, add or remove tickers) stays in the web app. The MCP surface is read-only for data queries.
  • Signing out of your Masscrest account also revokes the MCP connector's access on the next tool call.
Trial

Trial limits also apply here

The MCP connector uses the same connection method as the paid tier — subject to the trial limits: 25 tickers / UTC day, 100 API calls / UTC day, and daily aggregates only (intraday tools return an error the assistant will read and stop retrying). Cohort tools (sector / industry / ETF / ADR / single-stock rollups) are not figi-capped and are the natural surface for broad exploration on the trial tier.

See Trial vs. paid tier on the Data dictionary for the full policy.