Running Daily Stock Backtests with Yahoo Data in LumiBot
Summary
This guide explains how to run daily backtests for stocks and ETFs in LumiBot using Yahoo Finance data, without supplying a separate dataset or broker credentials. It outlines the flow from creating a Yahoo data backtester and backtesting broker to running a strategy, and notes that the framework generates plots in its logs directory. Two approaches to setting the backtest dates are described: pass dates in the strategy call or provide them through environment variables.
The guide limits this backtester to daily stock and ETF data; it cannot support intraday strategies or other security types. It notes that daily bars cannot provide the fine-grained opening-period evidence needed for an opening-range breakout example, and directs users to other backtesting data sources for different needs. The included buy-and-hold style example demonstrates wiring rather than a tested trading method. No performance findings, data-quality analysis, or transaction-cost treatment are given.
Key ideas
- Yahoo data can support daily stock and ETF backtests in LumiBot without a user-supplied dataset.
- Backtest date ranges can be set in the strategy call or through environment variables.
- The guide describes connecting the data backtester, broker, strategy, and trader components.
- Yahoo daily bars do not support intraday backtesting or opening-period analysis.
- The example demonstrates setup and workflow, not evidence of strategy performance.
Tags
Full text
# backtesting.yahoo
.. _backtesting.yahoo:
Free Daily Stock Backtesting with Yahoo and LumiBot
===================================================
.. meta::
:description: Run free daily stock and ETF backtests with Yahoo data in LumiBot without broker credentials, then inspect trades and performance reports.
**NOTE: Please ensure you have installed the latest lumibot version using ``pip install lumibot --upgrade`` before proceeding as there have been some major changes to the backtesting module in the latest version.**
Yahoo backtesting is so named because we get data for the backtesting from the Yahoo Finance website. The user is not required to supply data. Any stock information that is available in the Yahoo Finance API should be available for backtesting. The Yahoo backtester is only for stock data (including ETFs). Additionally, you cannot use the Yahoo backtester for intra-day trading, it is for daily trading only. For other securities, use the Polygon or Pandas backtesters.
For a complete AI stock example, see :doc:`the bull/bear large-cap team
<agents_example_bull_vs_bear_ai_stock_trading_bot>`. Yahoo daily bars cannot supply the
09:30 to 09:45 evidence required by the :doc:`opening range breakout
<agents_example_opening_range_breakout_ai_trading_bot>` tutorial.
Using Yahoo backtester, you can also run backtests very easily on your strategies, you do not have to modify anything in your strategies.
To use the Yahoo backtester, you must import the ``YahooDataBacktesting`` and ``BacktestingBroker`` objects.
.. code-block:: python
from lumibot.backtesting import BacktestingBroker, YahooDataBacktesting
To run a backtest, you must create a ``YahooDataBacktesting`` object, then pass it to the ``BacktestingBroker`` object. The ``BacktestingBroker`` object is then passed to the ``Strategy`` object. Finally, the ``Strategy`` object is passed to the ``Trader`` object and the backtest is run.
There are also several plots that are generated by the backtester. These plots are by defaults saved in the ``logs`` folder.
Option 1: Provide Start and End in Code
---------------------------------------
.. code-block:: python
from datetime import datetime
from lumibot.backtesting import BacktestingBroker, YahooDataBacktesting
from lumibot.strategies import Strategy
from lumibot.traders import Trader
# A simple strategy that buys AAPL on the first day
class MyStrategy(Strategy):
def on_trading_iteration(self):
if self.first_iteration:
aapl_price = self.get_last_price("AAPL")
quantity = self.portfolio_value // aapl_price
order = self.create_order("AAPL", quantity, "buy")
self.submit_order(order)
# Pick the dates that you want to start and end your backtest in code
backtesting_start = datetime(2025, 1, 1)
backtesting_end = datetime(2025, 1, 31)
# Run the backtest
result = MyStrategy.run_backtest(
YahooDataBacktesting,
backtesting_start,
backtesting_end,
)
Using Environment Variables (Optional)
--------------------------------------
You can set the following environment variables without making any changes to your code. **LumiBot will automatically use them if they are set.** If they are not set, LumiBot will default to normal behavior (i.e., you can specify the start/end in code as above).
Below is a table describing these optional environment variables:
.. list-table::
:header-rows: 1
:widths: 20 60 20
* - **Variable**
- **Description**
- **Example**
* - IS_BACKTESTING
- (Optional) Available to startup code that explicitly checks it. It does not change a ``run_backtest()`` call into a broker run; see :doc:`strategy_run_modes`.
- False
* - BACKTESTING_START
- (Optional) The start date for backtesting in the format "YYYY-MM-DD". Only needed if you are backtesting.
- 2025-01-01
* - BACKTESTING_END
- (Optional) The end date for backtesting in the format "YYYY-MM-DD". Only needed if you are backtesting.
- 2025-01-31
Examples of how you might set these variables:
- **Render / Other Cloud**: Define Environment Variables in your service’s dashboard.
- **Repl.it**: Add them to your project’s “Secrets.”
- **Local Development**: Create a `.env` file (or set them at the OS level).
Option 2: Rely on Environment Variables (No Start/End in Code)
--------------------------------------------------------------
Below is an example strategy that **omits** `backtesting_start` and `backtesting_end`:
.. code-block:: python
from lumibot.backtesting import BacktestingBroker, YahooDataBacktesting
from lumibot.strategies import Strategy
from lumibot.traders import Trader
# A simple strategy that buys AAPL on the first day
class MyStrategy(Strategy):
def on_trading_iteration(self):
if self.first_iteration:
aapl_price = self.get_last_price("AAPL")
quantity = self.portfolio_value // aapl_price
order = self.create_order("AAPL", quantity, "buy")
self.submit_order(order)
# This call always backtests. BACKTESTING_START and BACKTESTING_END
# can supply the dates when they are omitted here.
result = MyStrategy.run_backtest(
YahooDataBacktesting
)
This example calls ``run_backtest()`` in every case. ``BACKTESTING_START`` and
``BACKTESTING_END`` can supply omitted dates; ``IS_BACKTESTING=false`` does not
start broker execution. See :doc:`strategy_run_modes` for the separate broker
runner.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.