Portfolio VaR and CVaR

Nikesh Shrestha
10 min readApr 24, 2020

--

Scenario

A multi-national American company has buyers in Europe, the UK, and Australia. The buyers pay to the company in their local currency. So, the company faces a foreign currency exchange risk when it has to convert the payments from local currencies to USD. The buyers have scheduled the next payment after 30 days. Each buyer will pay 100,000 in their local currency (EUR, GBP, AUD) after 30 days.

The company is deciding to get forwards in the currency to fix the exchange rates to hedge the foreign exchange risk. The total earnings based on today’s spot rate, which are:

Figure 1: Earnings based on today’s spot rate.

Based on today’s spot rate, the company’s total earnings would be USD 297,430.

I am using Value at Risk (VaR) and Conditional Value at Risk (CVaR) as the measures of risk of the currency exchange rate.
VaR measures the worst potential loss in the earnings or portfolio. For example, if an asset has a 95% VaR of $5 million, the asset has a 5% probability of losing its value by $5 million in the upcoming days. Thus, VaR acts as a breakpoint showing the loss under extreme conditions.
On the other hand, CVaR (aka Expected Shortfall) estimates the expected loss if the assets go beyond the VaR cut-off.

Figure 2: Comparison of VaR and CVaR. (Souce: Informa PLC, n.d.)

As shown in Figure 2, VaR is the value at the cut-off point; commonly used cut-off points are 90%, 95%, or 99%. CVaR is the average of the values beyond the VaR cut-off.

Model Implementation

For calculating the VaR and CVaR, I used the daily spot exchange rates for the currency pairs from Federal Reserve Bank of St. Louis (2020), and calculated the daily changes (also known as returns: the rate at t divided by rate at t-1 period) in the exchange rates using following code:

# 'DEXUSEU' -> 1 EUR = x USD
# 'DEXUSUK' -> 1 GBP = x USD
# 'DEXUSAL' -> 1 AUD = x USD
# Create out portfolio of currencies
tickers = ['DEXUSEU', 'DEXUSUK', 'DEXUSAL']
# Getting the FX rate for the currency pair
data = pdr.get_data_fred(tickers)
# getting percentage change which the return in the FX rate
df_changes = data.pct_change()

I calculated the VaR and CVaR using the following three ways:

  • Parametric Approach
  • Historical Approach
  • Simulation Approach

Parametric Approach

The parametric approach (also known as variance-covariance approach) follows normality assumptions, which means the returns or changes in the values follow a normal distribution. The formula for estimating VaR is:

Figure 3: Formula for calculating VaR (Source: Jassy, 2019)

The last column in Figure 1 shows the weights of earnings from each currency conversions. For estimating the expected weighted return of return, we can use a dot-product of weights and the expected return of each currency pair.

# mean of each stock
avg_changes = df_changes.mean()
# Mean of porfolio
portfolio_mean = avg_changes.dot(weights)

For calculating the standard deviation of the portfolio change, we use the covariances of the changes in the exchange rates. As there are correlations amongst the fluctuation in different exchange rates, we account for these correlations using the covariance matrix. We can estimate the covariances using the following code:

# Get the Co-variance matrix
cov_matrix = df_changes.cov()
cov_matrix
Figure 4: Co-variance matrix between changes in different currency pairs.

Now, we can estimate the standard deviation using weights of our currency pairs and the covariance matrix using the following formula and code:

Figure 5: Formula for calculating portfolio standard deviation
portfolio_stdev = np.sqrt(weights.T.dot(cov_matrix).dot(weights))

Then, we can calculate our VaR using the following code:

def calc_VaR(alpha, mu, std, portfolio_value):
return abs((mu - norm.ppf(alpha) * std) * portfolio_value)

For CVaR, the general formula is as follow:

Figure 6: Formula for calculating CVaR. (Source: Chen, 2019)
Figure 7: CVaR with normal distribution

Since we have a normal distribution, we can use the formula shown in Figure 7, or the following code:

def calc_CVaR(alpha, mu, std, portfolio_value):
return (mu + alpha**-1 * norm.pdf(norm.ppf(alpha))* std) * portfolio_value

Using the above values, we can estimate the 1-day VaR and CVaRs. We calculate the 30 days VaR by suing the rule for scaling volatility of assets. Since we are assuming a normal distribution, the volatility of assets increases by the square root of N for N-th day (Harper, 2019). This expansion is calculated using the following code as:

# Parametric VaR at nth day
def calc_VaR_n_day(alpha, mu, std, portfolio_value, num_days):
'''
alpha = confidence level
mu = mean
std = portfolio change standard deviation
portfolio = portfolio value
num_days = nth day
'''
return abs((mu - norm.ppf(alpha) * std * np.sqrt(num_days)) * portfolio_value)
# Parametric CVaR at nth day
def calc_CVaR_n_day(alpha, mu, std, portfolio_value, num_days):
return (mu +alpha**-1 * norm.pdf(norm.ppf(alpha))* std * np.sqrt(num_days))*portfolio_value

Historical Approach

For the historical approach, we assume that the future changes in the exchange rate will follow the exact distribution as the past changes in the exchange rate. So, we multiply the current exchange rates with all the historical changes(returns), to create a distribution of future exchange rates. Then, the estimated future exchange rates are multiplied by local payments to calculate the estimated total earnings. Then, we can create profit and loss (P&L) distribution by subtracting our total earnings calculated using the current spot rate.

Then, the VaR is the value at a certain percentile of the P&L distribution. For instance, the VaR at 95% is the 95th percentile value of the estimated P&L distribution. Whereas, the CVaR at 95% is the average of the values beyond the 95th percentile value in the P&L distribution.

The following code calculates the 1-day VaR and CVaR using the historical approach:

# Confidence levels for estimating VaR and CVaR
conf_levels = [0.10, 0.05, 0.01]
#100K in local currencies
paymentsLocal = np.array([100000, 100000, 100000])
def createHistoricalSimulation(returns, payments):
return payments.dot(returns)
# Estimating change in earnings using past returns
histSimulation = np.array(
[createHistoricalSimulation(np.array(
df_changes.iloc[i, :]), paymentsLocal) for i in range( df_changes.shape[0])])
# VaRs using historical approach
histVaRs = [abs(np.percentile(histSimulation, conf_level)) for conf_level in conf_levels]
#CVaRs using historical approach
histCVaRs = [abs(np.mean(histSimulation[histSimulation < np.percentile(histSimulation, conf_level)])) for conf_level in conf_levels]

For estimating the nth-day VaR using the historical approach, I used a bootstrapping method. For a single trial, I chose a random set of returns/changes from the historical distribution with replacements for each day. Then, I performed the trials multiple times (same as the number of historical data points).

Then, the VaR is the value at a certain percentile of the values in the last column of the values generated by the bootstrapping method. Similarly, the CVaR is the average value beyond the VaR cutoff point.

The following code calculates VaR and CVaR using bootstrapping historical approach:

def bootstrapHistoricalSimulation(df_change, rates, num_days, paymentsLocal):
payments = []
for _ in range(num_days):
# randomly choosing one set of returns for each day
random_idx = np.random.choice(range(df_changes.shape[0]))

#getting the rates for next period
rates = rates * (1 + df_changes.iloc[random_idx, :])
#total payments in each period
payments.append(sum(rates * paymentsLocal))
return paymentsnum_days = 30
histSim = []
for _ in range(df_changes.shape[0]):
histSim.append(bootstrapHistoricalSimulation(df_changes, current_spot_rates, num_days, paymentsLocal))
histSim = np.array(histSim)# getting the last column or 30th column, (zero-index)
histSim_30 = histSim[:, num_days-1]
# calculating VaR at 30 days
hist_VaRs_30 = [abs(totalEarningUSD - np.percentile(histSim, conf_level * 100)) for conf_level in conf_levels]
# calculating CVaR at 30th day
hist_CVaRs_30 = [abs(totalEarningUSD - np.mean(histSim[histSim < np.percentile(histSim, conf_level * 100)])) for conf_level in conf_levels]

Simulation Approach

The simulation approach (aka Monte Carlo simulation approach) draws random samples from a given distribution to estimate future rates and earnings, which essentially creates a random walk (Figure 8).

Figure 8: The results of Monte Carlo Simulation Approach.

For this approach, I generated 4000 samples for each day from a multivariate normal distribution with a mean of 0 and covariance matrix, the same as the earlier computed covariance matrix. The multivariate distribution returns three values, one each for changes in the exchange rate of each currency pair. Instead of using three different independent normal distribution, I used the multivariate normal distribution, which preserves the correlation between the exchange rates via the covariance matrix.

Then, I calculated VaR and CVaR similarly to the historical approach. The VaR is the value at a certain percentile in that distribution. For instance, the VaR at 95% is the 95th percentile value of the estimated portfolio distribution. Whereas, the CVaR at 95% is the average of the values beyond VaR cutoff.

The code of Monte Carlo Simulation Approach is given below:

num_simulations = 4000
num_days = 30
# Data Frame to store simulated values
df_simulation = pd.DataFrame()
paymentsLocal = np.array([100000, 100000, 100000])
starting_point = paymentsLocal * data.iloc[-1]
totalEarningUSD = sum(starting_point)
for i in range(num_simulations):
sim_usd_values = starting_point.to_numpy()
sim_portfolio = [sum(sim_usd_values)]

for j in range(num_days):
# Using a normal multivariate distribution with a covariate
# matrix to get returns for each FX.
sim_returns = np.random.multivariate_normal([0, 0, 0], cov_matrix) sim_usd_values = (1 + sim_returns) * sim_usd_values
sim_portfolio.append(sum(sim_usd_values))
df_simulation[i] = sim_portfolio# VaRs
sim_VaRs = [abs(totalEarningUSD - np.percentile(df_simulation.iloc[1], conf_level * 100)) for conf_level in conf_levels]
tabulateData(sim_VaRs, conf_levels, title='Simulated VaR')
# Calculating CVaRs
sim_CVaRs = [abs(totalEarningUSD - np.mean(df_simulation.iloc[1][df_simulation.iloc[1] < np.percentile(df_simulation.iloc[1], conf_level * 100)])) for conf_level in conf_levels]

Results

Figure 9: VaR and CVaRs of the total earnings after 1 day
Figure 10: VaR and CVaRs of the total earnings after 30 days. These values are comparable to the actual loss in values we will have if we do not fix the spot prices.

The VaRs and CVaR estimated by simulated and parametric approaches are similar compared to the historical approach.

Both simulated and parametric approaches use normal distributions, with similar means and standard deviation/covariances. So, their VaR and CVaR are similar even after 30 days. The parametric approach uses the weighted mean of change in rates, which is extremely small and close to the mean of the multivariate normal distribution used in the simulation approach. The standard deviation for the normal distribution for the parametric approach is derived from the covariances matrix (calculated above). The multivariate distribution uses the same covariance matrix.

Whereas, the historical approach assumes that the changes in future forex rate will be similar to past returns. However, the past may not always define the future. With the current historical approach, the price changes in the oldest data point affect the current spot rate with equal weight as the newer data points, whereas the newer data points generally have a more significant effect on the change in the current exchange rate. On the other hand, the historical approach does retain the cross-correlation among different currency pairs as we randomly pick the set of changes at a time.

Figure 11: Distribution of different exchange rates over past.
Figure 12: Daily changes in different Forex rate.

As Figure 11 shows, the distributions of foreign exchange rates seem to have different distributions. Some are skewed, and some are multimodal. However, Figure 12 shows that the distributions of the rate of changes are unimodal and similar to a normal distribution, which supports the argument for parametric and simulation approaches. Instead of a single calculation like the parametric approach, the simulation approach adds randomness by random sampling as well as can measure VaR for non-linear assets (Finance Train, 2010). So, I recommend the simulation approach for calculating VaR.

Furthermore, VaR gives us a good idea about the losses when tail events (less than a given confidence interval) occur. However, it does not provide insights about the outliers that lie beyond the VaR cutoff point. For instance, let’s assume we have a VaR at 95% is 100K USD, which will require us to have capital enough to handle 100K loss. However, if there is a chance of a loss of 1 million USD beyond the 95th percentile, we will not be prepared if we only view at VaR and adjust our capital requirements accordingly, in case there is a 1 million USD.

Meanwhile, CVaR prompts us to be more risk-averse and is higher than VaR as it quantifies all the losses beyond the VaR cutoff. So, the CVaR will accommodate the loss of 1 million in its calculation and give us a better representation of the worst risk. Thus, CVaR is a better metric as it captures even the outliers beyond the cutoff point of VaR.

Figure 13: VaR at different confidence levels estimated using simulation approach

Finally, in our scenario, without the fixed exchange rates, we will have the worst loss of 19,000 USD (30-day CVaR of the simulation approach in Figure 10) with 99% confidence. In some scenarios, the rates might move in our favor, and we might have more earnings in USD. Thus, if we want to gamble on the benefits of the better exchange rate, we will need to have at least 19,000 USD reserve capital to capture the risk from the exchange rates without fixed forward contracts at the current spot rate.

Link to code is here.

Thank you for reading!

References

Calculating Value at Risk (VaR) of a stock portfolio using Python. (2018). Interviewqs.Com. https://www.interviewqs.com/blog/value_at_risk

Chen, J. (2019). Conditional Value at Risk (CVaR). Investopedia. https://www.investopedia.com/terms/c/conditional_value_at_risk.asp

Damodaran, A. (2007). Value at Risk. http://people.stern.nyu.edu/adamodar/pdfiles/papers/VAR.pdf

Expected shortfall. (2020, January 17). Wikipedia. https://en.wikipedia.org/wiki/Expected_shortfall

Federal Reserve Bank of St. Louis. (2020). Daily Rates | FRED | St. Louis Fed. Fred.Stlouisfed.Org. https://fred.stlouisfed.org/categories/94

Finance Train. (2010, October 19). Three Methodologies for Calculating VaR. Finance Train. https://financetrain.com/three-methodologies-for-calculating-var/

Harper, D. (2017). What is bootstrap historical simulation? FRM T1–6 [YouTube Video]. In Bionic Trutle. https://www.youtube.com/watch?v=XIQnEyzqHJ8

Harper, D. (2019). An Introduction to Value at Risk (VAR). Investopedia. https://www.investopedia.com/articles/04/092904.asp

Harper, D. R. (2019). How To Convert Value At Risk To Different Time Periods. Investopedia. https://www.investopedia.com/articles/04/101304.asp

Informa PLC. (n.d.). Conditional Value at Risk | Zephyr Associates, Inc. Www.Styleadvisor.Com. Retrieved April 24, 2020, from https://www.styleadvisor.com/resources/statfacts/conditional-value-risk

Jassy, D. (2019). How to Calculate Value at Risk (VaR) in Excel. Investopedia. https://www.investopedia.com/ask/answers/033115/how-can-you-calculate-value-risk-var-excel.asp

Lachowicz, P. (2016). Conditional Value-at-Risk in the Normal and Student t Linear VaR Model. Quantatrisk.Com. http://www.quantatrisk.com/2016/12/08/conditional-value-at-risk-normal-student-t-var-model-python/

--

--