edgekit

edgekit.factors

Regression & factor models — the linear-algebra layer that answers “is this return stream alpha, or just beta to something I already own?”. A from-scratch OLS core, CAPM, multi-factor exposures, rolling beta, and Newey-West standard errors, all in numpy.

What's inside. Everything sits on one engine: ols, a plain least-squares fit that returns coefficients with their t-stats and R². capm is the single-factor case that isolates alpha against a market; factor_exposures generalises it to a factor panel; rolling_beta tracks a time-varying loading; and newey_west_se gives you heteroskedasticity- and autocorrelation-robust standard errors when residuals are not i.i.d. (they rarely are).

t-stats are asymptotic, not exact
These are numpy least-squares fits — the reported tstat values assume large samples and (except via newey_west_se) i.i.d. residuals. Treat |t| > 2 as the rough significance bar, and reach for Newey-West standard errors whenever returns are overlapping or autocorrelated.

Core regression#

ols#

Ordinary least squares — regress y on X and get back coefficients, their standard errors and t-stats, fit quality, and the residuals. The primitive every other function in this module is built on.

ols(y, X, add_const=True) -> dict
ParamTypeDefaultMeaning
yarray (T,)The dependent variable (e.g. asset returns).
Xarray (T, k)The regressor matrix (one column per factor).
add_constboolTruePrepend an intercept column (the alpha term).

Returns: a dict with keys "beta" (coefficient vector, intercept first if add_const), "se" (standard errors), "tstat" (t-statistics), "r2", "adj_r2", "resid" (residuals), "fitted" (fitted values), and "n" (sample size).

import edgekit as ek
fit = ek.factors.ols(y, X)
fit["beta"], fit["tstat"], fit["adj_r2"]

Factor models#

capm#

The single-factor CAPM regression — regress an asset's excess return on the market's and read off alpha and beta with their significance. The first question to ask any new edge: does it have a positive, significant alpha once you strip out market exposure?

capm(asset_returns, market_returns, rf=0.0, periods_per_year=252.0) -> dict
ParamTypeDefaultMeaning
asset_returnsarray (T,)The asset / strategy return series.
market_returnsarray (T,)The market benchmark return series.
rffloat0.0Per-period risk-free rate subtracted from both.
periods_per_yearfloat252.0Annualisation factor for alpha_annual.

Returns: a dict with keys "alpha" (per-period intercept), "beta" (market loading), "alpha_t" and "beta_t" (their t-stats), "r2", and "alpha_annual" (alpha annualised by periods_per_year).

c = ek.factors.capm(strat_rets, mkt_rets, rf=0.0, periods_per_year=365)
c["alpha_annual"], c["alpha_t"], c["beta"]   # want alpha_t > 2 and beta near 0

factor_exposures#

Multi-factor regression — regress an asset on a panel of factors (market, size, value, momentum, …) to get its loading on each plus the residual alpha. The generalisation of capm to more than one systematic driver.

factor_exposures(asset_returns, factors) -> dict
ParamTypeDefaultMeaning
asset_returnsarray (T,)The asset / strategy return series.
factorsarray (T, k) / DataFrameThe factor-return panel (one column per factor).

Returns: a dict with keys "betas" (loading on each factor), "tstats" (their t-stats), "alpha" (the intercept), "alpha_t" (its t-stat), and "r2".

fx = ek.factors.factor_exposures(strat_rets, factor_panel)
fx["betas"], fx["alpha"], fx["alpha_t"]

Rolling & robust#

rolling_beta#

Beta of y on x estimated in a trailing window— a time-varying loading that shows when an exposure drifts (a strategy's market beta creeping up in a trend, say). Leading positions are NaN until the window fills.

rolling_beta(y, x, window) -> np.ndarray
ParamTypeDefaultMeaning
yarray (T,)Dependent series (e.g. strategy returns).
xarray (T,)Explanatory series (e.g. market returns).
windowintRolling lookback in bars.

Returns: a numpy array of rolling betas aligned to the inputs (leading positions NaN).

b = ek.factors.rolling_beta(strat_rets, mkt_rets, window=60)

newey_west_se#

Newey-West HAC standard errors — heteroskedasticity- and autocorrelation-consistent errors for a fitted regression. Use them whenever residuals are autocorrelated (overlapping returns, persistent series); the plain OLS standard errors understate uncertainty there and make weak edges look significant.

newey_west_se(X, resid, lags) -> np.ndarray
ParamTypeDefaultMeaning
Xarray (T, k)The regressor matrix used in the fit (include the constant column).
residarray (T,)The regression residuals (e.g. ols()["resid"]).
lagsintBandwidth: number of autocorrelation lags to correct for.

Returns: a numpy array of HAC standard errors, one per coefficient in X.

fit = ek.factors.ols(y, X)
se_hac = ek.factors.newey_west_se(fit["X"] if "X" in fit else X, fit["resid"], lags=5)
t_hac = fit["beta"] / se_hac   # robust t-stats

See also#