The Python Quants

DX Analytics Library

Multi Asset Valuation Models

In [1]:
from dx import *
In [2]:
import time
t0 = time.time()

Multi Risk Factor Classes

There are the following multiple risk factor valuation classes available:

  • valuation_mcs_european_multi
  • valuation_mcs_american_multi

Market Environments

In [3]:
r = constant_short_rate('r', 0.06)
In [4]:
me1 = market_environment('me1', dt.datetime(2015, 1, 1))
me2 = market_environment('me2', dt.datetime(2015, 1, 1))
In [5]:
me1.add_constant('initial_value', 36.)
me1.add_constant('volatility', 0.1)  # low vol
me1.add_constant('currency', 'EUR')
me1.add_constant('model', 'gbm')
In [6]:
me2.add_environment(me1)
me2.add_constant('initial_value', 36.)
me2.add_constant('volatility', 0.5)  # high vol
In [7]:
underlyings = {'gbm1' : me1, 'gbm2' : me2}
correlations = [['gbm1', 'gbm2', 0.5]]

Valuation Environment

In [8]:
val_env = market_environment('val_env', dt.datetime(2015, 1, 1))
In [9]:
val_env.add_constant('starting_date', val_env.pricing_date)
val_env.add_constant('final_date', dt.datetime(2015, 12, 31))
val_env.add_constant('frequency', 'M')
val_env.add_constant('paths', 10000)
val_env.add_curve('discount_curve', r)
val_env.add_constant('maturity', dt.datetime(2015, 12, 31))
val_env.add_constant('currency', 'EUR')

valuation_mcs_european_multi

In [10]:
# European maximum call option
payoff_func = "np.maximum(np.maximum(maturity_value['gbm1'], maturity_value['gbm2']) - 38, 0)"
vc = valuation_mcs_european_multi(
            name='European maximum call',
            val_env=val_env,
            assets=underlyings,
            correlations=correlations,
            payoff_func=payoff_func)
In [11]:
vc.assets
Out[11]:
{'gbm1': <dx_frame.market_environment at 0x1089bc090>,
 'gbm2': <dx_frame.market_environment at 0x1089bc050>}
In [12]:
vc.underlying_objects
Out[12]:
{'gbm1': <dx_models.geometric_brownian_motion at 0x1089bc410>,
 'gbm2': <dx_models.geometric_brownian_motion at 0x1089bc450>}
In [13]:
vc.correlations
Out[13]:
[['gbm1', 'gbm2', 0.5]]
In [14]:
vc.correlation_matrix
Out[14]:
gbm1 gbm2
gbm1 1.0 0.5
gbm2 0.5 1.0

2 rows × 2 columns

In [15]:
vc.val_env.get_list('cholesky_matrix')
Out[15]:
array([[ 1.       ,  0.       ],
       [ 0.5      ,  0.8660254]])
In [16]:
vc.generate_payoff()
Out[16]:
array([  1.61520289,   9.72068514,  41.97052045, ...,  25.44429074,
         0.        ,   2.89143985])
In [17]:
vc.present_value()
Out[17]:
7.700418
In [18]:
vc.update('gbm1', initial_value=50.)
In [19]:
vc.present_value()
Out[19]:
16.823032
In [20]:
vc.update('gbm2', volatility=0.5)
In [21]:
vc.present_value()
Out[21]:
16.823032
In [22]:
vc.update('gbm1', initial_value=36., volatility=0.1)
vc.update('gbm2', initial_value=36., volatility=0.5)
In [23]:
vc.delta('gbm2', interval=0.5)
Out[23]:
0.5713620000000006
In [24]:
vc.vega('gbm1')
Out[24]:
6.241099999999999

Sensitivities Positive Correlation

Sensitivities Asset No 1

In [25]:
%%time
s_list = np.arange(28., 46.1, 2.)
pv = []; de = []; ve = []
for s in s_list:
    vc.update('gbm1', initial_value=s)
    pv.append(vc.present_value())
    de.append(vc.delta('gbm1', .5))
    ve.append(vc.vega('gbm1', 0.2))
vc.update('gbm1', initial_value=36.)
CPU times: user 201 ms, sys: 3.67 ms, total: 205 ms
Wall time: 205 ms

In [26]:
%matplotlib inline
In [27]:
from dx_plot import *
plot_option_stats(s_list, pv, de, ve)

Sensitivities Asset No 2

In [28]:
%%time
s_list = np.arange(28., 46.1, 2.)
pv = []; de = []; ve = []
for s in s_list:
    vc.update('gbm2', initial_value=s)
    pv.append(vc.present_value())
    de.append(vc.delta('gbm2', .5))
    ve.append(vc.vega('gbm2', 0.2))
CPU times: user 211 ms, sys: 10.6 ms, total: 221 ms
Wall time: 222 ms

In [29]:
plot_option_stats(s_list, pv, de, ve)

Sensitivities with Negative Correlation

In [30]:
correlations = [['gbm1', 'gbm2', -0.9]]
In [31]:
# European maximum call option
payoff_func = "np.maximum(np.maximum(maturity_value['gbm1'], maturity_value['gbm2']) - 38, 0)"
vc = valuation_mcs_european_multi(
            name='European maximum call',
            val_env=val_env,
            assets=underlyings,
            correlations=correlations,
            payoff_func=payoff_func)

Sensitivities Asset No 1

In [32]:
%%time
s_list = np.arange(28., 46.1, 2.)
pv = []; de = []; ve = []
for s in s_list:
    vc.update('gbm1', initial_value=s)
    pv.append(vc.present_value())
    de.append(vc.delta('gbm1', .5))
    ve.append(vc.vega('gbm1', 0.2))
vc.update('gbm1', initial_value=36.)
CPU times: user 200 ms, sys: 2.94 ms, total: 203 ms
Wall time: 202 ms

In [33]:
plot_option_stats(s_list, pv, de, ve)

Sensitivities Asset No 2

In [34]:
%%time
s_list = np.arange(28., 46.1, 2.)
pv = []; de = []; ve = []
for s in s_list:
    vc.update('gbm2', initial_value=s)
    pv.append(vc.present_value())
    de.append(vc.delta('gbm2', .5))
    ve.append(vc.vega('gbm2', 0.2))
CPU times: user 192 ms, sys: 9.9 ms, total: 202 ms
Wall time: 202 ms

In [35]:
plot_option_stats(s_list, pv, de, ve)

Surfaces (Positive Correlations)

In [36]:
correlations = [['gbm1', 'gbm2', 0.5]]
In [37]:
# European maximum call option
payoff_func = "np.maximum(np.maximum(maturity_value['gbm1'], maturity_value['gbm2']) - 38, 0)"
vc = valuation_mcs_european_multi(
            name='European maximum call',
            val_env=val_env,
            assets=underlyings,
            correlations=correlations,
            payoff_func=payoff_func)

Value Surface

In [38]:
asset_1 = np.arange(28., 46.1, 2.)
asset_2 = asset_1
a_1, a_2 = np.meshgrid(asset_1, asset_2)
value = np.zeros_like(a_1)
In [39]:
%%time
for i in range(np.shape(value)[0]):
    for j in range(np.shape(value)[1]):
        vc.update('gbm1', initial_value=a_1[i, j])
        vc.update('gbm2', initial_value=a_2[i, j])
        value[i, j] = vc.present_value()
CPU times: user 670 ms, sys: 7.5 ms, total: 678 ms
Wall time: 677 ms

In [40]:
plot_greeks_3d([a_1, a_2, value], ['asset 1', 'asset 2', 'present value'])

Delta Surfaces

In [41]:
delta_1 = np.zeros_like(a_1)
delta_2 = np.zeros_like(a_1)
In [42]:
%%time
for i in range(np.shape(delta_1)[0]):
    for j in range(np.shape(delta_1)[1]):
        vc.update('gbm1', initial_value=a_1[i, j])
        vc.update('gbm2', initial_value=a_2[i, j])
        delta_1[i, j] = vc.delta('gbm1')
        delta_2[i, j] = vc.delta('gbm2')
CPU times: user 2.35 s, sys: 125 ms, total: 2.47 s
Wall time: 2.47 s

In [43]:
plot_greeks_3d([a_1, a_2, delta_1], ['asset 1', 'asset 2', 'delta asset 1'])
In [44]:
plot_greeks_3d([a_1, a_2, delta_2], ['asset 1', 'asset 2', 'delta asset 2'])

Vega Surfaces

In [45]:
vega_1 = np.zeros_like(a_1)
vega_2 = np.zeros_like(a_1)
In [46]:
for i in range(np.shape(vega_1)[0]):
    for j in range(np.shape(vega_1)[1]):
        vc.update('gbm1', initial_value=a_1[i, j])
        vc.update('gbm2', initial_value=a_2[i, j])
        vega_1[i, j] = vc.vega('gbm1')
        vega_2[i, j] = vc.vega('gbm2')
In [47]:
plot_greeks_3d([a_1, a_2, vega_1], ['asset 1', 'asset 2', 'vega asset 1'])
In [48]:
plot_greeks_3d([a_1, a_2, vega_2], ['asset 1', 'asset 2', 'vega asset 2'])
In [49]:
# restore initial values
vc.update('gbm1', initial_value=36., volatility=0.1)
vc.update('gbm2', initial_value=36., volatility=0.5)

valuation_mcs_american_multi

Present Values

In [50]:
# American put payoff
payoff_am = "np.maximum(34 - np.minimum(instrument_values['gbm1'], instrument_values['gbm2']), 0)"
In [51]:
# American put option on minimum of two assets
vca = valuation_mcs_american_multi(
            name='American minimum put',
            val_env=val_env,
            assets=underlyings,
            correlations=correlations,
            payoff_func=payoff_am)
In [52]:
vca.present_value()
Out[52]:
4.921935
In [53]:
asset_1 = np.arange(28., 44.1, 4.)
asset_2 = asset_1
a_1, a_2 = np.meshgrid(asset_1, asset_2)
value = np.zeros_like(a_1)
In [54]:
%%time
for i in range(np.shape(value)[0]):
    for j in range(np.shape(value)[1]):
        vca.update('gbm1', initial_value=a_1[i, j])
        vca.update('gbm2', initial_value=a_2[i, j])
        value[i, j] = vca.present_value()
CPU times: user 4.84 s, sys: 108 ms, total: 4.95 s
Wall time: 4.89 s

In [55]:
plot_greeks_3d([a_1, a_2, value], ['asset 1', 'asset 2', 'present value'])

Delta Surfaces

In [56]:
delta_1 = np.zeros_like(a_1)
delta_2 = np.zeros_like(a_1)
In [57]:
%%time
for i in range(np.shape(delta_1)[0]):
    for j in range(np.shape(delta_1)[1]):
        vca.update('gbm1', initial_value=a_1[i, j])
        vca.update('gbm2', initial_value=a_2[i, j])
        delta_1[i, j] = vca.delta('gbm1')
        delta_2[i, j] = vca.delta('gbm2')
CPU times: user 19.2 s, sys: 418 ms, total: 19.6 s
Wall time: 19.4 s

In [58]:
plot_greeks_3d([a_1, a_2, delta_1], ['asset 1', 'asset 2', 'delta asset 1'])
In [59]:
plot_greeks_3d([a_1, a_2, delta_2], ['asset 1', 'asset 2', 'delta asset 2'])

Vega Surfaces

In [60]:
vega_1 = np.zeros_like(a_1)
vega_2 = np.zeros_like(a_1)
In [61]:
%%time
for i in range(np.shape(vega_1)[0]):
    for j in range(np.shape(vega_1)[1]):
        vca.update('gbm1', initial_value=a_1[i, j])
        vca.update('gbm2', initial_value=a_2[i, j])
        vega_1[i, j] = vca.vega('gbm1')
        vega_2[i, j] = vca.vega('gbm2')
CPU times: user 20 s, sys: 458 ms, total: 20.5 s
Wall time: 20.2 s

In [62]:
plot_greeks_3d([a_1, a_2, vega_1], ['asset 1', 'asset 2', 'vega asset 1'])
In [63]:
plot_greeks_3d([a_1, a_2, vega_2], ['asset 1', 'asset 2', 'vega asset 2'])

More than Two Assets

Four Asset Basket Option

In [64]:
me3 = market_environment('me3', dt.datetime(2015, 1, 1))
me4 = market_environment('me4', dt.datetime(2015, 1, 1))
In [65]:
me3.add_environment(me1)
me4.add_environment(me1)
In [66]:
# for jump-diffusion
me3.add_constant('lambda', 0.5)
me3.add_constant('mu', -0.6)
me3.add_constant('delta', 0.1)
me3.add_constant('model', 'jd')
In [67]:
# for stoch vol jump model
me4.add_constant('kappa', 2.0)
me4.add_constant('theta', 0.3)
me4.add_constant('vol_vol', 0.2)
me4.add_constant('rho', -0.75)
me4.add_constant('lambda', 0.0)
me4.add_constant('mu', 0.0)
me4.add_constant('delta', 0.0)
me4.add_constant('model', 'svjd')
In [68]:
val_env.add_constant('paths', 10000)
In [69]:
underlyings = {'gbm1' : me1, 'gbm2' : me2, 'jd' : me3, 'sv' : me4}
correlations = [['gbm1', 'gbm2', 0.5], ['gbm2', 'jd', -0.5], ['gbm1', 'sv', 0.7]]
In [70]:
# European maximum call payoff
payoff_1 = "np.maximum(np.maximum(np.maximum(maturity_value['gbm1'], maturity_value['gbm2']),"
payoff_2 = " np.maximum(maturity_value['jd'], maturity_value['sv'])) - 40, 0)"
payoff = payoff_1 + payoff_2
In [71]:
payoff
Out[71]:
"np.maximum(np.maximum(np.maximum(maturity_value['gbm1'], maturity_value['gbm2']), np.maximum(maturity_value['jd'], maturity_value['sv'])) - 40, 0)"
In [72]:
vc = valuation_mcs_european_multi(
            name='European maximum call',
            val_env=val_env,
            assets=underlyings,
            correlations=correlations,
            payoff_func=payoff)

Example Output and Calculations

In [73]:
vc.assets
Out[73]:
{'gbm1': <dx_frame.market_environment at 0x1089bc090>,
 'gbm2': <dx_frame.market_environment at 0x1089bc050>,
 'jd': <dx_frame.market_environment at 0x109d0fd10>,
 'sv': <dx_frame.market_environment at 0x1099d5b90>}
In [74]:
vc.underlying_objects
Out[74]:
{'gbm1': <dx_models.geometric_brownian_motion at 0x1099d5590>,
 'gbm2': <dx_models.geometric_brownian_motion at 0x1099d5c10>,
 'jd': <dx_models.jump_diffusion at 0x1099d5650>,
 'sv': <dx_models.stoch_vol_jump_diffusion at 0x1099d5dd0>}
In [75]:
vc.present_value()
Out[75]:
10.204202
In [76]:
vc.instrument_values
Out[76]:
{'gbm1': array([[ 36.        ,  36.        ,  36.        , ...,  36.        ,
          36.        ,  36.        ],
        [ 36.9723811 ,  37.28369968,  34.52078134, ...,  37.13579493,
          37.05076414,  33.91689754],
        [ 35.83603337,  36.80218424,  34.10577969, ...,  38.25753549,
          37.44025144,  33.89417104],
        ..., 
        [ 39.34592781,  39.22506648,  35.90570067, ...,  44.3548775 ,
          35.86491998,  32.62911668],
        [ 39.04905654,  40.18343462,  36.00649123, ...,  45.93649183,
          35.53845693,  32.1621018 ],
        [ 38.29114228,  38.66062113,  34.26934522, ...,  47.33030951,
          35.6850472 ,  33.95900755]]),
 'gbm2': array([[ 36.        ,  36.        ,  36.        , ...,  36.        ,
          36.        ,  36.        ],
        [ 43.3330238 ,  32.39729735,  37.44329627, ...,  31.68378754,
          34.51749933,  30.90689883],
        [ 34.00939189,  32.69342429,  32.26195061, ...,  36.02033361,
          34.38236865,  26.01224315],
        ..., 
        [ 41.7634749 ,  66.74044592,  27.64120764, ...,  38.7344656 ,
          36.05209026,  44.23469551],
        [ 54.55065701,  75.51808709,  31.1591661 , ...,  42.60653253,
          38.05089411,  36.11929026],
        [ 52.0226659 ,  73.09966318,  32.03119153, ...,  45.39948071,
          39.82051546,  40.39763776]]),
 'jd': array([[ 36.        ,  36.        ,  36.        , ...,  36.        ,
          36.        ,  36.        ],
        [ 35.17701143,  37.12829304,  35.18218208, ...,  37.48067608,
          37.73419444,  34.96101568],
        [ 35.58620539,  36.82770356,  36.02286866, ...,  37.8197389 ,
          38.44234089,  35.17988413],
        ..., 
        [ 22.27469869,  18.68499775,  34.47775057, ...,  38.43826016,
          36.5242064 ,  13.57051467],
        [ 22.08328048,  18.4522535 ,  34.73681859, ...,  38.30593986,
          36.32808874,  13.8407922 ],
        [ 21.66024683,  18.95567692,  33.85382663, ...,  38.1120737 ,
          35.26767378,  13.72289009]]),
 'sv': array([[ 36.        ,  36.        ,  36.        , ...,  36.        ,
          36.        ,  36.        ],
        [ 36.73532177,  40.60674568,  31.39156784, ...,  38.79626551,
          38.24240844,  35.36769866],
        [ 39.18079661,  38.24293424,  30.13577666, ...,  40.13791505,
          38.13365985,  34.45711402],
        ..., 
        [ 36.97022049,  24.98050204,  32.54288922, ...,  64.49029696,
          20.94080158,  16.3786444 ],
        [ 30.15877962,  27.23388822,  25.15265912, ...,  72.19256963,
          16.51717543,  16.92369847],
        [ 31.23939577,  19.80467363,  18.63647467, ...,  81.30514352,
          16.84385565,  19.22404809]])}
In [77]:
vc.correlation_matrix
Out[77]:
gbm1 gbm2 jd sv
gbm1 1.0 0.5 0.0 0.7
gbm2 0.5 1.0 -0.5 0.0
jd 0.0 -0.5 1.0 0.0
sv 0.7 0.0 0.0 1.0

4 rows × 4 columns

In [78]:
vc.val_env.get_list('cholesky_matrix')
Out[78]:
array([[ 1.        ,  0.        ,  0.        ,  0.        ],
       [ 0.5       ,  0.8660254 ,  0.        ,  0.        ],
       [ 0.        , -0.57735027,  0.81649658,  0.        ],
       [ 0.7       , -0.40414519, -0.2857738 ,  0.51478151]])
In [79]:
vc.delta('jd', interval=0.1)
Out[79]:
0.13253999999999877
In [80]:
vc.delta('sv')
Out[80]:
0.3715569444444449
In [81]:
vc.vega('jd')
Out[81]:
5.586100000000016
In [82]:
vc.vega('sv')
Out[82]:
0.5770999999999304

Delta for Jump Diffusion and Stochastic Vol Process

In [83]:
delta_1 = np.zeros_like(a_1)
delta_2 = np.zeros_like(a_1)
In [84]:
%%time
for i in range(np.shape(delta_1)[0]):
    for j in range(np.shape(delta_1)[1]):
        vc.update('jd', initial_value=a_1[i, j])
        vc.update('sv', initial_value=a_2[i, j])
        delta_1[i, j] = vc.delta('jd')
        delta_2[i, j] = vc.delta('sv')
CPU times: user 6.05 s, sys: 382 ms, total: 6.43 s
Wall time: 6.27 s

In [85]:
plot_greeks_3d([a_1, a_2, delta_1], ['jump diffusion', 'stochastic vol', 'delta jd'])
In [86]:
plot_greeks_3d([a_1, a_2, delta_2], ['jump diffusion', 'stochastic vol', 'delta sv'])

Vega for Jump Diffusion and Stochastic Vol Process

In [87]:
vega_1 = np.zeros_like(a_1)
vega_2 = np.zeros_like(a_1)
In [88]:
%%time
for i in range(np.shape(vega_1)[0]):
    for j in range(np.shape(vega_1)[1]):
        vc.update('jd', initial_value=a_1[i, j])
        vc.update('sv', initial_value=a_2[i, j])
        vega_1[i, j] = vc.vega('jd')
        vega_2[i, j] = vc.vega('sv')
CPU times: user 5.73 s, sys: 359 ms, total: 6.08 s
Wall time: 5.94 s

In [89]:
plot_greeks_3d([a_1, a_2, vega_1], ['jump diffusion', 'stochastic vol', 'vega jd'])
In [90]:
plot_greeks_3d([a_1, a_2, vega_2], ['jump diffusion', 'stochastic vol', 'vega sv'])

American Exercise

In [91]:
# payoff of American maximum call option
payoff_am_1 = "np.maximum(np.maximum(np.maximum(instrument_values['gbm1'], instrument_values['gbm2']),"
payoff_am_2 = "np.maximum(instrument_values['jd'], instrument_values['sv'])) - 40, 0)"
payoff_am = payoff_am_1 + payoff_am_2
In [92]:
vca = valuation_mcs_american_multi(
            name='American maximum call',
            val_env=val_env,
            assets=underlyings,
            correlations=correlations,
            payoff_func=payoff_am)
In [93]:
vc.present_value()
Out[93]:
15.42048
In [94]:
%time vca.present_value()
CPU times: user 778 ms, sys: 69.4 ms, total: 848 ms
Wall time: 797 ms

Out[94]:
9.84029
In [95]:
%time vca.delta('jd')
CPU times: user 1.39 s, sys: 119 ms, total: 1.51 s
Wall time: 1.42 s

Out[95]:
0.1580402777777786
In [96]:
%time vca.delta('sv')
CPU times: user 1.66 s, sys: 146 ms, total: 1.8 s
Wall time: 1.7 s

Out[96]:
0.36796111111111107
In [97]:
%time vca.vega('jd')
CPU times: user 1.61 s, sys: 137 ms, total: 1.75 s
Wall time: 1.64 s

Out[97]:
5.232499999999973
In [98]:
%time vca.vega('sv')
CPU times: user 1.5 s, sys: 125 ms, total: 1.62 s
Wall time: 1.53 s

Out[98]:
-0.15459999999993812
In [99]:
print "Duration for whole notebook %.2f in min" % ((time.time() - t0) / 60)
Duration for whole notebook 1.39 in min