Source code for appliedlife.IntegratedLife.Scenarios
"""The space representing economic data
This space is parameterized with :attr:`date_id` and :attr:`sens_id`.
For each combination of :attr:`date_id` and :attr:`sens_id` values,
a dynamic subspace of this space is created,
representing a specific set of economic assumptions.
By default, the following scenarios are supported.
Users should customize the contents of this space to meet their own needs.
* Deterministic interest rate scenarios
* Stochastic risk-neutral index return scenarios
For the interest rate scenarios,
:func:`spot_rates` in this space reads annual spot rates from an Excel file into it.
:func:`spot_rates` uses sens_is as a key to select a sheet from the file.
For the stochastic risk-neutral index return scenarios,
:func:`log_return_mth` generates stochastic returns,
from the interest rates and volatility parameters read from an Excel file.
.. rubric:: Parameters
Attributes:
date_id: a string key representing the base date
sens_id: a string key representing interest rate sensitivity, which is either
"BASE", "UP" or "DOWN".
.. rubric:: References in the space
Attributes:
base_data: Reference to the :mod:`~appliedlife.IntegratedLife.BaseData` space
Example:
The sample code below demonstrates how to examine the contents of
:mod:`~appliedlife.IntegratedLife.Scenarios`
for specific values of :attr:`date_id` and :attr:`sens_id`, '202312' and 'BASE'.
.. code-block:: python
>>> import modelx as mx
>>> m = mx.read_model("IntegratedLife")
>>> m.Scenarios['202312', 'BASE'].spot_rates()
EUR GBP JPY USD
0 0.03357 0.04735 0.00072 0.04760
1 0.02690 0.04021 0.00191 0.04056
2 0.02439 0.03668 0.00280 0.03724
3 0.02350 0.03475 0.00363 0.03571
4 0.02323 0.03355 0.00448 0.03499
.. ... ... ... ...
145 0.03241 0.03229 0.03006 0.03364
146 0.03243 0.03231 0.03010 0.03365
147 0.03244 0.03232 0.03013 0.03365
148 0.03245 0.03234 0.03016 0.03366
149 0.03247 0.03235 0.03019 0.03366
[150 rows x 4 columns]
>>> m.Scenarios['202312', 'BASE'].forward_rates()
EUR GBP JPY USD
0 0.033570 0.047350 0.000720 0.047600
1 0.020273 0.033119 0.003101 0.033567
2 0.019388 0.029656 0.004582 0.030632
3 0.020835 0.028982 0.006124 0.031134
4 0.022151 0.028764 0.007887 0.032115
.. ... ... ... ...
145 0.033861 0.033741 0.034419 0.035091
146 0.035354 0.035234 0.035957 0.035111
147 0.033911 0.033791 0.034550 0.033650
148 0.033931 0.035304 0.034610 0.035141
149 0.035454 0.033841 0.034670 0.033660
[150 rows x 4 columns]
>>> m.Scenarios['202312', 'BASE'].cont_fwd_rates()
EUR GBP JPY USD
0 0.033019 0.046263 0.000720 0.046502
1 0.020070 0.032582 0.003097 0.033016
2 0.019203 0.029225 0.004572 0.030172
3 0.020621 0.028570 0.006105 0.030659
4 0.021909 0.028358 0.007856 0.031610
.. ... ... ... ...
145 0.033300 0.033184 0.033840 0.034489
146 0.034744 0.034628 0.035325 0.034509
147 0.033349 0.033233 0.033966 0.033096
148 0.033368 0.034695 0.034024 0.034538
149 0.034840 0.033281 0.034082 0.033106
[150 rows x 4 columns]
>>> m.Scenarios['202312', 'BASE'].log_return_mth()
FUND1 FUND2 FUND3 FUND4 FUND5 FUND6
scen t
1 0 -0.030397 0.047032 -0.010060 0.000816 0.000665 -0.040567
1 -0.029103 0.025734 0.004162 -0.018741 0.084592 0.058125
2 -0.015052 0.034508 -0.005399 0.003108 0.030602 -0.070345
3 0.015784 0.051717 0.015262 0.000348 0.034553 -0.091414
4 -0.001168 0.018826 -0.015521 0.002865 0.063022 0.153368
... ... ... ... ... ...
100 1795 0.005044 0.005891 0.006421 -0.009772 0.006747 -0.018034
1796 0.005050 -0.030197 -0.027247 0.002810 -0.017504 0.011297
1797 0.070869 0.008339 0.012401 -0.002405 0.014219 -0.023541
1798 -0.001515 0.049597 0.013523 -0.015077 0.070503 0.027821
1799 0.000753 -0.019089 0.017222 0.004629 0.005042 0.000108
[180000 rows x 6 columns]
>>> m.Scenarios['202312', 'BASE'].return_mth()
FUND1 FUND2 FUND3 FUND4 FUND5 FUND6
scen t
1 0 -0.029940 0.048156 -0.010010 0.000816 0.000665 -0.039755
1 -0.028684 0.026068 0.004171 -0.018567 0.088273 0.059847
2 -0.014940 0.035111 -0.005384 0.003113 0.031075 -0.067928
3 0.015909 0.053078 0.015379 0.000348 0.035157 -0.087360
4 -0.001168 0.019004 -0.015401 0.002869 0.065050 0.165754
... ... ... ... ... ...
100 1795 0.005057 0.005909 0.006442 -0.009724 0.006770 -0.017872
1796 0.005063 -0.029746 -0.026880 0.002814 -0.017352 0.011361
1797 0.073441 0.008374 0.012478 -0.002402 0.014320 -0.023266
1798 -0.001514 0.050848 0.013615 -0.014964 0.073048 0.028211
1799 0.000753 -0.018908 0.017371 0.004640 0.005055 0.000108
[180000 rows x 6 columns]
"""
from modelx.serialize.jsonvalues import *
_formula = lambda date_id, sens_id: None
_bases = []
_allow_none = None
_spaces = []
# ---------------------------------------------------------------------------
# Cells
def cont_fwd_rates():
"""Continuous compound forward rates"""
return np.log(1 + forward_rates())
[docs]
def forward_rates():
"""Forward interest rates by duration and currency
Returns annual forward interest rates for multiple currencies
as a pandas DataFrame, calculated from :func:`spot_rates`.
"""
df = (1 + spot_rates()).pow(spot_rates().index + 1, axis=0)
return df / df.shift(fill_value=1) - 1
[docs]
def index_count():
"""The number of func indexes"""
return index_params().index.size
[docs]
def index_params():
"""Fund index parameters
Reads fund index parameters from an Excel file,
and returns a DataFrame whose columns represents the parameters,
and whose index represents fund index IDs.
"""
dir_name: str = base_data.const_params().at["scen_dir", "value"]
file_name: str = base_data.const_params().at["scen_param_file", "value"]
file = _model.path.parent / dir_name / file_name
df = pd.read_excel(file,
sheet_name="Params",
index_col=0)
return df.T.astype(
{"currency": "object",
"return": "float64",
"volatility": "float64"})
[docs]
def index_vols():
"""Volatilities of fund indexes"""
return index_params()["volatility"]
[docs]
def log_return_mth():
"""Stochastic scenarios of fund indexes as monthly risk-neutral log returns
Generates stochastic scenarios of fund indexes
Generates monthly risk-neutral log returns of fund indexes,
Returns a DataFrame with columns of fund IDs
and with a MultiIndex with two levels, scenario ID and time in month.
"""
# Initialize random number generator
rng = np.random.default_rng(12345)
# Define parameters
dt = 1/12
rf = cont_fwd_rates()[index_params()["currency"]].loc[np.repeat(cont_fwd_rates().index, 12)]
vols = index_vols().values
mean = (rf - 0.5 * vols**2) * dt
var = vols * dt**0.5
# Generate
result = np.zeros((scen_size() * scen_len() * 12, index_count()))
for i in range(scen_size()):
scen = rng.normal(loc=mean, scale=var)
result[i * scen.shape[0]:(i + 1) * scen.shape[0], 0:scen.shape[1]] = scen
return pd.DataFrame(result, index=scen_index(), columns=index_params().index)
def mth_index():
return scenarios()
[docs]
def return_mth():
"""Monthly index returns"""
return np.exp(log_return_mth()) - 1
def scen_index():
"""pandas MultiIndex for the scenarios"""
return pd.MultiIndex.from_product(
[range(1, scen_size() + 1), range(12 * scen_len())],
names=["scen", "t"])
def scen_len():
"""The length of scenarios in years"""
return len(spot_rates())
def scen_size():
"""The number of scenarios"""
return 100
[docs]
def spot_rates():
"""Spot interest rates by duration and currency
Reads annual spot interest rates for multiple currencies from an Excel file,
and returns them as a pandas DataFrame.
The index and columns of the DataFrame represents duration
years and currencies respectively
The directory of the Excel file is specified by the user as a constant
parameter named "scen_dir".
"""
dir_name: str = base_data.const_params().at["scen_dir", "value"]
file_prefix: str = base_data.const_params().at["scen_file_prefix", "value"]
path = _model.path.parent / dir_name / f"{file_prefix}_{date_id}.xlsx"
return pd.read_excel(path, sheet_name=sens_id, index_col=0)
# ---------------------------------------------------------------------------
# References
base_data = ("Interface", ("..", "BaseData"), "auto")
date_id = "202312"
sens_id = "BASE"