Derivatives Portfolio Risk Statistics
=====================================
From a risk management perspective it is important to know **how
sensitive derivatives portfolios are** with regard to certain parameter
values (market quotes, model assumptions, etc.). This part illustrates
how to generate certain **risk reports** for ``derivatives_portfolio``
objects.
.. code:: python
import dx
import datetime as dt
import time
import numpy as np
Risk Factors
------------
The example is based on **two risk factors**, both modeled as geometric
Brownian motions.
.. code:: python
# constant short rate
r = dx.constant_short_rate('r', 0.01)
.. code:: python
# market environment
me_gbm_1 = dx.market_environment('gbm_1', dt.datetime(2015, 1, 1))
.. code:: python
# geometric Brownian motion
me_gbm_1.add_constant('initial_value', 40.)
me_gbm_1.add_constant('volatility', 0.2)
me_gbm_1.add_constant('currency', 'EUR')
me_gbm_1.add_constant('model', 'gbm')
.. code:: python
me_gbm_2 = dx.market_environment('gbm_2', me_gbm_1.pricing_date)
.. code:: python
# valuation environment
val_env = dx.market_environment('val_env', dt.datetime(2015, 1, 1))
val_env.add_constant('paths', 25000)
# 25,000 paths
val_env.add_constant('frequency', 'W')
# weekly frequency
val_env.add_curve('discount_curve', r)
val_env.add_constant('starting_date', dt.datetime(2015, 1, 1))
val_env.add_constant('final_date', dt.datetime(2015, 12, 31))
.. code:: python
# add valuation environment to market environments
me_gbm_1.add_environment(val_env)
me_gbm_2.add_environment(me_gbm_1)
me_gbm_2.add_constant('initial_value', 40.)
me_gbm_2.add_constant('volatility', 0.5)
# higher volatility
.. code:: python
risk_factors = {'gbm_1' : me_gbm_1, 'gbm_2' : me_gbm_2}
# market with two risk factors
Derivatives Positions
---------------------
We are going to model **total of 6 derivatives positions**.
Market Environment
~~~~~~~~~~~~~~~~~~
All derivatives instruments (positions) share the same
``market_environment`` object.
.. code:: python
# market environment for the options
me_option = dx.market_environment('put', dt.datetime(2015, 1, 1))
me_option.add_constant('maturity', dt.datetime(2015, 12, 31))
me_option.add_constant('currency', 'EUR')
me_option.add_environment(val_env)
Derivatives Positions
~~~~~~~~~~~~~~~~~~~~~
Two different kinds of derivatives make up the portfolio---an **American
put option** and a **European maximum call option**. Both types of
derivatives populate three positions, respectively.
.. code:: python
positions = {}
half = 3 # 2 times that many options
for i in range(half):
name = 'am_put_pos_%s' %i # same name for position key and name
positions[name] = dx.derivatives_position(
name=name,
quantity=1,
underlyings=['gbm_1'],
mar_env=me_option,
otype='American single',
payoff_func='np.maximum(instrument_values - 40., 0)')
multi_payoff = "np.maximum(np.maximum(maturity_value['gbm_1'], maturity_value['gbm_2']) - 40., 0)"
for i in range(half, 2 * half):
name = 'multi_pos_%s' %i # same name for position key and name
positions[name] = dx.derivatives_position(
name=name,
quantity=1,
underlyings=['gbm_1', 'gbm_2'],
mar_env=me_option,
otype='European multi',
payoff_func=multi_payoff)
Portfolio Modeling and Valuation
--------------------------------
The instantiation of the ``derivatives_portfolio`` object is as usual.
.. code:: python
portfolio = dx.derivatives_portfolio(
name='portfolio',
positions=positions,
val_env=val_env,
risk_factors=risk_factors,
correlations=None,
parallel=False)
.. code:: python
%time res = portfolio.get_values(fixed_seed=True)
.. parsed-literal::
Total
pos_value 40.971
dtype: float64
CPU times: user 724 ms, sys: 4 ms, total: 728 ms
Wall time: 728 ms
Here, the **value estimates** from the Monte Carlo simulation and
valuation.
.. code:: python
res
.. raw:: html
|
position |
name |
quantity |
otype |
risk_facts |
value |
currency |
pos_value |
0 |
am_put_pos_2 |
am_put_pos_2 |
1 |
American single |
[gbm_1] |
3.317 |
EUR |
3.317 |
1 |
am_put_pos_0 |
am_put_pos_0 |
1 |
American single |
[gbm_1] |
3.341 |
EUR |
3.341 |
2 |
am_put_pos_1 |
am_put_pos_1 |
1 |
American single |
[gbm_1] |
3.323 |
EUR |
3.323 |
3 |
multi_pos_3 |
multi_pos_3 |
1 |
European multi |
[gbm_1, gbm_2] |
10.330 |
EUR |
10.330 |
4 |
multi_pos_4 |
multi_pos_4 |
1 |
European multi |
[gbm_1, gbm_2] |
10.330 |
EUR |
10.330 |
5 |
multi_pos_5 |
multi_pos_5 |
1 |
European multi |
[gbm_1, gbm_2] |
10.330 |
EUR |
10.330 |
Portfolio Risk Reports
----------------------
Portfolio risk reports are meant to provide a broad overview of how
sensitive the value of a portfolio is with regard to the value of
certain input parameters (market data, model parameters). While
**Greeks** provide the same information with regard to marginal changes
in the input paramters, risk reports provide a **wider range
input-output (parameter-portfolio value) combinations**.
No Correlation
~~~~~~~~~~~~~~
First, consider the portfolio from before, i.e. **without correlation**.
.. code:: python
portfolio.val_env.get_list('cholesky_matrix')
.. parsed-literal::
array([[ 1., 0.],
[ 0., 1.]])
Calling the method ``get_port_risk`` and providing a key for the
respetive Greek yields sensitivities with regard to all risk factors
(here: ``gbm_1`` and ``gbm_2``).
portfolio.valuation_objects[3].underlying_objects['gbm_1'].update(initial_value=15)
.. code:: python
%%time
vegas, benchvalue = portfolio.get_port_risk(Greek='Vega',
fixed_seed=True)
.. parsed-literal::
gbm_1
0.8 0.9 1.0 1.1 1.2
gbm_2
0.8 0.9 1.0 1.1 1.2
CPU times: user 5.04 s, sys: 0 ns, total: 5.04 s
Wall time: 5.04 s
The return object is a pandas ``Panel`` object.
.. code:: python
vegas
.. parsed-literal::
Dimensions: 2 (items) x 5 (major_axis) x 2 (minor_axis)
Items axis: gbm_1_Vega to gbm_2_Vega
Major_axis axis: 0.8 to 1.2
Minor_axis axis: factor to value
Using the helper funtion ``risk_report`` allows the easy, readable
printout of the results, i.e. the **portfolio volatility
sensitivities**. In this case you can see that, for example, the
increase in the first risk fator's (``gbm_1``) volatility by 10% leads
to a portfolio value increase bya bit less than 1 currency unit.
Decreasing the same input parameter by 10% reduces the portfolio value
by a bit less than 1 currency unit.
.. code:: python
dx.risk_report(vegas)
.. parsed-literal::
gbm_1_Vega
0.8 0.9 1.0 1.1 1.2
factor 0.16 0.18 0.20 0.22 0.24
value 32.31 33.25 40.96 35.11 35.99
gbm_2_Vega
0.8 0.9 1.0 1.1 1.2
factor 0.40 0.45 0.50 0.55 0.60
value 29.45 31.82 40.96 36.52 38.86
Of course, you can generate the same risk report for the **portfolio
initial value sensitivities**.
.. code:: python
%time deltas, benchvalue = portfolio.get_port_risk(Greek='Delta', fixed_seed=True)
.. parsed-literal::
gbm_1
0.8 0.9 1.0 1.1 1.2
gbm_2
0.8 0.9 1.0 1.1 1.2
CPU times: user 5.38 s, sys: 3 ms, total: 5.38 s
Wall time: 5.38 s
For example, increasing the initial value of the first risk factor
(``gbm_1``) by 10% increases the portfolio value by about 11 currency
units.
.. code:: python
dx.risk_report(deltas)
.. parsed-literal::
gbm_1_Delta
0.8 0.9 1.0 1.1 1.2
factor 32.00 36.00 40.00 44.00 48.00
value 25.48 28.44 40.96 45.31 61.36
gbm_2_Delta
0.8 0.9 1.0 1.1 1.2
factor 32.00 36.00 40.00 44.0 48.00
value 24.14 28.31 40.96 41.6 50.09
.. code:: python
dx.risk_report(deltas.ix[:, :, 'value'] - benchvalue)
.. parsed-literal::
gbm_1_Delta
0.8 -15.49
0.9 -12.53
1.0 0.00
1.1 4.34
1.2 20.39
Name: gbm_1_Delta, dtype: float64
gbm_2_Delta
0.8 -16.83
0.9 -12.65
1.0 0.00
1.1 0.64
1.2 9.13
Name: gbm_2_Delta, dtype: float64
With Correlation
~~~~~~~~~~~~~~~~
Consider now a **highly negative correlation** case.
.. code:: python
correlations = [['gbm_1', 'gbm_2', -0.9]]
.. code:: python
portfolio = dx.derivatives_portfolio(
'portfolio', positions, val_env,
risk_factors, correlations, parallel=False)
.. code:: python
portfolio.val_env.get_list('cholesky_matrix')
.. parsed-literal::
array([[ 1. , 0. ],
[-0.9 , 0.43588989]])
Since the value of the European maximum call option is dependent on the
risk factor correlation you see a **significant change in this
derivative's value estimate**.
.. code:: python
%time portfolio.get_values(fixed_seed=True)
.. parsed-literal::
Total
pos_value 44.112
dtype: float64
CPU times: user 658 ms, sys: 0 ns, total: 658 ms
Wall time: 658 ms
.. raw:: html
|
position |
name |
quantity |
otype |
risk_facts |
value |
currency |
pos_value |
0 |
am_put_pos_2 |
am_put_pos_2 |
1 |
American single |
[gbm_1] |
3.293 |
EUR |
3.293 |
1 |
am_put_pos_0 |
am_put_pos_0 |
1 |
American single |
[gbm_1] |
3.293 |
EUR |
3.293 |
2 |
am_put_pos_1 |
am_put_pos_1 |
1 |
American single |
[gbm_1] |
3.293 |
EUR |
3.293 |
3 |
multi_pos_3 |
multi_pos_3 |
1 |
European multi |
[gbm_1, gbm_2] |
11.411 |
EUR |
11.411 |
4 |
multi_pos_4 |
multi_pos_4 |
1 |
European multi |
[gbm_1, gbm_2] |
11.411 |
EUR |
11.411 |
5 |
multi_pos_5 |
multi_pos_5 |
1 |
European multi |
[gbm_1, gbm_2] |
11.411 |
EUR |
11.411 |
Via the ``step`` parameter, you can influence the **granularity of the
risk report**.
.. code:: python
%%time
deltas, benchvalue = portfolio.get_port_risk(Greek='Delta',
fixed_seed=True,
step=0.05)
.. parsed-literal::
gbm_1
0.8 0.85 0.9 0.95 1.0 1.05 1.1 1.15 1.2
gbm_2
0.8 0.85 0.9 0.95 1.0 1.05 1.1 1.15 1.2
CPU times: user 7.82 s, sys: 3 ms, total: 7.83 s
Wall time: 7.83 s
In this case, an increase in the intial value of the first risk factor
(``gbm_1``) by 10% leads to a **much higher increase** in the portfolio
value of about 15 currency units.
.. code:: python
dx.risk_report(deltas)
.. parsed-literal::
gbm_1_Delta
0.80 0.85 0.90 0.95 1.00 1.05 1.10 1.15 1.20
factor 32.0 34.00 36.00 38.00 40.00 42.00 44.00 46.00 48.00
value 27.2 29.65 33.23 38.12 44.11 51.06 58.96 67.64 76.98
gbm_2_Delta
0.80 0.85 0.90 0.95 1.00 1.05 1.10 1.15 1.20
factor 32.00 34.00 36.00 38.00 40.00 42.00 44.00 46.0 48.00
value 31.67 34.38 37.36 40.62 44.11 47.82 51.73 55.8 60.02
.. code:: python
dx.risk_report(deltas.ix[:, :, 'value'] - benchvalue)
.. parsed-literal::
gbm_1_Delta
0.80 -16.92
0.85 -14.46
0.90 -10.88
0.95 -5.99
1.00 0.00
1.05 6.94
1.10 14.85
1.15 23.53
1.20 32.86
Name: gbm_1_Delta, dtype: float64
gbm_2_Delta
0.80 -12.45
0.85 -9.74
0.90 -6.75
0.95 -3.49
1.00 0.00
1.05 3.71
1.10 7.62
1.15 11.69
1.20 15.91
Name: gbm_2_Delta, dtype: float64
**Copyright, License & Disclaimer**
© Dr. Yves J. Hilpisch \| The Python Quants GmbH
DX Analytics (the "dx library") is licensed under the GNU Affero General
Public License version 3 or later (see http://www.gnu.org/licenses/).
DX Analytics comes with no representations or warranties, to the extent
permitted by applicable law.
http://tpq.io \| team@tpq.io \| http://twitter.com/dyjh
**Quant Platform** \| http://quant-platform.com
**Derivatives Analytics with Python (Wiley Finance)** \|
http://derivatives-analytics-with-python.com
**Python for Finance (O'Reilly)** \| http://python-for-finance.com