FM Factor Models
Decomposing returns into what's common and what's yours
Learning Objectives
- •Identify and construct common factors for FX and futures markets
- •Run factor attribution on strategy returns
- •Distinguish alpha from factor exposure
- •Evaluate whether apparent alpha is compensated risk
Explain Like I'm 5
Factor models break down your returns into pieces: how much came from broad market moves, how much from known risk premiums (momentum, carry, etc.), and how much is genuinely unexplained — your alpha.
Think of It This Way
Imagine you run faster on a windy day. A factor model separates your true speed from the tailwind. If 80% of your "performance" was the wind, you need to know that before entering a race on a calm day.
1Common Factors in FX and Futures
2Running Factor Attribution
3Factor Returns Over Time
4Alpha or Factor? The Existential Crisis
Key Formulas
Factor Model
Return decomposition into factor exposures (beta), unexplained alpha, and noise (epsilon)
Hands-On Code
Factor Attribution Analysis
import numpy as np
from sklearn.linear_model import LinearRegression
def factor_attribution(strategy_returns, factor_returns):
"""
Run factor attribution regression on strategy returns.
Parameters:
strategy_returns: array of daily strategy returns
factor_returns: dict of {'factor_name': returns_array}
Returns:
dict with alpha, betas, t-stats, R-squared
"""
factor_names = list(factor_returns.keys())
X = np.column_stack([factor_returns[f] for f in factor_names])
y = strategy_returns
reg = LinearRegression().fit(X, y)
y_pred = reg.predict(X)
residuals = y - y_pred
n, k = X.shape
dof = n - k - 1
mse = np.sum(residuals**2) / dof
X_with_const = np.column_stack([np.ones(n), X])
var_beta = mse * np.linalg.inv(X_with_const.T @ X_with_const)
se = np.sqrt(np.diag(var_beta))
alpha_annual = reg.intercept_ * 252
alpha_tstat = reg.intercept_ / se[0]
ss_res = np.sum(residuals**2)
ss_tot = np.sum((y - np.mean(y))**2)
r_squared = 1 - ss_res / ss_tot
results = {
'alpha_annual_pct': round(alpha_annual * 100, 2),
'alpha_tstat': round(alpha_tstat, 2),
'alpha_significant': abs(alpha_tstat) > 2.0,
'r_squared': round(r_squared, 3),
'factors': {}
}
for i, name in enumerate(factor_names):
results['factors'][name] = {
'beta': round(reg.coef_[i], 4),
't_stat': round(reg.coef_[i] / se[i+1], 2),
}
return resultsRuns OLS regression of strategy returns against factor returns to decompose performance into alpha and factor contributions, with t-statistics for significance testing.
Knowledge Check
Q1.If your strategy has an R-squared of 0.85 against a momentum-carry factor model, what does this imply?
Q2.A strategy alpha with a t-statistic of 1.3 over 5 years should be interpreted as:
Assignment
Construct simple momentum (12-month return) and carry (interest rate differential) factors for the G10 FX universe. Run factor attribution on a simple moving-average crossover strategy. What percentage of the strategy's returns are explained by momentum vs. carry? Is the residual alpha statistically significant?