Daily Drift Rebalancing for a 60/40 Stock and Bond Portfolio
Summary
The script demonstrates a classic allocation strategy that holds a portfolio with a target mix of 60% stocks and 40% bonds. It uses a drift rebalancer: when asset weights move away from their targets by a configured threshold, the strategy sells assets that have drifted furthest above target and buys those furthest below it. The exact assets and threshold are supplied through a separate configuration, which is not included in the excerpt.
The example runs a daily historical backtest over the stated date range using Alpaca data, with SPY as the benchmark, and prints results and filled-order details. The script is explicitly designed for backtesting and includes checks for paper credentials. It describes implementation and evaluation plumbing, but supplies no performance figures, transaction-cost analysis, or evidence that the allocation beats a benchmark. Outcomes therefore depend on the external configuration and backtesting assumptions.
Key ideas
- The example targets a 60% stock and 40% bond portfolio allocation.
- A drift rebalancer trades assets that deviate from target weights by a configured threshold.
- The strategy sells assets that are most overweight and buys those that are most underweight.
- The example uses daily historical backtesting and benchmarks against SPY.
- The asset list and rebalance threshold come from an external configuration not shown in the document.
Tags
Full text
# classic_60_40.py
```py
from datetime import datetime
import pytz
from lumibot.backtesting import AlpacaBacktesting
from lumibot.components.configs_helper import ConfigsHelper
from lumibot.credentials import ALPACA_TEST_CONFIG, IS_BACKTESTING
from lumibot.entities import Asset
from lumibot.example_strategies.drift_rebalancer import DriftRebalancer
from lumibot.tools.pandas import print_full_pandas_dataframes
from lumibot.traders.debug_log_trader import DebugLogTrader
print_full_pandas_dataframes()
"""
Strategy Description
This is an implementation of a classic 60% stocks, 40% bonds portfolio.
It demonstration the DriftRebalancer strategy and AlpacaBacktesting.
It rebalances a portfolio of assets to a target weight every time the asset drifts
by a certain threshold. The strategy will sell the assets that has drifted the most and buy the
assets that has drifted the least to bring the portfolio back to the target weights.
"""
if __name__ == "__main__":
configs_helper = ConfigsHelper(configs_folder="example_strategies")
parameters = configs_helper.load_config("classic_60_40_config")
if not IS_BACKTESTING:
print("This strategy is not meant to be run live. Please set IS_BACKTESTING to True.")
exit()
if not ALPACA_TEST_CONFIG:
print("This strategy requires an ALPACA_TEST_CONFIG config file to be set.")
exit()
if not ALPACA_TEST_CONFIG['PAPER']:
print(
"Even though this is a backtest, and only uses the alpaca keys for the data source"
"you should use paper keys."
)
exit()
tzinfo = pytz.timezone('America/New_York')
backtesting_start = tzinfo.localize(datetime(2022, 1, 1))
backtesting_end = tzinfo.localize(datetime(2025, 1, 1))
timestep = 'day'
auto_adjust = True
warm_up_trading_days = 0
refresh_cache = False
results, strategy = DriftRebalancer.run_backtest(
name="classic_60_40",
datasource_class=AlpacaBacktesting,
backtesting_start=backtesting_start,
backtesting_end=backtesting_end,
minutes_before_closing=0,
benchmark_asset=Asset("SPY"),
analyze_backtest=True,
parameters=parameters,
# For seeing logs (if using DebugLogTrader, set show_progress_bar to false)
trader_class=DebugLogTrader,
# show_progress_bar=True,
# AlpacaBacktesting kwargs
timestep=timestep,
market=parameters['market'],
config=ALPACA_TEST_CONFIG,
refresh_cache=refresh_cache,
warm_up_trading_days=warm_up_trading_days,
auto_adjust=auto_adjust,
)
print(results)
trades_df = strategy.broker._trade_event_log_df # noqa
filled_orders = trades_df[(trades_df["status"] == "fill")]
print(
"\nfilled_orders:\n",
f"{filled_orders[['time', 'symbol', 'type', 'side', 'status', 'price', 'filled_quantity', 'trade_cost']]}"
)
```Shown in full with attribution under the source's licence. Licence: GPL-3.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.