This document covers the core workflows for using the `gs_quant` library: establishing a session, constructing and resolving instruments, building portfolios, pricing historically, pricing with live market data, and extracting results.
GsSession.useAll 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().
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_domain — Environment.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.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)
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
...
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.
gs_quant.instrumentInstruments 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.
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
)
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.
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.
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.
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)
from gs_quant.instrument import IRSwaption
swaption = IRSwaption(
pay_or_receive='Receive',
termination_date='10y',
notional_currency='EUR',
expiration_date='1y',
strike='ATM',
)
from gs_quant.instrument import IRCap
cap = IRCap(
termination_date='1y',
notional_currency='USD',
)
from gs_quant.instrument import FXOption
option = FXOption(
pair='EURUSD',
expiration_date='3m',
option_type='Call',
strike_price='ATMF',
notional_amount=10e6,
)
from gs_quant.instrument import FXForward
fwd = FXForward(
pair='USDJPY',
settlement_date='6m',
notional_amount=10e6,
)
There are two common mistakes when working with FX options that can lead to confusing results:
premium=0 on FX OptionsWhen 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!
)
When constructing FXMultiCrossBinaryLeg objects (used within FXMultiCrossBinary for dual digital options), you must use OptionType.Binary_Call or OptionType.Binary_Put — not 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!
)
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'
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
resolve() DoesPricingContext (pricing date and market).in_place=True), the instrument is updated in place. Pass in_place=False to receive a new resolved copy instead.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()
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}
The Portfolio class groups instruments together so you can price, resolve, and analyse them as a single unit.
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'),
})
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')
# 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
portfolio.resolve() # resolves all instruments in place
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).
HistoricalPricingContextHistoricalPricingContext lets you compute risk measures across a range of dates using the close-of-business market for each date.
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
Pass an integer to price over the last N business days:
with HistoricalPricingContext(10):
price_f = swap.price()
price_series = price_f.result()
dates = [dt.date(2025, 1, 2), dt.date(2025, 3, 15), dt.date(2025, 6, 30)]
with HistoricalPricingContext(dates=dates):
price_f = swap.price()
| 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 |
with HistoricalPricingContext(dt.date(2025, 1, 2), dt.date(2025, 1, 31)):
result = portfolio.calc(DollarPrice)
# result is a PortfolioRiskResult with a date dimension
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.
| 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. |
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.
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
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.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
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
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()
from gs_quant.risk import ErrorValue
price = swap.dollar_price()
if isinstance(price, ErrorValue):
print(f'Calculation failed: {price.error}')