Fama-MacBeth Regression
Fit a Fama-MacBeth (1973) two-pass cross-sectional regression: asset_tickers is treated as the cross-section of test assets, factor_tickers as the risk factor(s) whose risk premia are estimated – the standard procedure for testing whether a proposed risk factor is actually priced.
The method runs in two passes:
- Time-series pass (per asset): regress each asset’s returns on the factor(s) to estimate its factor loadings (betas).
- Cross-sectional pass (per period): regress that period’s returns of all assets on those betas, which gives one estimate of each factor’s risk premium per period.
The formula is as follows:
\[\text{First pass:}\;\; \text{R\_i},\;\; t = \alpha_{i} + \text{beta\_i'} \cdot F_{t} + e_{i},\;\; \text{t},\;\; \text{for each asset i},\;\; \text{over the full sample}\] \[\text{Second pass:}\;\; \text{R\_i},\;\; t = \gamma_{0},\;\; t + \text{gamma\_t'} \cdot \beta_{i} + u_{i},\;\; \text{t},\;\; \text{for each period t (gamma\_0,t only with add\_constant=True)}\] \[\text{Risk premium:}\;\; \gamma_{hat} = \text{mean}_{t}(\gamma_{t})\] \[\text{Standard error:}\;\; \operatorname{SE}(\gamma_{hat}) = \text{std}_{t}(\gamma_{t}) / \sqrt{T},\;\; \text{with ddof=1}\] \[t = \gamma_{hat} / \operatorname{SE}(\gamma_{hat}),\;\; \text{with p-values from a Student-t distribution with T - 1 degrees of freedom}\]- R are the asset tickers’
column(Return by default) and F the factor tickers’column(the Benchmark by default)
The risk premium is the average of the per-period estimates, and its standard error comes from how much those estimates vary over time. That is the method’s key contribution: it accounts for correlation between the assets’ residuals without having to model it. A significant positive premium means investors are compensated for exposure to the factor. This uses a single full-sample set of betas (the textbook variant), not the original rolling-window betas.
The first-pass betas are estimates, which makes the second-pass standard errors too small; the Shanken (1992) correction, not applied here, widens them. The returns are not in excess of the risk-free rate, so the intercept is the return of a zero-beta asset, which should be close to the risk-free rate if the factors price the assets.
For more information about the method, see the following papers:
- Fama, E.F. & MacBeth, J.D. (1973). “Risk, Return, and Equilibrium: Empirical Tests.” Journal of Political Economy, 81(3), 607-636.
- Shanken, J. (1992). “On the Estimation of Beta-Pricing Models.” Review of Financial Studies, 5(1), 1-33.
- Cochrane, J.H. (2005). “Asset Pricing.” Princeton University Press, Chapter 12.
Also known as: two-pass regression, Fama-MacBeth procedure.
Fama-MacBeth Regression in Python or via the MCP
Install the Finance Toolkit with:
pip install financetoolkit -U
Then call get_fama_macbeth_regression as shown below.
from financetoolkit import Toolkit
toolkit = Toolkit(
["AAPL", "MSFT", "JPM", "XOM", "JNJ", "PG", "KO", "WMT", "CAT", "NEE"],
api_key="FINANCIAL_MODELING_PREP_KEY",
start_date="2021-01-01",
end_date="2025-12-31",
)
# The Benchmark (the default factor) is the single risk factor and the ten
# stocks are the test assets.
toolkit.econometrics.get_fama_macbeth_regression(period="monthly")
Or, after setting up the Finance Toolkit MCP, ask:
Was exposure to the S&P 500 rewarded across Apple, Microsoft, JPMorgan Chase, ExxonMobil, Johnson & Johnson, Procter & Gamble, Coca-Cola, Walmart, Caterpillar and NextEra Energy from 2021 to 2025, according to a Fama-MacBeth regression on monthly returns?
Which returns:
| Risk Premium | Std. Error | t-Statistic | P-Value | |
|---|---|---|---|---|
| Intercept | 0.0066 | 0.0058 | 1.1505 | 0.2547 |
| Benchmark | 0.0101 | 0.008 | 1.2668 | 0.2103 |
Using Your Own Data
get_fama_macbeth_regression runs on any data, not only prices: ratios, economic indicators, yield curves or your own dataset. Pass a DataFrame, a Series or a dictionary of them to Econometrics as data, with the dates in the rows or the columns, and set changes to how its changes are calculated: "percentage", "log", "difference" for rates and ratios, or None when the data are changes already. For example, for the monthly unemployment rates of the United States and Germany:
from financetoolkit import Economics, Econometrics
economics = Economics(start_date="2000-01-01")
unemployment = economics.get_unemployment_rate(
countries=["United States", "Germany"], period="monthly"
)
econometrics = Econometrics(data=unemployment, changes="difference")
econometrics.get_fama_macbeth_regression()
Parameters
get_fama_macbeth_regression accepts the following parameters.
| Parameter | Type | Description | Example |
|---|---|---|---|
factor_tickers | str | list[str] | None | The ticker(s) whose returns are used as the risk factor(s) (e.g. “Benchmark” for a single-factor/CAPM-style test). Defaults to None, meaning ["Benchmark"]. | "Benchmark" |
asset_tickers | str | list[str] | None | The ticker(s) forming the cross-section of test assets. Defaults to None, meaning every Toolkit ticker (including “Benchmark”) not already used as a factor. | ["AAPL", "MSFT", "GOOGL"] |
period | str | The data frequency (daily, weekly, monthly, quarterly, or yearly). Defaults to “daily”. | "weekly" |
column | str | The historical data column to regress on. Defaults to “Return”. | "Return" |
add_constant | bool | Whether to include an intercept in the second-pass cross-sectional regression. Defaults to True. | False |
rounding | int | None | The number of decimals to round the results to. Defaults to None. | 2 |