# Multi-strategy portfolios (/features/strategies/multi-strategy-portfolios)

How do you combine trading strategies and allocate between them? In VBT, each strategy's portfolio
can be treated as an asset: stack the strategies as columns, then weight them equally or by an
optimizer and rebalance on a schedule, with fees, in one more backtest.

```python title="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(  # (1)
...     {"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")  # (2)
>>> inv_vol = vbt.PFO.from_optimize_func(
...     wrapper, inverse_vol, returns, every="M", lookback_period="63D"
... )
>>> period = slice("2010-05", None)  # (3)
>>> 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
```

1.  The value of each strategy's portfolio becomes the price of a synthetic asset.
2.  Each month, the allocation resets the two strategies to half of the combined value each.
3.  Compares every portfolio from May 2010, after the first inverse-volatility allocation.

Growth of a trend strategy, a mean-reversion strategy, and two combinations of both on SPY from 2010 to 2024. [Figure data (JSON)](/assets/figures/features/strategies/multi-strategy-value.561b3ace1ecb.json)

Combining the two strategies lowered the drawdown below that of either one, but it did not beat the
trend strategy on return or Sharpe ratio, since the strategies were 62% correlated and both held
SPY. Inverse volatility did worse than equal weights: the mean-reversion strategy spends most days
in cash, looks less volatile, and so received more capital despite earning less. Combinations help
most when strategies trade different markets or behave differently.

## Choose how strategies work together \[#choose-how-strategies-work-together]

You can combine different entry rules, parameter sets, or markets. The choice is how their capital
should interact:

| What you want to test                                          | How to combine the strategies                                        |
| -------------------------------------------------------------- | -------------------------------------------------------------------- |
| Give each strategy a fixed starting allocation and let it grow | Stack finished portfolios and apply static weights.                  |
| Move capital between strategies on a schedule                  | Treat their portfolio values as prices and backtest the allocations. |
| Let strategies compete for the same cash as they trade         | Simulate their orders together in one cash-sharing group.            |

## Strategies as columns \[#strategies-as-columns]

Strategies with the same index are columns of one portfolio. They can use different simulation
methods, such as signals for one and explicit orders for another, and still be analyzed together.
`vbt.PF.column_stack` joins finished portfolios, and `group_by=True` treats them as one account that
never rebalances, so each strategy keeps the capital it started with and grows or shrinks with its
own results. `apply_weights` scales each strategy's capital, orders, and cash flows by a fixed
weight without simulating again:

```python title="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),  # (1)
... }
>>> 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
```

1.  `rescale=True` keeps the total capital: weights of 0.7 and 0.3 become multipliers of 1.4 and 0.6.

Left alone, the 50/50 mix drifted toward the trend strategy as it outgrew mean reversion, which
earned more than rebalancing monthly back to half each, with a slightly deeper drawdown. Weights
apply per strategy for the whole period, not per bar. To rebalance between strategies, use their
values as prices for a portfolio optimizer, as in the first example. Every allocation method on the
[Portfolio optimization](/features/optimization/portfolio-optimization/) page applies, from fixed
weights to PyPortfolioOpt and Riskfolio-Lib, and the fees of rebalancing are charged on the strategy
level.

### Share cash while strategies trade \[#share-cash-while-strategies-trade]

When strategies must draw from one cash balance, put their strategy and asset columns in the same
simulation group with `group_by=True` and `cash_sharing=True`. Separate groups give each group its
own cash. The [Portfolio accounting](/features/backtesting/portfolio-accounting/) page shows how
cash, positions, and value are tracked.

This lets one strategy use cash released by another. Position sizes and the order sequence decide
how that cash is used when several signals arrive together. For rules that depend on current
holdings, such as a limit on total open positions, use the callbacks described in
[Event-driven backtesting](/features/backtesting/event-driven-backtesting/#stateful-trading-rules).

## Inspect the mix and its components \[#inspect-the-mix-and-its-components]

Stacking keeps the original order records, so you can inspect a strategy's trades as well as the
combined return and drawdown. Give the stacked portfolios strategy names to select them later,
including when several trade the same symbol. Compare their return series and correlations to see
whether another strategy adds different behavior or mostly repeats an existing one.

The rebalanced example has two levels of results: the original trend and mean-reversion portfolios
contain their SPY trades, while the combined portfolio records allocations between those strategies.
Keep both to connect the combined performance to the underlying trades. The
[Trade analytics](/features/analytics/trade-analytics/) page covers winners, losers, and holding
periods.

Compare candidate weights and rebalance schedules over several periods, including periods held out
from fitting the allocation.
[Walk-forward optimization](/features/optimization/time-series-cross-validation/) applies that
process to rolling training and test windows.

## Recurring investing and dollar-cost averaging \[#recurring-investing-and-dollar-cost-averaging]

Cash deposits model saving on a schedule. Depositing $100 at the start of each month and buying with
it compares to investing the same total at once:

```python title="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),  # (1)
...     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
```

1.  Buys with all available cash on each deposit day and does nothing on other days.

In a market that rose for most of the decade, investing everything at once ended with 58% more.
Monthly deposits buy at many prices, which smooths the entry, but leave most of the money uninvested
for years. The same deposits work with any strategy, so a rule such as buying more after drawdowns
can be tested against plain monthly buying. Deposits can also fund a portfolio optimizer's scheduled
allocations, so recurring contributions and rebalancing can be tested together.

Use portfolio returns to compare strategy performance alongside final account values. VBT's return
calculation accounts for cash deposits and withdrawals, so adding money is not counted as a trading
gain. [Portfolio accounting](/features/backtesting/portfolio-accounting/#cash-deposits) covers the
cash-flow side of the comparison.

!!! info "Tutorial"
    The members-only [Portfolio optimization](https://members.vectorbt.pro/tutorials/portfolio-optimization/)
    tutorial builds allocation and optimization functions, including allocations that react to the
    simulated portfolio.


## Related pages

*   [Portfolio optimization](/features/optimization/portfolio-optimization/): Allocate with your own functions or optimizer libraries and backtest rebalancing
*   [Multidimensional research](/features/tooling/multidimensional-research/): Run assets, parameters, and strategies as labeled columns, then index and stack them
*   [Portfolio accounting](/features/backtesting/portfolio-accounting/): Track shared cash, deposits, dividends, positions, records, and exposure per bar
*   [Cross-sectional strategies](/features/strategies/cross-sectional-strategies/): Rank a universe each period, rotate into the leaders, and test factors by quintile