03-绩效归因
预计学习时间:1.5 小时
难度:⭐⭐⭐
核心问题:策略赚的钱从哪里来?Alpha 还是 Beta?选股还是择时?实盘和回测差多少?
绩效归因全景
┌────────────────────────────────────────────────────────────────┐
│ 绩效归因体系 │
├────────────────────────────────────────────────────────────────┤
│ │
│ 绩效归因 │
│ │ │
│ ┌──────────────────┼──────────────────┐ │
│ │ │ │ │
│ 收益分解 风险归因 质量评估 │
│ │ │ │ │
│ · Brinson 归因 · 因子归因 · 策略衰减 │
│ · Alpha/Beta · Barra 风险 · 回测差异 │
│ · 选股/择时 · 风险贡献 · 换手率分析 │
│ │
└────────────────────────────────────────────────────────────────┘
一、Brinson 归因模型
1.1 基本原理
Brinson 归因将组合收益与基准的差异分解为三个效应:
其中:
- :组合实际收益
- :基准收益
- :用基准权重 × 组合行业权重计算的收益(资产配置效应的参照)
- :用组合权重 × 基准行业收益计算的收益(选股效应的参照)
Brinson 归因分解示意
超额收益
┌────┴────┐
│ R_p-R_b │
└────┬────┘
┌─────────┼─────────┐
│ │ │
配置效应 选股效应 交互效应
┌──────┐ ┌──────┐ ┌──────┐
│行业 │ │行业内│ │同时 │
│权重 │ │选股 │ │改变 │
│偏离 │ │能力 │ │两者 │
└──────┘ └──────┘ └──────┘
1.2 完整实现
def brinson_attribution(portfolio_weights, portfolio_returns,
benchmark_weights, benchmark_returns,
sectors):
"""
Brison 归因分析(单期)
参数
----
portfolio_weights : dict
行业 -> 组合权重,如 {'金融': 0.30, '科技': 0.25}
portfolio_returns : dict
行业 -> 组合行业收益,如 {'金融': 0.05, '科技': 0.12}
benchmark_weights : dict
行业 -> 基准权重
benchmark_returns : dict
行业 -> 基准行业收益
sectors : list
行业列表
返回
----
result : dict
归因结果
"""
# 组合总收益
R_p = sum(portfolio_weights[s] * portfolio_returns[s] for s in sectors)
# 基准总收益
R_b = sum(benchmark_weights[s] * benchmark_returns[s] for s in sectors)
# 超额收益
excess = R_p - R_b
# 配置效应:用组合权重 × 基准收益 - 基准权重 × 基准收益
allocation = sum(
(portfolio_weights[s] - benchmark_weights[s]) * benchmark_returns[s]
for s in sectors
)
# 选股效应:用基准权重 × 组合收益 - 基准权重 × 基准收益
selection = sum(
benchmark_weights[s] * (portfolio_returns[s] - benchmark_returns[s])
for s in sectors
)
# 交互效应:组合权重与组合收益的"额外"贡献
interaction = sum(
(portfolio_weights[s] - benchmark_weights[s]) *
(portfolio_returns[s] - benchmark_returns[s])
for s in sectors
)
return {
'组合收益': R_p,
'基准收益': R_b,
'超额收益': excess,
'配置效应': allocation,
'选股效应': selection,
'交互效应': interaction,
'配置占比': allocation / excess if excess != 0 else 0,
'选股占比': selection / excess if excess != 0 else 0,
'交互占比': interaction / excess if excess != 0 else 0,
}
# 示例
sectors = ['金融', '科技', '消费', '医药', '能源']
portfolio_w = {'金融': 0.30, '科技': 0.25, '消费': 0.20,
'医药': 0.15, '能源': 0.10}
benchmark_w = {'金融': 0.25, '科技': 0.20, '消费': 0.25,
'医药': 0.20, '能源': 0.10}
portfolio_r = {'金融': 0.05, '科技': 0.15, '消费': 0.08,
'医药': 0.12, '能源': 0.03}
benchmark_r = {'金融': 0.04, '科技': 0.10, '消费': 0.06,
'医药': 0.08, '能源': 0.02}
result = brinson_attribution(portfolio_w, portfolio_r,
benchmark_w, benchmark_r, sectors)
print("=== Brinson 归因结果 ===")
for k, v in result.items():
if isinstance(v, float):
print(f" {k}: {v:.4%}")
else:
print(f" {k}: {v}")1.3 多期 Brinson 归因
实际应用中需要分析多期(如月度)归因,并进行连接处理。
def brinson_multiperiod(portfolio_weights_list, portfolio_returns_list,
benchmark_weights_list, benchmark_returns_list,
sectors):
"""
多期 Brinson 归因
参数
----
portfolio_weights_list : list of dict
每期的组合权重
portfolio_returns_list : list of dict
每期的组合行业收益
benchmark_weights_list : list of dict
每期的基准权重
benchmark_returns_list : list of dict
每期的基准行业收益
sectors : list
行业列表
返回
----
results_df : DataFrame
各期及累计归因结果
"""
all_results = []
cumulative = {'allocation': 0, 'selection': 0, 'interaction': 0}
for i, (pw, pr, bw, br) in enumerate(zip(
portfolio_weights_list, portfolio_returns_list,
benchmark_weights_list, benchmark_returns_list
)):
period_result = brinson_attribution(pw, pr, bw, br, sectors)
# 累积效应(简化加法连接)
cumulative['allocation'] += period_result['配置效应']
cumulative['selection'] += period_result['选股效应']
cumulative['interaction'] += period_result['交互效应']
all_results.append({
'期数': i + 1,
'超额收益': period_result['超额收益'],
'配置效应': period_result['配置效应'],
'选股效应': period_result['选股效应'],
'交互效应': period_result['交互效应'],
'累计配置': cumulative['allocation'],
'累计选股': cumulative['selection'],
'累计交互': cumulative['interaction'],
})
return pd.DataFrame(all_results).set_index('期数')
# 示例:3 期归因
np.random.seed(42)
n_periods = 3
pw_list = [portfolio_w.copy() for _ in range(n_periods)]
pr_list = [{s: v + np.random.normal(0, 0.01) for s, v in portfolio_r.items()}
for _ in range(n_periods)]
bw_list = [benchmark_w.copy() for _ in range(n_periods)]
br_list = [{s: v + np.random.normal(0, 0.005) for s, v in benchmark_r.items()}
for _ in range(n_periods)]
results_df = brinson_multiperiod(pw_list, pr_list, bw_list, br_list, sectors)
print("\n=== 多期 Brinson 归因 ===")
print(results_df.round(4))二、因子归因(Barra 风险模型)
2.1 基本原理
Barra 风险模型将组合收益分解为:
其中:
- :特异收益(选股 Alpha)
- :第 个因子的暴露
- :第 个因子的收益
- :残差(特质风险)
def factor_attribution(returns, factor_returns, factor_exposures=None,
benchmark_returns=None):
"""
因子归因分析(Barra 风险模型)
参数
----
returns : Series
组合日收益率
factor_returns : DataFrame
因子日收益率 (T x K)
factor_exposures : DataFrame, optional
组合对因子的暴露 (N x K)
benchmark_returns : Series, optional
基准收益率
返回
----
attribution : dict
因子归因结果
"""
# 如果没有提供暴露,通过回归估计
if factor_exposures is None:
X = np.column_stack([np.ones(len(factor_returns)), factor_returns])
y = returns.values
# OLS 回归: r_p = alpha + sum(beta_k * f_k) + epsilon
betas = np.linalg.lstsq(X, y, rcond=None)[0]
alpha = betas[0]
factor_betas = betas[1:]
y_pred = X @ betas
residual = y - y_pred
# 计算各因子贡献
factor_names = factor_returns.columns.tolist()
factor_contrib = {}
for i, name in enumerate(factor_names):
# 因子贡献 = 因子暴露 × 因子收益(按时间平均)
contrib = factor_betas[i] * factor_returns[name].mean() * 252
factor_contrib[name] = {
'beta': factor_betas[i],
'年化因子收益': factor_returns[name].mean() * 252,
'年化贡献': contrib,
}
# 总因子贡献
total_factor = sum(v['年化贡献'] for v in factor_contrib.values())
total_alpha = alpha * 252
attribution = {
'alpha 年化': total_alpha,
'因子总贡献 年化': total_factor,
'残差波动率': residual.std() * np.sqrt(252) if factor_exposures is None else None,
'R²': 1 - residual.var() / y.var() if factor_exposures is None else None,
'因子明细': factor_contrib,
}
return attribution
# 示例
np.random.seed(42)
n_days, n_factors = 500, 5
factor_names = ['Market', 'Size', 'Value', 'Momentum', 'Volatility']
factor_returns = pd.DataFrame(
np.random.normal(0.001, 0.01, (n_days, n_factors)),
columns=factor_names
)
# 真实因子暴露
true_betas = np.array([1.0, 0.3, -0.2, 0.5, -0.1])
alpha_true = 0.0002
# 生成组合收益
portfolio_returns = pd.Series(
alpha_true + factor_returns.values @ true_betas +
np.random.normal(0, 0.005, n_days)
)
attr = factor_attribution(portfolio_returns, factor_returns)
print("=== 因子归因结果 ===")
print(f"Alpha 年化: {attr['alpha 年化']:.2%}")
print(f"因子总贡献 年化: {attr['因子总贡献 年化']:.2%}")
print(f"R²: {attr['R²']:.4f}")
print(f"\n因子明细:")
for name, detail in attr['因子明细'].items():
print(f" {name}: beta={detail['beta']:.3f}, "
f"贡献={detail['年化贡献']:.2%}")2.2 因子归因可视化数据准备
def prepare_factor_attribution_report(attribution):
"""
将因子归因结果整理为可制表的 DataFrame
参数
----
attribution : dict
factor_attribution 的返回值
返回
----
df : DataFrame
因子归因汇总表
"""
rows = [{'因子': 'Alpha', 'Beta': '-', '年化贡献': attribution['alpha 年化']}]
for name, detail in attribution['因子明细'].items():
rows.append({
'因子': name,
'Beta': detail['beta'],
'年化贡献': detail['年化贡献'],
})
df = pd.DataFrame(rows)
return df
# 示例
report_df = prepare_factor_attribution_report(attr)
print("\n因子归因汇总表:")
print(report_df.to_string(index=False))三、Alpha 来源分析
3.1 Alpha 分解
Alpha 可以进一步分解为:
def alpha_decomposition(strategy_returns, benchmark_returns,
market_factor=None):
"""
Alpha 来源分解
参数
----
strategy_returns : Series
策略收益率
benchmark_returns : Series
基准收益率
market_factor : Series, optional
市场因子收益率
返回
----
decomposition : dict
Alpha 分解结果
"""
excess = strategy_returns - benchmark_returns
# 总 Alpha
total_alpha = excess.mean() * 252
# 市场 Beta 和市场择时 Alpha
if market_factor is not None:
X = np.column_stack([np.ones(len(market_factor)), market_factor])
y = strategy_returns.values
betas = np.linalg.lstsq(X, y, rcond=None)[0]
market_beta = betas[1]
alpha_from_market = betas[0] * 252
else:
X = np.column_stack([np.ones(len(benchmark_returns)),
benchmark_returns])
y = strategy_returns.values
betas = np.linalg.lstsq(X, y, rcond=None)[0]
market_beta = betas[1]
alpha_from_market = betas[0] * 252
# 选股 Alpha(超额收益中不可被基准解释的部分)
X_sel = np.column_stack([np.ones(len(benchmark_returns)),
benchmark_returns])
y_sel = excess.values
sel_betas = np.linalg.lstsq(X_sel, y_sel, rcond=None)[0]
selection_alpha = sel_betas[0] * 252
# 信息比率
tracking_error = excess.std() * np.sqrt(252)
information_ratio = total_alpha / tracking_error if tracking_error > 0 else 0
decomposition = {
'总 Alpha 年化': total_alpha,
'选股 Alpha 年化': selection_alpha,
'市场 Beta': market_beta,
'跟踪误差': tracking_error,
'信息比率 (IR)': information_ratio,
'胜率': (excess > 0).mean(),
'超额收益偏度': excess.skew(),
'超额收益峰度': excess.kurtosis(),
}
return decomposition
# 示例
np.random.seed(42)
n_days = 500
benchmark = pd.Series(np.random.normal(0.0005, 0.012, n_days))
strategy = pd.Series(
benchmark + 0.0003 + np.random.normal(0, 0.008, n_days)
)
decomp = alpha_decomposition(strategy, benchmark)
print("=== Alpha 分解 ===")
for k, v in decomp.items():
print(f" {k}: {v:.4f}" if isinstance(v, float) else f" {k}: {v}")3.2 滚动 Alpha 监控
def rolling_alpha_monitor(strategy_returns, benchmark_returns,
window=60):
"""
滚动窗口 Alpha 监控
参数
----
strategy_returns : Series
策略收益率
benchmark_returns : Series
基准收益率
window : int
滚动窗口
返回
----
rolling_stats : DataFrame
滚动统计量
"""
excess = strategy_returns - benchmark_returns
rolling = pd.DataFrame({
'滚动 Alpha 年化': excess.rolling(window).mean() * 252,
'滚动 IR': (excess.rolling(window).mean() * np.sqrt(252) /
excess.rolling(window).std()),
'滚动胜率': excess.rolling(window).apply(
lambda x: (x > 0).mean()),
'滚动跟踪误差': excess.rolling(window).std() * np.sqrt(252),
})
return rolling.dropna()
# 示例
rolling_stats = rolling_alpha_monitor(strategy, benchmark)
print(f"\n滚动 Alpha 监控(最近 {len(rolling_stats)} 期):")
print(rolling_stats.tail(5).round(4))四、策略衰减监控
4.1 衰减信号检测
策略性能随时间下降是常见现象。需要及时检测衰减信号。
| 衰减类型 | 表现 | 可能原因 |
|---|---|---|
| Alpha 衰减 | 超额收益持续下降 | Alpha 被套利殆尽 |
| 波动率变化 | 实际波动偏离预期 | 市场结构变化 |
| 容量下降 | 收益随规模递减 | 策略容量有限 |
| 相关性上升 | 与其他策略相关性增大 | 策略同质化 |
def decay_detector(returns, rolling_window=60, z_threshold=2.0):
"""
策略衰减检测器
参数
----
returns : Series
策略日收益率
rolling_window : int
滚动窗口
z_threshold : float
Z-score 阈值(超过视为衰减信号)
返回
----
signals : DataFrame
衰减信号列表
"""
# 滚动 Sharpe
rolling_mean = returns.rolling(rolling_window).mean() * 252
rolling_std = returns.rolling(rolling_window).std() * np.sqrt(252)
rolling_sharpe = rolling_mean / rolling_std
# 滚动最大回撤
cumret = (1 + returns).cumprod()
rolling_peak = cumret.rolling(rolling_window).max()
rolling_drawdown = (cumret - rolling_peak) / rolling_peak
# 与全样本统计量的 Z-score
full_mean = returns.mean() * 252
full_std = returns.std() * np.sqrt(252)
rolling_mean_z = (rolling_mean - full_mean) / (
returns.rolling(rolling_window).std() * np.sqrt(252))
signals = pd.DataFrame({
'滚动 Sharpe': rolling_sharpe,
'滚动年化收益': rolling_mean,
'滚动最大回撤': rolling_drawdown,
'收益 Z-score': rolling_mean_z,
})
# 标记衰减信号
signals['Alpha 衰减'] = rolling_mean_z < -z_threshold
signals['波动率异常'] = rolling_std > 2 * full_std
return signals.dropna()
# 示例:模拟包含衰减的收益序列
np.random.seed(42)
n = 500
# 前期正常,后期衰减
early = np.random.normal(0.001, 0.015, n // 2)
late = np.random.normal(0.0002, 0.015, n // 2) # Alpha 下降
all_returns = pd.Series(np.concatenate([early, late]))
signals = decay_detector(all_returns)
decay_periods = signals[signals['Alpha 衰减'] | signals['波动率异常']]
print(f"=== 策略衰减监控 ===")
print(f"检测到衰减信号: {len(decay_periods)} 个周期")
if len(decay_periods) > 0:
print(f"\n衰减期统计:")
print(f" 衰减期平均 Sharpe: {decay_periods['滚动 Sharpe'].mean():.2f}")
print(f" 衰减期平均回撤: {decay_periods['滚动最大回撤'].mean():.2%}")4.2 策略容量估算
def capacity_estimate(daily_volume, max_participation=0.05,
avg_trade_value=None):
"""
估算策略容量
参数
----
daily_volume : Series
日成交量(金额)
max_participation : float
最大参与率(占日成交量的比例)
avg_trade_value : float
平均单笔交易金额
返回
----
capacity : float
估算的策略容量
details : dict
容量明细
"""
avg_volume = daily_volume.mean()
capacity = avg_volume * max_participation
# 按不同参与率估算
details = {}
for pct in [0.01, 0.03, 0.05, 0.10]:
cap = avg_volume * pct
details[f'{pct:.0%} 参与率'] = cap
return capacity, details
# 示例
np.random.seed(42)
daily_volume = pd.Series(
np.random.lognormal(20, 0.5, 252) # 模拟日成交量
)
cap, details = capacity_estimate(daily_volume)
print(f"策略容量估算: {cap:,.0f} 元")
for k, v in details.items():
print(f" {k}: {v:,.0f} 元")五、实盘 vs 回测差异分析
5.1 差异来源
实盘表现与回测结果之间往往存在显著差异,这是量化交易中最常见也最棘手的问题。
回测 vs 实盘差异来源
┌─────────────────────────────────────────────────┐
│ 差异来源 │
├──────────┬──────────┬──────────┬────────────────┤
│ 执行成本 │ 滑点 │ 流动性 │ 模型假设 │
│ │ │ │ │
│ · 佣金 │ · 市价冲击│ · 涨跌停 │ · 幸存偏差 │
│ · 印花税 │ · 延迟执行│ · 停牌 │ · 前视偏差 │
│ · 过户费 │ · 部分成交│ · 闪崩 │ · 过度拟合 │
│ │ │ │ · 制度变化 │
└──────────┴──────────┴──────────┴────────────────┘
class PerformanceAttribution:
"""
绩效归因与实盘差异分析工具类
功能:Brinson 归因、因子归因、Alpha 分解、衰减监控、
实盘差异分析
"""
def __init__(self, backtest_returns, live_returns=None,
benchmark_returns=None, factor_returns=None):
"""
参数
----
backtest_returns : Series
回测收益率
live_returns : Series
实盘收益率
benchmark_returns : Series
基准收益率
factor_returns : DataFrame
因子收益率
"""
self.bt_returns = backtest_returns
self.live_returns = live_returns
self.bm_returns = benchmark_returns
self.factor_returns = factor_returns
# ---- 基本绩效指标 ----
def basic_metrics(self, returns):
"""计算基本绩效指标"""
if returns is None or len(returns) == 0:
return {}
annual_factor = 252
mean_annual = returns.mean() * annual_factor
vol_annual = returns.std() * np.sqrt(annual_factor)
sharpe = mean_annual / vol_annual if vol_annual > 0 else 0
# 最大回撤
cumret = (1 + returns).cumprod()
peak = cumret.cummax()
drawdown = (cumret - peak) / peak
max_dd = drawdown.min()
# Calmar 比率
calmar = mean_annual / abs(max_dd) if max_dd != 0 else 0
# 胜率
win_rate = (returns > 0).mean()
# 偏度和峰度
skew = returns.skew()
kurt = returns.kurtosis()
return {
'年化收益': mean_annual,
'年化波动率': vol_annual,
'Sharpe': sharpe,
'最大回撤': max_dd,
'Calmar': calmar,
'胜率': win_rate,
'偏度': skew,
'峰度': kurt,
}
# ---- 实盘差异分析 ----
def live_vs_backtest(self):
"""
分析实盘与回测的差异
返回
----
comparison : dict
差异分析结果
"""
if self.live_returns is None:
print("警告:未提供实盘数据")
return None
bt_metrics = self.basic_metrics(self.bt_returns)
live_metrics = self.basic_metrics(self.live_returns)
comparison = {
'指标': list(bt_metrics.keys()),
'回测值': list(bt_metrics.values()),
'实盘值': list(live_metrics.values()),
}
# 计算差异
diff = {}
for key in bt_metrics:
bt_val = bt_metrics[key]
live_val = live_metrics.get(key, 0)
# 相对差异
if bt_val != 0 and abs(bt_val) > 1e-6:
rel_diff = (live_val - bt_val) / abs(bt_val)
else:
rel_diff = 0
diff[key] = {
'回测': bt_val,
'实盘': live_val,
'绝对差异': live_val - bt_val,
'相对差异': rel_diff,
}
return diff
# ---- 收益差异归因 ----
def return_gap_attribution(self, costs_bps=5, slippage_bps=3):
"""
将回测与实盘收益差异归因到不同来源
参数
----
costs_bps : float
估计交易成本(基点/天)
slippage_bps : float
估计滑点成本(基点/天)
返回
----
gap_analysis : dict
差异归因
"""
if self.live_returns is None:
return None
# 对齐日期
common_idx = self.bt_returns.index.intersection(
self.live_returns.index)
bt = self.bt_returns.loc[common_idx]
live = self.live_returns.loc[common_idx]
# 总差异
total_gap = (bt.mean() - live.mean()) * 252 * 10000 # 基点
# 估算各项成本
estimated_cost = costs_bps # 基点/天
estimated_slippage = slippage_bps # 基点/天
unexplained = total_gap - estimated_cost - estimated_slippage
gap_analysis = {
'回测年化收益(bps)': bt.mean() * 252 * 10000,
'实盘年化收益(bps)': live.mean() * 252 * 10000,
'总差异(bps)': total_gap,
'交易成本(bps)': estimated_cost,
'滑点成本(bps)': estimated_slippage,
'未解释部分(bps)': unexplained,
'成本解释比例': (
(estimated_cost + estimated_slippage) / total_gap
if total_gap != 0 else 0
),
}
return gap_analysis
# ---- 综合报告 ----
def full_report(self):
"""生成完整的绩效归因报告"""
print("=" * 60)
print(" 绩效归因综合报告")
print("=" * 60)
# 基本指标
bt_metrics = self.basic_metrics(self.bt_returns)
print("\n--- 回测绩效指标 ---")
for k, v in bt_metrics.items():
print(f" {k:12s}: {v:.4f}" if isinstance(v, float) else f" {k:12s}: {v}")
# 实盘对比
if self.live_returns is not None:
comparison = self.live_vs_backtest()
print("\n--- 实盘 vs 回测差异 ---")
for metric, vals in comparison.items():
print(f" {metric:12s}: "
f"回测={vals['回测']:.4f}, "
f"实盘={vals['实盘']:.4f}, "
f"差异={vals['相对差异']:.1%}")
gap = self.return_gap_attribution()
print("\n--- 收益差异归因 ---")
for k, v in gap.items():
if isinstance(v, float):
print(f" {k:20s}: {v:.1f} bps" if v > 10 else f" {k:20s}: {v:.2%}")
else:
print(f" {k:20s}: {v}")
# 因子归因
if self.factor_returns is not None:
attr = factor_attribution(
self.bt_returns, self.factor_returns)
print("\n--- 因子归因 ---")
print(f" Alpha 年化: {attr['alpha 年化']:.2%}")
print(f" 因子总贡献: {attr['因子总贡献 年化']:.2%}")
print(f" R²: {attr['R²']:.4f}")
# 衰减监控
signals = decay_detector(self.bt_returns)
n_decay = signals['Alpha 衰减'].sum()
print(f"\n--- 策略衰减 ---")
print(f" 衰减信号数: {n_decay}")
print("\n" + "=" * 60)
# 使用示例
np.random.seed(42)
n_days = 500
dates = pd.bdate_range('2023-01-01', periods=n_days)
# 回测收益
bt_returns = pd.Series(
np.random.normal(0.001, 0.015, n_days), index=dates
)
# 实盘收益(扣除滑点和成本)
live_returns = pd.Series(
bt_returns.values - 0.0003 - np.random.normal(0, 0.002, n_days),
index=dates
)
# 基准收益
bm_returns = pd.Series(
np.random.normal(0.0005, 0.012, n_days), index=dates
)
# 因子收益
factor_ret = pd.DataFrame(
np.random.normal(0.001, 0.008, (n_days, 4)),
columns=['Market', 'Size', 'Value', 'Momentum'],
index=dates
)
pa = PerformanceAttribution(
bt_returns, live_returns, bm_returns, factor_ret
)
pa.full_report()六、关键指标速查表
| 指标 | 公式/定义 | 良好范围 | 含义 |
|---|---|---|---|
| Brinson 配置效应 | 正值为优 | 行业权重偏离的贡献 | |
| Brinson 选股效应 | 正值为优 | 行业内选股能力 | |
| Information Ratio | > 0.5 为优 | 单位跟踪误差的超额 | |
| Alpha 衰减 Z-score | 滚动均值偏离全样本 | |z| < 2 正常 | Alpha 稳定性 |
| 回测差异 | 越小越好 | 执行质量 | |
| 容量参与率 | 交易量 / 总成交量 | < 5% 安全 | 流动性风险 |
七、实战检查清单
在上线策略后,定期进行以下检查:
┌──────────────────────────────────────────────────────────┐
│ 绩效归因检查清单 │
├──────────────────────────────────────────────────────────┤
│ │
│ □ 每周: │
│ □ 本周 Alpha vs 上周 │
│ □ 各因子贡献变化 │
│ □ 回测与实盘收益差异 │
│ │
│ □ 每月: │
│ □ Brinson 归因分析 │
│ □ 因子暴露偏离监控 │
│ □ 滚动 Sharpe / IR 趋势 │
│ □ 策略容量检查 │
│ │
│ □ 每季度: │
│ □ 策略衰减综合评估 │
│ □ 交易成本 vs 预期 │
│ □ 与同类策略相关性 │
│ □ 策略参数是否需要重训练 │
│ │
└──────────────────────────────────────────────────────────┘
总结
| 归因维度 | 核心工具 | 关键洞察 |
|---|---|---|
| 收益分解 | Brinson 模型 | 区分配置 vs 选股贡献 |
| 风险来源 | 因子归因 | 识别真正的 Alpha |
| Alpha 质量 | IR / 衰减监控 | Alpha 稳定性比大小更重要 |
| 执行质量 | 实盘差异分析 | 成本是实盘的头号敌人 |
| 持续监控 | 滚动指标 | 早发现早处理 |
核心原则:不知道钱从哪里来的收益,是最危险的收益。定期归因是策略长期生存的保障。
模块完结:恭喜你完成了风险管理模块的全部学习。回顾 模块首页 巩固知识,或前往其他模块继续学习。