Modelling Monte Carlo VaR in Python

Modelling Monte Carlo VaR in Python

There are three methods in estimating a portfolio's Value at Risk (VaR). The parametric method estimates VaR from the left tail of a normal distribution while the historical method sorts and calculates the largest losses over a specified lookback period. In contrast, the Monte Carlo method is forward-looking. And rather than making an assumption on the portfolio's distribution, it simulates multiple risk factors to determine their combined effects on the overall portfolio.

Python provides powerful tools for handling arrays, matrices, and statistical functions that help to streamline the calculations for Monte Carlo VaR while providing intuitive, dynamic visualisations that let stakeholders better appreciate and grasp potential risks more effectively. This article aims to break down the process and provide a guide to achieving these objectives.

The Data

We will work on a diversified portfolio consisting of 10 asset classes according to the weights below. In order to determine the portfolio's Monte Carlo VaR, we will need the asset class returns, their volatilities, as well as the variance-covariance matrix:

The Code

Conducting a Monte Carlo simulation for a portfolio in Python can be accomplished with just a few lines of code, thanks to the availability of efficient functions designed for the task. The key is in leveraging the effectiveness of matrix algebra in generating, storing, and manipulating multi-dimensional data.

In this exercise, we will need to deal with matrices to model the 10 different asset classes, the number of days, and the number of portfolio simulations, each expanding the dimensions of the matrices. We will also need to perform matrix operations, such as "inner product", which would transform the random variables into correlated ones suitable for modelling portfolio returns.

As we perform matrix operations, our ultimate objective is to arrive at a single array that contains the cumulative portfolio returns over a 100-day timeframe for each of the 1,000 simulations. Hence, we will end up with a 100 x 1,000 array of portfolio values, from which we can derive all VaR calculations.

Here we write the code:

First, we declare some scalar constants:

  1. simulations = 1000 is the number of Monte Carlo simulations we will run
  2. days = 100 will be the timeframe, at the end of which we will calculate the portfolio values and consequently, VaR
  3. init_port = 100M is the portfolio's value at day 0

As promised, here comes the matrices!

The first three are more familiar -- those from our input data:

  1. returns = data['Daily Returns (%)'] are the return values,
  2. weights = data['Weights'] are the asset class weights, and
  3. varcov_matrix = varcovmatrix is the input variance-covariance matrix.

Weights and returns are one-dimensional arrays while varcov_matrix is 10x10.

Now, we look into the bigger arrays:

  • port_sim_matrix: This is an empty 100 by 1,000 matrix where we will store the 100 daily cumulative returns for each of the iteration of the 1,000 Monte Carlo simulations later. We can leave this behind until the end of this section.
  • returns_matrix: This is a 10 by 100 matrix where the daily return assumptions of 10 assets for each of the 100 days will be stored.
  • Z: This is another 10 by 100 matrix that contains the normal (uncorrelated) random variables that correspond to the daily return movements of the 10 assets for each of the 100 days. (Note that these independent random normal numbers do not take into account the asset returns' correlations yet.)
  • L: This is a 10 by 10 matrix similar to the variance-covariance matrix from our input, but which invokes what's called the Cholesky method, which simply carries over the values from the lower triangle of the matrix, while zeroing out the rest. This, when multiplied by Z, will take into account the fact that those returns are correlated.
  • np.inner(L, Z): As mentioned above, this is the inner product between L (10x10 array) and Z (100 x 10) that will transform the uncorrelated Z daily returns into ones that are. The product of a 10x10 and 100x10 matrices will follow the 100x10 dimension.
  • returns_matrix + np.inner(L, Z): The sum of 10x100 and 10x100 arrays preserve those same dimensions.
  • np.inner(weights, returns_matrix.T): This transforms the returns_matrix previously 10x100 into 1x100 as it consolidates the distinct asset class returns into one portfolio return for each of the 100 days.
  • np.cumprod(np.inner(weights, dailyReturns.T)+1)*initialPortfolio: This is also a 1x100 array that transforms the daily portfolio returns to cumulative portfolio values.
  • port_sim_matrix[:,m]: After all the calculations are performed, this variable array stores the above 100 cumulative portfolio values across 1,000 simulations. The array shape of 100 x 1,000 will appear as follows:

The Output

From the 100 x 1,000 port_sim_matrix, I extracted the portfolio values at the end of the 100-day period, and stored them in a 1 x 1,000 array variable called port_results. Referring to this array, I defined the two functions that will calculate VaR and conditional VaR based on one parameter, the alpha:

Using these functions, we can now compute for the different % VaR's and % conditional VaR's as follows:

Note that once we have the discrete distribution of simulated portfolio returns, the VaR will simply be the n'th percentile of those returns where n = alpha, and the conditional VaR will be the mean of all returns below VaR.

The Dashboard

Python also offers the flexibility to create dashboards that dynamically updates the figures, markers and labels, as new data are used.

I wrapped all the steps and included all plotting parameters in a function called create_VaR_dashboard. This function accommodates new parameters such as alpha, initial portfolio value, # of simulations, and the timeframe.

For example, for a first report on Monte Carlo VaR based on:

  • 1,000 simulations,
  • initial portfolio value of $100 million,
  • VaR timeframe of 100 days,
  • and 5% alpha,

I called the create_VaR_dashboard specifying the parameters as follows:

create_VaR_dashboard(simulations=1000, init_port_m = 100, days=100, alpha=5)        

Alternatively, we can revise the report that will reflect all changes by simply running another instance of the create_VaR_dashboard function:

create_VaR_dashboard(simulations=500, init_port_m = 50, days=90, alpha=16)        

Summary

The Monte Carlo method for VaR has important advantages. Compared to historical VaR, it is a more relevant forecasting tool, and compared to parametric VaR, it allows for modelling larger number of assets in a more complex portfolio. I hope the above discussion of the matrix operations for VaR modelling and the accompanying visualizations, sparked your analytical ideas for implementing quantitative techniques in risk analysis. Ultimately, learning how to use Python to develop VaR models can enhance your team's capabilities not only in managing risk but also optimizing portfolio performance.

要查看或添加评论,请登录

Abigail Urmeneta, CFA的更多文章

  • Constructing the optimal portfolio using Python

    Constructing the optimal portfolio using Python

    How often does your investment team update capital market assumptions? How frequently do you rebalance strategic asset…

    3 条评论

社区洞察

其他会员也浏览了