Multi-Risk Derivatives Valuation ================================ A specialty of DX Analytics is the valuation of derivatives instruments defined on multiple risk factors and portfolios composed of such derivatives. This section of the documentation illustrates the usage of the dedicated **multi-risk valuation classes**. .. code:: python import warnings; warnings.simplefilter('ignore') .. code:: python from dx import * import seaborn as sns; sns.set() .. code:: python import time t0 = time.time() There are the following **multiple risk factor valuation classes** available: - ``valuation_mcs_european_multi`` for the valuation of multi-risk derivatives with European exercise - ``valuation_mcs_american_multi`` for the valuation of multi-risk derivatives with American exercise The handling of these classes is similar to building a portfolio of single-risk derivatives positions. Market Environments ------------------- **Market environments for the risk factors** are the starting point. .. code:: python r = constant_short_rate('r', 0.06) .. code:: python me1 = market_environment('me1', dt.datetime(2015, 1, 1)) me2 = market_environment('me2', dt.datetime(2015, 1, 1)) .. code:: python me1.add_constant('initial_value', 36.) me1.add_constant('volatility', 0.1) # low volatility me1.add_constant('currency', 'EUR') me1.add_constant('model', 'gbm') .. code:: python me2.add_environment(me1) me2.add_constant('initial_value', 36.) me2.add_constant('volatility', 0.5) # high volatility We assum a **positive correlation** between the two risk factors. .. code:: python risk_factors = {'gbm1' : me1, 'gbm2' : me2} correlations = [['gbm1', 'gbm2', 0.5]] Valuation Environment --------------------- Similar to the instantiation of a ``derivatives_portfolio`` object, a **valuation environment** is needed (*unifying certain parameters/assumptions* for all relevant risk factors of a derivative). .. code:: python val_env = market_environment('val_env', dt.datetime(2015, 1, 1)) .. code:: python 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', 'W') val_env.add_constant('paths', 5000) 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 ------------------------------- As an example for a multi-risk derivative with European exercise consider a **maximum call option**. With multiple risk factors, payoff functions are defined by adding key (the name strings) to the ``maturity_value`` array object. As with the portfolio valuation class, the multi-risk factor valuation classes get passed ``market_environment`` objects only and not the risk factor model objects themsemselves. .. code:: python # 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', # name val_env=val_env, # valuation environment risk_factors=risk_factors, # the relevant risk factors correlations=correlations, # correlations between risk factors payoff_func=payoff_func) # payoff function .. code:: python vc.risk_factors .. parsed-literal:: {'gbm1': , 'gbm2': } At instantiation, the respective **risk factor model objects** are instantiated as well. .. code:: python vc.underlying_objects .. parsed-literal:: {'gbm1': , 'gbm2': } Correlations are stored as well and the resulting **corrleation and Cholesky** matrices are generated. .. code:: python vc.correlations .. parsed-literal:: [['gbm1', 'gbm2', 0.5]] .. code:: python vc.correlation_matrix .. raw:: html
gbm1 gbm2
gbm1 1.0 0.5
gbm2 0.5 1.0
.. code:: python vc.val_env.get_list('cholesky_matrix') .. parsed-literal:: array([[ 1. , 0. ], [ 0.5 , 0.8660254]]) The **payoff of a European option** is a one-dimensional ``ndarray`` object. .. code:: python np.shape(vc.generate_payoff()) .. parsed-literal:: (5000,) **Present value estimations** are generated by a call of the ``present_value`` method. .. code:: python vc.present_value() .. parsed-literal:: 7.649 The ``update`` method allows updating of certain parameters. .. code:: python vc.update('gbm1', initial_value=50.) .. code:: python vc.present_value() .. parsed-literal:: 16.738 .. code:: python vc.update('gbm2', volatility=0.6) .. code:: python vc.present_value() .. parsed-literal:: 17.984 Let us reset the values to the original parameters. .. code:: python vc.update('gbm1', initial_value=36., volatility=0.1) vc.update('gbm2', initial_value=36., volatility=0.5) When calculating **Greeks** the risk factor, depending on the specific statistic required, now has to be specified by providing its name. .. code:: python vc.delta('gbm2', interval=0.5) .. parsed-literal:: 0.5694 .. code:: python vc.gamma('gbm2') .. parsed-literal:: 0.0204 .. code:: python vc.dollar_gamma('gbm2') .. parsed-literal:: 13.2192 .. code:: python vc.vega('gbm1') .. parsed-literal:: 6.3703 .. code:: python vc.theta() .. parsed-literal:: -4.2273 .. code:: python vc.rho(interval=0.1) .. parsed-literal:: 25.6992 .. code:: python val_env.curves['discount_curve'].short_rate .. parsed-literal:: 0.06 Sensitivities Positive Correlation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Almos in complete analogy to the single-risk valuation classes, **sensitivities** can be estimated for the multi-risk valuation classes. Sensitivities Risk Factor 1 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Consider first the case from before with **positive correlation** between the two risk factors. The following estimates and plots the **sensitivities for the first risk factor ``gbm1``**. .. code:: python %%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.) .. parsed-literal:: CPU times: user 421 ms, sys: 23.2 ms, total: 444 ms Wall time: 463 ms .. code:: python %matplotlib inline .. code:: python plot_option_stats(s_list, pv, de, ve) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_54_0.png Sensitivities Risk Factor 2 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Now the **sensitivities for the second risk factor**. .. code:: python %%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)) .. parsed-literal:: CPU times: user 328 ms, sys: 18.2 ms, total: 346 ms Wall time: 348 ms .. code:: python plot_option_stats(s_list, pv, de, ve) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_58_0.png Sensitivities with Negative Correlation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The second case is for **highly negatively correlated** risk factors. .. code:: python correlations = [['gbm1', 'gbm2', -0.9]] .. code:: python # 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, risk_factors=risk_factors, correlations=correlations, payoff_func=payoff_func) Sensitivities Risk Factor 1 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Again, **sensitivities for the first risk factor** first. .. code:: python %%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.) .. parsed-literal:: CPU times: user 365 ms, sys: 17.5 ms, total: 382 ms Wall time: 386 ms .. code:: python plot_option_stats(s_list, pv, de, ve) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_66_0.png Sensitivities Risk Factor 2 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Finally, the **sensitivities for the second risk factor** for this second scenario. .. code:: python %%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)) .. parsed-literal:: CPU times: user 338 ms, sys: 24.1 ms, total: 362 ms Wall time: 362 ms .. code:: python plot_option_stats(s_list, pv, de, ve) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_70_0.png Surfaces for Positive Correlation Case ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Let us return to the case of **positive correlation between the two relevant risk factors**. .. code:: python correlations = [['gbm1', 'gbm2', 0.5]] .. code:: python # 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, risk_factors=risk_factors, correlations=correlations, payoff_func=payoff_func) Value Surface ^^^^^^^^^^^^^ We are now interested in the **value surface** of the derivative instrument for both different initial values of the first and second risk factor. .. code:: python asset_1 = np.arange(28., 46.1, 4.) # range of initial values asset_2 = asset_1 a_1, a_2 = np.meshgrid(asset_1, asset_2) # two-dimensional grids out of the value vectors value = np.zeros_like(a_1) The following estimates for **all possible combinations** of the initial values---given the assumptions from above---the present value of the European maximum call option. .. code:: python %%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() .. parsed-literal:: CPU times: user 374 ms, sys: 20 ms, total: 394 ms Wall time: 394 ms The resulting plot then looks as follows. Here, a helper plot function of DX Analytics is used. .. code:: python plot_greeks_3d([a_1, a_2, value], ['gbm1', 'gbm2', 'present value']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_81_0.png Delta Surfaces ^^^^^^^^^^^^^^ Applying a very similar approach, a **delta surface** for all possible combinations of the intial values is as easily generated. .. code:: python delta_1 = np.zeros_like(a_1) delta_2 = np.zeros_like(a_1) .. code:: python %%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') .. parsed-literal:: CPU times: user 1.33 s, sys: 84.8 ms, total: 1.41 s Wall time: 1.44 s The plot for the **delta surface of the first risk factor**. .. code:: python plot_greeks_3d([a_1, a_2, delta_1], ['gbm1', 'gbm2', 'delta gbm1']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_87_0.png And the plot for the **delta of the second risk factor**. .. code:: python plot_greeks_3d([a_1, a_2, delta_2], ['gbm1', 'gbm2', 'delta gbm2']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_89_0.png Vega Surfaces ^^^^^^^^^^^^^ The same approach can of course be applied to generate **vega surfaces**. .. code:: python vega_1 = np.zeros_like(a_1) vega_2 = np.zeros_like(a_1) .. code:: python 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') The **surface for the first risk factor**. .. code:: python plot_greeks_3d([a_1, a_2, vega_1], ['gbm1', 'gbm2', 'vega gbm1']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_95_0.png And the one for the second **risk factor**. .. code:: python plot_greeks_3d([a_1, a_2, vega_2], ['gbm1', 'gbm2', 'vega gbm2']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_97_0.png Finally, we reset the intial values and the volatilities for the two risk factors. .. code:: python # 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 ------------------------------- In general, the modeling and handling of the valuation classes for American exercise is not too different from those for European exercise. The **major difference** is in the definition of payoff function. Present Values ~~~~~~~~~~~~~~ This example models an **American minimum put** on the two risk factors from before. .. code:: python # American put payoff payoff_am = "np.maximum(34 - np.minimum(instrument_values['gbm1'], instrument_values['gbm2']), 0)" # finer time grid and more paths val_env.add_constant('frequency', 'B') val_env.add_curve('time_grid', None) # delete existing time grid information val_env.add_constant('paths', 5000) .. code:: python # American put option on minimum of two assets vca = valuation_mcs_american_multi( name='American minimum put', val_env=val_env, risk_factors=risk_factors, correlations=correlations, payoff_func=payoff_am) .. code:: python vca.present_value() .. parsed-literal:: 4.568 .. code:: python for key, obj in vca.instrument_values.items(): print np.shape(vca.instrument_values[key]) .. parsed-literal:: (261, 5000) (261, 5000) The **present value surface** is generated in the same way as before for the European option on the two risk factors. The computational burden is of course much higher for the American option, which are valued by the use of the **Least-Squares Monte Carlo approach (LSM)** according to Longstaff-Schwartz (2001). .. code:: python 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) .. code:: python %%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() .. parsed-literal:: CPU times: user 18.2 s, sys: 1.33 s, total: 19.5 s Wall time: 19 s .. code:: python plot_greeks_3d([a_1, a_2, value], ['gbm1', 'gbm2', 'present value']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_111_0.png Delta Surfaces ~~~~~~~~~~~~~~ The same exercise as before for the two **delta surfaces**. .. code:: python delta_1 = np.zeros_like(a_1) delta_2 = np.zeros_like(a_1) .. code:: python %%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') .. parsed-literal:: CPU times: user 1min 5s, sys: 4.54 s, total: 1min 10s Wall time: 1min 7s .. code:: python plot_greeks_3d([a_1, a_2, delta_1], ['gbm1', 'gbm2', 'delta gbm1']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_116_0.png .. code:: python plot_greeks_3d([a_1, a_2, delta_2], ['gbm1', 'gbm2', 'delta gbm2']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_117_0.png Vega Surfaces ~~~~~~~~~~~~~ And finally for the **vega surfaces**. .. code:: python vega_1 = np.zeros_like(a_1) vega_2 = np.zeros_like(a_1) .. code:: python %%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') .. parsed-literal:: CPU times: user 1min 4s, sys: 4.31 s, total: 1min 8s Wall time: 1min 5s .. code:: python plot_greeks_3d([a_1, a_2, vega_1], ['gbm1', 'gbm2', 'vega gbm1']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_122_0.png .. code:: python plot_greeks_3d([a_1, a_2, vega_2], ['gbm1', 'gbm2', 'vega gbm2']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_123_0.png More than Two Risk Factors -------------------------- The principles of working with multi-risk valuation classes can be illustrated quite well in the two risk factor case. However, there is---in theory---\ **no limitation on the number of risk factors** used for derivatives modeling. Four Asset Basket Option ~~~~~~~~~~~~~~~~~~~~~~~~ Consider a **maximum basket option** on four different risk factors. We add a **jump diffusion** as well as a **stochastic volatility** model to the mix .. code:: python me3 = market_environment('me3', dt.datetime(2015, 1, 1)) me4 = market_environment('me4', dt.datetime(2015, 1, 1)) .. code:: python me3.add_environment(me1) me4.add_environment(me1) .. code:: python # 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') .. code:: python # for stoch volatility 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('model', 'sv') .. code:: python val_env.add_constant('paths', 10000) val_env.add_constant('frequency', 'W') val_env.add_curve('time_grid', None) In this case, we need to specify **three correlation** values. .. code:: python risk_factors = {'gbm1' : me1, 'gbm2' : me2, 'jd' : me3, 'sv' : me4} correlations = [['gbm1', 'gbm2', 0.5], ['gbm2', 'jd', -0.5], ['gbm1', 'sv', 0.7]] The **payoff function** in this case gets a bit more complex. .. code:: python # 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 .. code:: python payoff .. parsed-literal:: "np.maximum(np.maximum(np.maximum(maturity_value['gbm1'], maturity_value['gbm2']), np.maximum(maturity_value['jd'], maturity_value['sv'])) - 40, 0)" However, the instantiation of the valuation classe remains the same. .. code:: python vc = valuation_mcs_european_multi( name='European maximum call', val_env=val_env, risk_factors=risk_factors, correlations=correlations, payoff_func=payoff) Example Output and Calculations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following just displays some example output and the results from certain calculations. .. code:: python vc.risk_factors .. parsed-literal:: {'gbm1': , 'gbm2': , 'jd': , 'sv': } .. code:: python vc.underlying_objects .. parsed-literal:: {'gbm1': , 'gbm2': , 'jd': , 'sv': } .. code:: python vc.present_value() .. parsed-literal:: 13.191 The **correlation and Cholesky matrices** now are of shape 4x4. .. code:: python vc.correlation_matrix .. raw:: html
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
.. code:: python vc.val_env.get_list('cholesky_matrix') .. parsed-literal:: 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]]) **Delta and vega estimates** are generated in exactly the same fashion as in the two risk factor case. .. code:: python vc.delta('jd', interval=0.1) .. parsed-literal:: 0.4481 .. code:: python vc.delta('sv') .. parsed-literal:: 0.3723 .. code:: python vc.vega('jd') .. parsed-literal:: 6.0256 .. code:: python vc.vega('sv') .. parsed-literal:: 1.1536 Delta for Jump Diffusion and Stochastic Vol Process ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Of course, we cannot visualize **Greek surfaces** dependent on initial values for all four risk factors but still for two. In what follows we generate the **delta surfaces** with respect to the jump diffusion- and stochastic volatility-based risk factors. .. code:: python delta_1 = np.zeros_like(a_1) delta_2 = np.zeros_like(a_1) .. code:: python %%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') .. parsed-literal:: CPU times: user 11.1 s, sys: 1.43 s, total: 12.6 s Wall time: 12.3 s .. code:: python plot_greeks_3d([a_1, a_2, delta_1], ['jump diffusion', 'stochastic vol', 'delta jd']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_157_0.png .. code:: python plot_greeks_3d([a_1, a_2, delta_2], ['jump diffusion', 'stochastic vol', 'delta sv']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_158_0.png Vega for Jump Diffusion and Stochastic Vol Process ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Now the same exercise for the **vega surfaces** for the same two risk factors. .. code:: python vega_1 = np.zeros_like(a_1) vega_2 = np.zeros_like(a_1) .. code:: python %%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') .. parsed-literal:: CPU times: user 10.9 s, sys: 1.39 s, total: 12.3 s Wall time: 11.9 s .. code:: python plot_greeks_3d([a_1, a_2, vega_1], ['jump diffusion', 'stochastic vol', 'vega jd']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_163_0.png .. code:: python plot_greeks_3d([a_1, a_2, vega_2], ['jump diffusion', 'stochastic vol', 'vega sv']) .. image:: 04_dx_valuation_multi_risk_files/04_dx_valuation_multi_risk_164_0.png American Exercise ~~~~~~~~~~~~~~~~~ As a final illustration consider the case of an **American minimum put option** on the four risk factors. This again is a step that leads to a much increased computational burden due to the necessity to apply the least-squares regression approach. .. code:: python # payoff of American minimum put option payoff_am_1 = "np.maximum(40 - np.minimum(np.minimum(instrument_values['gbm1'], instrument_values['gbm2'])," payoff_am_2 = "np.minimum(instrument_values['jd'], instrument_values['sv'])), 0)" payoff_am = payoff_am_1 + payoff_am_2 .. code:: python vca = valuation_mcs_american_multi( name='American minimum put', val_env=val_env, risk_factors=risk_factors, correlations=correlations, payoff_func=payoff_am) However, another illustration that even such a complex instrument can be handled as elegantly as the most simple one (i.e. European option on single risk factor). Let us compare the **present value estimates** for both the European and American maximum basket options. .. code:: python # restore initial values vc.update('jd', initial_value=36., volatility=0.1) vc.update('sv', initial_value=36., volatility=0.1) %time vc.present_value() .. parsed-literal:: CPU times: user 4.91 ms, sys: 1 µs, total: 4.91 ms Wall time: 4.92 ms .. parsed-literal:: 13.191 .. code:: python %time vca.present_value() .. parsed-literal:: CPU times: user 1.49 s, sys: 385 ms, total: 1.88 s Wall time: 1.69 s .. parsed-literal:: 14.305 .. code:: python %time vca.delta('gbm1') .. parsed-literal:: CPU times: user 2.59 s, sys: 695 ms, total: 3.28 s Wall time: 2.92 s .. parsed-literal:: -0.0081 .. code:: python %time vca.delta('gbm2') .. parsed-literal:: CPU times: user 2.61 s, sys: 694 ms, total: 3.3 s Wall time: 2.93 s .. parsed-literal:: -0.2241 .. code:: python %time vca.vega('jd') .. parsed-literal:: CPU times: user 2.65 s, sys: 701 ms, total: 3.35 s Wall time: 2.98 s .. parsed-literal:: 1.2914 .. code:: python %time vca.vega('sv') .. parsed-literal:: CPU times: user 2.7 s, sys: 702 ms, total: 3.4 s Wall time: 3.04 s .. parsed-literal:: 0.7437 .. code:: python print "Duration for whole notebook %.2f in min" % ((time.time() - t0) / 60) .. parsed-literal:: Duration for whole notebook 3.45 in min **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