A Daily Buy-and-Hold Strategy for a Single Asset
Summary
This example implements a simple long-only strategy that buys one configurable asset and then holds it. It runs once per day, records the asset’s latest price, and adds that price to an indicator chart. If the strategy has no positions, it uses the portfolio value and current price to calculate a whole-share quantity, then submits a buy order. Later iterations do not add to the position while any position remains open.
The example demonstrates the basic structure of a strategy lifecycle and a historical simulation using daily data, with SPY as the configured asset and benchmark. It does not report simulation results or compare the approach with alternatives. The sizing rule does not reserve cash for costs or fractional shares, and the position check treats any existing position as a reason not to buy. The code is an instructional example rather than a complete account of risk controls or live-trading safeguards.
Key ideas
- The strategy checks for a position once per daily iteration and buys only when none exists.
- It sizes the purchase using portfolio value divided by the asset’s latest price, rounded down to a whole share.
- The asset symbol is configurable, and the example simulation uses SPY.
- The document provides no performance results or explicit exit, stop-loss, or rebalancing rules.
Tags
Full text
# stock_buy_and_hold.py
```py
import datetime as dt
import pytz
from lumibot.credentials import ALPACA_TEST_CONFIG
from lumibot.strategies.strategy import Strategy
"""
Strategy Description
Simply buys one asset and holds onto it.
"""
class BuyAndHold(Strategy):
parameters = {
"buy_symbol": "QQQ",
}
# =====Overloading lifecycle methods=============
def initialize(self):
# Set the sleep time to one day (the strategy will run once per day)
self.sleeptime = "1D"
def on_trading_iteration(self):
"""Buys the self.buy_symbol once, then never again"""
# Get the current datetime and log it
dt = self.get_datetime() # We use this function so that we get the time in teh backtesting environment
self.log_message(f"Current datetime: {dt}")
# Get the symbol to buy from the parameters
buy_symbol = self.parameters["buy_symbol"]
# What to do each iteration
# Get the current value of the symbol and log it
current_value = self.get_last_price(buy_symbol)
self.log_message(f"The value of {buy_symbol} is {current_value}")
# Add a line to the indicator chart
self.add_line(f"{buy_symbol} Value", current_value)
# Get all the positions that we have
all_positions = self.get_positions()
# If we don't own anything (other than USD), buy the asset
if len(all_positions) == 0:
# Calculate the quantity to buy
quantity = int(self.get_portfolio_value() // current_value)
# Create the order and submit it
purchase_order = self.create_order(buy_symbol, quantity, "buy")
self.submit_order(purchase_order)
if __name__ == "__main__":
IS_BACKTESTING = True
if IS_BACKTESTING:
from lumibot.backtesting import AlpacaBacktesting
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(dt.datetime(2023, 1, 1))
backtesting_end = tzinfo.localize(dt.datetime(2024, 9, 1))
timestep = 'day'
auto_adjust = True
warm_up_trading_days = 0
refresh_cache = False
results, strategy = BuyAndHold.run_backtest(
datasource_class=AlpacaBacktesting,
backtesting_start=backtesting_start,
backtesting_end=backtesting_end,
minutes_before_closing=0,
benchmark_asset='SPY',
analyze_backtest=True,
parameters={
"buy_symbol": "SPY",
},
show_progress_bar=True,
# AlpacaBacktesting kwargs
timestep=timestep,
market='NYSE',
config=ALPACA_TEST_CONFIG,
refresh_cache=refresh_cache,
warm_up_trading_days=warm_up_trading_days,
auto_adjust=auto_adjust,
)
# Print the results
print(results)
else:
ALPACA_CONFIG = {
"API_KEY": "YOUR_API_KEY",
"API_SECRET": "YOUR_API_SECRET",
"PAPER": True,
}
from lumibot.brokers import Alpaca
broker = Alpaca(ALPACA_CONFIG)
strategy = BuyAndHold(broker=broker)
strategy.run_live()
```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.