Skip to content

Latest commit

 

History

History
961 lines (711 loc) · 29.7 KB

File metadata and controls

961 lines (711 loc) · 29.7 KB
name gs-quant
description This document covers the core workflows for using the `gs_quant` library: establishing a session, constructing and resolving instruments, building portfolios, pricing historically, and extracting results.

1. Creating a Session with GsSession.use

All API communication in gs_quant flows through an authenticated GsSession. Before making any pricing or data calls you must initialise a session with GsSession.use().

OAuth2 (Application Credentials)

from gs_quant.session import GsSession, Environment

GsSession.use(
    environment_or_domain=Environment.PROD,
    client_id='my_client_id',
    client_secret='my_client_secret',
    scopes=('run_analytics',)
)
  • environment_or_domainEnvironment.PROD (default), Environment.QA, or Environment.DEV. You can also pass a raw URL string.
  • client_id / client_secret — OAuth2 application credentials. When both are provided the library creates an OAuth2Session.
  • scopes — Optional iterable of GsSession.Scopes values. Usually when pricing trade you will need RUN_ANALYTICS.

Kerberos / SSO (Internal GS)

If no client_id is supplied, the library will attempt Kerberos or pass-through authentication automatically: This is for internal GS users only and requires appropriate network access and installation of gs_quant_internal.

GsSession.use(Environment.PROD)

Using as a Context Manager

GsSession can also be used as a context manager so the session is cleaned up on exit:

with GsSession.get(Environment.PROD, client_id='...', client_secret='...') as session:
    # session is active inside this block
    ...

Verifying the Session

After calling GsSession.use(...), the active session is accessible as:

GsSession.current  # the currently active GsSession instance

All subsequent API calls (instrument resolution, pricing, data queries) will use this session automatically.


2. Constructing Trades with gs_quant.instrument

Instruments are the building blocks of gs_quant. Every tradeable product is represented as a dataclass in gs_quant.instrument. Construct an instrument by importing its class and supplying the key economic parameters — any parameter you omit will be resolved by the server later.

Common Instrument Examples

Interest Rate Swap

from gs_quant.instrument import IRSwap

swap = IRSwap(
    pay_or_receive='Pay',       # 'Pay' or 'Receive' the fixed leg
    termination_date='10y',     # tenor or explicit date
    notional_currency='USD',    # currency
    fixed_rate=0.03,            # optional — leave None to resolve at market
)

Cross-Currency Swap — Fix / Fix (IRXccySwapFixFix)

Both legs pay a fixed coupon. Each leg has its own notional, set independently to encode the agreed FX rate at inception.

from gs_quant.instrument import IRXccySwapFixFix
from gs_quant.common import Currency, PrincipalExchange

swap = IRXccySwapFixFix(
    termination_date='5y',
    effective_date='0b',                # spot start
    payer_currency=Currency.USD,
    payer_rate=0.04,                    # 4.00% fixed USD coupon
    receiver_currency=Currency.EUR,
    receiver_rate=0.025,                # 2.50% fixed EUR coupon
    notional_amount=10e6,
    principal_exchange=PrincipalExchange.Both,
)
swap.resolve()

Key parameters: payer_rate, receiver_rate, notional_amount (payer leg), receiver_notional_amount (receiver leg — set explicitly to encode the agreed FX rate), payer_frequency, receiver_frequency, payer_day_count_fraction, receiver_day_count_fraction, payer_business_day_convention, receiver_business_day_convention.

Cross-Currency Swap — Fix / Float (IRXccySwapFixFlt)

One leg pays a fixed rate; the other pays a floating rate (LIBOR/RFR + spread). pay_or_receive controls which side you pay. Currencies are specified via fixed_rate_currency and floating_rate_currency (not payer/receiver).

from gs_quant.instrument import IRXccySwapFixFlt
from gs_quant.common import Currency, PrincipalExchange, PayReceive

swap = IRXccySwapFixFlt(
    pay_or_receive=PayReceive.Pay,      # pay fixed USD, receive floating EUR
    termination_date='5y',
    effective_date='0b',
    fixed_rate_currency=Currency.USD,
    fixed_rate=0.04,                    # 4.00% fixed USD rate
    floating_rate_currency=Currency.EUR,
    floating_rate_spread=0.0,
    notional_amount=10000,
    principal_exchange=PrincipalExchange.Both,
)
swap.resolve()

Key parameters: pay_or_receive, fixed_rate_currency, fixed_rate, fixed_rate_frequency, fixed_rate_day_count_fraction, fixed_rate_business_day_convention, fixed_first_stub, fixed_last_stub, fixed_holidays, floating_rate_currency, floating_rate_option, floating_rate_designated_maturity, floating_rate_spread, floating_rate_frequency, floating_rate_day_count_fraction, floating_rate_business_day_convention, floating_first_stub, floating_last_stub, floating_holidays, floating_rate_for_the_initial_calculation_period.

Cross-Currency Swap — Float / Float non-MTM (IRXccySwapFltFlt)

Both legs pay a floating rate in different currencies. The notional is fixed for the life of the trade — the FX rate does not reset. Set receiver_amount to encode the agreed FX rate. The XCcy basis spread is typically applied as receiver_spread.

from gs_quant.instrument import IRXccySwapFltFlt
from gs_quant.common import Currency, PrincipalExchange

swap = IRXccySwapFltFlt(
    termination_date='5y',
    effective_date='0b',
    payer_currency=Currency.USD,
    payer_spread=0.0,
    receiver_currency=Currency.EUR,
    receiver_spread=0.0,                # XCcy basis spread — resolve at par if omitted
    notional_amount=10000,
    principal_exchange=PrincipalExchange.Both,
)
swap.resolve()

Key parameters: payer_rate_option, payer_designated_maturity, payer_spread, payer_frequency, payer_day_count_fraction, payer_business_day_convention, payer_first_stub, payer_last_stub, payer_holidays, and the equivalent receiver_* fields. receiver_amount encodes the agreed FX rate and is fixed at inception.

Cross-Currency Swap — Float / Float MTM (IRXccySwap)

Same structure as IRXccySwapFltFlt but the receiver notional resets to FX spot at each period start, eliminating FX credit exposure. This is the standard interbank product. Note: receiver_amount is not a field — the receiver notional is computed automatically each period.

from gs_quant.instrument import IRXccySwap
from gs_quant.common import Currency, PrincipalExchange, PayReceive

swap = IRXccySwap(
    termination_date='5y',
    effective_date='0b',
    payer_currency=Currency.USD,
    payer_spread=0.0,
    receiver_currency=Currency.EUR,
    receiver_spread=0.0,                # XCcy basis — resolve at par if omitted
    notional_amount=10000,         # payer notional only; receiver resets to FX spot
    principal_exchange=PrincipalExchange.Both,
    # initial_fx_rate=1.10,             # optional: pin the opening FX rate
    # notional_reset_side=PayReceive.Receive,  # default — receiver resets (standard MTM)
)
swap.resolve()

Key additional parameters vs IRXccySwapFltFlt: initial_fx_rate (optional, pins the opening FX rate), notional_reset_side (PayReceive.Receive by default — the standard convention). receiver_amount is absent; do not set it.

MTM vs non-MTM at a glance:

IRXccySwap (MTM) IRXccySwapFltFlt (non-MTM)
Receiver notional Resets to FX spot each period Fixed at inception
FX credit exposure Eliminated Builds up over trade life
receiver_amount field Not present Required — encodes the agreed FX rate
initial_fx_rate field Available Not available
notional_reset_side field Available Not available

All four XCcy swap types accept principal_exchange (PrincipalExchange.Both is standard — notionals exchanged at start and maturity) and an optional fee / fee_currency / fee_payment_date. Note if you have a principal exchange which is in the past this cash flow will not be ignored by the Price measure. So in general only have exchanges which are in the past relative to the PricingContext. Relevant risk measures:

from gs_quant.risk import IRDeltaParallel, IRXccyDeltaParallel, IRDelta, IRXccyDelta
# IRDeltaParallel      — total rate DV01 (1bp parallel shift in discount/fwd curve, USD)
# IRXccyDeltaParallel  — total XCcy basis DV01 (1bp shift in cross-ccy basis, USD)
# IRDelta              — bucketed rate delta ladder (per tenor)
# IRXccyDelta          — bucketed XCcy basis delta ladder (per tenor)

Interest Rate Swaption

from gs_quant.instrument import IRSwaption

swaption = IRSwaption(
    pay_or_receive='Receive',
    termination_date='10y',
    notional_currency='EUR',
    expiration_date='1y',
    strike='ATM',
)

Interest Rate Cap

from gs_quant.instrument import IRCap

cap = IRCap(
    termination_date='1y',
    notional_currency='USD',
)

FX Option

from gs_quant.instrument import FXOption

option = FXOption(
    pair='EURUSD',
    expiration_date='3m',
    option_type='Call',
    strike_price='ATMF',
    notional_amount=10e6,
)

FX Forward

from gs_quant.instrument import FXForward

fwd = FXForward(
    pair='USDJPY',
    settlement_date='6m',
    notional_amount=10e6,
)

Important: FX Instrument Pitfalls

There are two common mistakes when working with FX options that can lead to confusing results:

1. Always Set premium=0 on FX Options

When constructing FX options (FXOption, FXBinary, FXMultiCrossBinary, etc.), if you don't specify a premium, the instrument resolution will automatically set a premium such that the DollarPrice becomes zero. This is by design — it represents a "fair value" trade where the premium exactly offsets the option value.

Problem: If you want to know the cost/value of the option, you'll always get 0.

Solution: Always set premium=0 explicitly when you want DollarPrice to return the actual option value:

from gs_quant.instrument import FXOption, FXBinary

# WRONG - DollarPrice will be ~0 after resolution
option_wrong = FXOption(
    pair='EURUSD',
    expiration_date='3m',
    option_type='Call',
    strike_price='ATMF',
    notional_amount=10e6,
)

# CORRECT - DollarPrice will be the option value
option_correct = FXOption(
    pair='EURUSD',
    expiration_date='3m',
    option_type='Call',
    strike_price='ATMF',
    notional_amount=10e6,
    premium=0,  # <-- Important!
)

# Same applies to FXBinary
binary = FXBinary(
    pair='EURUSD',
    buy_sell=BuySell.Buy,
    option_type=OptionType.Call,
    strike_price='s',
    notional_amount=1e6,
    notional_currency=Currency.USD,
    expiration_date='3m',
    premium=0,  # <-- Important!
)

2. FXMultiCrossBinaryLeg Uses Different OptionType Values

When constructing FXMultiCrossBinaryLeg objects (used within FXMultiCrossBinary for dual digital options), you must use OptionType.Binary_Call or OptionType.Binary_Putnot OptionType.Call or OptionType.Put.

This is different from FXBinary which uses OptionType.Call / OptionType.Put.

from gs_quant.instrument import FXBinary, FXMultiCrossBinary, FXMultiCrossBinaryLeg
from gs_quant.common import BuySell, OptionType, Currency

# FXBinary uses OptionType.Call / OptionType.Put
single_digital = FXBinary(
    pair='EURUSD',
    buy_sell=BuySell.Buy,
    option_type=OptionType.Call,  # <-- Call or Put
    strike_price='s',
    notional_amount=1e6,
    notional_currency=Currency.USD,
    expiration_date='3m',
    premium=0,
)

# FXMultiCrossBinaryLeg uses OptionType.Binary_Call / OptionType.Binary_Put
dual_digital = FXMultiCrossBinary(
    legs=(
        FXMultiCrossBinaryLeg(
            pair='EURUSD',
            option_type=OptionType.Binary_Call,  # <-- Binary_Call or Binary_Put
            strike_price='s',
        ),
        FXMultiCrossBinaryLeg(
            pair='USDJPY',
            option_type=OptionType.Binary_Call,  # <-- Binary_Call or Binary_Put
            strike_price='s',
        ),
    ),
    buy_sell=BuySell.Buy,
    notional_amount=1e6,
    notional_currency=Currency.USD,
    expiration_date='3m',
    premium=0,  # <-- Don't forget this too!
)

Equity Option

from gs_quant.instrument import EqOption

eq_opt = EqOption(
    underlier='.SPX',
    expiration_date='3m',
    strike_price='ATMF',
    option_type='Call',
    option_style='European',
)

You can set the instrument name property for easy identification later:

swap.name = 'USD 10y Payer'

3. Resolving an Instrument

When you construct an instrument you typically only specify a subset of its parameters. Resolving fills in all remaining fields by sending the instrument to the GS pricing service, which returns a fully specified version based on current market data.

from gs_quant.instrument import IRSwap

swap = IRSwap('Pay', '10y', 'USD')

# Before resolve: swap.fixed_rate is None
swap.resolve()
# After resolve: swap.fixed_rate is now populated with the current par rate

print(swap.fixed_rate)  # e.g. 0.0345

What resolve() Does

  1. Sends the instrument to the GS analytics service along with the current PricingContext (pricing date and market).
  2. The service computes any missing parameters — for example the fixed rate of a par swap, the premium of an option, or the forward points of an FX forward.
  3. By default (in_place=True), the instrument is updated in place. Pass in_place=False to receive a new resolved copy instead.

Resolve Under a Specific Pricing Date

from gs_quant.markets import PricingContext
import datetime as dt

with PricingContext(pricing_date=dt.date(2025, 1, 15)):
    resolved = swap.resolve(in_place=False)
    resolved_swap = resolved.result()

Historical Resolution

Resolution can be done across multiple dates via HistoricalPricingContext, but in_place must be False:

from gs_quant.markets import HistoricalPricingContext
import datetime as dt

with HistoricalPricingContext(dt.date(2025, 1, 1), dt.date(2025, 1, 31)):
    resolved_by_date = swap.resolve(in_place=False)

# resolved_by_date is a dict of {date: resolved_instrument}

4. Combining Instruments in Portfolios

The Portfolio class groups instruments together so you can price, resolve, and analyse them as a single unit.

Creating a Portfolio

from gs_quant.instrument import IRSwap, IRSwaption
from gs_quant.markets.portfolio import Portfolio

swap = IRSwap('Pay', '10y', 'USD', name='USD 10y Payer')
swaption = IRSwaption('Receive', '10y', 'EUR', expiration_date='1y', name='EUR 1y10y Receiver')

portfolio = Portfolio([swap, swaption], name='My Portfolio')

You can also construct a portfolio from a dictionary (keys become instrument names):

portfolio = Portfolio({
    'USD 10y Payer': IRSwap('Pay', '10y', 'USD'),
    'EUR 5y Receiver': IRSwap('Receive', '5y', 'EUR'),
})

Nesting Portfolios

Portfolios can contain other portfolios, creating a hierarchy:

usd_book = Portfolio([IRSwap('Pay', '5y', 'USD'), IRSwap('Receive', '10y', 'USD')], name='USD Book')
eur_book = Portfolio([IRSwap('Pay', '5y', 'EUR')], name='EUR Book')

master = Portfolio([usd_book, eur_book], name='Master Book')

Portfolio Operations

# Add instruments
portfolio.append(IRSwap('Pay', '2y', 'GBP'))

# Iterate
for instrument in portfolio:
    print(instrument)

# Access by index
first = portfolio[0]

# Access by name
usd_swap = portfolio['USD 10y Payer']

# Number of top-level priceables
len(portfolio)

# All instruments across nested portfolios
portfolio.all_instruments

Resolving a Portfolio

portfolio.resolve()  # resolves all instruments in place

Pricing a Portfolio

from gs_quant.risk import DollarPrice, IRDelta

# Single risk measure
prices = portfolio.calc(DollarPrice)

# Multiple risk measures at once
results = portfolio.calc([DollarPrice, IRDelta])

The result is a PortfolioRiskResult which can be sliced by instrument, risk measure, or date (see section 6).


5. Historical Pricing with HistoricalPricingContext

HistoricalPricingContext lets you compute risk measures across a range of dates using the close-of-business market for each date.

Basic Usage

import datetime as dt
from gs_quant.instrument import IRSwap
from gs_quant.markets import HistoricalPricingContext
from gs_quant.risk import DollarPrice

swap = IRSwap('Pay', '10y', 'USD')

with HistoricalPricingContext(dt.date(2025, 1, 2), dt.date(2025, 1, 31)):
    price_f = swap.price()

price_series = price_f.result()  # a pandas Series indexed by date

By Number of Business Days

Pass an integer to price over the last N business days:

with HistoricalPricingContext(10):
    price_f = swap.price()

price_series = price_f.result()

With Custom Date List

dates = [dt.date(2025, 1, 2), dt.date(2025, 3, 15), dt.date(2025, 6, 30)]

with HistoricalPricingContext(dates=dates):
    price_f = swap.price()

Key Parameters

Parameter Description
start Start date (or number of business days back from today)
end End date (defaults to today)
calendars Holiday calendar(s) for date generation
dates Explicit iterable of dates (mutually exclusive with start)
is_batch Use batch mode for long-running calculations (avoids timeouts)
is_async Return immediately without blocking
show_progress Display a tqdm progress bar
market_data_location 'NYC', 'LDN', or 'HKG' (defaults to 'LDN')
csa_term CSA term for discounting

Historical Pricing with Portfolios

with HistoricalPricingContext(dt.date(2025, 1, 2), dt.date(2025, 1, 31)):
    result = portfolio.calc(DollarPrice)

# result is a PortfolioRiskResult with a date dimension

6. Result Extraction

Calculation results in gs_quant are rich typed objects that carry metadata (risk key, unit, error info) alongside the actual values. Understanding the result types and how to extract data from them is essential.

Result Types

Type Description
FloatWithInfo Scalar result (e.g. present value). Behaves like a float but carries a risk_key and unit.
SeriesWithInfo Time series result (historical pricing). A pandas.Series with metadata.
DataFrameWithInfo Structured/bucketed result (e.g. delta ladder). A pandas.DataFrame with metadata.
ErrorValue Indicates a calculation error. Check .error for the message.
MultipleRiskMeasureResult Dict-like mapping of RiskMeasure → result when multiple measures are requested on a single instrument.
PortfolioRiskResult Result for a portfolio — can be sliced by instrument, risk measure, or date.

Single Instrument Results

from gs_quant.instrument import IRSwap
from gs_quant.risk import DollarPrice, IRDelta

swap = IRSwap('Pay', '10y', 'USD')

# Scalar result
price = swap.dollar_price()       # FloatWithInfo
print(float(price))               # the numeric value

# Local currency price
local_price = swap.price()        # FloatWithInfo

# Structured result
delta = swap.calc(IRDelta)        # DataFrameWithInfo — a bucketed delta ladder
print(delta)                      # displays as a DataFrame with columns like mkt_type, mkt_asset, etc.

Using Futures (Async / Batched)

Inside an entered PricingContext, calculations return PricingFuture objects. Call .result() after exiting the context:

from gs_quant.markets import PricingContext

with PricingContext():
    price_f = swap.dollar_price()
    delta_f = swap.calc(IRDelta)

price = price_f.result()   # FloatWithInfo
delta = delta_f.result()   # DataFrameWithInfo

Multiple Risk Measures on a Single Instrument

from gs_quant.risk import DollarPrice, IRDelta, IRVega

result = swap.calc([DollarPrice, IRDelta, IRVega])  # MultipleRiskMeasureResult

price = result[DollarPrice]    # FloatWithInfo
delta = result[IRDelta]        # DataFrameWithInfo
vega = result[IRVega]          # DataFrameWithInfo

Portfolio Results

portfolio.calc() returns a PortfolioRiskResult which supports flexible slicing:

from gs_quant.risk import DollarPrice, IRDelta

result = portfolio.calc([DollarPrice, IRDelta])

# Slice by risk measure
prices = result[DollarPrice]          # PortfolioRiskResult for DollarPrice only

# Slice by instrument (name or object)
swap_result = result['USD 10y Payer']  # MultipleRiskMeasureResult for that instrument
swap_price = swap_result[DollarPrice]  # FloatWithInfo

# Slice by index
first_result = result[0]

# Iterate over instruments
for instrument_result in result:
    print(instrument_result)

# Aggregate across all instruments
total_price = result[DollarPrice].aggregate()  # sums all instrument prices

Historical Results

When using HistoricalPricingContext, scalar results become time series:

import datetime as dt
from gs_quant.markets import HistoricalPricingContext

with HistoricalPricingContext(dt.date(2025, 1, 2), dt.date(2025, 1, 31)):
    price_f = swap.price()

price_series = price_f.result()  # SeriesWithInfo indexed by date
print(price_series)

# Access value for a specific date
jan_15_price = price_series[dt.date(2025, 1, 15)]

Historical portfolio results can also be sliced by date:

with HistoricalPricingContext(dt.date(2025, 1, 2), dt.date(2025, 1, 31)):
    result = portfolio.calc(DollarPrice)

# Slice by date
jan_15 = result[dt.date(2025, 1, 15)]  # PortfolioRiskResult for that single date

# Available dates
result.dates  # tuple of dt.date

Converting to DataFrames

All result types support conversion to pandas DataFrames:

# Portfolio result to DataFrame (pivoted)
df = result.to_frame()

# Unpivoted (raw records)
df_raw = result.to_frame(values=None, index=None, columns=None)

# Custom pivoting
df_custom = result.to_frame(values='value', index='dates', columns='instrument_name')

MultipleRiskMeasureResult also supports .to_frame():

multi_result = swap.calc([DollarPrice, IRDelta])
df = multi_result.to_frame()

Checking for Errors

from gs_quant.risk import ErrorValue

price = swap.dollar_price()
if isinstance(price, ErrorValue):
    print(f'Calculation failed: {price.error}')
else:
    print(f'Price: {float(price)}')

Arithmetic on Results

Results support arithmetic operations, which is useful for computing P&L or scaling:

# Multiply portfolio result by a scalar
scaled = result * 1000

# Add results from different portfolios
combined = result_a + result_b

7. Backtesting

gs_quant includes a full backtesting framework under gs_quant.backtests. It lets you define trading strategies as combinations of triggers (when to act) and actions (what to do), then simulate them historically using one of several engines.

For the complete backtesting guide — including all trigger types, action types, engine selection, and result extraction — see:

📄 gs_quant/backtests/SKILL.md

Quick Example — Monthly FX Option Roll

from datetime import date
from gs_quant.instrument import FXOption
from gs_quant.common import BuySell, OptionType
from gs_quant.backtests.triggers import PeriodicTrigger, PeriodicTriggerRequirements
from gs_quant.backtests.actions import AddTradeAction
from gs_quant.backtests.generic_engine import GenericEngine
from gs_quant.backtests.strategy import Strategy
from gs_quant.risk import Price

start_date = date(2023, 1, 3)
end_date = date(2024, 12, 31)

call = FXOption(
    buy_sell=BuySell.Buy, option_type=OptionType.Call,
    pair='USDJPY', strike_price='ATMF', expiration_date='2y',
    name='2y_call', premium=0,
)

trig_req = PeriodicTriggerRequirements(start_date=start_date, end_date=end_date, frequency='1m')
action = AddTradeAction(call, '1m')
trigger = PeriodicTrigger(trig_req, action)

strategy = Strategy(None, trigger)
GE = GenericEngine()
backtest = GE.run_backtest(strategy, start=start_date, end=end_date, frequency='1b', show_progress=True)

# View results
backtest.result_summary['Total'].plot(title='Performance')

8. Accessing Data with Dataset

The Dataset class in gs_quant.data provides access to Marquee datasets — structured, time-series collections of market and reference data. Each dataset has a fixed schema, a set of symbol dimensions (e.g. bbid, assetId, ticker), and entitlements that control access.

from gs_quant.data import Dataset
import datetime as dt

Constructing a Dataset

Pass the dataset ID string (visible in the Marquee catalog URL):

ds = Dataset('FXIVOL_STANDARD')

You can also use the built-in vendor enums to avoid hardcoding strings:

ds = Dataset(Dataset.GS.HOLIDAY)
ds = Dataset(Dataset.TR.TREOD)

Equities and listed instruments: For equities and most other listed instruments (equity indices, ETFs, futures, etc.) the correct dataset is almost always TREOD (Thomson Reuters End-of-Day). This is a broad coverage, daily EOD dataset sourced from Refinitiv. Use bbid as the symbol dimension.

ds = Dataset('TREOD')                          # or Dataset(Dataset.TR.TREOD)
df = ds.get_data(dt.date(2025, 1, 2), dt.date(2026, 3, 19), bbid=['GS UN', 'AAPL UW'])

get_data — Fetch a DataFrame

The primary method. Returns a pandas.DataFrame with one row per data point.

df = ds.get_data(
    start=dt.date(2025, 1, 2),
    end=dt.date(2025, 3, 19),
    bbid=['EURUSD', 'USDJPY'],      # filter by symbol dimension — passed as kwargs
)

Key parameters:

Parameter Description
start Start date or datetime of the query
end End date or datetime (inclusive)
as_of Return data as it existed at this point in time
since Return data updated since this datetime
fields List of field names to return; omit for all fields
**kwargs Symbol dimension filters, e.g. bbid=['EURUSD'], ticker='SPX', assetId='...'

Filter kwargs match the dataset's symbol dimensions exactly — check the Marquee catalog page for the correct dimension name. Multiple values are passed as a list.

# Filter by a single value
df = ds.get_data(dt.date(2025, 1, 2), dt.date(2025, 3, 19), bbid='EURUSD')

# Filter by multiple values
df = ds.get_data(dt.date(2025, 1, 2), dt.date(2025, 3, 19), bbid=['EURUSD', 'GBPUSD'])

# Restrict to specific fields
df = ds.get_data(dt.date(2025, 1, 2), dt.date(2025, 3, 19),
                 bbid=['EURUSD'],
                 fields=['impliedVolatility', 'tenor'])

# Query specific dates rather than a range
df = ds.get_data(dates=[dt.date(2025, 1, 15), dt.date(2025, 3, 19)], bbid=['EURUSD'])

get_data_series — Fetch a Single-Field Time Series

Returns a pandas.Series indexed by date/time when the dataset has exactly one symbol dimension and you want a single field:

series = ds.get_data_series(
    field='impliedVolatility',
    start=dt.date(2025, 1, 2),
    end=dt.date(2025, 3, 19),
    bbid='EURUSD',
)
# series is a pd.Series indexed by date

get_data_last — Most Recent Data Point

Returns the latest available row at or before as_of:

latest = ds.get_data_last(
    as_of=dt.datetime.now(),
    bbid=['EURUSD', 'USDJPY'],
)

get_coverage — What Assets Are Available

Returns a DataFrame listing every symbol covered by the dataset:

coverage = ds.get_coverage()
print(coverage.head())

# Include the history start date for each asset
coverage = ds.get_coverage(include_history=True)
coverage = coverage.sort_values('historyStartDate')

Iterating Over Large Queries

For datasets with many assets or long date ranges, break the query into smaller chunks to avoid API limits:

import datetime as dt

def query_in_batches(dataset, ids, start, end, id_field='bbid', time_delta=dt.timedelta(days=30)):
    """Fetch data in time batches for a list of symbol IDs."""
    frames = []
    batch_start = start
    while batch_start < end:
        batch_end = min(batch_start + time_delta, end)
        df = dataset.get_data(batch_start, batch_end, **{id_field: ids})
        frames.append(df)
        batch_start = batch_end
    return pd.concat(frames) if frames else pd.DataFrame()

import pandas as pd
ds = Dataset('EDRVOL_PERCENT_V1_STANDARD')
coverage = ds.get_coverage()
ids = coverage['assetId'].tolist()[:10]   # first 10 assets

df = query_in_batches(ds, ids, dt.date(2024, 1, 1), dt.date(2025, 3, 19), id_field='assetId')

Uploading Data

You can write data back to a dataset you own:

import pandas as pd

data = [
    {'date': '2025-03-19', 'city': 'London', 'maxTemperature': 14.0, 'minTemperature': 7.0},
    {'date': '2025-03-19', 'city': 'New York', 'maxTemperature': 18.0, 'minTemperature': 9.0},
]
ds = Dataset('MY_CUSTOM_DATASET')
ds.upload_data(data)                     # accepts list of dicts or a DataFrame

Common Pitfalls

  • Wrong dimension name — each dataset has its own symbol dimension (bbid, assetId, ticker, ric, etc.). Check the Marquee catalog page. Passing the wrong kwarg silently returns an empty DataFrame.
  • Query size limits — very wide queries (many assets × long date range) will time out or be rejected. Iterate in batches as shown above.
  • Entitlements — if get_data returns an empty DataFrame unexpectedly, your session may not have the required entitlement scope (e.g. read_product_data). Ensure your GsSession was initialised with the appropriate scopes.
  • Intraday vs daily — some datasets are indexed by datetime (intraday); others by date (EOD). Pass dt.datetime objects for intraday datasets and dt.date for EOD.