Skip to content

Analytics recipes

Multi-request Bloomberg workflows as single calls.

xbbg.ext packages workflows such as futures rolls, CDX series resolution, volatility surfaces, index constituents, and ETF NAV lookups. Most run as shared Rust recipes, so Python and Node.js return the same columns and raise the same errors.

WARNING

Recipes make ordinary Bloomberg requests and need the matching licenses and entitlements.

How recipes work ​

python
from xbbg import ext

members = ext.index_members("SPX Index", asof="2024-01-02")
active = ext.active_futures("ES1 Index", "2024-01-15")
surface = ext.vol_surface("SPX Index", start_date="2024-01-02", end_date="2024-01-05")
resolved = ext.resolve_isins(["US0378331005", "INVALIDISIN000"])
  • Import from xbbg.ext. Recipes are not re-exported from xbbg.blp; in 1.8.0 blp.index_members(...), blp.active_futures(...), blp.vol_surface(...), and blp.resolve_isins(...) raise AttributeError. Technical analysis is not part of xbbg.ext: call xbbg.bta (also available as xbbg.blp.bta).
  • Sync and async. Every helper has an a-prefixed async twin, such as ext.aindex_members. Sync helpers work in scripts and in Jupyter, VS Code Interactive, and marimo; inside any other running event loop they raise RuntimeError naming the async function to await (see Async).
  • Output. Data recipes return the configured default backend unless you pass backend= (Output backends). Resolvers such as fut_ticker return a str.
  • Security column. Recipe results name their security column after the engine's key column: ticker by default, or security with configure(key_column="security") or Engine(key_column="security"). Since 1.8.0, results keyed by ticker emit a FutureWarning, because the next major release makes security the default. The column names in the tables below use the default.
  • Request controls. dividend, earnings, turnover, etf_holdings, fut_ticker, active_futures, and convert_ccy forward request options such as overrides (including ovr() specs), validate_fields, field_types, return_eids, and Bloomberg override keywords to every request they make. dividend_yield, futures_curve, vol_surface, index_members, resolve_isins, issuer_isins, etf_nav_relationships, etf_nav_snapshot, and etf_nav_history take only their documented arguments and silently ignore other keywords. The CDX analytics and option helpers pass extra keywords to bdp, bds, or bql.
  • Errors. Engine failures raise the same typed BlpError subclasses as request APIs, invalid arguments raise ValueError, and incomplete or inconsistent Bloomberg data raises RuntimeError (Errors and warnings).

Bond, YAS, preferred, corporate-bond, and BQR helpers are covered in Fixed income, and the auction helpers in Exchange auctions.

AreaHelpersImplementation
Historicaldividend, dividend_yield, earnings, turnover, etf_holdingsNative recipes
ETF NAV and iNAVetf_nav_relationships, etf_nav_snapshot, etf_nav_history, subscribe_etf_inavNative recipes, then a subscription
Futuresfut_ticker, active_futures, futures_curveNative recipes
CDX seriescdx_ticker, active_cdxNative recipes
CDX analyticscdx_info, cdx_defaults, cdx_pricing, cdx_risk, cdx_basis, cdx_default_prob, cdx_cashflows, cdx_curvebdp field bundles and bds bulk fields
Volatilityvol_surface, VolSurfacePresetNative recipe
Indices and identifiersindex_members, resolve_isins, issuer_isinsNative recipes
Currencyconvert_ccyNative recipe
Optionsoption_info, option_greeks, option_pricing, option_chain, option_chain_bql, option_screen, and enumsbdp, bds, and BQL
Technical analysisxbbg.bta, ta_studies, ta_study_params, generate_ta_stubs//blp/tasvc studies

The async names are the same with an a prefix: adividend, aetf_nav_history, asubscribe_etf_inav, afut_ticker, aactive_cdx, acdx_pricing, avol_surface, aconvert_ccy, aoption_chain_bql, xbbg.abta, and so on.

Historical data ​

python
from xbbg import ext

dividends = ext.dividend("AAPL US Equity", start_date="2023-01-01", end_date="2023-12-31")
splits = ext.dividend(["AAPL US Equity", "NVDA US Equity"], "split")
trailing = ext.dividend_yield("AAPL US Equity", start_date="2024-01-02", end_date="2024-06-28")
geo_revenue = ext.earnings("AAPL US Equity", by="Geo", typ="Revenue")
traded_value = ext.turnover(["AAPL US Equity", "VOD LN Equity"], start_date="2024-01-02", end_date="2024-01-31")
holdings = ext.etf_holdings("SPY US Equity")

dividend(tickers, typ="all", *, start_date=None, end_date=None, **kwargs) returns dividend or split bulk history with Bloomberg's own sub-field labels (for example Declared Date and Dividend Amount) for every security you pass. typ is a Bloomberg bulk field name or one of these aliases:

typBulk fieldtypBulk field
allDVD_Hist_Alladj_fundEqy_DVD_Adj_Fund
dvdDVD_Histwith_amtDVD_Hist_All_with_Amt_Status
splitEqy_DVD_Hist_Splitsdvd_amtDVD_Hist_with_Amt_Status
grossEqy_DVD_Hist_Grossgross_amtDVD_Hist_Gross_with_Amt_Stat
adjustEqy_DVD_Adjust_FactprojectedBDVD_Pr_Ex_Dts_DVD_Amts_w_Ann

dividend_yield(tickers, *, start_date, end_date, dividend_types=None, window_days=365, backend=None) computes trailing realized dividends and yield from DVD_HIST_ALL and PX_LAST. Columns: ticker, date, dividend_amount, trailing_dividend_amount, price, dividend_yield, dividend_type, declared_date, record_date, and payable_date. dividend_types keeps only the listed Bloomberg dividend types; window_days (at least 1) sets the trailing window.

earnings(ticker, by="Geo", typ="Revenue", *, ccy=None, level=None, year=None, periods=None, **kwargs) returns earnings breakdowns with adjacent percentage columns. by is Geo, Product, Q, or A; typ is Revenue, Operating_Income, Assets, Gross_Profit, Capital_Expenditures, IS, BS, or CF (an optional PG_ prefix is accepted); level is 1 or 2; year and periods of 0 omit the override. Other values raise ValueError.

turnover(tickers, *, start_date=None, end_date=None, ccy="USD", factor=1e6, **kwargs) returns traded value in TURNOVER columns with Date32 dates. Securities without Bloomberg TURNOVER fall back to EQY_WEIGHTED_AVG_PX × VOLUME. Values are converted to ccy ("local" skips conversion) and then divided by factor (millions by default; it must be finite and nonzero). end_date defaults to yesterday and start_date to 30 days before end_date.

etf_holdings(etf_ticker, *, fields=None, **kwargs) runs a BQL holdings query that requests id_isin, weights, and id().position plus any extra fields, and returns the columns as BQL labels them. A bare ticker such as "SPY" is completed to SPY US Equity.

ETF NAV and iNAV ​

The ETF recipes follow Bloomberg's own ETF_NAV_TICKER and ETF_INAV_TICKER relationships instead of guessing ticker suffixes, normalize each target to end in a single Index yellow key, and check that it is a genuine Index security. NAV and iNAV are independently nullable.

python
import asyncio

from xbbg import ext

# QQQ US Equity -> QQQNV Index / QXV Index; AT1 LN Equity -> no daily NAV / AT1IN Index
relationships = ext.etf_nav_relationships(["QQQ US Equity", "AT1 LN Equity"])

# Mapped Index targets price with PX_LAST; AT1's missing daily NAV falls back to the
# fund's FUND_NET_ASSET_VAL (see nav_source_ticker / nav_source_field on each row)
history = ext.etf_nav_history(["QQQ US Equity", "AT1 LN Equity"], start_date="2026-06-01", end_date="2026-07-01")


async def first_inav_update() -> None:
    sub = await ext.asubscribe_etf_inav("QQQ US Equity")  # subscribes QXV Index, LAST_PRICE
    try:
        async for table in sub:
            print(table.to_pylist())
            break
    finally:
        await sub.unsubscribe()


asyncio.run(first_inav_update())
HelperReturns
etf_nav_relationships(etfs, *, backend=None)input_order, etf_ticker, then nav_ticker, nav_market_sector_des, nav_name, nav_validation_error and the same four inav_* columns
etf_nav_snapshot(etfs, *, backend=None)input_order, etf_ticker, nav_ticker, nav_value, nav_source_ticker, nav_source_field, inav_ticker, inav_value
etf_nav_history(etfs, *, start_date, end_date, backend=None)The snapshot columns plus a Date32 date, one row per day, both dates inclusive
subscribe_etf_inav(etfs, fields="LAST_PRICE", *, raw=False, all_fields=False, backend=None, options=None, conflate=False, tick_mode=False, flush_threshold=None, stream_capacity=None, overflow_policy=None, output=None)A Subscription on the resolved iNAV tickers

subscribe_etf_inav validates every mapping before it subscribes and raises ValueError for an empty input, duplicate ETFs, or a missing, invalid, or ambiguous iNAV relationship. Its topics are the iNAV tickers, so add() and remove() expect iNAV tickers, not ETFs. See Streaming for the subscription options.

Futures ​

python
from xbbg import ext

contract = ext.fut_ticker("ES1 Index", "2024-01-15")         # "ESH24 Index"
active = ext.active_futures("CL1 Comdty", "2024-02-14")
quarterly = ext.fut_ticker("ES1 Index", "2024-01-15", freq="Q")
curve = ext.futures_curve("CL1 Comdty", asof="2024-01-15", max_contracts=12)

fut_ticker(gen_ticker, dt, **kwargs) resolves a generic ticker to the specific contract for a date. Generated candidates are checked by LAST_TRADEABLE_DT first; when that window cannot supply the contract, Bloomberg's historical FUT_CHAIN_LAST_TRADE_DATES chain (with CHAIN_DATE=dt) is used. Contracts expiring on or before dt are excluded. freq="Q" or "QE" generates quarterly candidates; the default is monthly.

active_futures(ticker, dt, **kwargs) returns Bloomberg's FUT_CUR_GEN_TICKER mapping when it has one. Otherwise it resolves the front and second contracts, keeps the front before its maturity month, and within that month compares each contract's latest non-null VOLUME over a 10-calendar-day window; ties or no observations keep the front. The input must be generic (ES1, not ESH24), or ValueError is raised.

futures_curve(gen_ticker, *, asof=None, chain_field=None, fields=None, max_contracts=None, backend=None) builds the chain from FUT_CHAIN_LAST_TRADE_DATES (or chain_field), as of asof through the CHAIN_DATE override, and prices each contract. Columns: source_ticker, contract_ticker, generic_number, px_bid, px_ask, last_tradeable_dt, mid, and annualized_carry; extra fields follow as text columns.

CDX indices ​

python
from xbbg import ext

series = ext.cdx_ticker("CDX IG CDSI GEN 5Y Corp", "2020-06-01")   # "CDX IG CDSI S34 V1 5Y Corp"
legacy = ext.cdx_ticker("CDX IG CDSI GEN 5Y Corp", "2020-06-01", versionless=True)
# S45: S46 started accruing on 2026-03-20 but first printed on 2026-03-27
traded = ext.active_cdx("CDX HY CDSI GEN 5Y Corp", "2026-03-24", lookback_days=10)

pricing = ext.cdx_pricing("CDX IG CDSI GEN 5Y Corp", recovery_rate=40)
curve = ext.cdx_curve("CDX IG CDSI GEN 5Y Corp", tenors=["3Y", "5Y", "10Y"])

cdx_ticker(gen_ticker, dt, versionless=False) resolves the series as of dt: the highest series whose CDS_FIRST_ACCRUAL_START_DATE is on or before dt, read from Bloomberg rather than assumed from the roll calendar, so the answer never moves backwards as dt advances. The Vn token is the latest version Bloomberg reports for that series (V1 included); versionless=True drops it for legacy aliases.

active_cdx(gen_ticker, dt, lookback_days=10, versionless=False) matches cdx_ticker except between a roll and the new series' first PX_LAST print, when it returns the previous series. lookback_days is a minimum: the activity window always reaches back to the resolved series' first accrual date.

Neither resolver accepts other keyword arguments (TypeError). They raise ValueError for a non-generic ticker or a date before the index's first series, and RuntimeError when Bloomberg's series metadata is missing or inconsistent, or when neither candidate series printed in the window (active_cdx).

Analytics helperData
cdx_info(ticker, **kwargs)bdp of ROLLING_SERIES, VERSION, ON_THE_RUN_CURRENT_BD_INDICATOR, CDS_FIRST_ACCRUAL_START_DATE, NAME, NUM_CURRENT_COMPANIES_CCY_TKR, NUM_ORIG_COMPANIES_CRNCY_TKR, PX_LAST
cdx_pricing(ticker, *, recovery_rate=None, **kwargs)bdp of PX_LAST, PX_BID, PX_ASK, UPFRONT_LAST, UPFRONT_BID, UPFRONT_ASK, CDS_FLAT_SPREAD, UPFRONT_FEE, PV_CDS_PREMIUM_LEG, PV_CDS_DEFAULT_LEG
cdx_risk(ticker, *, recovery_rate=None, **kwargs)bdp of SW_CNV_BPV, SW_EQV_BPV, CDS_SPREAD_MID_MODIFIED_DURATION, CDS_SPREAD_MID_CONVEXITY, RECOVERY_RATE_SEN, CDS_RECOVERY_RT
cdx_basis(ticker, **kwargs)bdp of CDS_INDEX_INTRINSIC_VALUE, CDS_INDEX_INTRINSIC_BASIS_VALUE, CDS_IDX_DUR_BASED_INTRINSIC_VAL, CDS_INDEX_DUR_BASED_BASIS_VAL, PX_LAST
cdx_defaults(ticker, **kwargs)bds of CDS_INDEX_DEFAULT_INFORMATION
cdx_default_prob(ticker, **kwargs)bds of CDS_DEFAULT_PROB
cdx_cashflows(ticker, **kwargs)bds of CASHFLOW_SCHEDULE
cdx_curve(gen_ticker, tenors=None, **kwargs)bdp across tenors (default 3Y, 5Y, 7Y, 10Y), replacing the tenor token in the ticker

recovery_rate is a percentage (40 means 40%) sent as the CDS_RR override.

Volatility surfaces ​

python
from xbbg import ext

surface = ext.vol_surface(
    ["SPX Index", "NDX Index"],
    start_date="2024-01-02",
    end_date="2024-01-31",
    preset=[ext.VolSurfacePreset.MONEYNESS_30D, ext.VolSurfacePreset.MONEYNESS_3M],
    include_derived=True,
    risk_free_rate=0.05,
)

vol_surface(tickers, *, start_date, end_date, preset=VolSurfacePreset.MONEYNESS_30D, fields=None, as_decimal=True, include_derived=False, risk_free_rate=None, dividend_yield_field=None, backend=None) returns a tidy historical implied-volatility surface with columns ticker, date, metric, tenor, point_type, point, field, and value.

  • preset takes a VolSurfacePreset, its string value, or a list: DELTA_1M_2M (25-delta puts and calls at 1M and 2M), MONEYNESS_30D, MONEYNESS_60D, MONEYNESS_3M, MONEYNESS_6M, or MONEYNESS_12M.
  • fields adds custom Bloomberg fields, as names or as a mapping from field name to its metric, tenor, point_type, and point labels.
  • as_decimal=True divides Bloomberg's percent-quoted volatilities and dividend yields by 100.
  • include_derived=True adds spot (PX_LAST) and dividend_yield rows (dividend_yield_field, default EQY_DVD_YLD_12M). With risk_free_rate it also adds risk_free_rate, forward, and discount_factor rows for each tenor; a rate whose absolute value is above 1 is read as a percentage.

Index members and identifiers ​

python
from xbbg import ext

members = ext.index_members("SPX Index", asof="2024-01-02")
tickers_only = ext.index_members("SPX Index", field="INDX_MEMBERS")
resolved = ext.resolve_isins(["US0378331005", "INVALIDISIN000"])
issuers = ext.issuer_isins(["US037833FB15"])

index_members(index, *, field="INDX_MWEIGHT", asof=None, backend=None) reads INDX_MWEIGHT, INDX_MEMBERS, or INDX_MEMBERS3 (other fields raise ValueError). asof is sent as END_DATE_OVERRIDE. Bloomberg's bulk rows come back with the member identifier column renamed to member; other columns keep Bloomberg's labels.

resolve_isins(isins, *, backend=None) looks equity ISINs up through Bloomberg's /ISIN/<id> namespace. Columns: input_order, input_isin, lookup_ticker, parsekyable_des, primary_exchange, resolved_ticker, status (resolved or unresolved), and error. ISINs whose parse key carries another yellow key (Pfd, Corp, Govt, and so on) are unresolved, with the market sector named in error.

issuer_isins(bond_isins, *, backend=None) maps bond ISINs to the issuer's equity ISIN. Columns: input_order, input_isin, lookup_ticker, bond_ticker, parent_ticker, issuer_equity_isin, status, and error.

Currency conversion ​

python
import xbbg
from xbbg import ext

prices = xbbg.bdh(["VOD LN Equity", "SAP GY Equity"], "PX_LAST", "2024-01-02", "2024-01-31")
in_usd = ext.convert_ccy(prices, ccy="USD")

convert_ccy(data, ccy="USD", **kwargs) converts historical data into ccy using each security's CRNCY and daily FX rates. It accepts long data (the key column, ticker or security, plus date and a value column) or wide data whose columns are named by security or security|field, from pandas, Polars, PyArrow, Narwhals, or native results; since 1.8.0, xbbg's default Narwhals results take the direct Arrow path without a warning. Numeric text stays text, integers become Float64, securities already in ccy pass through, and ccy="local" returns the input unchanged. A missing FX rate turns that value into a null; a failed currency or FX request raises instead of returning partly converted data.

Options ​

python
from xbbg import ext

calls = ext.option_chain("AAPL US Equity", put_call=ext.PutCall.CALL, strike=ext.StrikeRef.ATM)
puts = ext.option_chain_bql(
    "AAPL US Equity",
    put_call=ext.PutCall.PUT,
    expiry_start="2027-01-01",
    expiry_end="2027-03-31",
    delta_low=-0.6,
    delta_high=-0.4,
    min_open_int=1000,
)
HelperData
option_info(ticker, **kwargs)bdp contract metadata: strike, expiry, exercise type, underlying
option_greeks(ticker, **kwargs)bdp Greeks and implied volatility, mid and bid/ask
option_pricing(ticker, **kwargs)bdp prices, intrinsic and time value, open interest
option_chain(underlying, *, put_call=None, expiry_dt=None, strike=None, points=None, periodicity=None, exercise_type=None, expiry_match=None, **kwargs)bds of CHAIN_TICKERS with chain overrides
option_chain_bql(underlying, *, put_call=None, expiry_start=None, expiry_end=None, strike_low=None, strike_high=None, ..., get_fields=None, **kwargs)BQL options() universe filtered by strike, delta, gamma, vega, theta, implied volatility, moneyness, open interest, volume, bid/ask, exchange, exercise type, and extra_filters
option_screen(tickers, flds=None, **kwargs)bdp across many option tickers

The enums are PutCall (CALL, PUT), ChainPeriodicity (WEEKLY, MONTHLY, QUARTERLY, YEARLY, ALL), StrikeRef (ATM), ExerciseType (AMERICAN, EUROPEAN), and ExpiryMatch (EXACT, CLOSEST); the string codes they wrap ("C", "P", "ATM", ...) are accepted too. option_chain_bql requests strike_px(), expire_dt(), ivol(), delta(), open_int(), px_last(), px_bid(), and px_ask() unless you pass get_fields, and forwards other keywords (backend, app_name) to bql.

Technical analysis ​

python
import xbbg

rsi = xbbg.bta("AAPL US Equity", "rsi", start_date="2024-01-02", end_date="2024-06-28", period=14)
bands = xbbg.bta(["AAPL US Equity", "MSFT US Equity"], "boll", start_date="2024-01-02", end_date="2024-06-28")

print(xbbg.ta_studies())              # ['ado', 'adx', 'al', 'atr', ...]
print(xbbg.ta_study_params("boll"))   # {'period': 20, 'upperBand': 2.0, 'lowerBand': 2.0, 'priceSourceClose': 'PX_LAST'}
  • bta(tickers, study, *, start_date=None, end_date=None, periodicity="DAILY", interval=None, **study_params) (async: abta) runs Bloomberg //blp/tasvc studies, one request per security sent concurrently. study is a name from ta_studies() or a Bloomberg ...StudyAttributes name, and study_params override the defaults from ta_study_params(study). periodicity is DAILY, WEEKLY, MONTHLY, or INTRADAY (trade bars of interval minutes, default 60). Pass dates as YYYY-MM-DD or YYYYMMDD strings. The result uses the configured default backend. If some securities fail, each failure is reported as a UserWarning and the others are returned; if all fail, RuntimeError is raised.
  • ta_studies() lists the 54 study names and aliases; ta_study_params(study) returns a study's default parameters (an empty dict for unknown studies).
  • generate_ta_stubs(output_dir=None) reads the //blp/tasvc schema (so it needs a Bloomberg session or a cached schema), writes ta_studies.pyi and ta_studies.py with one TypedDict per study to output_dir (default ~/.xbbg/stubs/), adds that directory to the first existing VS Code, Pyright, or ty configuration it finds in the project (it never creates one), and returns the .pyi path.

Upgrading from earlier releases ​

  • 1.8.0: results keyed by the default ticker column emit a FutureWarning; switch with key_column="security". convert_ccy accepts the default Narwhals results directly.
  • 1.6.0 (breaking): the Python recipes run on the shared Rust implementations and adopt their validation, error messages, and output contracts. dividend keeps Bloomberg's column labels and no longer drops non-equities; etf_holdings keeps the BQL labels instead of renaming columns; turnover returns TURNOVER columns with Date32 dates; convert_ccy raises when a currency or FX request fails instead of returning unconverted data, and missing FX rates become nulls; futures selection is candidate-first with a 10-day volume window.
  • 1.5.0 (breaking): engine errors from xbbg.ext recipes raise typed BlpError subclasses (BlpTimeoutError, BlpRequestError, BlpValidationError, ...) instead of RuntimeError, so except RuntimeError no longer catches them.
  • 1.4.6 (breaking): cdx_ticker and active_cdx resolve the series as of the requested date (previously they read today's ROLLING_SERIES), raise instead of returning an empty string, no longer accept extra keyword arguments, and treat lookback_days as a minimum.
  • 1.4.5: CDX tickers carry an explicit Vn version by default; pass versionless=True for the legacy alias.

See the changelog for the full history.

Fixed income

YAS, bond analytics, preferreds, corporate bonds, and BQR.

xbbg is independent open-source software and is not affiliated with, endorsed by, sponsored by, or approved by Bloomberg Finance L.P.