4  Factor Catalog

A comprehensive, self-contained reference covering every factor in the canonical research set — including the economic rationale, stylistic guidance, platform implementation details, and empirical performance results for each factor, organized by factor family.

Scope note — factors, not strategies. A factor is a measured, predictive characteristic of an instrument (this chapter); a strategy is a downstream (factor_id, mechanic, params) triple that trades one — see the Strategy Catalog. Several factors below are traded by more than one strategy, and a couple of entries exist only because a downstream strategy needs a distinct evaluation path (funnel-exempt entries, flagged where they occur). This chapter deliberately stops at the factor boundary: mechanic, universe, sizing, and backtest/promotion detail all live in the Strategy Catalog, not here.

4.1 Provenance

  • Theory fields (economic justification, style, anchor citation, hypothesis class, expected sign, source table/column, lifecycle status) are pulled directly from config/factors/*.yaml in SBFoundation as of 2026-08-03.
  • Empirical fields (IC-IR, MCPT) come from the diagnostic run 260526_6ddcd9 (2026-05-26), synthesized in platform/factors.md. That run predates three fixes that materially change these numbers — F-136 (MCPT nightly permutations 100 → 1000, corrected null construction), F-137 (Alphalens forward-return winsorization), F-138 (composite contribution rescaling). Treat every MCPT figure below as provisional pending a rerun.
  • Two columns — naive t-stat and HAC (Newey–West) t-stat — are specified but not yet computed. No sidecar currently produces them; see Method below for exactly how to fill them in. They are marked PENDING throughout rather than guessed.
  • Re-grounding against live config surfaced a correction to the run narrative: bb_lower_20, ma_50d, ma_200d, and vwap_20d were described in the 260526_6ddcd9 synthesis as the four factors with “real” pre-fix MCPT significance — but all four were subsequently deprecated for cause on 2026-07-20 (F-289/LH-25, run 260718_31a51e): they’re raw price-level source columns with a CF-1 look-ahead issue, not a validated edge. bb_lower_20’s entry below reflects this; bb_pct (§13) is its scale-free replacement.
  • Known duplicates — resolved 2026-08-05. beta ↔︎ market_beta_252d and momentum_12_1 ↔︎ momentum_12m_1m are numerically identical to 14 digits per the run synthesis; neither pair has ever had two independent config/factors/*.yaml files (no beta.yaml or momentum_12_1.yaml exists — both are stale legacy ids with no config of their own). beta and momentum_12_1 are retired; market_beta_252d and momentum_12m_1m are the canonical survivors and are the only ones counted in the platform’s family-wise trial count (F-321).

4.2 Method: the pending t-stat columns

Every factor entry below has two PENDING fields. Both are specified the same way, stated once here rather than repeated 24 times:

  • Naive t-stat — the classic Fama–MacBeth / Fama–French second-pass report. Take the daily factor-return series F_t for the factor_id from ops.risk_factor_return (factor-models-and-benchmarks.md §2.2), and compute t_naive = mean(F) / (std(F) / sqrt(T)).
  • HAC (Newey–West) t-stat — regress the same series on a constant only (X = [1]) using the platform’s existing HAC estimator, sbattribution.ols.ols_hac (src/sbattribution/ols.py:79-125) — the identical kernel already used for CAPM/attribution alpha (Bartlett weights, lag L = floor(4·(n/100)^(2/9)), Newey–West 1994). The intercept is the mean factor return; its HAC standard error replaces the naive std(F)/sqrt(T) denominator.
  • Why both: daily factor returns built on slow-moving or overlapping-window characteristics (12-1 momentum, vol-scaling, TSMOM) are serially correlated by construction, so the naive t-stat overstates significance. The gap between the two is itself diagnostic.
  • Neither is a promotion gate. Per research-platform/08-validation-and-anti-overfitting.md, a t-stat — naive or HAC — is exactly the single-test statistic the Harvey–Liu–Zhu multiple-testing critique targets. The platform’s actual promotion gate is IC-IR + MCPT permutation p-value against an honest trial count; these two columns are diagnostic context, not a second gate.
  • Not yet wired: ols_hac today only runs against strategy NAV streams (beta_attribution_service.py), not the per-factor return panel — computing these columns requires a small new script, not a config change.

4.3 1. Market

Compensation for bearing undiversifiable market risk — the one factor everyone must hold; the CAPM premium. Sharpe 1964 [#116], Lintner 1965 [#117].

4.3.1 market_beta_252d

  • Style: beta
  • Source: fact_eod.market_beta_252d_f — trailing 252-day rolling OLS slope vs. SPY
  • Equation: \(\beta_{i,t} = \dfrac{\mathrm{Cov}_{252d}(r_i,\, r_{SPY})}{\mathrm{Var}_{252d}(r_{SPY})}\)
  • Hypothesis class / expected sign: not set in config
  • Lifecycle: experimental — canonical survivor of the beta/market_beta_252d duplicate pair (resolved 2026-08-05); beta is retired
  • IC-IR @21d: not itemized in the run synthesis
  • MCPT (pre-fix): p=1.000 (ceiling), unbiased_ic_ir ≈ −6.8
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: daily; diagnostics “healthy” quadrant (stationary, entropy ≥3)
  • Notes: beta and market_beta_252d are numerically identical to 14 digits; beta was a legacy id with no independent config/factors/beta.yaml and is now retired.

4.4 2. Size

Small caps earn a premium for illiquidity, distress, and limited analyst coverage; partly compensation, partly a limits-to-arbitrage effect. Banz 1981 [#125], Fama–French 1992 [#55].

4.4.1 market_cap

  • Style: size
  • Source: fact_valuation_annual.market_cap_fyear_end_close × shares_outstanding
  • Equation: \(\text{MktCap}_{i,t} = P^{YE}_{i,t} \times \text{SharesOut}_{i,t}\)
  • Hypothesis class / expected sign: not set in config
  • Lifecycle: experimental
  • IC-IR @21d: ≈ −0.5 (inverted — part of the low-vol/size cluster; trade short)
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual — short-T caveat applies (ADF/entropy diagnostics have little power at n≈12)
  • Notes: —

4.5 3. Value

Cheap (high E/P, B/M, FCF-yield) stocks out-earn expensive ones — risk premium for distressed/low-growth firms and mispricing from extrapolation. Fama–French 1992/1993 [#55][#44].

4.5.1 value_composite

  • Style: value
  • Source: fact_fundamental_annual.value_composite_f — multi-ratio value z-score blend (earnings, book, sales, cash-flow yields)
  • Equation: \(\text{ValueComposite}_{i,t} = \dfrac{1}{4}\displaystyle\sum_{k \,\in\, \{EY,\; B/M,\; S/P,\; FCF/P\}} z\!\left(x_{k,i,t}\right)\) — config specifies the four component yields and calls it a “blend”; the equal weighting shown here is the simplest reading and is not independently confirmed by a documented weight vector.
  • Hypothesis class / expected sign: risk_premium, positive
  • Lifecycle: experimental, promotion candidate
  • IC-IR @21d: +0.75 — highest in the run
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual
  • Notes: sign-flips at h=1 (don’t trade intraday) — classic value-investor pattern, right direction only at a month+ horizon

4.5.2 earnings_yield

  • Style: value
  • Source: fact_valuation_annual.earnings_yield_feps_diluted / year_end_close
  • Equation: \(EY_{i,t} = \dfrac{EPS^{diluted}_{i,t}}{P^{YE}_{i,t}}\) (inverse of pe_ratio)
  • Hypothesis class / expected sign: risk_premium, positive
  • Lifecycle: experimental
  • IC-IR @21d: sign-flip — negative at h=1, positive at h≥21
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual
  • Notes: —

4.5.3 pe_ratio

  • Style: value
  • Source: fact_valuation_annual.pe_ratio_fyear_end_close / eps_diluted
  • Equation: \(PE_{i,t} = \dfrac{P^{YE}_{i,t}}{EPS^{diluted}_{i,t}}\)
  • Hypothesis class / expected sign: risk_premium, negative
  • Lifecycle: experimental
  • IC-IR @21d: −0.5 to −0.7 (inverted group, with volatility_*/market_cap/amihud)
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual
  • Notes: —

4.5.4 pb_ratio

  • Style: value
  • Source: fact_valuation_annual.pb_ratio_fmarket_cap / total_stockholders_equity
  • Equation: \(PB_{i,t} = \dfrac{P^{YE}_{i,t} \times \overline{Shs}^{diluted}_{i,t}}{\text{StockholdersEquity}_{i,t}}\)
  • Hypothesis class / expected sign: risk_premium, negative
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): raw Alphalens top-minus-bottom spread +37.40 at h=21 — a pre-F-137 artifact (un-winsorized cross-section outlier dominance), not a real spread; bounded post-fix
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual
  • Notes: —

4.5.5 ps_ratio

  • Style: value
  • Source: fact_valuation_annual.ps_ratio_fmarket_cap / revenue
  • Equation: \(PS_{i,t} = \dfrac{P^{YE}_{i,t} \times \overline{Shs}^{diluted}_{i,t}}{\text{Revenue}_{i,t}}\)
  • Hypothesis class / expected sign: risk_premium, negative
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): raw Alphalens spread +44.78 at h=21 — pre-F-137 artifact, bounded post-fix
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual
  • Notes: —

4.5.6 pfcf_ratio

  • Style: value
  • Source: fact_valuation_annual.pfcf_ratio_fmarket_cap / free_cash_flow
  • Equation: \(PFCF_{i,t} = \dfrac{P^{YE}_{i,t} \times \overline{Shs}^{diluted}_{i,t}}{\text{FCF}_{i,t}}\) (inverse of fcf_yield)
  • Hypothesis class / expected sign: risk_premium, negative
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): raw Alphalens spread −36.78 at h=21 — pre-F-137 artifact, bounded post-fix
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual
  • Notes: —

4.6 4. Momentum

Past 12-1 winners keep winning — under-reaction to news and delayed information diffusion (behavioral). Jegadeesh–Titman 1993 [#49], Carhart 1997 [#57].

4.6.1 momentum_12m_1m

  • Style: momentum
  • Source: fact_eod.momentum_12_1_f
  • Equation: \(\text{Mom}_{i,t} = \dfrac{P_{i,t-21}}{P_{i,t-252}} - 1\) (12-month cumulative return, skipping the most recent month)
  • Hypothesis class / expected sign: behavioral, positive
  • Lifecycle: experimental — canonical survivor of the momentum_12_1/ momentum_12m_1m duplicate pair (resolved 2026-08-05); momentum_12_1 is retired
  • IC-IR @21d: +0.586
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: daily
  • Notes: momentum_12_1 and momentum_12m_1m are numerically identical to 14 digits; momentum_12_1 was a legacy id (still used as a raw bundle/feature column name in sbbacktest, but with no independent config/factors/momentum_12_1.yaml) and is now retired as a factor id.

4.6.2 momentum_12_1_vol_scaled

  • Style: momentum
  • Source: fact_eod.momentum_12_1_vol_scaled_f — the 12-1 trend divided by trailing realized volatility (worked example: factor-models-and-benchmarks.md §6)
  • Equation: \(\text{Mom}^{vol}_{i,t} = \dfrac{\text{Mom}_{i,t}}{\sigma^{126d}_{i,t}\sqrt{252}}\), where \(\text{Mom}_{i,t}\) is the §4 12-1 return above and \(\sigma^{126d}_{i,t}\) is the trailing 126-day daily-return standard deviation
  • Hypothesis class / expected sign: behavioral, positive
  • Lifecycle: experimental, promotion candidate
  • IC-IR @21d: +0.629
  • MCPT (pre-fix): ceiling-pinned pre-fix; vol-scaling is expected to help it survive overlap correction post-F-136
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: daily
  • Notes: same source column also backs the distinct factor entity tsmom_12_1_vol_scaled (§12) — identical value, different evaluation path (this factor is scored cross-sectionally; §12 is funnel-exempt). The strategies that trade each — classic-momentum / classic-momentum-long-only / classic-momentum-dollar-neutral for this one, etf-trend for §12 — are documented in the Strategy Catalog.

4.6.3 residual_momentum_252d_eod (not in the original 13-row citation table — added here because it has the strongest result in the run)

  • Style: momentum
  • Source: fact_eod.residual_momentum_252d_f — market-neutral momentum: residual of a 252-day rolling OLS of return vs. an equal-weighted benchmark, summed over [-252:-21] and z-scaled
  • Equation: \(\varepsilon_{i,\tau} = r_{i,\tau} - \big(\hat\alpha_i + \hat\beta_i\, r_{EW,\tau}\big)\) from a 252-day rolling OLS, then \(\text{ResMom}_{i,t} = z\!\left(\displaystyle\sum_{\tau=t-252}^{t-21}\varepsilon_{i,\tau}\right)\)
  • Hypothesis class / expected sign: not set in config
  • Lifecycle: experimental — atomic sibling of the deprecated composite residual_momentum_252d (status: deprecated, B-F-108.1/TASK-907; no FactorCompositionService exists to compute the composite recipe, so it carries zero rows). Consumers should use this atomic id, not the composite.
  • IC-IR @21d: +0.724 — highest in the entire run
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: daily; diagnostics “healthy” quadrant
  • Notes: candidate for a 14th row in the citation table given this result — anchor citation would be Blitz–Huij–Martens (residual momentum) [#50]. Traded by the residual-momentum strategy — see Strategy Catalog §4.

4.7 5. Profitability / Quality

Profitable, safe, well-managed firms (high gross-profitability, ROIC, QMJ) out-earn junk — a premium for quality that markets under-price. Novy-Marx 2013 [#92], Fama–French 2015 [#56], Asness–Frazzini–Pedersen 2019 [#113].

4.7.1 qmj_composite

  • Style: quality
  • Source: fact_fundamental_annual.qmj_composite_f — Asness–Frazzini–Pedersen Quality-Minus-Junk blend across profitability, growth, safety, and payout
  • Equation: \(QMJ_{i,t} = z(\text{Profitability}_{i,t}) + z(\text{Growth}_{i,t}) + z(\text{Safety}_{i,t}) + z(\text{Payout}_{i,t})\)
  • Hypothesis class / expected sign: behavioral, positive
  • Lifecycle: experimental. kind: atomic by design — despite the name, it’s a single pre-blended z-score column computed upstream, not a true composite; will always land as single_input in the factor-contribution sidecar (F-138).
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): floor bucket (null_mean_ic_ir ≈ 0, degenerate permutation null — typical of slow-moving annual fundamentals)
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual — short-T caveat applies
  • Notes: —

4.7.2 roic_spread

  • Style: quality
  • Source: fact_moat_annual.roic_spread_f — current-year ROIC minus WACC, winsorized and industry-z-scored to [0,1]
  • Equation: \(\text{ROICSpread}_{i,t} = \text{clip}_{[0,1]}\Big(z_{industry}\big(\text{ROIC}_{i,t} - \text{WACC}_{i,t}\big)\Big)\), where \(\text{ROIC} = \text{NOPAT}/\text{InvestedCapital}\)
  • Hypothesis class / expected sign: risk_premium, positive
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual — short-T caveat applies
  • Notes: —

4.7.3 gross_profitability

  • Style: quality
  • Source: fact_moat_annual.gross_profitability_fgross_profit / total_assets (Novy-Marx 2013)
  • Equation: \(GP_{i,t} = \dfrac{\text{GrossProfit}_{i,t}}{\text{TotalAssets}_{i,t}}\)
  • Hypothesis class / expected sign: risk_premium, positive
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual — short-T caveat applies
  • Notes: —

4.7.4 piotroski

  • Style: quality
  • Source: fact_fundamental_annual.piotroski_f — 9-criterion financial-strength score
  • Equation: \(F_{i,t} = \displaystyle\sum_{k=1}^{9} \mathbb{1}[\text{criterion}_k \text{ met}] \in [0,9]\)
  • Hypothesis class / expected sign: behavioral, positive
  • Lifecycle: experimental
  • IC-IR @21d: sign-flip — negative at h=1, positive at h≥21
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual
  • Notes: —

4.8 6. Investment / Accruals

Firms that invest conservatively and have low accruals out-earn aggressive investors — over-investment and earnings-quality mispricing. Sloan 1996 [#83], Cooper–Gulen–Schill 2008 [#103], Fama–French 2015 [#56].

4.8.1 accruals

  • Style: quality
  • Source: fact_moat_annual.accruals_f — Sloan (1996) Total-Accruals-to-Total-Assets
  • Equation: \(\text{Accruals}_{i,t} = \dfrac{\text{NetIncome}_{i,t} - \text{CFO}_{i,t}}{\text{TotalAssets}_{i,t}}\)
  • Hypothesis class / expected sign: behavioral, negative
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: annual — short-T caveat applies
  • Notes: §10’s original mapping also names earnings_growth_qoq for this family; no config file for that id was found under this exact name — needs reconciling against whatever earnings-growth id is actually registered.

4.9 7. Low volatility / Defensive

Low-risk stocks earn higher risk-adjusted returns — leverage constraints and lottery-preference bid up high-vol names (the low-vol anomaly / BAB). Ang et al. 2006 [#108], Frazzini–Pedersen 2014 [#93], Novy-Marx 2014 [#10].

4.9.1 volatility_30d

  • Style: volatility
  • Source: fact_eod.volatility_30d_f — STDDEV of daily log returns over 30 sessions × √252
  • Equation: \(\sigma^{30d}_{i,t} = \text{STDDEV}_{30d}\big(\ln(P_{i,\tau}/P_{i,\tau-1})\big) \times \sqrt{252}\)
  • Hypothesis class / expected sign: behavioral, negative
  • Lifecycle: experimental; strong signal — invert sign on promotion
  • IC-IR @21d: −0.5 to −0.7, alongside volatility_60/126/252d — part of the low-vol/size inverted cluster
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: daily
  • Notes: —

4.10 8. Liquidity / Illiquidity

Illiquid stocks (high Amihud price-impact, low $ADV) require a return premium for the cost and risk of trading them. Amihud 2002 [#48].

4.10.1 amihud

  • Style: liquidity
  • Source: fact_eod.amihud_f — mean of |daily_return| / dollar_volume over a trailing window
  • Equation: \(\text{Amihud}_{i,t} = \dfrac{1}{N}\displaystyle\sum_{\tau=t-N+1}^{t} \dfrac{|r_{i,\tau}|}{\$\text{Vol}_{i,\tau}}\)
  • Hypothesis class / expected sign: not set in config
  • Lifecycle: experimental
  • IC-IR @21d: inverted group (with pe_ratio, volatility_*, market_cap)
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: daily; diagnostics “healthy” quadrant
  • Notes: —

4.10.2 adv_dollar_20d

  • Style: liquidity
  • Source: fact_eod.adv_dollar_20d_f — mean of adj_close × volume over the trailing 20 sessions
  • Equation: \(\text{ADV\$}^{20d}_{i,t} = \dfrac{1}{20}\displaystyle\sum_{\tau=t-19}^{t} P_{i,\tau} \times V_{i,\tau}\)
  • Hypothesis class / expected sign: not set in config
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: daily
  • Notes: don’t confuse with adv_20d (undiscounted volume, no dollar scaling) — that id sits in the “drift” diagnostics quadrant (non-stationary, high entropy), a distinct factor.

4.11 9. Short interest / Positioning

Heavily-shorted / high-days-to-cover names underperform — informed short sellers and short-sale-constraint overvaluation (divergence of opinion). Boehmer–Jones–Zhang 2008 [#101], Rapach–Ringgenberg–Zhou 2016 [#100], Miller 1977 [#111].

4.11.1 days_to_cover

  • Style: positioning
  • Source: fact_short_interest.days_to_cover_f — FINRA’s daysToCoverQuantity, trusted verbatim from the vendor (no platform recompute from ADV)
  • Equation: \(\text{DTC}_{i,t} = \dfrac{\text{ShortInterest}_{i,t}}{\text{ADV}_{i,t}}\)
  • Hypothesis class / expected sign: behavioral, negative
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: FINRA disclosure-lag snap applies (18 calendar days)
  • Notes: —

4.11.2 short_pct_float

  • Style: positioning
  • Source: fact_short_interest.short_pct_float_f — short interest / diluted shares outstanding (a float proxy)
  • Equation: \(\text{ShortPctFloat}_{i,t} = \dfrac{\text{ShortInterest}_{i,t}}{\text{DilutedSharesOut}_{i,t}}\)
  • Hypothesis class / expected sign: behavioral, negative
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: FINRA disclosure-lag snap applies (18 calendar days)
  • Notes: —

4.12 10. Seasonality

Same-calendar-month historical returns recur — persistent cross-sectional seasonalities from mood, liquidity, and information cycles. Heston–Sadka 2008 [#37], Hirshleifer et al. 2020 [#40].

4.12.1 seasonality_same_month

  • Style: momentum
  • Source: fact_eod.seasonality_same_month_f — mean, over the prior ≤20 years (≥5 required), of the instrument’s monthly return in the same calendar month (Heston–Sadka
  • Equation: \(\text{Seas}_{i,t} = \dfrac{1}{K}\displaystyle\sum_{y=1}^{K} r^{month}_{i,\,\text{same-month}(t),\,y}\), \(5 \le K \le 20\)
  • Hypothesis class / expected sign: behavioral, positive
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: daily
  • Notes: —

4.13 11. Earnings momentum / PEAD

Prices under-react to earnings surprises (SUE), drifting for weeks after the announcement — the post-earnings-announcement drift. Bernard–Thomas 1989 [#75].

4.13.1 sue

  • Style: growth
  • Source: fact_earnings_momentum.sue_feps_surprise_pct / STDDEV(eps_surprise_pct) over the strictly-prior 8 announcements
  • Equation: \(\text{SUE}_{i,q} = \dfrac{\Delta EPS\%_{i,q}}{\text{STDDEV}_8\big(\Delta EPS\%_{i,\,q-1:q-8}\big)}\)
  • Hypothesis class / expected sign: behavioral, positive
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: not itemized
  • Notes: traded by the sue-earnings-momentum strategy — see Strategy Catalog §5.

4.14 12. Time-series momentum / Trend

An asset’s own past 12-1 return predicts its next-month return across asset classes — trend-following / slow-diffusion premium. Moskowitz–Ooi–Pedersen 2012 [#115], Hurst–Ooi–Pedersen 2017 [#95].

4.14.1 tsmom_12_1_vol_scaled

  • Style: momentum
  • Source: fact_eod.momentum_12_1_vol_scaled_f — same source column as momentum_12_1_vol_scaled (§4), but registered as its own factor entity because it has its own evaluation path: funnel_exempt: true in config, so it is never scored by the cross-sectional IC/MCPT/Alphalens funnel. Its only evidence comes from the strategy that trades it — see Strategy Catalog §3 for the long_flat_trend mechanic (own-trend-sign selection) and its walk-forward backtest.
  • Equation: \(\text{TSMom}_{i,t} = \text{Mom}^{vol}_{i,t}\) — identical formula to §4’s momentum_12_1_vol_scaled (same source column); the long_flat_trend mechanic consumes \(\text{sign}(\text{TSMom}_{i,t})\), not its cross-sectional rank
  • Hypothesis class / expected sign: risk_premium, positive
  • Lifecycle: experimental
  • IC-IR @21d: not itemized (funnel-exempt — not scored by cross-sectional IC)
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: daily
  • Notes: —

4.15 13. Short-horizon reversal

Very-short-horizon losers bounce (and winners fade) — liquidity provision / over-reaction correction (contrarian). Lehmann 1990 [#96].

4.15.1 bb_pct

  • Style: volatility
  • Source: fact_eod.bb_pct_f(adj_close - bb_lower_20_f) / (bb_upper_20_f - bb_lower_20_f)
  • Equation: \(\%B_{i,t} = \dfrac{P_{i,t} - L^{20}_{i,t}}{U^{20}_{i,t} - L^{20}_{i,t}}\), where \(U^{20} = \text{SMA}^{20d} + 2\sigma^{20d}\) and \(L^{20} = \text{SMA}^{20d} - 2\sigma^{20d}\) (0 at the lower band, 1 at the upper band) — a ratio of price differences, so a future split scales numerator and denominator identically and cancels (scale-free, PIT-clean w.r.t. corporate actions; F-289/LH-25)
  • Hypothesis class / expected sign: behavioral, negative
  • Lifecycle: experimental
  • IC-IR @21d: not itemized
  • MCPT (pre-fix): not itemized
  • t-stat (naive): PENDING
  • t-stat (HAC, Newey–West): PENDING
  • n_obs / cadence: daily; diagnostics “healthy” quadrant
  • Notes: —

4.15.2 bb_lower_20 (superseded predecessor — not in the original 13-row table, included for the correction it forces)

  • Style: volatility
  • Source: fact_eod.bb_lower_20_f — 20-day SMA of adj_close − 2×20-day stddev
  • Equation: \(L^{20}_{i,t} = \text{SMA}^{20d}_{i,t} - 2\,\sigma^{20d}_{i,t}\) — a raw price level (not scale-free), which is precisely the CF-1 look-ahead defect that got it deprecated (see Lifecycle below)
  • Hypothesis class / expected sign: behavioral, positive — short-horizon mean-reversion (Lehmann 1990 contrarian reversal)
  • Lifecycle: deprecated for cause (F-289/LH-25, 2026-07-20, run 260718_31a51e) — raw price-level source column with a CF-1 look-ahead defect. Replaced by bb_pct (scale-free).
  • IC-IR @21d: not itemized
  • MCPT (pre-fix, run 260526_6ddcd9): one of only four factors with a non-collapsed permutation null and positive unbiased_ic_ir (0.21–1.35) at the time — this result is now known to be contaminated by the CF-1 look-ahead, not evidence of a real edge. The other three factors in that same “real significance” group at the time — ma_50d, ma_200d, vwap_20d — were deprecated for the identical reason on the same date and are not part of this catalog’s 13 families.
  • t-stat (naive): PENDING (moot — factor is deprecated)
  • t-stat (HAC, Newey–West): PENDING (moot — factor is deprecated)
  • n_obs / cadence: daily
  • Notes: kept in the catalog specifically as a cautionary entry — the 260526_6ddcd9 run synthesis called this factor’s MCPT result the cleanest in the library; two months later it was found to be a look-ahead artifact. A clean permutation p-value is necessary evidence, not sufficient.

4.16 Next steps

  1. Write the query/script computing t_naive and t_HAC per factor_id against ops.risk_factor_return, and fill in every PENDING field above.
  2. Resolve the two duplicate pairs — done 2026-08-05: market_beta_252d and momentum_12m_1m are the canonical survivors; beta and momentum_12_1 are retired. Remaining follow-up: if either legacy id has rows in ops.factor, flip their status there too (a DB change, out of scope for this doc pass).
  3. Backfill hypothesis_class/expected_sign for the factors missing it in config (market_beta_252d, market_cap, amihud, adv_dollar_20d).
  4. Reconcile the §6 (Investment/Accruals) mapping — earnings_growth_qoq has no matching config file under that id.
  5. Regenerate the empirical fields against the first post-F-136/137/138 NIGHTLY_FULL/RESEARCH_DAY run.
  6. Consider formally adding residual_momentum_252d_eod (§4) as a 14th citation row, given its result is the strongest in the run.