Gamma Risk#
Calculation#
Gamma, technically cross-gamma, risks are calculated in rateslib in a very similar manner to delta, albeit the expression of the result is more complicated, since cross-gamma risks are a matrix of values, whereas delta risk is typically a vector.
The output is a DataFrame indexed with a hierarchical index allowing the information to be viewed all at once or sliced using pandas indexing tools.
The below gives a full example of constructing solvers with dependency chains, then constructing a multi-currency portfolio and viewing the gamma risks in local and base currencies.
First we initialise the Curve
s. A SOFR, ESTR and
cross-currency curve for valuing EUR cashflows collateralized in USD.
In [1]: sofr = Curve(
...: nodes={dt(2022, 1, 1): 1.0, dt(2032, 1, 1): 1.0, dt(2042, 1, 1): 1.0},
...: id="sofr"
...: )
...:
In [2]: estr = Curve(
...: nodes={dt(2022, 1, 1): 1.0, dt(2032, 1, 1): 1.0, dt(2042, 1, 1): 1.0},
...: id="estr"
...: )
...:
In [3]: eurusd = Curve(
...: nodes={dt(2022, 1, 1): 1.0, dt(2032, 1, 1): 1.0, dt(2042, 1, 1): 1.0},
...: id="eurusd"
...: )
...:
Then we define our FXForwards
object and associations.
In [4]: fxr = FXRates({"eurusd": 1.05}, settlement=dt(2022, 1, 3))
In [5]: fxf = FXForwards(fxr, {
...: "eureur": estr,
...: "eurusd": eurusd,
...: "usdusd": sofr
...: })
...:
Then we define the instruments that will be
incorporated into our Solver
s.
In [6]: instruments = [
...: IRS(dt(2022, 1, 1), "10y", "A", currency="usd", curves="sofr"),
...: IRS(dt(2032, 1, 1), "10y", "A", currency="usd", curves="sofr"),
...: IRS(dt(2022, 1, 1), "10y", "A", currency="eur", curves="estr"),
...: IRS(dt(2032, 1, 1), "10y", "A", currency="eur", curves="estr"),
...: XCS(dt(2022, 1, 1), "10y", "A", currency="usd", leg2_currency="usd", curves=["estr", "eurusd", "sofr", "sofr"]),
...: XCS(dt(2032, 1, 1), "10y", "A", currency="usd", leg2_currency="eur", curves=["estr", "eurusd", "sofr", "sofr"]),
...: ]
...:
Then we solve the SOFR and ESTR curves independently given local currency swap markets.
In [7]: sofr_solver= Solver(
...: curves=[sofr],
...: instruments=instruments[:2],
...: s=[3.45, 2.85],
...: instrument_labels=["10y", "10y10y"],
...: id="sofr",
...: fx=fxf
...: )
...:
SUCCESS: `func_tol` reached after 6 iterations (levenberg_marquardt), `f_val`: 6.1823256691788175e-15, `time`: 0.0231s
In [8]: estr_solver= Solver(
...: curves=[estr],
...: instruments=instruments[2:4],
...: s=[2.25, 0.90],
...: instrument_labels=["10y", "10y10y"],
...: id="estr",
...: fx=fxf
...: )
...:
SUCCESS: `func_tol` reached after 6 iterations (levenberg_marquardt), `f_val`: 2.2273797378672649e-13, `time`: 0.0227s
Finally we solve the cross-currency solver with a dependency to the single currency
markets, as specified within the pre_solvers
argument.
In [9]: solver= Solver(
...: curves=[eurusd],
...: instruments=instruments[4:],
...: s=[-10, -15],
...: instrument_labels=["10y", "10y10y"],
...: id="eurusd",
...: fx=fxf,
...: pre_solvers=[sofr_solver, estr_solver],
...: )
...:
SUCCESS: `func_tol` reached after 4 iterations (levenberg_marquardt), `f_val`: 3.01104003064156e-17, `time`: 0.0661s
Now we create a multi-currency Portfolio
and
calculate its cross-gamma.
In [10]: pf = Portfolio([
....: IRS(dt(2022, 1, 1), "20Y", "A", currency="eur", fixed_rate=2.0, notional=1e8, curves="estr"),
....: IRS(dt(2022, 1, 1), "20Y", "A", currency="usd", fixed_rate=1.5, notional=-1.1e8, curves="sofr")
....: ])
....:
In [11]: cgamma = pf.gamma(solver=solver, base="eur")
In [12]: cgamma
Out[12]:
type instruments fx
solver sofr estr eurusd fx
label 10y 10y10y 10y 10y10y 10y 10y10y eurusd
local_ccy display_ccy type solver label
eur eur instruments sofr 10y 0.00 0.00 0.00 0.00 0.00 0.00 0.00
10y10y 0.00 0.00 0.00 0.00 0.00 0.00 0.00
estr 10y 0.00 0.00 -102.97 -81.01 0.00 0.00 0.00
10y10y 0.00 0.00 -81.01 -87.84 0.00 0.00 0.00
eurusd 10y 0.00 0.00 0.00 0.00 0.00 0.00 0.00
10y10y 0.00 0.00 0.00 0.00 0.00 0.00 0.00
fx fx eurusd 0.00 0.00 0.00 0.00 0.00 0.00 0.00
usd eur instruments sofr 10y 74.43 58.88 0.04 -0.00 0.04 -0.00 6.76
10y10y 58.88 63.69 0.03 -0.00 0.03 -0.00 5.72
estr 10y 0.04 0.03 -0.00 0.00 -0.00 0.00 -0.00
10y10y -0.00 -0.00 0.00 -0.00 0.00 -0.00 0.00
eurusd 10y 0.04 0.03 -0.00 0.00 -0.00 0.00 -0.00
10y10y -0.00 -0.00 0.00 -0.00 0.00 -0.00 0.00
fx fx eurusd 6.76 5.72 -0.00 0.00 -0.00 0.00 -0.47
usd instruments sofr 10y 78.23 61.85 0.00 0.00 0.00 0.00 0.00
10y10y 61.85 66.87 0.00 0.00 0.00 0.00 0.00
estr 10y 0.00 0.00 0.00 0.00 0.00 0.00 0.00
10y10y 0.00 0.00 0.00 0.00 0.00 0.00 0.00
eurusd 10y 0.00 0.00 0.00 0.00 0.00 0.00 0.00
10y10y 0.00 0.00 0.00 0.00 0.00 0.00 0.00
fx fx eurusd 0.00 0.00 0.00 0.00 0.00 0.00 0.00
all eur instruments sofr 10y 74.43 58.88 0.04 -0.00 0.04 -0.00 6.76
10y10y 58.88 63.69 0.03 -0.00 0.03 -0.00 5.72
estr 10y 0.04 0.03 -102.97 -81.01 -0.00 0.00 -0.00
10y10y -0.00 -0.00 -81.01 -87.84 0.00 -0.00 0.00
eurusd 10y 0.04 0.03 -0.00 0.00 -0.00 0.00 -0.00
10y10y -0.00 -0.00 0.00 -0.00 0.00 -0.00 0.00
fx fx eurusd 6.76 5.72 -0.00 0.00 -0.00 0.00 -0.47
We can slice this to display only the EUR risk.
In [13]: idx = ("eur", "eur", slice(None), ["estr", "fx"], slice(None))
In [14]: cgamma.loc[idx, (slice(None), ["estr", "fx"], slice(None))]
Out[14]:
type instruments fx
solver estr fx
label 10y 10y10y eurusd
local_ccy display_ccy type solver label
eur eur instruments estr 10y -102.97 -81.01 0.00
10y10y -81.01 -87.84 0.00
fx fx eurusd 0.00 0.00 0.00