功能

多策略投资组合

将策略堆叠为同一投资组合、配置权重,并比较定期投资计划

如何组合交易策略并在它们之间分配资金?在 VBT 中,每个策略的投资组合都可以视为一项资产:将策略堆叠为多列,再使用等权或优化器分配权重,最后通过另一次回测,按计划再平衡并计入手续费。

Combine a trend and a mean-reversion strategy, equally and by inverse volatility
spy = vbt.YFData.pull("SPY", start="2010-01-01", end="2025-01-01")
fast = spy.close.vbt.rolling_mean(50)
slow = spy.close.vbt.rolling_mean(200)
trend = vbt.PF.from_signals(spy, fast > slow, fast < slow, fees=0.001)
rsi = vbt.RSI.run(spy.close, window=2).rsi
reversion = vbt.PF.from_signals(spy, rsi < 10, rsi > 70, fees=0.001)

strategies = pd.concat(  
    {"trend": trend.value, "reversion": reversion.value}, axis=1
)
returns = vbt.RepEval(
    "strategies.pct_change().iloc[index_slice]", context=dict(strategies=strategies)
)

def inverse_vol(returns):
    inv = 1 / returns.std()
    return inv / inv.sum()

wrapper = vbt.ArrayWrapper.from_obj(strategies)
fifty = vbt.PFO.from_uniform(wrapper, every="M")  
inv_vol = vbt.PFO.from_optimize_func(
    wrapper, inverse_vol, returns, every="M", lookback_period="63D"
)
period = slice("2010-05", None)  
pfs = {
    "Trend": trend,
    "Mean reversion": reversion,
    "50/50 monthly": fifty.simulate(strategies, fees=0.001),
    "Inverse volatility": inv_vol.simulate(strategies, fees=0.001),
}
metrics = ["total_return", "sharpe_ratio", "max_dd"]
stats = {k: pf.loc[period].stats(metrics) for k, pf in pfs.items()}
pd.DataFrame(stats).astype(float).round(2)
                   Trend  Mean reversion  50/50 monthly  Inverse volatility
Total Return [%]  285.49           51.41         145.83               86.76
Sharpe Ratio        0.89            0.39           0.74                0.57
Max Drawdown [%]   33.72           31.79          27.95               28.11
2010 至 2024 年 SPY 趋势策略、均值回归策略及两种组合方案的净值增长 图表数据(JSON)

将两个策略组合后,回撤低于任一单独策略,但收益和夏普比率均未超过趋势策略,因为两者的相关性为 62%,且都持有 SPY。波动率倒数配置不如等权:均值回归策略多数时间持有现金,表面波动更低,因此尽管收益更少,却获得了更多资金。策略交易不同市场或行为差异更大时,组合通常更有帮助。

选择策略之间的协作方式

您可以组合不同入场规则、参数集或市场。需要决定的是,它们的资金如何交互:

您想测试什么如何组合策略
为各策略分配固定初始资金,让它们自行增长堆叠已完成的投资组合,并应用静态权重。
按计划在策略之间调配资金将策略投资组合的净值视为价格,对配置方案进行回测。
让策略在交易时竞争同一笔现金在同一现金共享组中,一起模拟它们的订单。

将策略作为列

具有相同索引的策略可以组成一个投资组合的不同列。即使一个策略使用信号,另一个使用显式订单等不同模拟方法,也能一起分析。vbt.PF.column_stack 合并已完成的投资组合,group_by=True 将它们视为一个永不再平衡的账户,因此各策略保留各自的初始资金,并随自身结果增长或缩减。apply_weights 可按固定权重缩放每个策略的资金、订单和现金流,无须重新模拟:

Hold the two strategies without rebalancing
combined = vbt.PF.column_stack(
    [trend, reversion], wrapper_kwargs=dict(group_by=True)
)
static = {
    "50/50 static": combined,
    "70/30 static": combined.apply_weights([0.7, 0.3], rescale=True),  
}
stats.update({k: pf.loc[period].stats(metrics) for k, pf in static.items()})
pd.DataFrame(stats).iloc[:, 2:].astype(float).round(2)
                  50/50 monthly  Inverse volatility  50/50 static  70/30 static
Total Return [%]         145.83               86.76        166.90        213.95
Sharpe Ratio               0.74                0.57          0.77          0.83
Max Drawdown [%]          27.95               28.11         29.88         31.70

不进行调整时,50/50 组合会随着趋势策略增长更快而逐渐偏向它。与每月恢复各占一半相比,这种做法收益更高,但回撤略深。权重按策略应用于整个时期,并非逐根 K 线设置。若要在策略间再平衡,可像首个示例一样,将它们的净值作为投资组合优化器的价格。投资组合优化页的所有配置方法都适用,从固定权重到 PyPortfolioOpt 和 Riskfolio-Lib,再平衡手续费在策略层面收取。

策略交易时共享现金

若策略必须使用同一现金余额,可通过 group_by=True 和 cash_sharing=True,将它们的策略列与资产列放入同一模拟组。分开的组各自持有独立现金。投资组合记账页说明如何跟踪现金、持仓和净值。

这样,一个策略可以使用另一策略释放的现金。多个信号同时出现时,仓位大小和订单顺序决定如何分配资金。若规则依赖当前持仓,例如限制未平仓头寸总数,可使用事件驱动回测中介绍的回调。

检查组合及其组成部分

堆叠会保留原始订单记录,因此可以同时检查单个策略的交易以及组合收益和回撤。为堆叠后的投资组合添加策略名称,便于之后选择,即使多个策略交易同一品种也不受影响。比较收益序列与相关性,判断新增策略是否带来不同行为,还是主要重复现有策略。

再平衡示例有两层结果:原始趋势和均值回归投资组合包含各自的 SPY 交易,组合投资组合则记录策略之间的资金配置。同时保留两者,就能将组合表现追溯到底层交易。交易分析页介绍盈利交易、亏损交易和持有期分析。

在多个时期比较候选权重和再平衡计划,包括未参与配置拟合的留出时期。前向滚动优化将这一过程应用于滚动训练和测试窗口。

定期投资与定额投资

现金入金可以模拟定期储蓄。下面比较每月初存入并买入 100 美元,与一次性投入相同总额:

Invest $100 every month, or $12,000 at the start
close = spy.close.loc["2015":"2024"]
month = pd.Series(close.index.month, index=close.index)
first_days = month != month.shift(1)
deposits = pd.Series(np.where(first_days, 100.0, 0.0), index=close.index)
dca = vbt.PF.from_orders(
    close,
    size=np.where(first_days, np.inf, np.nan),  
    init_cash=0,
    cash_deposits=deposits,
)
lump = vbt.PF.from_orders(
    close,
    size=np.where(np.arange(len(close)) == 0, np.inf, np.nan),
    init_cash=deposits.sum(),
)
print(deposits.sum(), round(dca.final_value, 2), round(lump.final_value, 2))
12000.0 25724.02 40748.85

在这十年大部分时间上涨的市场中,一次性投入的最终净值高出 58%。每月投入会在不同价格买入,使入场价格更平滑,但也使大部分资金多年处于未投资状态。同一入金计划适用于任何策略,因此可以将回撤后加大买入等规则与普通月度买入比较。入金也可为投资组合优化器的定期配置提供资金,从而一同测试定期投入和再平衡。

比较策略表现时,除最终账户净值外,也应使用投资组合收益率。VBT 的收益率计算考虑入金和出金,不会将新增资金当作交易收益。投资组合记账介绍这种比较中的现金流处理。

教程

会员专享的投资组合优化(英文)教程介绍配置和优化函数的构建,包括根据模拟投资组合状态调整的配置方法。

版权所有 © 2021–2026 Oleg Polakow。保留所有权利。

本站内容与文档供使用和评估 VectorBT PRO 以及教育用途。任何其他用途,包括开发或支持竞争产品或服务,均须事先取得书面同意。