做什么(One-liner)
TVP-KFAVAR v2 宏观体制检测:sklearn PCA 提取第一因子 → statsmodels Markov Switching AR(1) + switching variance 拟合 → 3-state 标签。收敛失败自动回退至 v1 滚动统计。macro_snapshot_v1 / macro_factor_v1 的 regime 字段主要来源。
怎么用
Canvas 间接使用(macro_snapshot_v1 聚合 regime 字段)。直接 Python:
from services.mfle.tvp_kfavar import detect_regime, detect_regime_from_yfinance, detect_regime_from_fred
# 从 DataFrame
r = detect_regime(df, window=60, k_regimes=3)
# 从 yfinance 默认 ['SPY', 'TLT', 'GLD', 'QQQ']
r = detect_regime_from_yfinance(symbols=None, window=60)
# 从 FRED (INDPRO + UNRATE)
r = detect_regime_from_fred(window=12)核心公式
if len(df) < window:
return {regime: expansion, confidence: 0.5, method: 'insufficient_data'}
# Step 1: PCA factor extraction
factor_1, var_explained = PCA(n_components=1).fit_transform(standardize(df.pct_change()))
# Step 2: Markov Switching AR(1)
res = MarkovAutoregression(factor_1, k_regimes=3, order=1,
switching_ar=False, switching_variance=True).fit()
if res fails: res = same but k=2 fallback
if still fails: return _rolling_fallback(...)
# Step 3: Smoothed probs → current regime
current_probs = res.smoothed_marginal_probabilities[-1]
current_state = argmax(current_probs)
# Step 4: Label states
# k=2: higher mean → expansion
# k=3: highest variance → high_volatility; of remaining higher mean → expansion
current_regime = label_map[current_state]
confidence = clip(current_probs[current_state], 0.1, 0.99)
return {regime, confidence, factors: {factor_1, regime_probs, transition_matrix, volatility}, method='markov_switching'}假设与适用场景
假设:DataFrame 频率一致 + ≥ window 观察 + PCA 单因子充分 + statsmodels MS-AR 收敛 + 无切换 AR 需求。
适用:Canvas 宏观 regime 字段、institution 决策 context、跨资产 narrative overlay。
不适用:intraday、NBER-official 复现、严格 FAVAR 完整、结构性 break 分析、< window 短序列。
输入 / 输出契约
detect_regime(df, window?=60, vol_threshold?=1.5, growth_threshold?=0.0, k_regimes?=3) → {regime, confidence, factors (content differs by method), timestamp, method}.
Wrappers:
detect_regime_from_yfinance(symbols?=['SPY','TLT','GLD','QQQ'], window?=60)— 6mo 1d hardcoded fetchdetect_regime_from_fred(window?=12)— INDPRO + UNRATE only
已知局限
- Markov 单次 EM fit init 敏感;不同 init 可能给不同 regime 排序
- k_regimes=3 → 2 auto-fallback:label_map 维度变化,transition_matrix 尺寸不同
- PCA 单因子:Bernanke FAVAR 简化版,非完整多因子 VAR
- switching_variance=True / switching_ar=False / order=1 硬编码:无可配置
- from_yfinance 6mo 1d 硬编码:period 不可覆盖
- from_fred 只 INDPRO+UNRATE(不含 FEDFUNDS,和 macro_cli_v1 不同)
- method='markov_switching' vs 'rolling_fallback' 的 factors 字段完全不同:客户端必须 branch
- statsmodels DataFrame→ndarray shim 继承自 markov_regime fix (2026-04-20)
- insufficient_data 默认返回 EXPANSION:caller 必须看 method 字段才能区分
参考文献
Hamilton (1989) Markov Switching AR + Bernanke-Boivin-Eliasz (2005) FAVAR + Primiceri (2005) TVP-VAR + Kim-Nelson (1999) State-Space with Regime Switching。详见 frontmatter。
Golden Test
tests/golden/fixtures/tier3/tvp_kfavar_regime/,captured_snapshot with monkey-patched upstream. 1e-6 tolerance. 13 tests:
Direct detect_regime (4 scenarios)
trending_up— 6-ticker drift → Markov convergesregime_shift— 80/20 low→high vol → labels split across 3 statestoo_short— 3 obs → method='insufficient_data'borderline_data— 1 ticker, 70 obs → primary or fallback depending on convergence
Wrappers (4 scenarios)
yfinance_wrapper_ok— monkey-patched MultiIndex download → full pipelineyfinance_wrapper_empty— empty → expansion + confidence 0.5fred_wrapper_unavailable— fred_available=False → note 'FRED_API_KEY not set'fred_wrapper_fetch_none— fetch None → note 'FRED fetch failed'
Contract tests
regime_enum_contract— 3 enum values onlymethod_enum_contract— 3 enum values onlyconfidence_clipping_primary— [0.1, 0.99]determinism_primary_path— 5 reruns 一致regime_probs_sum_close_to_one— 分布有效
Changelog
- 2.0.0 (2026-04-21) — 首次 Active,锁定 v2 PCA + Markov Switching pipeline(Tier 3 batch 4)
- 1.0.0 (2026-03-xx) — 历史 v1 rolling-stats(保留为 fallback 路径)

