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.


In [1]:
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.


In [2]:
# constant short rate
r = dx.constant_short_rate('r', 0.01)

In [3]:
# market environment
me_gbm_1 = dx.market_environment('gbm_1', dt.datetime(2015, 1, 1))

In [4]:
# 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')

In [5]:
me_gbm_2 = dx.market_environment('gbm_2', me_gbm_1.pricing_date)

In [6]:
# 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))

In [7]:
# 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

In [8]:
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.


In [9]:
# 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.


In [10]:
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.


In [11]:
portfolio = dx.derivatives_portfolio(
                        name='portfolio',
                        positions=positions,
                        val_env=val_env,
                        risk_factors=risk_factors,
                        correlations=None,
                        parallel=False)

In [12]:
%time res = portfolio.get_values(fixed_seed=True)


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.


In [13]:
res


Out[13]:
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.


In [14]:
portfolio.val_env.get_list('cholesky_matrix')


Out[14]:
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)

In [15]:
%%time
vegas, benchvalue = portfolio.get_port_risk(Greek='Vega',
                                fixed_seed=True)


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.


In [16]:
vegas


Out[16]:
<class 'pandas.core.panel.Panel'>
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.


In [17]:
dx.risk_report(vegas)


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.


In [18]:
%time deltas, benchvalue = portfolio.get_port_risk(Greek='Delta', fixed_seed=True)


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.


In [19]:
dx.risk_report(deltas)


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

In [20]:
dx.risk_report(deltas.ix[:, :, 'value'] - benchvalue)


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.


In [21]:
correlations = [['gbm_1', 'gbm_2', -0.9]]

In [22]:
portfolio = dx.derivatives_portfolio(
                        'portfolio', positions, val_env,
                        risk_factors, correlations, parallel=False)

In [23]:
portfolio.val_env.get_list('cholesky_matrix')


Out[23]:
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.


In [24]:
%time portfolio.get_values(fixed_seed=True)


Total
pos_value    44.112
dtype: float64
CPU times: user 658 ms, sys: 0 ns, total: 658 ms
Wall time: 658 ms
Out[24]:
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.


In [25]:
%%time 
deltas, benchvalue = portfolio.get_port_risk(Greek='Delta',
                                 fixed_seed=True,
                                 step=0.05)


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.


In [26]:
dx.risk_report(deltas)


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

In [27]:
dx.risk_report(deltas.ix[:, :, 'value'] - benchvalue)


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