Files
strategy-lab/to_explore/pyquantnews/23_ConditionalValueAtRisk.ipynb
David Brazda e3da60c647 daily update
2024-10-21 20:57:56 +02:00

15 KiB

No description has been provided for this image

This code calculates Value at Risk (VaR) and Conditional Value at Risk (CVaR) for a portfolio of stocks. It uses historical stock data to compute returns, then scales these returns to simulate a portfolio's performance. It defines functions to calculate VaR and CVaR, both of which are measures of potential losses in a portfolio. The code also generates visualizations of these risk metrics to aid in financial risk assessment. This is practical for portfolio management and risk analysis in finance.

In [ ]:
import numpy as np
import pandas as pd
import yfinance as yf
In [ ]:
import matplotlib.pyplot as plt

Define a list of stock tickers representing the portfolio components

In [ ]:
oex = ['MMM','T','ABBV','ABT','ACN','ALL','GOOGL','GOOG','MO','AMZN','AXP','AIG','AMGN','AAPL','BAC',
       'BRK-B','BIIB','BLK','BA','BMY','CVS','COF','CAT','CVX','CSCO','C','KO','CL','CMCSA',
       'COP','DHR','DUK','DD','EMC','EMR','EXC','XOM','META','FDX','F','GD','GE','GM','GILD',
       'GS','HAL','HD','HON','INTC','IBM','JPM','JNJ','KMI','LLY','LMT','LOW','MA','MCD','MDT','MRK',
       'MET','MSFT','MS','NKE','NEE','OXY','ORCL','PYPL','PEP','PFE','PM','PG','QCOM',
       'SLB','SPG','SO','SBUX','TGT','TXN','BK','USB','UNP','UPS','UNH','VZ','V','WMT',
       'WBA','DIS','WFC']

Count the number of stocks in the portfolio

In [ ]:
num_stocks = len(oex)

Download historical stock data for the defined period

In [ ]:
data = yf.download(oex, start='2014-01-01', end='2016-04-04')

Calculate daily returns and de-mean the returns by subtracting the mean

In [ ]:
returns = data['Adj Close'].pct_change()
returns = returns - returns.mean(skipna=True)

Plot the de-meaned returns for visualization

In [ ]:
returns.plot(legend=None)

Define a function to scale weights so their absolute values sum to one

In [ ]:
def scale(x):
    return x / np.sum(np.abs(x))

Generate random weights for the portfolio and scale them

In [ ]:
weights = scale(np.random.random(num_stocks))
plt.bar(np.arange(num_stocks), weights)

Define a function to calculate Value at Risk (VaR)

In [ ]:
def value_at_risk(value_invested, returns, weights, alpha=0.95, lookback_days=500):
    """Calculates Value at Risk (VaR) for a portfolio
    
    Parameters
    ----------
    value_invested : float
        Total value of the portfolio
    returns : pd.DataFrame
        Historical returns of the portfolio components
    weights : np.ndarray
        Weights of each asset in the portfolio
    alpha : float, optional
        Confidence level for VaR, by default 0.95
    lookback_days : int, optional
        Number of days to look back for historical data, by default 500
    
    Returns
    -------
    float
        The Value at Risk (VaR) at the specified confidence level
    """
    
    # Fill missing values in returns with zero and calculate portfolio returns

    returns = returns.fillna(0.0)
    portfolio_returns = returns.iloc[-lookback_days:].dot(weights)

    # Calculate the VaR as the percentile of portfolio returns

    return np.percentile(portfolio_returns, 100 * (1 - alpha)) * value_invested

Define the total value invested in the portfolio

In [ ]:
value_invested = 1000000

Calculate Value at Risk (VaR) using the defined function

In [ ]:
value_at_risk(value_invested, returns, weights, alpha=0.95, lookback_days=520)

Define parameters for lookback days and confidence level

In [ ]:
lookback_days = 500
alpha = 0.95

Calculate portfolio returns using historical data and weights

In [ ]:
portfolio_returns = returns.fillna(0.0).iloc[-lookback_days:].dot(weights)

Calculate VaR and express it as a return rather than absolute loss

In [ ]:
portfolio_VaR = value_at_risk(value_invested, returns, weights, alpha=0.95)
portfolio_VaR_return = portfolio_VaR / value_invested

Plot histogram of portfolio returns and mark the VaR on the plot

In [ ]:
plt.hist(portfolio_returns, bins=30)
plt.axvline(portfolio_VaR_return, color='red', linestyle='solid')
plt.legend(['VaR', 'Returns'])
plt.title('Historical VaR')
plt.xlabel('Return')
plt.ylabel('Observation Frequency')

Define the number of iterations for VaR calculation and initialize an array

In [ ]:
N = 1000
VaRs = np.zeros((N, 1))

Iterate to calculate VaR over different lookback windows

In [ ]:
for i in range(N):
    VaRs[i] = value_at_risk(value_invested, returns, weights, lookback_days=i + 1)

Plot the VaR values over different lookback windows

In [ ]:
plt.plot(VaRs)
plt.xlabel('Lookback Window')
plt.ylabel('VaR')

Define a function to calculate Conditional Value at Risk (CVaR)

In [ ]:
def cvar(value_invested, returns, weights, alpha=0.95, lookback_days=500):
    """Calculates Conditional Value at Risk (CVaR) for a portfolio
    
    Parameters
    ----------
    value_invested : float
        Total value of the portfolio
    returns : pd.DataFrame
        Historical returns of the portfolio components
    weights : np.ndarray
        Weights of each asset in the portfolio
    alpha : float, optional
        Confidence level for CVaR, by default 0.95
    lookback_days : int, optional
        Number of days to look back for historical data, by default 500
    
    Returns
    -------
    float
        The Conditional Value at Risk (CVaR) at the specified confidence level
    """
    
    # Calculate VaR and portfolio returns for the specified lookback period

    var = value_at_risk(value_invested, returns, weights, alpha, lookback_days=lookback_days)
    
    returns = returns.fillna(0.0)
    portfolio_returns = returns.iloc[-lookback_days:].dot(weights)
    var_pct_loss = var / value_invested
    
    # Calculate the mean of returns below the VaR threshold

    return np.nanmean(portfolio_returns[portfolio_returns < var_pct_loss]) * value_invested

Calculate CVaR using the defined function

In [ ]:
cvar(value_invested, returns, weights, lookback_days=500)

Calculate VaR again for consistency

In [ ]:
value_at_risk(value_invested, returns, weights, lookback_days=500)

Calculate portfolio returns using historical data and weights

In [ ]:
lookback_days = 500
In [ ]:
portfolio_returns = returns.fillna(0.0).iloc[-lookback_days:].dot(weights)

Calculate VaR and CVaR and express them as returns

In [ ]:
portfolio_VaR = value_at_risk(value_invested, returns, weights)
portfolio_VaR_return = portfolio_VaR / value_invested
In [ ]:
portfolio_CVaR = cvar(value_invested, returns, weights)
portfolio_CVaR_return = portfolio_CVaR / value_invested

Plot histogram of portfolio returns, marking VaR and CVaR on the plot

In [ ]:
plt.hist(portfolio_returns[portfolio_returns > portfolio_VaR_return], bins=20)
plt.hist(portfolio_returns[portfolio_returns < portfolio_VaR_return], bins=10)
plt.axvline(portfolio_VaR_return, color='red', linestyle='solid')
plt.axvline(portfolio_CVaR_return, color='red', linestyle='dashed')
plt.legend(['VaR', 'CVaR', 'Returns', 'Returns < VaR'])
plt.title('Historical VaR and CVaR')
plt.xlabel('Return')
plt.ylabel('Observation Frequency')

PyQuant News is where finance practitioners level up with Python for quant finance, algorithmic trading, and market data analysis. Looking to get started? Check out the fastest growing, top-selling course to get started with Python for quant finance. For educational purposes. Not investment advise. Use at your own risk.