Value at Risk¶
Introduction¶
This notebook first calculates random values to be used as a test dataset. A dataset of 5 years is created including mean returns, standard deviation and credit risk parameters (PD, EAD and LGD) for different sectors. Let's assume there are six different sectors in which the bank adjusts its asset allocation according to annual growth rates ranging between -6% and 6%. The goal is to provide an example of calculating the value at risk (VaR) for the total portfolio per year.
In [0]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
# Constants
years = [2020, 2021, 2022, 2023, 2024]
num_assets_sectors = 6
my_path = os.getcwd()
parent_path = os.path.dirname(my_path)
parent_path = os.path.dirname(parent_path)
# Mean returns (in decimal form, e.g., 0.05 for 5% return)
mean_returns = np.random.uniform(0.03, 0.10, (num_assets_sectors, len(years)))
# Standard deviations (in decimal form, e.g., 0.15 for 15% volatility)
std_devs = np.random.uniform(0.10, 0.30, (num_assets_sectors, len(years)))
# Initial asset values for the first year
initial_asset_values = np.random.uniform(10, 100, (num_assets_sectors, 1))
# Initialize an empty array for asset values with shape (10 assets, 5 years)
asset_values = np.zeros((num_assets_sectors, len(years)))
# Fill the first column with the initial asset values
asset_values[:, 0] = initial_asset_values[:, 0]
# Growth rates between -6% and 6%, as portfolio can shrink or increase based on strategy
growth_rates = np.random.uniform(-0.06, 0.06, (num_assets_sectors, len(years) - 1))
# Apply the growth rates to get the values for subsequent years
for i in range(1, len(years)):
asset_values[:, i] = asset_values[:, i-1] * (1 + growth_rates[:, i-1])
# Generating synthetic data for Probability of Default (PD), Exposure at Default (EAD), and Loss Given Default (LGD)
# Constants for PD, EAD, and LGD
pd_values = np.random.uniform(0.01, 0.10, (num_assets_sectors, len(years))) # PD between 1% and 10%
ead_values = np.random.uniform(10, 100, (num_assets_sectors, len(years))) # EAD in millions, similar to asset values
lgd_values = np.random.uniform(0.20, 0.60, (num_assets_sectors, len(years))) # LGD between 20% and 60%
# Creating a DataFrame of all random variables
data = {
'Year': np.tile(years, num_assets_sectors),
'Asset': np.repeat([f'Asset {i+1}' for i in range(num_assets_sectors)], len(years)),
'Mean_Return': mean_returns.flatten(),
'Standard_Deviation': std_devs.flatten(),
'Asset_Value': asset_values.flatten(),
'Probability_of_Default': pd_values.flatten(),
'Exposure_at_Default': ead_values.flatten(),
'Loss_Given_Default': lgd_values.flatten()
}
df = pd.DataFrame(data)
print(df)
#To calculate a VaR parameter for all the assets we need a covariance matrix.
data_correlation = {
'Date': pd.date_range(start='2019-01-01', periods=5*252, freq='B'), # 5 years of daily data (approx. 252 trading days per year)
'Asset Sector 1': np.random.normal(100, 10, 5*252),
'Asset Sector 2': np.random.normal(50, 5, 5*252),
'Asset Sector 3': np.random.normal(75, 8, 5*252),
'Asset Sector 4': np.random.normal(30, 3, 5*252),
'Asset Sector 5': np.random.normal(90, 9, 5*252),
'Asset Sector 6': np.random.normal(120, 12, 5*252),
}
price_data = pd.DataFrame(data_correlation)
price_data.set_index('Date', inplace=True)
# Calculate the returns
returns_data = price_data.pct_change().dropna()
# Build the covariance matrix
covariance_matrix = returns_data.cov()
print(covariance_matrix)
Year Asset Mean_Return Standard_Deviation Asset_Value \
0 2020 Asset 1 0.036287 0.292367 81.813796
1 2021 Asset 1 0.065915 0.285218 77.036361
2 2022 Asset 1 0.082209 0.100228 77.074037
3 2023 Asset 1 0.081169 0.142456 75.987783
4 2024 Asset 1 0.060593 0.101352 75.233527
5 2020 Asset 2 0.031729 0.188255 98.718405
6 2021 Asset 2 0.089463 0.197786 102.655957
7 2022 Asset 2 0.048380 0.296551 106.909381
8 2023 Asset 2 0.077261 0.291070 104.804951
9 2024 Asset 2 0.031617 0.161843 105.689817
10 2020 Asset 3 0.045192 0.104624 77.092018
11 2021 Asset 3 0.061690 0.278742 76.697109
12 2022 Asset 3 0.066776 0.162562 81.272041
13 2023 Asset 3 0.048380 0.221918 84.039701
14 2024 Asset 3 0.058015 0.238862 87.934152
15 2020 Asset 4 0.097658 0.106208 30.751311
16 2021 Asset 4 0.034725 0.166891 31.470370
17 2022 Asset 4 0.045612 0.132897 31.178602
18 2023 Asset 4 0.070149 0.217437 32.634713
19 2024 Asset 4 0.059807 0.199491 30.902078
20 2020 Asset 5 0.087031 0.244788 15.353861
21 2021 Asset 5 0.093114 0.113315 16.206771
22 2022 Asset 5 0.087720 0.296664 15.629626
23 2023 Asset 5 0.086868 0.105738 15.137044
24 2024 Asset 5 0.086808 0.117880 14.344742
25 2020 Asset 6 0.040576 0.177411 59.969044
26 2021 Asset 6 0.099049 0.103151 61.727299
27 2022 Asset 6 0.082261 0.178279 60.141239
28 2023 Asset 6 0.070330 0.232254 63.703086
29 2024 Asset 6 0.069532 0.274213 65.338703
Probability_of_Default Exposure_at_Default Loss_Given_Default
0 0.032623 26.523659 0.236369
1 0.076328 43.541445 0.489250
2 0.075108 44.167150 0.584470
3 0.033297 33.005055 0.565319
4 0.078964 60.135140 0.462460
5 0.066999 24.723975 0.592446
6 0.052635 58.000920 0.416834
7 0.070292 88.955507 0.311118
8 0.050450 16.414573 0.399057
9 0.087363 24.131437 0.487735
10 0.011710 36.378152 0.330955
11 0.066320 35.377199 0.307551
12 0.052807 81.029322 0.342513
13 0.047794 87.199177 0.596701
14 0.060070 36.329617 0.300115
15 0.086204 69.486819 0.591756
16 0.061581 65.455612 0.527988
17 0.077970 75.605023 0.593548
18 0.039462 60.586206 0.456449
19 0.035139 32.119228 0.353843
20 0.022247 55.757839 0.224971
21 0.013541 15.983665 0.313419
22 0.090589 58.301467 0.222324
23 0.094019 67.350599 0.431268
24 0.064866 86.754809 0.239614
25 0.060664 30.468969 0.509858
26 0.070466 72.735144 0.215967
27 0.062535 17.227261 0.210467
28 0.029298 79.111095 0.534515
29 0.031498 50.935758 0.280303
Asset Sector 1 Asset Sector 2 Asset Sector 3 \
Asset Sector 1 0.019498 0.000033 0.000036
Asset Sector 2 0.000033 0.022761 -0.000518
Asset Sector 3 0.000036 -0.000518 0.026458
Asset Sector 4 0.000116 -0.000280 -0.000176
Asset Sector 5 0.001148 -0.001221 0.001062
Asset Sector 6 -0.000076 -0.000022 0.000269
Asset Sector 4 Asset Sector 5 Asset Sector 6
Asset Sector 1 0.000116 0.001148 -0.000076
Asset Sector 2 -0.000280 -0.001221 -0.000022
Asset Sector 3 -0.000176 0.001062 0.000269
Asset Sector 4 0.020076 -0.000711 -0.000501
Asset Sector 5 -0.000711 0.020490 -0.000397
Asset Sector 6 -0.000501 -0.000397 0.021727
In [0]:
df2 = df.groupby(["Year"]).Asset_Value.sum()
result = pd.merge(left=df, right=df2, how='left', on="Year")
df["Weight"] = result["Asset_Value_x"]/result["Asset_Value_y"]
print(df["Weight"])
0 0.224950 1 0.210600 2 0.207074 3 0.201930 4 0.198274 5 0.271429 6 0.280639 7 0.287233 8 0.278509 9 0.278539 10 0.211967 11 0.209673 12 0.218353 13 0.223327 14 0.231745 15 0.084552 16 0.086033 17 0.083767 18 0.086724 19 0.081441 20 0.042216 21 0.044306 22 0.041992 23 0.040225 24 0.037805 25 0.164887 26 0.168749 27 0.161581 28 0.169285 29 0.172196 Name: Weight, dtype: float64
In [0]:
#calculating Mean return per year
Mean_return_portfolio_per_year = (df['Weight'] * df['Mean_Return']).groupby(df['Year']).sum()
Portfolio_asset_value_per_year = df.groupby(["Year"]).Asset_Value.sum()
year_df = pd.merge(Mean_return_portfolio_per_year.to_frame().reset_index(), Portfolio_asset_value_per_year, on="Year")
year_df.columns = ["Year", "Mean_return", "Portfolio_asset_value"]
#calculating the portfolio variance, using the weights and the covariance matrix.
portfolio_variance = []
for year in range (2020, 2024+1):
df_year_weights = df[df["Year"]==year]["Weight"]
portfolio_variance.append([year, np.dot(df_year_weights.T, np.dot(covariance_matrix, df_year_weights))])
#calculate the VaR for 2025 for a confidence interval of 95% (Z-score = -1.96)
#To calculate the VaR for 2025 the mean and variance are averaged over 5 years.
pvdf = pd.DataFrame.from_dict(portfolio_variance)
pvdf.columns = ["Year", "Portfolio_variance"]
portfolio_df = pd.merge(pvdf, year_df, on="Year")
year_2024_data = portfolio_df[portfolio_df["Year"] == 2024].iloc[0]
mean_return = np.mean(portfolio_df["Mean_return"])
std_deviation = np.sqrt(np.mean(portfolio_df["Portfolio_variance"]))
VaR_abs = (mean_return + std_deviation * -1.96) * year_2024_data["Portfolio_asset_value"]
VaR_rel = mean_return + std_deviation * -1.96
VaR_df = pd.DataFrame({
"Year": [2024],
"Mean return": [mean_return],
"Standard Deviation": [std_deviation],
"Absolute Value at Risk": [VaR_abs],
"Relative Value at Risk": [VaR_rel]
})
html_content = VaR_df.to_html()
# Write the HTML content to the file
# with open(parent_path + '/data-analytic-app/static/table_var.html', 'w') as file:
# file.write(
# """<!DOCTYPE html>
# <html>
# <head>
# <link rel="stylesheet" href="style_table.css">
# </head>"""
# + html_content)
print(VaR_df)
Year Mean return Standard Deviation Absolute Value at Risk \ 0 2024 0.062322 0.067899 -26.849045 Relative Value at Risk 0 -0.070759
In [0]:
plt.figure(figsize=(10, 6))
plt.plot(portfolio_df['Year'], portfolio_df['Portfolio_asset_value'], marker='o', linestyle='-', color='tab:green', label='Portfolio Asset Value')
plt.xticks(portfolio_df['Year'])
plt.xlabel('Year')
plt.ylabel('Portfolio Asset Value (mln)')
plt.title('Portfolio Asset Values Over Years')
# plt.savefig(parent_path + '/data-analytic-app/static/portfolio_assets.png')
plt.show()
fig, ax1 = plt.subplots(figsize=(10, 6))
plt.xticks(portfolio_df['Year'].astype(int))
plt.xlabel('Year')
ax1.set_ylabel('Mean Return (%)', color='tab:blue')
ax1.plot(portfolio_df['Year'], portfolio_df['Mean_return']*100, marker='o', linestyle='-', color='tab:blue', label='Mean Return')
ax1.tick_params(axis='y', labelcolor='tab:blue')
ax2 = ax1.twinx()
ax2.set_ylabel('Portfolio Standard dev (%)', color='tab:red')
ax2.plot(portfolio_df['Year'], np.sqrt(portfolio_df['Portfolio_variance'])*100, marker='o', linestyle='--', color='tab:red', label='Portfolio Standard dev')
ax2.tick_params(axis='y', labelcolor='tab:red')
plt.title('Portfolio Over Years')
fig.legend(loc='upper left', bbox_to_anchor=(0.13,0.87))
# plt.savefig(parent_path + '/data-analytic-app/static/portfolio_metrics.png')
plt.show()
Meaning of VaR¶
But what do these values mean? In this example the Value at Risk is calculated per year in absolute terms by multiplying it with the portfolio value of that year. It entails that there is a 95% chance that we will not lose more then x amount of the portfolio in 1 year.