1.1 β Returns and Volatility for Backtesting
1.1 β Returns and Volatility for Backtesting#
To evaluate a backtest you need returns (how much the strategy gained or lost) and volatility (how variable those returns are). This lesson defines simple returns, log returns, volatility, and annualization, and shows how they feed into metrics like the Sharpe ratio. This is the statistics foundation for Project 1 β Backtesting Engine.
Simple returns#
The simple return from time to is:
where is price (or portfolio value) at time . In pandas, for a column of prices:
df['returns'] = df['close'].pct_change()
- Total return over a period: compound the one-period returns: , or from equity:
(equity[-1] / equity[0]) - 1. - Mean return (average per period):
returns.mean(). For annualized return (below), youβll scale this by the number of periods per year.
Log returns (optional)#
Log return is . For small returns itβs close to the simple return. Useful for compounding over many periods: the sum of log returns equals the log of the total gross return. In pandas:
import numpy as np
df['log_returns'] = np.log(df['close'] / df['close'].shift(1))
For a first backtest, simple returns are enough; use log returns when you need time aggregation or theoretical properties.
Volatility (standard deviation of returns)#
Volatility here means the standard deviation of returns. It measures how much returns vary around their average.
- Sample volatility (one period, e.g. daily): . In pandas:
returns.std(). - Annualized volatility: If you have daily returns and 252 trading days per year, annualized vol is . In code:
returns.std() * np.sqrt(252).
Use the same period (e.g. daily) for both returns and volatility when annualizing.
Annualized return#
If is the mean daily return:
- Annualized return (compound): (approximate).
- Or from total return over days: .
Keep your definition consistent (e.g. 252 business days per year for US equities).
Sharpe ratio (concept)#
The Sharpe ratio measures risk-adjusted return. Conceptually:
- Risk-free rate: often a short-term rate (e.g. T-bill). For a simple backtest you can use 0.
- Interpretation: Higher Sharpe means more return per unit of volatility. Values often between 0 and 2 for many strategies; negative means the strategy underperformed the risk-free rate.
In code (with risk-free rate = 0 for simplicity):
ann_return = (1 + returns.mean()) ** 252 - 1
ann_vol = returns.std() * np.sqrt(252)
sharpe = ann_return / ann_vol if ann_vol > 0 else 0
Summary for backtesting#
| Concept | Definition | Pandas / formula |
|---|---|---|
| Simple return | df['close'].pct_change() | |
| Total return | From start to end equity | (equity[-1]/equity[0]) - 1 |
| Volatility | Std dev of returns | returns.std() |
| Annualized vol | Scale daily std to year | returns.std() * np.sqrt(252) |
| Sharpe (simple) | Return per unit risk | ann_return / ann_vol (with 0 risk-free) |
Use these to build your backtest report: total return, annualized return, annualized volatility, and Sharpe ratio. For more on probability and interpreting strategy results, see Quant Research β Introduction.
Next steps#
Chapter 2 covers hypothesis testing and p-values, which help you ask whether backtest results could be due to chance. For Project 1, returns and volatility are enough to produce a basic performance report.