Hello! Welcome to the fourth lesson in our module on mathematical foundations.
In our last lesson, you did a great job mastering the core matrix operations in NumPy: transposition, multiplication with the @ operator, and inversion. You even got a preview of the portfolio variance formula, , in the exercise.
Today, we'll put that knowledge to immediate practical use. This lesson focuses on the learning outcome: Calculate portfolio returns and volatility using vector and matrix algebra. These are two of the most fundamental calculations in quantitative finance, forming the basis for risk management, performance analysis, and portfolio optimization. Your background in software development will be a great asset here, as we translate these key financial formulas into efficient, clean Python code.
1. The Core Formulas: Return and Risk
At the heart of portfolio theory are two key questions: "What is my expected return?" and "How risky is it?". Vector and matrix algebra provide a powerful and concise way to answer them.
Let's define our terms:
- : A weights vector of length n, where n is the number of assets in our portfolio. Each element is the fraction of the portfolio's total value invested in asset i. The sum of all weights must be 1 (i.e., ).
- : A vector of expected returns of length n. Each element is the expected return for asset i. For now, we'll estimate this using the historical average return.
- : The covariance matrix of asset returns. This matrix is crucial:
- The elements on its diagonal are the variances () of each individual asset.
- The off-diagonal elements are the covariances between pairs of assets, which measure how they move together.
With these defined, the formulas are surprisingly elegant:
-
Expected Portfolio Return (): This is the weighted average of the individual asset returns.
In NumPy, this is a simple dot product:weights @ mean_returns. -
Portfolio Variance (): This measures the portfolio's overall risk, accounting for both individual asset volatilities and their interactions.
This is the exact formula from our last lesson's exercise. -
Portfolio Volatility (): This is the standard deviation of the portfolio's returns, and is simply the square root of the variance. It's more interpretable as it's in the same units as return (e.g., a 15% annualized volatility).
The following resource provides a detailed walkthrough of these formulas and their implementation.
This article, 'NumPy Financial Examples', is a comprehensive guide that we'll use throughout this lesson. It directly implements the formulas we just discussed.
Please read the section titled 'Portfolio Returns and Risk'. Focus on the 'Math Recap' and the Python function in 'Step 2: Portfolio Return and Volatility for a Given Weight Vector'. Notice how cleanly the formulas translate to NumPy code using the @ operator.
2. A Practical Walkthrough: From Prices to Portfolio Stats
Let's apply these concepts in a step-by-step example. We'll start with a matrix of asset prices and end with the annualized return and volatility of a portfolio built from them.
Step 1: Calculate Asset Returns
First, we need to convert our raw price data into returns. As a quant, you'll work with both simple returns and log returns. While simple returns are more intuitive, log returns have convenient mathematical properties (like additivity over time) that make them preferable for many models.
Let's revisit the 'NumPy Financial Examples' article to see how to calculate different types of returns.
First, read the section 'Rates of Return' to see how to calculate simple returns. Then, read the section 'Log Returns', paying special attention to the 'Why Use Log Returns?' part. This explains the practical reasons quants favor them.
Here's how you can do it in code, assuming you have a prices matrix where each row is an asset and each column is a time period (e.g., a day).
import numpy as np
# Example: 2 assets, 5 days of prices
prices = np.array([
[100, 102, 101, 103, 105], # Asset A
[50, 51, 52, 51, 53] # Asset B
])
# Calculate simple returns
# R_t = (P_t / P_{t-1}) - 1
simple_returns = prices[:, 1:] / prices[:, :-1] - 1
# Calculate log returns
# r_t = ln(P_t / P_{t-1})
log_returns = np.log(prices[:, 1:] / prices[:, :-1])
print("Simple Returns:\n", simple_returns)
print("\nLog Returns:\n", log_returns)
For the rest of this example, we'll use simple_returns.
Step 2: Estimate Expected Returns () and Covariance ()
Using our historical returns matrix, we can now estimate the inputs for our portfolio formulas.
- The expected return vector () is estimated by taking the mean of returns for each asset over time.
- The covariance matrix () is calculated using NumPy's
np.cov()function.
# Use the simple_returns calculated before
# 1. Mean daily return for each stock (axis=1 averages across time)
mu_daily = np.mean(simple_returns, axis=1)
# 2. Covariance matrix of daily returns
cov_matrix = np.cov(simple_returns)
print("Mean Daily Returns (μ):\n", mu_daily)
print("\nCovariance Matrix (Σ):\n", cov_matrix)
The diagonal of the covariance matrix [[0.00028, 0.00026], [0.00026, 0.00023]] represents the variance of Asset A (0.00028) and Asset B (0.00023). The off-diagonal value (0.00026) is the covariance between them.
Step 3: Define Weights and Calculate Portfolio Statistics
Let's assume an equal-weight portfolio (50% in Asset A, 50% in Asset B).
# Define portfolio weights
weights = np.array([0.5, 0.5])
# Calculate daily portfolio return
port_return_daily = weights @ mu_daily
# Calculate daily portfolio variance and volatility
port_variance_daily = weights @ cov_matrix @ weights
port_volatility_daily = np.sqrt(port_variance_daily)
print(f"Daily Portfolio Return: {port_return_daily:.5f}")
print(f"Daily Portfolio Volatility: {port_volatility_daily:.5f}")
Step 4: Annualize the Results
Daily statistics are useful, but performance is usually reported in annualized terms. The standard convention is:
- Multiply the mean daily return by the number of trading days in a year (typically 252).
- Multiply the daily volatility by the square root of the number of trading days.
The reason for the square root is that variance grows linearly with time, and since volatility is the square root of variance, it grows with the square root of time.
trading_days = 252
# Annualize the portfolio return and volatility
port_return_annual = port_return_daily * trading_days
port_volatility_annual = port_volatility_daily * np.sqrt(trading_days)
print(f"Annualized Portfolio Return: {port_return_annual:.2%}")
print(f"Annualized Portfolio Volatility: {port_volatility_annual:.2%}")
This entire process, from returns to annualized statistics, is demonstrated very effectively in the following video.
Portfolio Optimization in Python: Boost Your Financial Performance
To see these calculations in action within a real script, watch the following video from Ryan O'Connell. It walks through a very similar process to what we just did.
Watch the segment from 10:24 to 13:37. This part covers the implementation of two functions: one for standard deviation (volatility) and one for expected return. Note how the code directly mirrors the mathematical formulas we've been using.
Test your understanding!
You are given a matrix of daily log returns for three stocks (MSFT, AAPL, GOOG) over several days. You are also given the weights for a portfolio.
import numpy as np
# Daily log returns for MSFT, AAPL, GOOG
log_returns = np.array([
[ 0.005, -0.010, 0.012], # MSFT
[ 0.008, -0.015, 0.020], # AAPL
[ 0.002, -0.005, 0.008] # GOOG
])
# Portfolio weights: 40% MSFT, 30% AAPL, 30% GOOG
weights = np.array([0.4, 0.3, 0.3])
trading_days = 252
Your task is to calculate the annualized portfolio return and annualized portfolio volatility.
Instructions:
- Calculate the mean daily return vector () from
log_returns. - Calculate the daily covariance matrix () from
log_returns. - Use the formulas and to find the daily portfolio statistics.
- Annualize both the return and the volatility.
Show answer
import numpy as np
# Given data
log_returns = np.array([
[ 0.005, -0.010, 0.012], # MSFT
[ 0.008, -0.015, 0.020], # AAPL
[ 0.002, -0.005, 0.008] # GOOG
])
weights = np.array([0.4, 0.3, 0.3])
trading_days = 252
# 1. Calculate mean daily return vector
mu_daily = np.mean(log_returns, axis=1)
# 2. Calculate daily covariance matrix
# Note: ddof=1 can be used for sample covariance, but for this exercise default is fine.
cov_daily = np.cov(log_returns)
# 3. Calculate daily portfolio statistics
port_return_daily = weights @ mu_daily
port_variance_daily = weights @ cov_daily @ weights
port_volatility_daily = np.sqrt(port_variance_daily)
# 4. Annualize the results
port_return_annual = port_return_daily * trading_days
port_volatility_annual = port_volatility_daily * np.sqrt(trading_days)
print(f"Mean Daily Returns (μ):\n{mu_daily}")
print(f"\nDaily Covariance Matrix (Σ):\n{cov_daily}")
print("\n--- Results ---")
print(f"Annualized Portfolio Return: {port_return_annual:.2%}")
print(f"Annualized Portfolio Volatility: {port_volatility_annual:.2%}")
# Expected output (approximate):
# Annualized Portfolio Return: 120.96%
# Annualized Portfolio Volatility: 22.36%
This exercise consolidates all the steps we've covered: estimating statistical inputs from data, applying the core matrix formulas, and converting the results into standard annualized figures.
Conclusion
Congratulations! You've successfully combined the linear algebra concepts from the past few lessons to perform one of the most fundamental tasks in quantitative finance. Being able to efficiently calculate portfolio return and risk is a non-negotiable skill for a quant developer, and now you have the tools and understanding to do it.
Key Takeaways:
- Portfolio return is the weighted average of individual asset returns, calculated as .
- Portfolio volatility is the total risk of the portfolio, crucially accounting for how assets move together via the covariance matrix . The formula is .
- These complex financial formulas translate into clean, efficient, and readable code thanks to NumPy's matrix operations, especially the
@operator. - Daily statistics are converted to annualized figures by multiplying return by and volatility by , where is the number of trading days.
Preview of the Next Lesson:
Today, we used the covariance matrix as a critical input. In the next lesson, "Explain the concepts of covariance and correlation matrices and compute them for a set of asset returns," we will take a much deeper dive into this matrix. You'll learn how to interpret its structure, understand the difference between covariance and the more intuitive concept of correlation, and explore what these measures tell us about portfolio diversification.