CAPM Beta in Python: Regression on Market Returns
- CAPM
- Capital Asset Pricing Model
- beta
- covariance
- variance
- linear regression
- np.polyfit
- ddof
CAPM Beta - Covariance, Variance and a Regression Line
Scene: the equity research pod, Thursday afternoon. Your DCF model for a cyclical industrial is finished except for one cell: the cost of equity. The senior analyst reads over your shoulder and asks the question every model review asks: "Which beta, and where did it come from?" Answering "the stock is about one and a half times as volatile as the index" gets the model sent back. In this lesson you estimate beta the way a reviewer expects, prove it with a regression, and turn it into the return shareholders should demand.
What it is: Beta (β) measures how far a stock's return moves, on average, for each 1% move in the market. A beta of 1.2 means a +1% market month has come with about +1.2% on the stock. The Capital Asset Pricing Model (CAPM) then says investors are paid only for this market risk, because the rest can be diversified away: expected return = risk-free rate + β × market risk premium.
The two formulas
β = Cov(r_stock, r_market) / Var(r_market)
E(r) = r_f + β × (E(r_market) - r_f)
🧠 The mental model: beta is a slope, not a size
Picture a scatter plot with one dot per month: the market's return on the x-axis, the stock's on the y-axis. Draw the best straight line through the cloud. Beta is the slope of that line.
- Volatility measures how tall the stock's cloud is.
- Beta measures how steeply the cloud tilts as the market moves.
A stock can be very volatile and still have a low beta, if most of its moves come from its own news (a trial result, a lawsuit, a takeover rumour) rather than from the market. That is why "stock volatility ÷ market volatility" is the classic wrong answer: it treats every wiggle as market-driven. The correct link runs through correlation:
β = ρ × (σ_stock / σ_market)
Correlation ρ is never above 1, so relative volatility is a ceiling on the size of beta, and it equals beta only in the special case where the correlation is exactly 1.
Two routes to one number
pandas computes beta from two return Series in one line (here named r_stock and r_mkt):
beta = r_stock.cov(r_mkt, ddof=1) / r_mkt.var(ddof=1)
NumPy fits the regression line directly. Degree 1 means a straight line, and the coefficients come back highest power first, so the slope arrives before the intercept:
slope, intercept = np.polyfit(r_mkt, r_stock, 1) # x = market, y = stock
The least-squares slope is Σ(x - mean x)(y - mean y) / Σ(x - mean x)²: covariance over variance with the n - 1 cancelled out. So the two routes agree to floating-point precision, provided the top and the bottom of your ratio use the same convention.
⚠️ The sample convention: ddof=1 on top and bottom
Sample statistics divide by n - 1 (ddof=1, "delta degrees of freedom"); population statistics divide by n (ddof=0). pandas .cov() and .var() both default to ddof=1. NumPy is not consistent: np.cov divides by n - 1 but np.var by n. Pair a pandas covariance with np.var and your beta is silently inflated by n / (n - 1), which is 12/11 on a year of monthly data. This lesson uses ddof=1 for both halves and writes it out, so a reviewer can see the choice.
From beta to a required return
CAPM turns beta into the return shareholders should demand: the cost of equity that feeds WACC in a DCF. This exercise uses the desk's stated assumptions, a risk-free rate of 3% a year and a market risk premium of 5% a year:
E(r) = 3% + β × 5%
A beta of exactly 1 is priced at the market's expected 8%; above 1 demands more, below 1 less. Beta is a pure number (percent per percent), so an estimate from monthly returns combines directly with annual rates. Twelve points keep this example readable; a real estimate uses a longer window, and the number moves with the window and the return frequency you choose.
Your Task
The starter holds 12 months of returns for a cyclical stock and the market. It prints beta, a regression cross-check and the CAPM expected return. Everything works except the 👉 line: it divides the stock's standard deviation by the market's, which is relative volatility, and the cross-check duly prints MISMATCH.
Predict before you run: the starter reports a "beta" of 1.46 and an expected return of 10.30%. Once beta is covariance over variance, will it come out higher, lower or the same, and which way will the expected return move? (Use β = ρ × volatility ratio.) Then fix the line and check that the cross-check prints MATCH.
Related terms in the glossary