{ "cells": [ { "cell_type": "markdown", "id": "431b9977", "metadata": {}, "source": [ "
" ] }, { "cell_type": "markdown", "id": "937cf4f7", "metadata": {}, "source": [ "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." ] }, { "cell_type": "code", "execution_count": null, "id": "5b1162c9", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import yfinance as yf" ] }, { "cell_type": "code", "execution_count": null, "id": "b3d76629", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "id": "b8b5d355", "metadata": {}, "source": [ "Define a list of stock tickers representing the portfolio components" ] }, { "cell_type": "code", "execution_count": null, "id": "9fd6d834", "metadata": {}, "outputs": [], "source": [ "oex = ['MMM','T','ABBV','ABT','ACN','ALL','GOOGL','GOOG','MO','AMZN','AXP','AIG','AMGN','AAPL','BAC',\n", " 'BRK-B','BIIB','BLK','BA','BMY','CVS','COF','CAT','CVX','CSCO','C','KO','CL','CMCSA',\n", " 'COP','DHR','DUK','DD','EMC','EMR','EXC','XOM','META','FDX','F','GD','GE','GM','GILD',\n", " 'GS','HAL','HD','HON','INTC','IBM','JPM','JNJ','KMI','LLY','LMT','LOW','MA','MCD','MDT','MRK',\n", " 'MET','MSFT','MS','NKE','NEE','OXY','ORCL','PYPL','PEP','PFE','PM','PG','QCOM',\n", " 'SLB','SPG','SO','SBUX','TGT','TXN','BK','USB','UNP','UPS','UNH','VZ','V','WMT',\n", " 'WBA','DIS','WFC']" ] }, { "cell_type": "markdown", "id": "2e00c960", "metadata": {}, "source": [ "Count the number of stocks in the portfolio" ] }, { "cell_type": "code", "execution_count": null, "id": "c5583926", "metadata": {}, "outputs": [], "source": [ "num_stocks = len(oex)" ] }, { "cell_type": "markdown", "id": "37a412ad", "metadata": {}, "source": [ "Download historical stock data for the defined period" ] }, { "cell_type": "code", "execution_count": null, "id": "c794c59a", "metadata": {}, "outputs": [], "source": [ "data = yf.download(oex, start='2014-01-01', end='2016-04-04')" ] }, { "cell_type": "markdown", "id": "13db52ed", "metadata": {}, "source": [ "Calculate daily returns and de-mean the returns by subtracting the mean" ] }, { "cell_type": "code", "execution_count": null, "id": "c61098bd", "metadata": {}, "outputs": [], "source": [ "returns = data['Adj Close'].pct_change()\n", "returns = returns - returns.mean(skipna=True)" ] }, { "cell_type": "markdown", "id": "ec932e56", "metadata": {}, "source": [ "Plot the de-meaned returns for visualization" ] }, { "cell_type": "code", "execution_count": null, "id": "f2d5ad90", "metadata": { "lines_to_next_cell": 1 }, "outputs": [], "source": [ "returns.plot(legend=None)" ] }, { "cell_type": "markdown", "id": "38d7b1de", "metadata": {}, "source": [ "Define a function to scale weights so their absolute values sum to one" ] }, { "cell_type": "code", "execution_count": null, "id": "31ffb424", "metadata": { "lines_to_next_cell": 1 }, "outputs": [], "source": [ "def scale(x):\n", " return x / np.sum(np.abs(x))" ] }, { "cell_type": "markdown", "id": "87766d95", "metadata": {}, "source": [ "Generate random weights for the portfolio and scale them" ] }, { "cell_type": "code", "execution_count": null, "id": "0884a511", "metadata": { "lines_to_next_cell": 1 }, "outputs": [], "source": [ "weights = scale(np.random.random(num_stocks))\n", "plt.bar(np.arange(num_stocks), weights)" ] }, { "cell_type": "markdown", "id": "1f97cb07", "metadata": {}, "source": [ "Define a function to calculate Value at Risk (VaR)" ] }, { "cell_type": "code", "execution_count": null, "id": "5bccf1c9", "metadata": { "lines_to_next_cell": 1 }, "outputs": [], "source": [ "def value_at_risk(value_invested, returns, weights, alpha=0.95, lookback_days=500):\n", " \"\"\"Calculates Value at Risk (VaR) for a portfolio\n", " \n", " Parameters\n", " ----------\n", " value_invested : float\n", " Total value of the portfolio\n", " returns : pd.DataFrame\n", " Historical returns of the portfolio components\n", " weights : np.ndarray\n", " Weights of each asset in the portfolio\n", " alpha : float, optional\n", " Confidence level for VaR, by default 0.95\n", " lookback_days : int, optional\n", " Number of days to look back for historical data, by default 500\n", " \n", " Returns\n", " -------\n", " float\n", " The Value at Risk (VaR) at the specified confidence level\n", " \"\"\"\n", " \n", " # Fill missing values in returns with zero and calculate portfolio returns\n", "\n", " returns = returns.fillna(0.0)\n", " portfolio_returns = returns.iloc[-lookback_days:].dot(weights)\n", "\n", " # Calculate the VaR as the percentile of portfolio returns\n", "\n", " return np.percentile(portfolio_returns, 100 * (1 - alpha)) * value_invested" ] }, { "cell_type": "markdown", "id": "700ce7bd", "metadata": {}, "source": [ "Define the total value invested in the portfolio" ] }, { "cell_type": "code", "execution_count": null, "id": "b087d682", "metadata": {}, "outputs": [], "source": [ "value_invested = 1000000" ] }, { "cell_type": "markdown", "id": "926f99ac", "metadata": {}, "source": [ "Calculate Value at Risk (VaR) using the defined function" ] }, { "cell_type": "code", "execution_count": null, "id": "b0d5a7f9", "metadata": {}, "outputs": [], "source": [ "value_at_risk(value_invested, returns, weights, alpha=0.95, lookback_days=520)" ] }, { "cell_type": "markdown", "id": "c6bc2883", "metadata": {}, "source": [ "Define parameters for lookback days and confidence level" ] }, { "cell_type": "code", "execution_count": null, "id": "5e9379f3", "metadata": {}, "outputs": [], "source": [ "lookback_days = 500\n", "alpha = 0.95" ] }, { "cell_type": "markdown", "id": "260bc52d", "metadata": {}, "source": [ "Calculate portfolio returns using historical data and weights" ] }, { "cell_type": "code", "execution_count": null, "id": "2396b2f1", "metadata": {}, "outputs": [], "source": [ "portfolio_returns = returns.fillna(0.0).iloc[-lookback_days:].dot(weights)" ] }, { "cell_type": "markdown", "id": "79001b24", "metadata": {}, "source": [ "Calculate VaR and express it as a return rather than absolute loss" ] }, { "cell_type": "code", "execution_count": null, "id": "64973e13", "metadata": {}, "outputs": [], "source": [ "portfolio_VaR = value_at_risk(value_invested, returns, weights, alpha=0.95)\n", "portfolio_VaR_return = portfolio_VaR / value_invested" ] }, { "cell_type": "markdown", "id": "43704fbe", "metadata": {}, "source": [ "Plot histogram of portfolio returns and mark the VaR on the plot" ] }, { "cell_type": "code", "execution_count": null, "id": "bd62e764", "metadata": {}, "outputs": [], "source": [ "plt.hist(portfolio_returns, bins=30)\n", "plt.axvline(portfolio_VaR_return, color='red', linestyle='solid')\n", "plt.legend(['VaR', 'Returns'])\n", "plt.title('Historical VaR')\n", "plt.xlabel('Return')\n", "plt.ylabel('Observation Frequency')" ] }, { "cell_type": "markdown", "id": "376798ce", "metadata": {}, "source": [ "Define the number of iterations for VaR calculation and initialize an array" ] }, { "cell_type": "code", "execution_count": null, "id": "c6eecfc0", "metadata": {}, "outputs": [], "source": [ "N = 1000\n", "VaRs = np.zeros((N, 1))" ] }, { "cell_type": "markdown", "id": "9d5cb851", "metadata": {}, "source": [ "Iterate to calculate VaR over different lookback windows" ] }, { "cell_type": "code", "execution_count": null, "id": "58bf2dfe", "metadata": {}, "outputs": [], "source": [ "for i in range(N):\n", " VaRs[i] = value_at_risk(value_invested, returns, weights, lookback_days=i + 1)" ] }, { "cell_type": "markdown", "id": "aadb983c", "metadata": {}, "source": [ "Plot the VaR values over different lookback windows" ] }, { "cell_type": "code", "execution_count": null, "id": "58715c8f", "metadata": { "lines_to_next_cell": 1 }, "outputs": [], "source": [ "plt.plot(VaRs)\n", "plt.xlabel('Lookback Window')\n", "plt.ylabel('VaR')" ] }, { "cell_type": "markdown", "id": "9d78afc3", "metadata": {}, "source": [ "Define a function to calculate Conditional Value at Risk (CVaR)" ] }, { "cell_type": "code", "execution_count": null, "id": "c9debae5", "metadata": { "lines_to_next_cell": 1 }, "outputs": [], "source": [ "def cvar(value_invested, returns, weights, alpha=0.95, lookback_days=500):\n", " \"\"\"Calculates Conditional Value at Risk (CVaR) for a portfolio\n", " \n", " Parameters\n", " ----------\n", " value_invested : float\n", " Total value of the portfolio\n", " returns : pd.DataFrame\n", " Historical returns of the portfolio components\n", " weights : np.ndarray\n", " Weights of each asset in the portfolio\n", " alpha : float, optional\n", " Confidence level for CVaR, by default 0.95\n", " lookback_days : int, optional\n", " Number of days to look back for historical data, by default 500\n", " \n", " Returns\n", " -------\n", " float\n", " The Conditional Value at Risk (CVaR) at the specified confidence level\n", " \"\"\"\n", " \n", " # Calculate VaR and portfolio returns for the specified lookback period\n", "\n", " var = value_at_risk(value_invested, returns, weights, alpha, lookback_days=lookback_days)\n", " \n", " returns = returns.fillna(0.0)\n", " portfolio_returns = returns.iloc[-lookback_days:].dot(weights)\n", " var_pct_loss = var / value_invested\n", " \n", " # Calculate the mean of returns below the VaR threshold\n", "\n", " return np.nanmean(portfolio_returns[portfolio_returns < var_pct_loss]) * value_invested" ] }, { "cell_type": "markdown", "id": "3362b55d", "metadata": {}, "source": [ "Calculate CVaR using the defined function" ] }, { "cell_type": "code", "execution_count": null, "id": "f8273f2f", "metadata": {}, "outputs": [], "source": [ "cvar(value_invested, returns, weights, lookback_days=500)" ] }, { "cell_type": "markdown", "id": "64439069", "metadata": {}, "source": [ "Calculate VaR again for consistency" ] }, { "cell_type": "code", "execution_count": null, "id": "ff33b141", "metadata": {}, "outputs": [], "source": [ "value_at_risk(value_invested, returns, weights, lookback_days=500)" ] }, { "cell_type": "markdown", "id": "db4a0e67", "metadata": {}, "source": [ "Calculate portfolio returns using historical data and weights" ] }, { "cell_type": "code", "execution_count": null, "id": "2dc1b37c", "metadata": {}, "outputs": [], "source": [ "lookback_days = 500" ] }, { "cell_type": "code", "execution_count": null, "id": "a2a13483", "metadata": {}, "outputs": [], "source": [ "portfolio_returns = returns.fillna(0.0).iloc[-lookback_days:].dot(weights)" ] }, { "cell_type": "markdown", "id": "0ac89dd2", "metadata": {}, "source": [ "Calculate VaR and CVaR and express them as returns" ] }, { "cell_type": "code", "execution_count": null, "id": "36b35c9e", "metadata": {}, "outputs": [], "source": [ "portfolio_VaR = value_at_risk(value_invested, returns, weights)\n", "portfolio_VaR_return = portfolio_VaR / value_invested" ] }, { "cell_type": "code", "execution_count": null, "id": "8efa2126", "metadata": {}, "outputs": [], "source": [ "portfolio_CVaR = cvar(value_invested, returns, weights)\n", "portfolio_CVaR_return = portfolio_CVaR / value_invested" ] }, { "cell_type": "markdown", "id": "40bead66", "metadata": {}, "source": [ "Plot histogram of portfolio returns, marking VaR and CVaR on the plot" ] }, { "cell_type": "code", "execution_count": null, "id": "b4bc480d", "metadata": {}, "outputs": [], "source": [ "plt.hist(portfolio_returns[portfolio_returns > portfolio_VaR_return], bins=20)\n", "plt.hist(portfolio_returns[portfolio_returns < portfolio_VaR_return], bins=10)\n", "plt.axvline(portfolio_VaR_return, color='red', linestyle='solid')\n", "plt.axvline(portfolio_CVaR_return, color='red', linestyle='dashed')\n", "plt.legend(['VaR', 'CVaR', 'Returns', 'Returns < VaR'])\n", "plt.title('Historical VaR and CVaR')\n", "plt.xlabel('Return')\n", "plt.ylabel('Observation Frequency')" ] }, { "cell_type": "markdown", "id": "4b6c6fbc", "metadata": {}, "source": [ "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." ] } ], "metadata": { "jupytext": { "cell_metadata_filter": "-all", "main_language": "python", "notebook_metadata_filter": "-all" } }, "nbformat": 4, "nbformat_minor": 5 }