Beta and alpha by regression
The data are case study 3.5.5's 60 invented months, rebuilt from the same seed. They are the index's monthly return , which that case study used as the future's, and the stock's return . Both are rounded to 0.01%.
They were built with a beta of 1.2. Each month the stock returned 0.3%, plus 1.2 times the index, plus a move of its own of typical size 3.5%. Real data never reveals its beta; here every estimate can be checked against 1.2.
The capital asset pricing model (lesson 4.6.3) is about returns in excess of the risk-free rate. Take = 3% a year, Level 4's rate, which is 0.25% a month.
Rule: Beta and alpha by regression
numbers the months. The slope is lesson 4.6.3's beta and the intercept the stock's alpha against the index, per month. The error is the stock's own move, its specific risk.
Least squares (lesson 5.10.1) gives , exactly case study 3.5.5's hedge ratio. Subtracting shifts both series by a constant and leaves every covariance alone, so the slope does not change. is case study 3.5.5's , the share of the stock's variance that the index explains.
The intercept is a month, or a year. The residual standard deviation is 3.31% a month, against the true 3.5%: the stock's specific volatility.
The true alpha is not 0.3%. In excess returns the stock is , so a month, 4.2% a year. The estimate even has the wrong sign.
Try it
The first lines rebuild case study 3.5.5's data from its seed and form the excess returns.
lstsq fits the line with a column of ones (lesson 5.10.2). The cell prints and , and , and case study 3.5.5's hedge ratio from the raw returns.
How precise are beta and alpha?
Lesson 5.10.3 gives each estimate a standard error, , with degrees of freedom. The t quantile is .
| per month | estimate | standard error | 95% interval | t for 0 | p |
|---|---|---|---|---|---|
| alpha | 0.436% | to 0.734% | 0.752 | ||
| beta | 1.1518 | 0.0929 | 0.9658 to 1.3379 | 12.3927 |
Beta is far from 0. A t statistic of 12.39 leaves no doubt that the stock moves with the index. The interval covers the true 1.2.
Is beta above 1? The test of gives and , and the interval includes 1. Five years of data cannot show that the stock amplifies the index, although its true beta is 1.2.
Alpha cannot be seen. Its t statistic is , with . In a year the interval runs from to 8.81%: it covers 0, and it covers the true 4.2%.
Here the market's mean excess return is small next to its spread, so is close to . An alpha of 0.35% a month with specific risk of 3.5% reaches only when : 400 months, or 33.3 years. Case study 5.8.5 meets the same problem with a Sharpe ratio.
Try it
The cell computes the standard errors, the t quantile, the intervals, and the tests of 0 for both coefficients and of 1 for beta.
The last lines give alpha's interval in a year, 12 times the monthly one, and beside its standard error. Then the months an alpha of 0.35% needs for .
What the uncertainty costs the hedge
The interval for beta runs from 0.9658 to 1.3379: from $9.66 million to $13.38 million of futures on $10 million of stock. That looks wide, but the hedged risk hardly changes across it.
On the same 60 months, hedging at either end of the interval leaves 11.74% a year. The estimate leaves 11.36%, and the true 1.2 leaves 11.38%.
Case study 3.5.5's parabola of variance against the hedge ratio is flat near its lowest point. So an error of two standard errors in beta costs less than half a point of volatility.
Try it
The loop computes the annual volatility of the stock hedged at each ratio, on case study 3.5.5's 60 months. The ratios are the two ends of the interval, the estimate and the truth; the middle column is the futures sold on $10 million of stock.
Rolling estimates
Case study 3.5.5 estimated the hedge ratio on every run of 24 months and found values from 0.9518 to 1.3486. It warned that the ratio drifts. In these data the true beta was 1.2 in every month, so the whole of that drift is sampling noise.
Regression says how much noise to expect. Each 24-month window has 22 degrees of freedom, and its beta has a standard error between 0.1102 and 0.1797. With , each 95% interval reaches 0.23 to 0.37 either side of its estimate, and 36 of the 37 cover 1.2.
Rolling 24-month betas with their 95% intervals. The beta that made the data never changed, yet the estimates wander by about 0.4: noise of the size the band shows.
How often does a constant beta produce a range this wide? In 2,000 simulated five-year histories with beta 1.2 throughout, the 37 rolling betas span a median of 0.3942. In 49.25% of the histories they span at least these data's 0.3969.
Longer windows wobble less. Here 36-month windows run from 1.076 to 1.1875, with standard errors from 0.0993 to 0.1263. They would also react more slowly to a real change in beta: the window length trades noise against speed.
Try it
The first lines compute the rolling betas and standard errors for windows of 24 and 36 months. For each length they count the intervals that cover 1.2 and print how far the narrowest and widest reach either side.
The simulation uses sliding_window_view, which turns each history into its 37 overlapping windows at once. Each slope is (lesson 5.10.1).
Two factors
An invented airline's stock moves with the market and against the price of oil, which it burns. The data use the same 60 months of the index, plus a seeded oil future's monthly return . Oil has a volatility of 7.71% a month and a correlation of 0.2456 with the index.
The airline was built as 0.2% plus 1.1 times the index, minus 0.35 times oil, plus a move of its own of typical size 4%. So its true market beta is 1.1, and its true oil beta is .
Rule: A two-factor regression
is the market beta with oil held fixed, and the oil beta with the market held fixed (lesson 5.10.2). The oil future's return needs no subtracted: a future costs nothing to enter, as case study 3.5.5 assumed.
| fit | market beta | oil beta | |
|---|---|---|---|
| market alone | 0.9627 (se 0.1439) | 0.4355 | |
| market and oil | 1.0994 (se 0.1303) | (se 0.0783) | 0.5724 |
The oil beta has , with a p-value of 0.0001: oil matters, and its interval, to , covers the true . The market beta's interval with oil in the model runs from 0.8384 to 1.3605.
Leaving oil out lowers the market beta by lesson 5.10.2's rule: , where 0.4087 is the slope of oil on the index. Oil tends to rise with the market and hurts the airline. So on its own, the market seems to move the airline less than it does.
Which beta hedges?
It depends on what the desk trades. With index futures alone, the best ratio is the one-factor slope, 0.9627, the of case study 3.5.5. It cuts the airline's 6.76% a month to 5.08%; selling 1.0994 instead would leave 5.12%.
With oil futures too, case study 3.5.5's several-hedge equations are this regression's normal equations. Sell $1.0994 of index futures and buy $0.3346 of oil futures per dollar of airline, and 4.42% is left: 57.24% of the variance removed, against 43.55% with the index alone.
Try it
The cell builds the oil returns and the airline from a second seed, on the same index months. fit returns the coefficients, their standard errors and .
Then the omitted-variable check, and the monthly volatility left by each hedge.
Try it
The first lines fit the raw returns and check the gap between the two intercepts. The last line refits everything in percent.
Beta, alpha and rolling betas by regression
Write two functions with NumPy, as in this case study.
capm_regression(stock, market, rf)returns(alpha, beta, se_alpha, se_beta), per period. It regresses the stock's excess returns on the market's;rfis one number or one rate per period. The standard errors are those of lesson 5.10.3, with degrees of freedom.rolling_beta(stock, market, rf, window)returns two arrays,(betas, ses): beta and its standard error on each run ofwindowconsecutive periods, in time order. There arewindowof them.
Raise ValueError when the series have different lengths or fewer than three periods, or when the market's excess return is constant. Raise it too when window is not a whole number from 3 to .
The tests use case study 3.5.5's 60 months: the beta, alpha and standard errors above, and the rolling 24-month betas. They also try , a rate per month, percent units and each bad input.
See the tests
import numpy as np
def _data():
rng = np.random.default_rng(355)
index = rng.normal(0.006, 0.045, 60)
stock = 0.003 + 1.2 * index + rng.normal(0, 0.035, 60)
return np.round(stock, 4), np.round(index, 4)
def _raises(f, *args, what=""):
try:
f(*args)
except ValueError:
return
raise AssertionError(f"expected ValueError: {what}")
def test_beta():
"""The 60 months give beta 1.1518 with standard error 0.0929"""
stock, market = _data()
alpha, beta, se_a, se_b = capm_regression(stock, market, 0.0025)
expect_close(beta, np.cov(stock, market)[0, 1] / np.var(market, ddof=1), what="beta")
expect_close(se_b, 0.092944, rel=1e-4, what="se of beta")
def test_alpha():
"""The 60 months give alpha −0.1384% a month with standard error 0.436%"""
stock, market = _data()
alpha, beta, se_a, se_b = capm_regression(stock, market, 0.0025)
expect_close(alpha, -0.001384, rel=1e-3, what="alpha")
expect_close(se_a, 0.00436, rel=1e-3, what="se of alpha")
def test_rf_moves_alpha_not_beta():
"""With rf = 0 beta is unchanged and alpha moves by rf(1 − beta)"""
stock, market = _data()
a0, b0, _, se0 = capm_regression(stock, market, 0.0)
a1, b1, _, se1 = capm_regression(stock, market, 0.0025)
expect_close(b0, b1, what="beta with rf = 0")
expect_close(se0, se1, what="se of beta with rf = 0")
expect_close(a1 - a0, -0.0025 * (1 - b1), what="change in alpha")
def test_rf_per_month():
"""A risk-free rate given month by month gives the same answers as one number"""
stock, market = _data()
expect_close(capm_regression(stock, market, np.full(60, 0.0025)), capm_regression(stock, market, 0.0025), what="rf as an array")
def test_percent_units():
"""Returns in percent give the same beta and an alpha 100 times larger"""
stock, market = _data()
a, b, _, _ = capm_regression(stock, market, 0.0025)
ap, bp, _, _ = capm_regression(100 * stock, 100 * market, 0.25)
expect_close(bp, b, what="beta in percent")
expect_close(ap, 100 * a, what="alpha in percent")
def test_rolling():
"""Rolling 24-month betas: 37 of them, from 0.9518 to 1.3486, with standard errors from 0.1102 to 0.1797"""
stock, market = _data()
betas, ses = rolling_beta(stock, market, 0.0025, 24)
expect_equal(len(betas), 37, what="number of 24-month windows in 60 months")
expect_close([betas.min(), betas.max()], [0.951770, 1.348637], rel=1e-5, what="smallest and largest beta")
expect_close([ses.min(), ses.max()], [0.110216, 0.179719], rel=1e-5, what="smallest and largest standard error")
def test_full_window():
"""A window of all 60 months gives one beta, the full-sample one"""
stock, market = _data()
betas, ses = rolling_beta(stock, market, 0.0025, 60)
expect_close(betas, [capm_regression(stock, market, 0.0025)[1]], what="betas with window 60")
def test_bad_inputs():
"""Different lengths, a constant market, a window below 3 or above the data raise ValueError"""
stock, market = _data()
_raises(capm_regression, stock, market[:59], 0.0025, what="60 and 59 returns")
_raises(capm_regression, stock, np.full(60, 0.01), 0.0025, what="a constant market")
_raises(capm_regression, stock[:2], market[:2], 0.0025, what="two periods")
_raises(rolling_beta, stock, market, 0.0025, 2, what="window 2")
_raises(rolling_beta, stock, market, 0.0025, 61, what="window 61 with 60 months")
_raises(rolling_beta, stock, market[:50], 0.0025, 24, what="different lengths")
Summary card
- Beta and alpha come from regressing excess returns, . For case study 3.5.5's 60 months, , its hedge ratio, with .
- Beta's standard error is 0.0929. Its 95% interval, 0.9658 to 1.3379, covers the true 1.2 and also 1: five years cannot show that beta is above 1.
- Alpha is a month with standard error 0.436%; its interval covers 0 and the true 0.35%. Detecting 0.35% a month against 3.5% of specific risk would take about 33 years.
- Rolling 24-month betas wander from 0.9518 to 1.3486 though the true beta never moves; 36 of their 37 intervals cover 1.2. Compare rolling moves with their standard errors.
- A second factor changes the market beta when the factors are correlated: the airline's 0.9627 alone becomes 1.0994 beside oil's . The one-factor slope hedges with the index alone; the two-factor slopes hedge with both.
- Subtract for alpha, keep units consistent, and remember that the standard errors assume constant variance and uncorrelated errors.