Calculating Returns and Volatility

· 5 min read · Updated March 7, 2026 · intermediate
finance stocks returns volatility data-analysis

In this tutorial, you’ll learn how to calculate investment returns and volatility from stock price data. These are foundational concepts for any quantitative finance work. By the end, you’ll be able to compute simple returns, log returns, annualized metrics, and understand what volatility tells you about risk.

This tutorial builds on the previous two in the series. We assume you already know how to fetch stock data with yfinance. If you haven’t done so, start with the “Fetching Stock Data with yfinance” tutorial.

Simple Returns

The simplest way to measure returns is the percentage change between consecutive prices. If a stock goes from $100 to $110, that’s a 10% return. This is called a simple return, and it’s the most intuitive way to think about profit or loss.

import yfinance as yf
import pandas as pd
import numpy as np

# Fetch Apple stock data
aapl = yf.download("AAPL", start="2024-01-01", end="2024-12-31")
prices = aapl["Close"]

# Calculate simple returns using pct_change()
simple_returns = prices.pct_change()
print(simple_returns.head(10))

The output shows daily percentage changes:

Date
2024-01-02         NaN
2024-01-03    0.01832
2024-01-04   -0.00456
2024-01-05    0.01287
2024-01-08    0.00921
...

The first value is NaN because there’s no previous day to compare. You can drop it with .dropna().

The pct_change() function calculates the percentage change between each consecutive value. It’s equivalent to (price_today - price_yesterday) / price_yesterday.

Log Returns

Log returns are the natural logarithm of the price ratio. They’re mathematically convenient for aggregating returns across multiple periods.

# Calculate log returns
log_returns = np.log(prices / prices.shift(1))
print(log_returns.head(10))

The output:

Date
2024-01-02         NaN
2024-01-03    0.018148
2024-01-04   -0.004570
2024-01-05    0.012794
2024-01-08    0.009175
...

For small returns, log returns and simple returns are nearly identical. The difference becomes noticeable for larger moves. For example, a 50% gain has a log return of about 0.405 (ln(1.5)), not 0.50.

Why Use Log Returns?

Log returns have two key advantages over simple returns. First, they’re additive across time periods. The log return for a year is the sum of daily log returns. Second, they’re symmetric: a 50% gain followed by a 50% loss doesn’t return you to break-even (you lose 25%), and log returns reflect this correctly.

# Demonstrate additivity of log returns
daily_log = log_returns.dropna()
total_log_return = daily_log.sum()
print(f"Total log return: {total_log_return:.4f}")

# Convert back to simple return
simple_total = np.exp(total_log_return) - 1
print(f"Total simple return: {simple_total:.4f}")

This additivity property makes log returns essential for multi-period analysis. When you want to know the total return over a year, you simply add up the daily log returns, then convert back to a simple return if needed.

Annualized Returns

To compare returns across different time periods, annualize them. For daily data, multiply the mean daily return by 252 (the approximate number of trading days per year in the US).

# Calculate annualized return
mean_daily_return = simple_returns.dropna().mean()
annualized_return = (1 + mean_daily_return) ** 252 - 1
print(f"Annualized return: {annualized_return:.2%}")

For log returns, simply multiply by 252:

# Annualized return from log returns
mean_daily_log = log_returns.dropna().mean()
annualized_log = mean_daily_log * 252
print(f"Annualized log return: {annualized_log:.2%}")

Both approaches give similar results for small daily returns. The compounding method (1 + r)^252 - 1 accounts for the effect of reinvesting returns throughout the year.

Volatility

Volatility measures how much returns vary. It’s calculated as the standard deviation of returns. Higher volatility means more risk, because the price can move more dramatically in either direction.

# Calculate daily volatility
daily_volatility = simple_returns.dropna().std()
print(f"Daily volatility: {daily_volatility:.4f}")

# Annualize volatility (multiply by sqrt of trading days)
annualized_volatility = daily_volatility * np.sqrt(252)
print(f"Annualized volatility: {annualized_volatility:.2%}")

Typical stock volatility ranges from 15% to 40% annually. Low-volatility stocks like consumer staples or utilities might have 10-15% volatility, while volatile tech stocks or growth companies can exceed 50%. The square root of 252 is approximately 15.87, which is why we multiply daily volatility by about 15.87 to annualize it.

Comparing Stocks

Let’s compare volatility across several stocks to see how risk varies:

# Compare volatility of different stocks
tickers = ["AAPL", "MSFT", "GOOGL", "TSLA", "JNJ"]
data = yf.download(tickers, start="2024-01-01", end="2024-12-31")["Close"]

returns = data.pct_change().dropna()
volatilities = returns.std() * np.sqrt(252)

print("Annualized Volatility:")
print(volatilities.sort_values().round(2))

The output shows JNJ (a healthcare stock) typically has lower volatility than TSLA (an EV company):

JNJ     0.18
MSFT    0.22
AAPL    0.25
GOOGL   0.28
TSLA    0.45

This ranking makes intuitive sense: Johnson & Johnson is a stable healthcare company, while Tesla is known for dramatic price swings. When building a portfolio, understanding each asset’s volatility helps you manage risk.

Rolling Volatility

Volatility isn’t constant over time. It changes based on market conditions, news events, and economic factors. You can calculate rolling volatility to see how it evolves:

# 30-day rolling volatility
rolling_vol = simple_returns.rolling(window=30).std() * np.sqrt(252)
print(rolling_vol.dropna().head(10))

This shows the 30-day trailing volatility, which typically spikes during market stress. During calm periods, volatility might be 12-15%, but during a crisis like the 2020 COVID crash or 2022 rate shock, it can spike to 30% or higher. Traders often use rolling volatility to time their trades or adjust position sizes.

Summary

You now know how to:

  • Calculate simple returns with pct_change()
  • Calculate log returns with np.log()
  • Annualize returns by compounding daily returns
  • Measure volatility with standard deviation
  • Annualize volatility using the square root of time rule
  • Compare volatility across stocks
  • Calculate rolling volatility

These metrics form the foundation for more advanced finance topics like risk management, portfolio optimization, and derivative pricing. Understanding returns and volatility lets you quantify what you’re earning for the risk you’re taking.

Next Steps

Continue to the next tutorial in this series: “Portfolio Analysis in Python”. You’ll learn how to combine multiple assets, calculate portfolio returns, and measure portfolio-level risk using concepts from this tutorial.