How to calculate value at risk for portfolio risk in Python

How to calculate value at risk for portfolio risk in Python
Value at Risk Explained: How to Measure and Calculate Portfolio Risk in Python
Value at risk (VaR) is a single number that estimates a loss threshold for your portfolio. It answers a practical question, how much could you lose on a bad day? If your portfolio has a one-day VaR of $10,000 at 95% confidence, that means on 95 out of 100 trading days you'd expect losses to stay below $10,000. On the remaining 5 days, losses could be larger. A 95% confidence level means the model sets the threshold so that only 5% of days fall below it. VaR doesn't tell you the worst possible outcome. It tells you where the boundary sits most of the time. If you're new to gauging portfolio risk, that guide covers the basics. The course Getting Started with Python for Quant Finance walks through the Python foundations you'll need.
Many firms use VaR to estimate portfolio loss, and you can calculate a basic version in Python with NumPy and SciPy. In this article, you'll compute VaR from daily price data with three methods and see where each one breaks down.
How to Calculate Value at Risk in Python
There's no single formula for VaR. People usually calculate it in three common ways, and each method makes different assumptions about how returns behave.
Historical VaR
The simplest method uses your actual past returns. You start with daily returns and rank them from worst to best. Then you find the 5th percentile for a 95% confidence level. That percentile becomes your VaR estimate.
This approach treats past daily returns as a stand-in for future risk. It makes no assumptions about the shape of the return distribution. But the estimate depends heavily on the dates you choose. If your data doesn't include a major crash, your VaR won't reflect that kind of loss.
The code below downloads daily prices for SPY and converts each day's closing price into the percentage move from the previous day with pct_change(). The result is expressed as a negative return. For example, a value of -0.0213 means a 2.13% loss. To convert that into a dollar amount, multiply by your portfolio value.
import numpy as np
import yfinance as yf
# Download price data and calculate daily returns
prices = yf.download("SPY", start="2018-01-01", end="2023-12-31")["Close"]
returns = prices.pct_change().dropna()
# Calculate 1-day historical VaR at 95% confidence
confidence_level = 0.95
var_historical = -np.percentile(returns, (1 - confidence_level) * 100)
portfolio_value = 100_000
print(f"Historical VaR (95%): {var_historical:.4f}")
print(f"Dollar VaR: ${var_historical * portfolio_value:,.2f}")
One thing to watch is that the result depends on clean price data. Stock splits, missing prices, or a very short history can distort the estimate.
Parametric VaR
Financial returns don't always match a bell-curve pattern, and that's the main weakness of the parametric method. It assumes returns follow a normal distribution, which is a bell curve, then uses the average return and the typical size of daily moves, called standard deviation, to estimate the loss threshold.
This method is easy to compute. The code below uses norm.ppf() from SciPy to calculate the cutoff point directly, instead of hard-coding a number. The variable z comes out negative because we're looking at the left tail of the distribution, which is the part that contains losses.
from scipy.stats import norm
mean = returns.mean()
std = returns.std()
# Calculate the z-score for the left tail
z = norm.ppf(1 - confidence_level)
var_parametric = -(mean + z * std)
print(f"Parametric VaR (95%): {var_parametric:.4f}")
Real markets produce more extreme losses than the bell curve predicts. During turbulent periods, parametric VaR can underestimate how bad things get.
Monte Carlo VaR
Monte Carlo simulation creates thousands of possible return paths by drawing random values from an assumed distribution. You apply each simulated return to the portfolio, then read off the loss at your chosen percentile from those simulated outcomes. It's more flexible than the other two methods because you can build in more realistic assumptions about how returns behave.
# Simulate 10,000 possible daily returns
np.random.seed(42)
simulated = np.random.normal(mean, std, 10_000)
var_monte_carlo = -np.percentile(simulated, (1 - confidence_level) * 100)
print(f"Monte Carlo VaR (95%): {var_monte_carlo:.4f}")
This example uses a normal distribution for simplicity, but you could swap in a distribution with fatter tails, which means more probability of extreme moves, to better capture large losses. The Monte Carlo VaR guide covers more advanced setups.
What Value at Risk Doesn't Tell You
VaR tells you the threshold, but not what happens beyond it. If your 95% VaR is $10,000, you know that 5% of days will be worse. You don't know whether "worse" means $11,000 or $50,000.
That's where CVaR comes in. CVaR stands for Conditional Value at Risk, and some people call it Expected Shortfall. It calculates the average loss in that worst 5% of scenarios. It gives you a clearer picture of tail risk, which means rare but severe losses. You can read more about using CVaR to capture tail risk and see how it compares to standard VaR.
Applying VaR to a Real Portfolio
Computing VaR for a single stock is straightforward. For a portfolio with several holdings, you need to account for whether those assets tend to rise and fall together. If two stocks usually drop on the same days, the combined portfolio can lose more than you'd expect from looking at each stock alone. If one tends to hold steady when the other falls, the combined risk is lower.
The portfolio VaR walkthrough shows how to extend these calculations to multiple assets, including how to weight holdings and combine return series. The risk metrics in Python guide covers VaR alongside related measures.
All the examples above use a one-day time horizon. You can extend VaR to a week or a month, but a five-day VaR is not the same as multiplying the one-day number by five. The relationship depends on how returns behave over longer periods.
VaR helps you measure one part of portfolio risk, but it doesn't capture everything. You should pair it with other measures. One useful measure tracks how far a portfolio drops from its highest point before it recovers, a concept called drawdown. The drawdown and risk management guide explains how to implement that in Python.
Start by computing one-day historical VaR on your own holdings. Then compare it with CVaR so you can see both the threshold and the average loss in the worst days. That pair of numbers will tell you more about your actual risk than either one alone.