用 Python 揭秘均值回归策略:你的收益从何而来?
2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含 500 篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!
你是否好奇过:今天市场是牛市,明天还会继续牛下去吗?熊市结束后,更可能进入震荡还是直接反转?
这些问题,数学家 150 年前就给出了一个优雅的工具 —— 马尔可夫链(Markov Chain)。它不是什么稳赚不赔的圣杯,但它能让你用概率的视角,清晰地理解市场"状态切换"的规律。
本文将用 Python 带你一步步构建一个三状态(牛市 / 熊市 / 震荡)的市场机制切换模型,并诚实地告诉你:它能做什么,不能做什么。
随机过程(Stochastic Process) 就是一组按时间索引的随机变量:
举三个例子,帮助你理解"时间索引"还不够,必须明确时间之间的依赖关系:
这说明:依赖结构决定了过程的本质。
如果一个离散时间随机过程满足下面这个性质,就叫做马尔可夫链:
通俗翻译:下一步的概率只和当前状态有关,和更早的历史无关。
注意:这是一个假设,不是万能真理。金融收益率往往存在长期记忆和路径依赖。
一个反例:假如规则是"只有连续两次下跌之后才能上涨",那么下一步就依赖于前两步,这就不是一阶马尔可夫链了。不过可以通过扩展状态空间(比如定义状态 = (上一步动作, 当前位置))把它转化为一阶马尔可夫链。
我们对 S&P 500 日收益率定义三个状态:
阈值 ±2% 是人为选择的,改变阈值会改变模型,一定要做鲁棒性测试。
转移概率的自然估计就是:
完整的 Python 代码如下:
import numpy as np
import pandas as pd
import yfinance as yf
def label_states(returns, window=20, bull_thresh=0.02, bear_thresh=-0.02):
# 计算 20 日滚动累计收益率
rolling_ret = returns.rolling(window).sum()
# 根据阈值打上状态标签:0 = 牛市,1 = 熊市,2 = 震荡
states = np.where(rolling_ret > bull_thresh, 0,
np.where(rolling_ret < bear_thresh, 1, 2))
return pd.Series(states, index=returns.index)
def transition_matrix(states, n_states=3):
# 初始化计数矩阵
counts = np.zeros((n_states, n_states))
vals = states.dropna().values.astype(int)
# 统计每一对相邻状态的转移次数
for i in range(len(vals) - 1):
counts[vals[i], vals[i+1]] += 1
# 按行归一化得到概率
P = np.zeros_like(counts)
for i in range(n_states):
row_sum = counts[i].sum()
if row_sum > 0:
P[i] = counts[i] / row_sum
return P
# 拉取 SPY 近 10 年日线数据
ticker = yf.Ticker("SPY")
df = ticker.history(period="10y", interval="1d")
returns = df['Close'].pct_change().dropna()
# 打标签 + 估计转移矩阵
states = label_states(returns)
P = transition_matrix(states)
print(pd.DataFrame(P, index=['Bull', 'Bear', 'Side'],
columns=['Bull', 'Bear', 'Side']).round(3))运行后,你可能得到类似这样的输出:
Bull Bear Side
Bull 0.65 0.05 0.30
Bear 0.08 0.70 0.22
Side 0.20 0.10 0.70解读:牛市有 65% 概率继续牛,熊市有 70% 概率继续熊 —— 市场状态确实具有持续性(persistence)。
从状态 经过 步到状态 的概率,就是矩阵 的 元素:
一行代码搞定:
def multi_step_prob(P, n_steps):
# 用矩阵的 n 次幂直接计算 n 步转移概率
return np.linalg.matrix_power(P, n_steps)当 时,在不可约且非周期的条件下,状态分布会收敛到唯一的稳态分布:
就是长期来看市场停留在状态 的比例。
def stationary_distribution(P):
n = P.shape[0]
# 构造线性方程组:pi * P = pi,同时满足 sum(pi) = 1
A = (P.T - np.eye(n))
A[-1] = 1.0 # 用最后一行代替为"概率之和 = 1"的约束
b = np.zeros(n)
b[-1] = 1.0
pi = np.linalg.solve(A, b)
return pi举例:如果 ,则长期看市场有 50% 时间震荡,30% 牛,20% 熊。
重要警告:稳态分布是一个渐进概念。真实市场是非平稳的,今天的转移矩阵未必明年还有效。
设每个状态 的历史平均收益为 ,马尔可夫链给出下一期状态的概率 ,则期望收益为:
仓位应该与期望收益成正比,并按波动率缩放(比如凯利公式)。
如果市场是鞅(效率市场假说的最简形式),那么:
没有任何马尔可夫链能产生正的期望利润。所以在实盘之前,务必检验:扣除交易成本后,条件期望收益是否显著不为零?
走步前进法(在滚动历史窗口上重新估计转移矩阵)能避免前视偏差。但即使回测做对了,也要注意:
务必对比以下基准:
当状态不可直接观测时(这才更贴近真实金融市场),应该使用 HMM。真实状态 是隐藏的,你只观测到从状态相关分布中产生的收益率。
核心算法:
注意事项:
from hmmlearn import hmm
import numpy as np
def fit_hmm(returns, n_states=3, n_restarts=10):
best_model = None
best_score = -np.inf
# 多次随机初始化,选择似然最高的模型
for _ in range(n_restarts):
model = hmm.GaussianHMM(n_components=n_states,
covariance_type="full",
n_iter=1000)
model.fit(returns.values.reshape(-1, 1))
score = model.score(returns.values.reshape(-1, 1))
if score > best_score:
best_score = score
best_model = model
return best_modelimport numpy as np
import pandas as pd
import yfinance as yf
class RegimeMarkovModel:
def __init__(self, lookback=252, regime_window=20,
bull_thresh=0.02, bear_thresh=-0.02,
transaction_cost=0.001):
# 历史窗口、阈值、交易成本的设置
self.lookback = lookback
self.regime_window = regime_window
self.bull_thresh = bull_thresh
self.bear_thresh = bear_thresh
self.transaction_cost = transaction_cost
def label_states(self, returns):
# 打标签:牛 / 熊 / 震荡
rolling_ret = returns.rolling(self.regime_window).sum()
states = np.where(rolling_ret > self.bull_thresh, 0,
np.where(rolling_ret < self.bear_thresh, 1, 2))
return pd.Series(states, index=returns.index)
def estimate_transition(self, states):
# 估计转移矩阵
n = 3
counts = np.zeros((n, n))
vals = states.dropna().values.astype(int)
for i in range(len(vals) - 1):
counts[vals[i], vals[i+1]] += 1
P = np.zeros_like(counts)
for i in range(n):
s = counts[i].sum()
if s > 0:
P[i] = counts[i] / s
return P
def regime_returns(self, states, returns):
# 计算各状态的历史平均收益
df = pd.DataFrame({'ret': returns, 'state': states}).dropna()
return df.groupby('state')['ret'].mean().values
def run_backtest(self, returns):
states = self.label_states(returns)
signals, positions, dates = [], [], []
# 走步前进:每天用过去 lookback 天的数据重新估计
for i in range(self.lookback, len(states)):
hist_states = states.iloc[i - self.lookback:i]
hist_returns = returns.iloc[i - self.lookback:i]
P = self.estimate_transition(hist_states)
mu = self.regime_returns(hist_states, hist_returns)
# 根据当前状态计算下一期期望收益
current_state = int(states.iloc[i])
next_probs = P[current_state]
expected_return = np.dot(next_probs, mu)
# 基于期望收益打开多头 / 空头 / 空仓
if expected_return > 0.0005: # 5 bps 的噪音阈值
pos = 1.0
elif expected_return < -0.0005:
pos = -1.0
else:
pos = 0.0
positions.append(pos)
signals.append(expected_return)
dates.append(states.index[i])
positions = pd.Series(positions, index=dates)
strategy_ret = positions.shift(1) * returns.loc[dates]
# 扣除仓位变动带来的交易成本
pos_changes = positions.diff().fillna(0).abs()
costs = pos_changes * self.transaction_cost
net_ret = strategy_ret - costs
# 计算绩效指标
sharpe = net_ret.mean() / net_ret.std() * np.sqrt(252)
cum = (1 + net_ret).cumprod()
max_dd = (cum / cum.cummax() - 1).min()
return net_ret, sharpe, max_dd
# 运行回测
data = yf.Ticker("SPY").history(period="15y", interval="1d")
rets = data['Close'].pct_change().dropna()
model = RegimeMarkovModel(transaction_cost=0.001)
net_ret, sharpe, mdd = model.run_backtest(rets)
print(f"扣除成本后的 Sharpe: {sharpe:.3f}, 最大回撤: {mdd:.2%}")你多半会看到:扣除成本后 Sharpe 大约在 0.2 ~ 0.5 之间,通常还不如简单的买入持有。这就是现实。
马尔可夫链是非常优美的数学工具,对于状态识别和风险管理确实有用。但它不是稳定盈利的秘密武器。
在你部署任何此类策略之前,请扪心自问:
最终一句话:用马尔可夫链去理解概率,而不是去追逐盈利。真正的优势,就藏在这份理解之中。
2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:
星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!
好文推荐
1. 用 Python 打造股票预测系统:Transformer 模型教程(一)
2. 用 Python 打造股票预测系统:Transformer 模型教程(二)
3. 用 Python 打造股票预测系统:Transformer 模型教程(三)
4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)
6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用
9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解
好书推荐