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):
    positions[i] = dx.derivatives_position(
                        name='am_put_pos_%s' %i,
                        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):
    positions[i] = dx.derivatives_position(
                        name='multi_pos_%s' %i,
                        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.999
dtype: float64
CPU times: user 1.19 s, sys: 182 ms, total: 1.38 s
Wall time: 1.39 s

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 0 am_put_pos_0 1 American single [gbm_1] 3.288 EUR 3.288
1 1 am_put_pos_1 1 American single [gbm_1] 3.258 EUR 3.258
2 2 am_put_pos_2 1 American single [gbm_1] 3.319 EUR 3.319
3 3 multi_pos_3 1 European multi [gbm_1, gbm_2] 10.378 EUR 10.378
4 4 multi_pos_4 1 European multi [gbm_1, gbm_2] 10.378 EUR 10.378
5 5 multi_pos_5 1 European multi [gbm_1, gbm_2] 10.378 EUR 10.378

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).


In [15]:
%%time
vegas, benchmark = 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 7.7 s, sys: 1.02 s, total: 8.71 s
Wall time: 8.96 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, benchmark = 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 8.42 s, sys: 1.12 s, total: 9.54 s
Wall time: 10.6 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

With Correlation

Consider now a highly negative correlation case.


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

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

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


Out[22]:
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 [23]:
%time portfolio.get_values(fixed_seed=True)


Total
pos_value    44.112
dtype: float64
CPU times: user 996 ms, sys: 86.3 ms, total: 1.08 s
Wall time: 1.05 s
Out[23]:
position name quantity otype risk_facts value currency pos_value
0 0 am_put_pos_0 1 American single [gbm_1] 3.293 EUR 3.293
1 1 am_put_pos_1 1 American single [gbm_1] 3.293 EUR 3.293
2 2 am_put_pos_2 1 American single [gbm_1] 3.293 EUR 3.293
3 3 multi_pos_3 1 European multi [gbm_1, gbm_2] 11.411 EUR 11.411
4 4 multi_pos_4 1 European multi [gbm_1, gbm_2] 11.411 EUR 11.411
5 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 [24]:
%%time 
deltas, benchmark = 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 12 s, sys: 1.34 s, total: 13.3 s
Wall time: 12.9 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 [25]:
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

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://www.pythonquants.com | analytics@pythonquants.com | http://twitter.com/dyjh

Python Quant Platform | http://quant-platform.com

Derivatives Analytics with Python (Wiley Finance) | http://derivatives-analytics-with-python.com

Python for Finance (O'Reilly) | http://shop.oreilly.com/product/0636920032441.do