Portfolio & Sector Gamma Exposure: Aggregate GEX Across a Basket with One API Call | FlashAlpha

Portfolio & Sector Gamma Exposure: Aggregate GEX Across a Basket with One API Call

Most GEX tools are single-ticker. The FlashAlpha exposure basket endpoint rolls GEX, DEX, VEX and CHEX up across up to 50 symbols into one weighted aggregate in a single API call, apply portfolio weights, rank dominant names by contribution, read each holding's gamma regime, and frame dispersion trades. Growth plan, one quota debit per call.

T
Tomasz Dobrowolski Quant Engineer
Jun 13, 2026
33 min read
GEX DealerPositioning Portfolio SectorRotation API DeveloperGuide Python Dispersion

If you are searching for a portfolio gamma exposure API, a sector GEX aggregator, a multi-symbol dealer positioning endpoint, a basket gamma scanner, or a way to compute net GEX across a watchlist, this is the reference. The FlashAlpha /v1/exposure/basket endpoint takes a comma-separated list of up to 50 symbols (with optional weights) and returns one weighted net GEX / DEX / VEX / CHEX read for the whole basket, each constituent's per-greek contribution and gamma regime, and any symbols that lacked data reported back so you know exactly what fed the aggregate. It is on the Growth plan, and the entire basket costs a single rate-limit debit regardless of how many symbols you pass.

Read this first. The basket is a weighted roll-up of the same settled-OI exposure model behind the single-symbol GEX endpoint, it inherits that model's assumptions (dealer-short-calls / dealer-long-puts sign convention, settled open interest, no intraday flow adjustment). The JSON samples below are illustrative shapes with placeholder numbers, not live market data. One caveat to internalise up front: the endpoint does not pre-validate index/ETF tier gating per constituent, so confirm your entitlement covers every symbol you pass before trusting each row.


Why a Basket Endpoint Exists

Single-ticker GEX is solved. SpotGamma, Unusual Whales, GEXRadar, and FlashAlpha's own GEX endpoint all give you dealer gamma for SPY, or NVDA, or TSLA, one name at a time. What none of them give you directly is the aggregate. And the aggregate is where a lot of the real signal lives:

  • Sector regimes. "Is semis short gamma into this print?" is a basket question. The answer is the sum of NVDA, AMD, AVGO, MU, SMCI dealer gamma, not any one of them.
  • Book-level hedging. If you run a multi-name options book, your real dealer-flow exposure is the weighted sum across your holdings, not a single line item.
  • Dispersion framing. Index-vs-constituents trades hinge on the gap between aggregate basket exposure and the index's own exposure. You need both sides.

Building that aggregate by hand means calling a single-symbol GEX endpoint 50 times, normalising weights, summing four greeks, and computing each name's contribution share, per refresh, for every basket. The basket endpoint does it server-side in one call, for one quota debit.

Weighted net GEX / DEX / VEX / CHEX across up to 50 symbols in one call

Per-constituent contributions and gamma regimes. Growth plan. One rate-limit debit for the whole basket.

Get API Access

Quick Start: Aggregate a Basket

Pass symbols as a comma-separated list (max 50). Optionally pass weights of the same length, they are renormalised to sum to 1, so 0.4,0.3,0.3 and 4,3,3 are equivalent. Omit weights for equal weight.

from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_API_KEY")

# Mega-cap tech basket, custom weights (pass a list or a comma string)
data = fa.exposure_basket(
    ["AAPL", "MSFT", "NVDA", "AMZN", "META", "GOOGL"],
    weights=[0.28, 0.22, 0.20, 0.12, 0.10, 0.08],
)

agg = data["aggregate"]
print(f"Basket net GEX: {agg['net_gex']:,}  net DEX: {agg['net_dex']:,}")
print(f"Constituents counted: {data['constituent_count']}  missing: {data['missing_symbols']}")
import { FlashAlpha } from 'flashalpha';

const fa = new FlashAlpha('YOUR_API_KEY');

const data = await fa.exposureBasket({
    symbols: 'AAPL,MSFT,NVDA,AMZN,META,GOOGL',
    weights: '0.28,0.22,0.20,0.12,0.10,0.08',
});

const agg = data.aggregate;
console.log(`Basket net GEX: ${agg.net_gex.toLocaleString()}  net DEX: ${agg.net_dex.toLocaleString()}`);
console.log(`Missing: ${data.missing_symbols.join(', ') || 'none'}`);
using FlashAlpha;

var client = new FlashAlphaClient("YOUR_API_KEY");

// Weights are optional; returns a JsonElement
var data = await client.ExposureBasketAsync(
    new[] { "AAPL", "MSFT", "NVDA", "AMZN", "META", "GOOGL" },
    new[] { 0.28, 0.22, 0.20, 0.12, 0.10, 0.08 });

var agg = data.GetProperty("aggregate");
Console.WriteLine($"Basket net GEX: {agg.GetProperty("net_gex").GetInt64():N0}");
package main

import (
    "context"
    "fmt"

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

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

    data, err := fa.ExposureBasket(context.Background(),
        "AAPL,MSFT,NVDA,AMZN,META,GOOGL",
        flashalpha.WithBasketWeights("0.28,0.22,0.20,0.12,0.10,0.08"))
    if err != nil {
        panic(err)
    }

    agg := data["aggregate"].(map[string]interface{})
    fmt.Printf("Basket net GEX: %v\n", agg["net_gex"])
}
# Equal-weight basket (omit weights)
curl -H "X-Api-Key: YOUR_KEY" \
  "https://lab.flashalpha.com/v1/exposure/basket?symbols=NVDA,AMD,AVGO,MU,SMCI"

# Custom-weight basket (renormalised to sum to 1)
curl -H "X-Api-Key: YOUR_KEY" \
  "https://lab.flashalpha.com/v1/exposure/basket?symbols=AAPL,MSFT,NVDA&weights=0.4,0.3,0.3"
$ pip install flashalpha  |  npm install flashalpha  |  dotnet add package FlashAlpha  |  go get github.com/FlashAlpha-lab/flashalpha-go

Response Shape

The JSON below is an illustrative shape only, placeholder numbers, snake_case fields. The aggregate carries the four weighted net greeks; each constituent reports its renormalised weight, underlying price, per-greek values, a contribution percentage, and a gamma-regime tag. Symbols requested but missing from the data store come back in missing_symbols.

{
  "as_of": "2026-06-13T15:30:00Z",
  "constituent_count": 3,
  "missing_symbols": [],
  "aggregate": {
    "net_gex": 1640000000,
    "net_dex": -210000000,
    "net_vex":  580000000,
    "net_chex": 420000000
  },
  "constituents": [
    { "symbol": "AAPL", "weight": 0.40, "underlying_price": 210.45, "net_gex":  920000000, "net_dex": -120000000, "net_vex": 340000000, "net_chex": 260000000, "contribution_pct": 42.13, "regime": "positive_gamma" },
    { "symbol": "MSFT", "weight": 0.30, "underlying_price": 432.80, "net_gex":  410000000, "net_dex":  -60000000, "net_vex": 140000000, "net_chex":  90000000, "contribution_pct": 18.77, "regime": "positive_gamma" },
    { "symbol": "NVDA", "weight": 0.30, "underlying_price": 121.10, "net_gex":  310000000, "net_dex":  -30000000, "net_vex": 100000000, "net_chex":  70000000, "contribution_pct": 39.10, "regime": "positive_gamma" }
  ]
}

Key Fields

FieldWhat it means
aggregate.net_gex (and net_dex / net_vex / net_chex)The weighted sum Σ wᵢ × net_greekᵢ across surviving constituents, after weight renormalisation. This is the basket-level dealer exposure number.
constituent_countHow many symbols actually contributed, after dropping any with no data.
missing_symbols[]Symbols you requested that had no data (invalid, illiquid, or still warming). Check this, a half-missing basket is a misleading aggregate.
constituents[].weightThe renormalised weight applied to this name. Across the response these sum to 1, even after drops.
constituents[].contribution_pctThis name's share of basket GEX, on weighted GEX (not raw |net_gex|), 0-100. A tiny-weight, huge-GEX name does not dominate the display when its actual influence on the aggregate is small.
constituents[].regimepositive_gamma when that name's net GEX ≥ 0 (dealers dampen moves), else negative_gamma (dealers amplify).

One subtlety worth internalising: because weights renormalise after drops, a basket where three of five symbols are missing still returns a clean aggregate that looks complete. Always read missing_symbols and constituent_count before you trust the number.

How To: Scan a Sector for a Short-Gamma Regime

The single highest-value pattern: run a fixed sector basket on a schedule and alert when the aggregate flips into negative gamma. Negative basket gamma means dealers across the sector are positioned to amplify moves, the sector is primed to trend rather than mean-revert. This is a regime read you cannot get from any single ticker.

from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_API_KEY")

SECTORS = {
    "semis":     "NVDA,AMD,AVGO,MU,SMCI,TSM,QCOM,ASML",
    "megacap":   "AAPL,MSFT,NVDA,AMZN,META,GOOGL,TSLA",
    "banks":     "JPM,BAC,WFC,C,GS,MS",
}

for name, syms in SECTORS.items():
    d = fa.exposure_basket(syms)
    agg = d["aggregate"]
    regime = "NEGATIVE - trending/amplifying" if agg["net_gex"] < 0 else "positive - dampened"
    missing = d["missing_symbols"]
    print(f"{name:8s}  net GEX {agg['net_gex']:>16,}  {regime}"
          + (f"  (missing: {missing})" if missing else ""))
    # Rank the names driving the basket
    top = sorted(d["constituents"], key=lambda c: c["contribution_pct"], reverse=True)[:3]
    for c in top:
        print(f"           {c['symbol']:6s} {c['contribution_pct']:5.1f}%  {c['regime']}")
import { FlashAlpha } from 'flashalpha';

const fa = new FlashAlpha('YOUR_API_KEY');

const SECTORS = {
    semis:   'NVDA,AMD,AVGO,MU,SMCI,TSM,QCOM,ASML',
    megacap: 'AAPL,MSFT,NVDA,AMZN,META,GOOGL,TSLA',
    banks:   'JPM,BAC,WFC,C,GS,MS',
};

for (const [name, syms] of Object.entries(SECTORS)) {
    const d = await fa.exposureBasket({ symbols: syms });
    const g = d.aggregate.net_gex;
    const regime = g < 0 ? 'NEGATIVE - trending' : 'positive - dampened';
    console.log(`${name}  net GEX ${g.toLocaleString()}  ${regime}`
        + (d.missing_symbols.length ? `  missing: ${d.missing_symbols}` : ''));
}
# Semis basket - is the sector short gamma?
curl -H "X-Api-Key: YOUR_KEY" \
  "https://lab.flashalpha.com/v1/exposure/basket?symbols=NVDA,AMD,AVGO,MU,SMCI,TSM,QCOM,ASML"

# Read aggregate.net_gex - negative means dealers across the sector amplify moves
$ one call per sector  |  negative aggregate GEX = amplifying regime  |  contribution_pct ranks the drivers

The pattern generalises: define your baskets once, poll them on whatever cadence your strategy needs, and watch two things, the sign of aggregate.net_gex (the regime) and the top contribution_pct names (what is driving it). A sector that flips negative on the back of one dominant name is a different setup from one that is broadly short gamma across all constituents.

How To: Weight It to Your Real Book

Equal weight answers "what is the sector doing." Your actual portfolio answers "what is my dealer-flow exposure." Pass weights matching your position sizing and the aggregate reflects your book, not a generic proxy. Weights are renormalised to sum to 1, so you can pass dollar notionals, share counts, or percentages, whatever you already track.

# Weights as portfolio dollar exposure (renormalised server-side)
positions = {
    "AAPL": 240_000,
    "MSFT": 180_000,
    "NVDA": 320_000,
    "AMD":   95_000,
}
d = fa.exposure_basket(list(positions), weights=list(positions.values()))

print(f"Book-weighted net GEX: {d['aggregate']['net_gex']:,}")
print(f"Book-weighted net DEX: {d['aggregate']['net_dex']:,}  "
      "(directional dealer hedging load behind your names)")

Now aggregate.net_dex tells you the net directional dealer-hedging load sitting behind your specific holdings, and contribution_pct tells you which position is responsible for most of it, often a useful, non-obvious risk concentration read that has nothing to do with your dollar exposure ranking.

How To: Frame a Dispersion Trade

Dispersion trades live on the gap between an index and its constituents. The basket endpoint gives you the constituent side of that comparison in one call; pair it with the index's own exposure to see where single names diverge from the benchmark:

  1. Pull the constituent basket: /v1/exposure/basket?symbols=... for the index members (weighted to the index if you want a like-for-like read).
  2. Pull the index itself: the single-symbol GEX endpoint for SPX / NDX / SPY / QQQ.
  3. Compare regimes. When the constituent basket is broadly negative gamma while the index sits positive (or vice versa), single-name dealer hedging is pulling against the index, the structural setup dispersion is built to harvest.
  4. Read constituents[].regime to see which names carry the divergence, and pair with the dispersion endpoint for the implied-vol side of the same trade.

Why Not Build It Yourself?

You can. Here is what an in-house basket aggregator requires, and why the single-call version is worth it:

  • N single-symbol calls per refresh. A 50-name basket is 50 GEX requests against your rate limit every time you refresh. The basket endpoint is one debit.
  • Weight renormalisation and drop handling. Symbols go missing (illiquid, warming, invalid). You have to renormalise surviving weights so the aggregate stays valid, and surface what was dropped, or you ship a silently wrong number.
  • Consistent contribution math. Naive |net_gex| ranking lets a tiny-weight, huge-GEX name dominate your display while contributing almost nothing to the actual aggregate. The endpoint uses weighted GEX share so the ranking matches real influence.
  • Four greeks, not one. GEX, DEX, VEX and CHEX each aggregated the same way, in the same response.

API Access and Pricing

The exposure basket endpoint is on the Growth plan and higher. The entire basket, up to 50 symbols, costs a single rate-limit debit, which makes it materially cheaper than fanning out single-symbol GEX calls on metered tiers.

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

Explore the shape in the browser first via the basket endpoint docs (it has a live "Try It" widget), or the interactive API playground. SDKs are available in Python, JavaScript, C#, Go, and Java.

Aggregate dealer gamma across a sector, watchlist, or your real book, in one call

Growth plan. Up to 50 symbols, one quota debit, weighted GEX / DEX / VEX / CHEX with per-name contributions.

Get API Access

Frequently Asked Questions

Yes. The FlashAlpha GET /v1/exposure/basket endpoint takes a comma-separated list of up to 50 symbols (with optional weights) and returns one weighted aggregate of net GEX, DEX, VEX and CHEX, plus each constituent's contribution and gamma regime. It is on the Growth plan and the whole basket counts as a single rate-limit debit.
Pass the sector's members to GET /v1/exposure/basket?symbols=... and read aggregate.net_gex. A negative value means dealers across the basket are net short gamma and positioned to amplify moves (a trending regime); positive means dampened. The per-constituent regime tags then show whether the read is broad-based or driven by one or two names.
Weights are optional and renormalised to sum to 1, so you can pass dollar notionals, share counts, or percentages, 4,3,3 and 0.4,0.3,0.3 are equivalent. Omit them for equal weight. Negative weights and a zero sum are rejected, and the weights list must be the same length as the symbols list. If a symbol is dropped for missing data, the surviving weights are renormalised so the aggregate stays valid.
Symbols with no data in the store are dropped from the aggregate and returned in missing_symbols, with the surviving weights renormalised. Always check missing_symbols and constituent_count, a basket that silently lost half its names still returns a clean-looking aggregate. If none of the requested symbols had data, the call returns a 404 no_data.
The exposure basket is on the Growth plan (from $239/mo, 2,500 req/day) and the Alpha plan (from $1,199/mo, unlimited). It is not available on Free or Basic. The whole basket, up to 50 symbols, costs one rate-limit debit, so it is materially cheaper than fanning out single-symbol GEX calls.

Live Market Pulse

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

Intelligent Screening

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

Execution-Ready

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

Join the Community

Discord

Engage in real time conversations with us!

Twitter / X

Follow us for real-time updates and insights!

GitHub

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