This part illustrates that you can model, value and risk manage quite complex derivatives portfolios with DX Analytics.
In [1]:
from dx import *
import time
import matplotlib.pyplot as plt
import seaborn as sns; sns.set()
%matplotlib inline
np.random.seed(10000)
The example is based on a multiple, correlated risk factors, all (for the ease of exposition) geometric_brownian_motion
objects.
In [2]:
mer = market_environment(name='me', pricing_date=dt.datetime(2015, 1, 1))
mer.add_constant('initial_value', 0.01)
mer.add_constant('volatility', 0.1)
mer.add_constant('kappa', 2.0)
mer.add_constant('theta', 0.05)
mer.add_constant('paths', 100) # dummy
mer.add_constant('frequency', 'M') # dummy
mer.add_constant('starting_date', mer.pricing_date)
mer.add_constant('final_date', dt.datetime(2015, 12, 31)) # dummy
ssr = stochastic_short_rate('ssr', mer)
In [3]:
plt.figure(figsize=(10, 6))
plt.plot(ssr.process.time_grid, ssr.process.get_instrument_values()[:, :10]);
plt.gcf().autofmt_xdate()
In [4]:
# market environments
me = market_environment('gbm', dt.datetime(2015, 1, 1))
In [5]:
# geometric Brownian motion
me.add_constant('initial_value', 36.)
me.add_constant('volatility', 0.2)
me.add_constant('currency', 'EUR')
In [6]:
# jump diffusion
me.add_constant('lambda', 0.4)
me.add_constant('mu', -0.4)
me.add_constant('delta', 0.2)
In [7]:
# stochastic volatility
me.add_constant('kappa', 2.0)
me.add_constant('theta', 0.3)
me.add_constant('vol_vol', 0.5)
me.add_constant('rho', -0.5)
Using 2,500 paths and monthly discretization for the example.
In [8]:
# valuation environment
val_env = market_environment('val_env', dt.datetime(2015, 1, 1))
val_env.add_constant('paths', 2500)
val_env.add_constant('frequency', 'M')
val_env.add_curve('discount_curve', ssr)
val_env.add_constant('starting_date', dt.datetime(2015, 1, 1))
val_env.add_constant('final_date', dt.datetime(2016, 12, 31))
In [9]:
# add valuation environment to market environments
me.add_environment(val_env)
In [10]:
no = 50 # 50 different risk factors in total
In [11]:
risk_factors = {}
for rf in range(no):
# random model choice
sm = np.random.choice(['gbm', 'jd', 'sv'])
key = '%3d_%s' % (rf + 1, sm)
risk_factors[key] = market_environment(key, me.pricing_date)
risk_factors[key].add_environment(me)
# random initial_value
risk_factors[key].add_constant('initial_value',
np.random.random() * 40. + 20.)
# radnom volatility
risk_factors[key].add_constant('volatility',
np.random.random() * 0.6 + 0.05)
# the simulation model to choose
risk_factors[key].add_constant('model', sm)
In [12]:
correlations = []
keys = sorted(risk_factors.keys())
for key in keys[1:]:
correlations.append([keys[0], key, np.random.choice([-0.1, 0.0, 0.1])])
correlations[:3]
Out[12]:
We model a certain number of derivative instruments with the following major assumptions.
In [13]:
me_option = market_environment('option', me.pricing_date)
# choose from a set of maturity dates (month ends)
maturities = pd.date_range(start=me.pricing_date,
end=val_env.get_constant('final_date'),
freq='M').to_pydatetime()
me_option.add_constant('maturity', np.random.choice(maturities))
me_option.add_constant('currency', 'EUR')
me_option.add_environment(val_env)
The derivatives_portfolio
object we compose consists of multiple derivatives positions. Each option differs with respect to the strike and the risk factor it is dependent on.
In [14]:
# 5 times the number of risk factors
# as portfolio positions/instruments
pos = 5 * no
In [15]:
positions = {}
for i in range(pos):
ot = np.random.choice(['am_put', 'eur_call'])
if ot == 'am_put':
otype = 'American single'
payoff_func = 'np.maximum(%5.3f - instrument_values, 0)'
else:
otype = 'European single'
payoff_func = 'np.maximum(maturity_value - %5.3f, 0)'
# random strike
strike = np.random.randint(36, 40)
underlying = sorted(risk_factors.keys())[(i + no) % no]
name = '%d_option_pos_%d' % (i, strike)
positions[name] = derivatives_position(
name=name,
quantity=np.random.randint(1, 10),
underlyings=[underlying],
mar_env=me_option,
otype=otype,
payoff_func=payoff_func % strike)
In [16]:
# number of derivatives positions
len(positions)
Out[16]:
First, the derivatives portfolio with sequential valuation.
In [17]:
port = derivatives_portfolio(
name='portfolio',
positions=positions,
val_env=val_env,
risk_factors=risk_factors,
correlations=correlations,
parallel=True) # sequential calculation
In [18]:
port.val_env.get_list('cholesky_matrix')
Out[18]:
The call of the get_values
method to value all instruments.
In [19]:
%time res = port.get_statistics(fixed_seed=True)
In [20]:
res.set_index('position', inplace=False)
Out[20]:
Full distribution of portfolio present values illustrated via histogram.
In [21]:
%time pvs = port.get_present_values()
In [22]:
plt.figure(figsize=(10, 6))
plt.hist(pvs, bins=30);
plt.xlabel('portfolio present values')
plt.ylabel('frequency')
Out[22]:
Some statistics via pandas.
In [23]:
pdf = pd.DataFrame(pvs)
pdf.describe()
Out[23]:
The delta risk report.
In [24]:
%%time
deltas, benchmark = port.get_port_risk(Greek='Delta', fixed_seed=True, step=0.2,
risk_factors=risk_factors.keys()[:4])
risk_report(deltas)
The vega risk report.
In [25]:
%%time
vegas, benchmark = port.get_port_risk(Greek='Vega', fixed_seed=True, step=0.2,
risk_factors=risk_factors.keys()[:3])
risk_report(vegas)
Selected results visualized.
In [26]:
res[['pos_value', 'pos_delta', 'pos_vega']].hist(bins=30, figsize=(9, 6))
plt.ylabel('frequency')
Out[26]:
Sample paths for three underlyings.
In [27]:
paths_0 = port.underlying_objects.values()[0]
paths_0.generate_paths()
paths_1 = port.underlying_objects.values()[1]
paths_1.generate_paths()
paths_2 = port.underlying_objects.values()[2]
paths_2.generate_paths()
In [28]:
pa = 5
plt.figure(figsize=(10, 6))
plt.plot(port.time_grid, paths_0.instrument_values[:, :pa], 'b');
print 'Paths for %s (blue)' % paths_0.name
plt.plot(port.time_grid, paths_1.instrument_values[:, :pa], 'r.-');
print 'Paths for %s (red)' % paths_1.name
plt.plot(port.time_grid, paths_2.instrument_values[:, :pa], 'g-.', lw=2.5);
print 'Paths for %s (green)' % paths_2.name
plt.ylabel('risk factor level')
plt.gcf().autofmt_xdate()
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