Skip to content

4.9 多策略组合回测

概念详解

实战中,量化基金很少只跑一个策略。多策略组合(Multi-Strategy Portfolio)通过分散不同风格的策略,降低单一策略失效带来的风险。但简单的"拼盘式"组合收益并不一定更好——只有当各策略的相关性较低时,组合才有" (1+1>2 )"的效果。

名词解释:策略相关性

两个策略的日收益率序列的 Pearson 相关系数。相关性越接近 +1,组合分散化效果越差;越接近 0 或负,组合分散化效果越好。金融市场的多数策略两两相关性为 0.3-0.7(显著正相关但不完全)。

名词解释:风险平价

风险平价(Risk Parity)是一种资金分配方法,目标是让每个策略对组合总风险的贡献相等,而非资金量相等。例如高夏普但低波动策略分配的资金 > 高波动策略的资金,直到两者对组合的边际风险贡献相同。

为什么需要多策略?

场景单策略多策略
熊市单一趋势策略可能全亏趋势+均值回归+套利,部分对冲
风格切换策略失效风格分散覆盖多个 regime
黑天鹅单策略极端回撤部分策略对冲或反向
资金管理受单策略容量限制多策略分散容量压力

注意:多策略不是越多越好。3-5 个互补的策略 >> 20 个同质化策略。

资金分配 4 种主流方法

方法公式适用场景
等权 (1/N)wi=1/N入门、无法估计相关性时
风险平价wi1/σi降低高波动策略主导
最大夏普 (Markowitz)maxwμpσp准确估计协方差时
Kelly 公式(分数)wi=μiNσi2,部分分数如 0.25单策略最优加仓

再平衡的 2 种触发方式

触发方式优点缺点
固定周期(每月/每季)简单、易于监控频繁再平衡会增加成本
阈值触发(权重偏离>5%)节省成本时机选择较主观

性能归因

性能归因(Performance Attribution)回答"收益从哪里来"。多策略组合中,要区分两类:

  1. 策略贡献:哪个策略赚得最多?
  2. 时间贡献:策略赚钱是大多数时候还是只在某段行情?

常用工具:Brinson 模型(行业归因)、Fundamental Law of Active Management (信息系数 × 决策广度)。

数学原理

多策略组合的核心是现代投资组合理论(Modern Portfolio Theory, MPT)。下面对几个关键公式给出推导。

1. 组合收益与组合波动率

N 个策略的收益率为 {r1,r2,,rN},权重为 {w1,w2,,wN},wi=1

E[rp]=i=1NwiμiVar[rp]=ijwiwjσij=wTΣw

其中 σij=ρijσiσj

2. 加权夏普比率

Sharpep=E[rp]rfwTΣw

关键洞察:组合夏普未必等于各策略夏普的均值或加权平均——取决于相关性矩阵。当 ρij 较低时,组合夏普可能高于单个策略最高夏普。

3. Markowitz 最大夏普权重

约束最优化问题:

maxwμTwwTΣws.t.wi=1,wi0

关键警告:Σ 估计误差导致 Markowitz 优化结果非常不稳定,所以实盘中常用 Ledoit-Wolf 收缩(shrinkage)估计协方差矩阵。

4. 风险平价权重

每个策略对组合总风险的边际贡献相等:

wiσpwi=constwi1σiρii

实际中常用迭代算法(CCP, Cyclical Coordinate Descent)求出近似解。

Python实战

📌 案例:3 种策略组合 + 资金分配 + 再平衡

PYTHON142 行 · 5.0 KB
📄此处有展示代码142 行 · 5.0 KB展开 ▼
python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from typing import Dict, List, Tuple


def trend_following_strategy(returns: pd.Series, fast: int = 10, slow: int = 50) -> pd.Series:
    """趋势策略:动量
    短均线上穿长均线 → 持有,下穿 → 空仓
    """
    prices = (1 + returns).cumprod()
    fast_ma = prices.rolling(fast).mean()
    slow_ma = prices.rolling(slow).mean()
    signal = (fast_ma > slow_ma).astype(int).shift(1).fillna(0)
    return signal


def mean_reversion_strategy(returns: pd.Series, window: int = 20) -> pd.Series:
    """均值回归策略:反向
    短期大幅下跌后买入,反弹后卖出
    """
    prices = (1 + returns).cumprod()
    r = prices.pct_change()
    z_score = (r - r.rolling(window).mean()) / r.rolling(window).std()
    signal = (z_score < -1.5).astype(int).shift(1).fillna(0)
    return signal


def long_short_strategy(returns: pd.Series, half_life: int = 30) -> pd.Series:
    """套利(长短腿)风格策略:横截面动量
    假设有多只股票池,做多强势做空弱势。这里用绝对动量近似
    """
    prices = (1 + returns).cumprod()
    momentum = prices.pct_change(half_life)
    signal = (momentum > 0).astype(int).shift(1).fillna(0)
    return signal


def compute_strategy_returns(returns: pd.Series, signal: pd.Series,
                            fee: float = 0.0003) -> pd.Series:
    """根据持仓信号计算策略净收益(扣除成本)"""
    str_returns = returns * signal
    turnover = signal.diff().abs().fillna(signal.iloc[0]) * fee
    return str_returns - turnover


def generate_correlated_returns(n_days: int = 1260,
                                n_assets: int = 1,
                                true_sharpe: float = 1.0,
                                vol: float = 0.02,
                                regime: str = 'normal') -> pd.Series:
    """生成一段 Heston 风格的收益序列,确保三个策略在其中表现有差异"""
    np.random.seed(0)
    dates = pd.bdate_range('2020-01-01', periods=n_days)

    # 模拟不同 regime
    if regime == 'normal':
        # 正常市场:动量有效,均值回归震荡
        n = n_days
        v = vol**2
        r = np.random.normal(0.0005, vol, n)
    elif regime == 'trending':
        # 强趋势:动量大胜
        r = np.concatenate([
            np.cumsum(np.random.normal(0.003, 0.015, n_days // 2)),
            np.cumsum(np.random.normal(0.0005, 0.015, n_days // 2)),
        ]) - np.concatenate([
            np.zeros(n_days // 2),
            np.full(n_days // 2, np.random.uniform(0.5, 1.5)),
        ])
    else:
        r = np.random.normal(-0.0005, 0.03, n_days)

    return pd.Series(r, index=dates, name='underlying_returns')


# ============================================================
# 模拟 3 个不同风格的策略
# ============================================================

np.random.seed(42)
n_days = 1260
dates = pd.bdate_range('2020-01-01', periods=n_days)

# 构造 3 个独立的资产,使其风格不同
# (这样我们做组合时,有"分散化空间")
def gen_independent_returns(n_days, mean, vol, seed):
    np.random.seed(seed)
    return pd.Series(
        np.random.normal(mean, vol, n_days),
        index=dates,
        name=f'asset_{seed}'
    )

asset_trend = gen_independent_returns(n_days, 0.0008, 0.018, 1)   # 高均值低波动 → 趋势行情
asset_meanrev = gen_independent_returns(n_days, 0.0003, 0.025, 2)  # 中等 → 均值回归震荡
asset_ls = gen_independent_returns(n_days, 0.0005, 0.022, 3)       # 中等 → 套利风格

# 计算三策略各自的持仓信号和净收益
signal_trend = trend_following_strategy(asset_trend)
signal_meanrev = mean_reversion_strategy(asset_meanrev)
signal_ls = long_short_strategy(asset_ls)

ret_trend = compute_strategy_returns(asset_trend, signal_trend)
ret_meanrev = compute_strategy_returns(asset_meanrev, signal_meanrev)
ret_ls = compute_strategy_returns(asset_ls, signal_ls)

returns_df = pd.DataFrame({
    'Trend': ret_trend,
    'MeanRev': ret_meanrev,
    'LongShort': ret_ls,
})
print("3 个策略的相关性矩阵:")
print(returns_df.corr().round(3))

# ============================================================
# 计算单策略绩效
# ============================================================

def perf_stats(returns: pd.Series, label: str = 'Series') -> Dict[str, float]:
    equity = (1 + returns).cumprod()
    total_return = equity.iloc[-1] - 1
    ann_return = (1 + total_return) ** (252 / len(returns)) - 1
    ann_vol = returns.std() * np.sqrt(252)
    sharpe = ann_return / ann_vol if ann_vol > 0 else 0
    max_dd = (equity / equity.cummax() - 1).min()
    return {
        f'{label} - 总收益': f"{total_return:.2%}",
        f'{label} - 夏普': f"{sharpe:.2f}",
        f'{label} - 最大回撤': f"{max_dd:.2%}",
        f'{label} - 年化波动': f"{ann_vol:.2%}",
    }


print("\n" + "=" * 50)
print("单策略表现")
print("=" * 50)
for col in returns_df.columns:
    stats = perf_stats(returns_df[col], col)
    for k, v in stats.items():
        print(f"  {k}: {v}")
    print()
点击展开可浏览运行结果
3 个策略的相关性矩阵:
           Trend  MeanRev  LongShort
Trend      1.000   -0.002     -0.057
MeanRev   -0.002    1.000     -0.006
LongShort -0.057   -0.006      1.000

==================================================
单策略表现
==================================================
  Trend - 总收益: 130.98%
  Trend - 夏普: 0.82
  Trend - 最大回撤: -31.97%
  Trend - 年化波动: 22.33%

  MeanRev - 总收益: -2.39%
  MeanRev - 夏普: -0.05
  MeanRev - 最大回撤: -23.49%
  MeanRev - 年化波动: 9.67%

  LongShort - 总收益: -37.90%
  LongShort - 夏普: -0.38
  LongShort - 最大回撤: -54.12%
  LongShort - 年化波动: 24.17%

📌 案例:4 种资金分配方法对比

PYTHON96 行 · 3.1 KB
📄此处有展示代码96 行 · 3.1 KB展开 ▼
python
# 模块级 fixture:3 策略模拟收益(让 fence 自包含)
np.random.seed(42)
n_days = 1260
_dates = pd.date_range('2020-01-01', periods=n_days, freq='D')
returns_df = pd.DataFrame({
    'Trend':     np.random.normal(0.0008, 0.018, n_days),
    'MeanRev':   np.random.normal(0.0003, 0.025, n_days),
    'LongShort': np.random.normal(0.0005, 0.022, n_days),
}, index=_dates)


def equal_weight_allocation(n_strategies: int) -> np.ndarray:
    """等权分配"""
    return np.array([1.0 / n_strategies] * n_strategies)


def risk_parity_allocation(returns: pd.DataFrame) -> np.ndarray:
    """风险平价分配:权重 ∝ 1/σ(波动率的倒数)"""
    volatilities = returns.std() * np.sqrt(252)
    inverse_vol = 1.0 / volatilities
    weights = inverse_vol / inverse_vol.sum()
    return weights.values


def max_sharpe_allocation(returns: pd.DataFrame, risk_free: float = 0.025) -> np.ndarray:
    """最大夏普分配:Markowitz 优化
    注意:实盘中需用 Ledoit-Wolf 收缩,这里用 sklearn-标准实现
    """
    from scipy.optimize import minimize

    mu = returns.mean() * 252 - risk_free
    cov = returns.cov() * 252

    n = len(mu)
    def neg_sharpe(w):
        p_ret = w @ mu
        p_vol = np.sqrt(w @ cov @ w)
        return -p_ret / (p_vol + 1e-9)

    # 约束:权重和为1,每个权重在 [0, 0.5]
    constraints = [{'type': 'eq', 'fun': lambda w: w.sum() - 1}]
    bounds = [(0, 0.5)] * n

    result = minimize(
        neg_sharpe,
        x0=np.array([1/n] * n),
        bounds=bounds,
        constraints=constraints,
        method='SLSQP'
    )
    return result.x


def kelly_allocation(returns: pd.DataFrame, fraction: float = 0.25) -> np.ndarray:
    """Kelly 公式分配(分数 Kelly,fraction=0.25 表示 1/4 Kelly)
    Kelly 的简化形式:w = μ / σ²,在多策略情况下用每个策略各自的 Kelly
    """
    mu = returns.mean() * 252
    var = returns.var() * 252
    kelly_weights = (mu / var).clip(lower=0)  # 不允许负权重(简化版)
    kelly_weights *= fraction
    kelly_weights /= kelly_weights.sum()  # 归一化
    return kelly_weights.values


# === 比较 4 种分配方法 ===
methods = {
    '等权 (1/N)': equal_weight_allocation(3),
    '风险平价': risk_parity_allocation(returns_df),
    '最大夏普': max_sharpe_allocation(returns_df),
    '分数 Kelly (0.25)': kelly_allocation(returns_df),
}

print("=" * 60)
print("4 种资金分配方法对比")
print("=" * 60)
print(f"{'方法':<20} {'Trend':>10} {'MeanRev':>10} {'LongShort':>10}")
print("-" * 60)
for name, weights in methods.items():
    print(f"{name:<20} {weights[0]:>10.2%} {weights[1]:>10.2%} {weights[2]:>10.2%}")


# === 计算每种组合的组合夏普 ===
def portfolio_sharpe(returns: pd.DataFrame, weights: np.ndarray) -> float:
    p_ret = returns @ weights
    ann_ret = p_ret.mean() * 252
    ann_vol = p_ret.std() * np.sqrt(252)
    return ann_ret / (ann_vol + 1e-9)


print("\n" + "=" * 60)
print("4 种分配方法下的组合夏普")
print("=" * 60)
for name, weights in methods.items():
    sharpe = portfolio_sharpe(returns_df, weights)
    print(f"  {name:<20}: {sharpe:.2f}")
点击展开可浏览运行结果
============================================================
4 种资金分配方法对比
============================================================
方法                        Trend    MeanRev  LongShort
------------------------------------------------------------
等权 (1/N)                 33.33%     33.33%     33.33%
风险平价                     39.72%     28.92%     31.36%
最大夏普                     50.00%     27.00%     23.00%
分数 Kelly (0.25)          60.95%     21.58%     17.48%

============================================================
4 种分配方法下的组合夏普
============================================================
  等权 (1/N)            : 1.33
  风险平价                : 1.42
  最大夏普                : 1.52
  分数 Kelly (0.25)     : 1.55

📌 案例:组合再平衡(含具体触发逻辑)

PYTHON98 行 · 4.1 KB
📄此处有展示代码98 行 · 4.1 KB展开 ▼
python
class PortfolioRebalancer:
    """组合再平衡器:支持固定周期 + 阈值触发两种模式"""

    def __init__(self, target_weights: np.ndarray, strategy_returns: pd.DataFrame,
                 rebalance_freq: str = 'M',  # 'D', 'W', 'M', 'Q'
                 threshold: float = 0.05):    # 5% 偏离阈值
        self.target_weights = target_weights
        self.returns = strategy_returns
        self.rebalance_freq = rebalance_freq
        self.threshold = threshold
        self.current_weights = target_weights.copy()
        self.rebalance_log = []
        self.equity = [1.0]
        self.weights_history = []

    def run(self) -> Tuple[pd.Series, pd.DataFrame]:
        """执行再平衡回测"""
        equity_curve = [1.0]
        weights_history = [self.current_weights.copy()]

        last_rebal_idx = 0
        for i in range(1, len(self.returns)):
            # 当日收益
            daily_ret = (self.returns.iloc[i] * self.current_weights).sum()
            equity_curve.append(equity_curve[-1] * (1 + daily_ret))

            # 更新当前权重
            new_w = self.current_weights * (1 + self.returns.iloc[i].values)
            new_w = new_w / new_w.sum()
            self.current_weights = new_w
            weights_history.append(new_w)

            # 检查是否需要再平衡
            deviation = np.abs(new_w - self.target_weights)
            days_since_last = i - last_rebal_idx

            # 触发条件 1:周期触发
            period_trigger = False
            if self.rebalance_freq == 'D':
                period_trigger = True
            elif self.rebalance_freq == 'W':
                period_trigger = (i % 5 == 0)
            elif self.rebalance_freq == 'M':
                period_trigger = (i % 21 == 0)

            # 触发条件 2:阈值触发
            threshold_trigger = deviation.max() > self.threshold

            if period_trigger or threshold_trigger:
                self.rebalance_log.append({
                    'date': self.returns.index[i],
                    'reason': 'period' if period_trigger else 'threshold',
                    'old_weights': self.current_weights.copy(),
                    'new_weights': self.target_weights.copy(),
                })
                self.current_weights = self.target_weights.copy()
                last_rebal_idx = i

        equity_series = pd.Series(equity_curve, index=self.returns.index[:len(equity_curve)])
        weights_df = pd.DataFrame(weights_history, index=self.returns.index)
        return equity_series, weights_df


def compare_rebalancing_methods():
    """比较固定周期再平衡 vs 阈值触发再平衡"""
    methods = {
        '不再平衡 (Buy & Hold)': None,
        '月度再平衡': PortfolioRebalancer(np.array([1/3]*3), returns_df, 'M', 0.05),
        '季度再平衡': PortfolioRebalancer(np.array([1/3]*3), returns_df, 'Q', 0.05),
        '阈值 5%': PortfolioRebalancer(np.array([1/3]*3), returns_df, 'NEVER', 0.05),
        '阈值 10%': PortfolioRebalancer(np.array([1/3]*3), returns_df, 'NEVER', 0.10),
    }

    print("=" * 70)
    print(f"{'方法':<25} {'年化收益':>10} {'夏普':>8} {'最大回撤':>10} {'再平衡次数':>10}")
    print("-" * 70)

    for name, engine in methods.items():
        if engine is None:
            # Buy & Hold
            p_ret = returns_df @ np.array([1/3]*3)
            ann_ret = p_ret.mean() * 252
            ann_vol = p_ret.std() * np.sqrt(252)
            sharpe = ann_ret / (ann_vol + 1e-9)
            eq = (1 + p_ret).cumprod()
            max_dd = (eq / eq.cummax() - 1).min()
            print(f"{name:<25} {ann_ret:>10.2%} {sharpe:>8.2f} {max_dd:>10.2%} {0:>10}")
        else:
            equity_curve, weights_df = engine.run()
            p_ret = equity_curve.pct_change().dropna()
            ann_ret = p_ret.mean() * 252
            ann_vol = p_ret.std() * np.sqrt(252)
            sharpe = ann_ret / (ann_vol + 1e-9)
            max_dd = (equity_curve / equity_curve.cummax() - 1).min()
            rebalances = len(engine.rebalance_log)
            print(f"{name:<25} {ann_ret:>10.2%} {sharpe:>8.2f} {max_dd:>10.2%} {rebalances:>10}")

compare_rebalancing_methods()
点击展开可浏览运行结果
📘 本段代码定义了 2 个函数/类:类 `PortfolioRebalancer`(组合再平衡器:支持固定周期 + 阈值触发两种模式)、函数 `compare_rebalancing_methods`(比较固定周期再平衡 vs 阈值触发再平衡)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。

📌 案例:性能归因

PYTHON31 行 · 1.1 KB
📄此处有展示代码31 行 · 1.1 KB展开 ▼
python
def performance_attribution(returns: pd.DataFrame, weights: np.ndarray) -> Dict[str, float]:
    """Brinson 模型简化版:策略贡献分解"""
    p_returns = returns @ weights
    n = len(returns.columns)

    contribution = {}
    for i, col in enumerate(returns.columns):
        # 每个策略的"加权累积贡献"
        cumulative_contrib = (returns[col] * weights[i]).sum()
        contribution[col] = cumulative_contrib

    total = sum(contribution.values())
    print(f"\n策略贡献度(% of 总贡献):")
    for col, contrib in contribution.items():
        pct = contrib / total if total != 0 else 0
        print(f"  {col}: {pct:.2%}")

    # 时间贡献:每个月哪个策略赚钱最多
    monthly_contrib = returns.apply(lambda x: x * weights[returns.columns.get_loc(x.name)])
    monthly_winner = monthly_contrib.idxmax(axis=1).value_counts()
    print(f"\n每月最佳策略次数:")
    for k, v in monthly_winner.items():
        print(f"  {k}: {v} 次")

    return contribution


print("=" * 60)
print("性能归因:3 个策略组合的贡献")
print("=" * 60)
contribution = performance_attribution(returns_df, np.array([1/3]*3))
点击展开可浏览运行结果
📘 本段代码定义了 1 个函数/类:函数 `performance_attribution`(Brinson 模型简化版:策略贡献分解)。该片段为教学展示(未包含独立运行的输入数据),可在实战练习中结合真实数据调用。

常见误区

  1. "组合后夏普必然提高":错。如果 3 个策略都强相关(同涨同跌),组合的夏普接近单策略最高值,不会有明显分散化效果。要看到 "1+1>2" 需要策略相关性 < 0.5。

  2. "用历史相关性代替未来":错。历史相关性充满噪声,真实相关性漂移(market regime change)经常导致历史低相关的策略在实际中变成高相关。

  3. "Markowitz 优化等于最优":错。Markowitz 优化依赖输入协方差矩阵,对估计误差极度敏感。实盘应至少加:① Ledoit-Wolf 收缩估计;② 最小实现方差;③ 等权作 fallback。

  4. "再平衡越频繁越好":错。再平衡产生的交易成本会侵蚀收益。在 A 股市场,月度或季度再平衡通常足够,频繁触发只增加手续费。

  5. "Kelly 一定是最好":理论上 Kelly 是最大长期增长,但实际中:① Kelly 假设"估计完美",实际不可能;② 全 Kelly 会导致极端回撤;③ 用 1/4 Kelly 是经验值,仍可能在尾部爆炸。

  6. "策略越多越好":错。20 个弱相关策略并不比 5 个高 Sharpe 策略更好。边际策略的回报递减,反而增加运营复杂度。

  7. "组合权重长期不变":错。市场 regime 切换需要调整资金分配。例如低波动率后高波动率来临,可临时降低高波动策略仓位。

组合资金分配饼图示例

策略等权风险平价最大夏普Kelly (1/4)
Trend33.3%38.2%45.1%40.5%
MeanRev33.3%28.7%22.4%31.2%
LongShort33.3%33.1%32.5%28.3%

解读:

  • 绿色:权重大于 35%(聚焦此策略)
  • 黄色:权重大于 30%(中度)
  • 红色:权重小于 30%(分散)

关键洞察:

  • 风险平价给低波动率策略(Trend)更高权重,逻辑合理。
  • 最大夏普集中度最高,但实盘风险也最大。
  • Kelly 介于两者之间,但 1/4 Kelly 已经接近"等权",说明单策略估计收益噪声大。

小测验

题目 1:3 个高相关性策略(两两 ρ=0.95)组合后,夏普一定比单策略高吗?

A. 是,多个策略必然提高组合夏普 B. 否,组合夏普接近单策略最高夏普,因为高相关性导致分散化失效 C. 看具体策略而不只看相关性 D. 不一定,取决于资金分配

查看答案与解析

答案:B

当 ρ=0.95 时,组合方差 ≈ 加权单策略方差,组合不会"分散"风险,夏普近似等于单策略加权平均而非最高值。要看到 "1+1>2" 必须 ρ < 0.5。

题目 2:实盘中为什么通常不用全 Kelly 而是 1/4 Kelly?

A. 全 Kelly 需要完美估计,实际不可达;且全 Kelly 在尾部会极端爆炸 B. 全 Kelly 计算太慢 C. 1/4 Kelly 是行业规定 D. Kelly 公式本来就是 1/4

查看答案与解析

答案:A

Kelly 公式需精确估计 μ、σ、ρ,实际中必然有偏差。把 Kelly 的位置缩到 1/4(或更小)是工程上的"折中":既保留 Kelly 的复利优势,又能在估计错误时显著降低回撤。

题目 3:以下哪种组合在 A 股市场最适合?

A. 6 个低相关性、低换手因子选股策略 B. 2 个高换手、双均线趋势策略 C. 5 个高相关性、仅依赖动量的策略 D. 10 个随机生成的"伪 alpha"策略

查看答案与解析

答案:A

A 既有分散化,又有低换手优势(成本低),且因子选股在 A 股有长期 alpha,是最佳组合。 B 双均线虽然是经典策略,但趋势策略在 A 股市场长期偏弱,且高换手侵蚀成本。 C 高相关性等同于没有分散,效果差。 D 随机策略不会有 alpha,但会引入随机噪声。

实战练习

  1. 实现 3-策略组合:把本节的 Trend/MeanRev/LongShort 三策略在合成数据上跑 5 年回测,输出:

    • 单策略净值曲线
    • 等权组合净值曲线
    • 最大夏普组合净值曲线
    • 列出组合后的夏普对比
  2. 相关性矩阵实验:用 Tushare 拉取沪深 300 成分股 2020-2024 年日线,计算多只股票两两相关性矩阵,验证"金融市场的相关性通常在 [0.3, 0.7]"这一经验规律。

  3. 资金分配对比:实现 Risk Parity 的完整算法(CCP / 牛顿法),与等权分配对比,在 3 个不同风格策略上的差异。

  4. 再平衡策略对比:对固定周期(月/季)与阈值触发(5%/10%)做对比回测,统计再平衡次数和最终净值差异。

  5. 压力测试下的组合:用 4.8 节的合成数据生成器构造"2008 风格"暴跌场景,看 3 个策略组合能否在单策略全回撤时保护下行。

延伸阅读

本章要点

  • 多策略组合通过分散化降低组合风险,但仅当策略相关性 < 0.5 时效果显著。高相关性等同于"假分散"。
  • 4 种资金分配方法各有取舍:等权最稳健、风险平价平衡、最大夏普最激进但估计敏感、Kelly 长期复利但尾部爆炸。实盘中等权 + 风险平价是最稳健的默认选择。
  • Markowitz 优化对协方差矩阵估计极度敏感,实盘中必须用 Ledoit-Wolf 收缩或最小实现方差作为稳健化。
  • 再平衡频率:A 股月度或季度足够,频繁再平衡只增加成本而无显著收益提升。阈值触发适合降低交易成本。
  • 性能归因:Brinson 模型分解策略贡献,但没有"最优"归因,常见工具只提供视角。
  • 多策略组合的反面:策略越多不代表越好,边际策略收益递减、运营复杂度上升,3-5 个互补策略通常足够。
  • Kelly 不是"免费午餐":Kelly 公式需精确估计,但收益分布真有负值时 Kelly 会破产。1/4 Kelly 是工程折中。
  • 核心洞察:多策略的真正价值不在于"赚更多",而在于"回撤更可控"。