.. _backtesting.polygon: Polygon.io Backtesting =================================== .. meta:: :description: Configure Polygon.io stock, option, forex, and crypto backtesting with LumiBot, including API keys, historical data, caching, and runnable Python examples. Choosing a data source? Compare :doc:`free daily stock data `, :doc:`ThetaData for stocks and options `, and :doc:`Databento for market-data schemas ` before setting up Polygon.io. Availability, history, and pricing vary by provider and account. .. include:: _snippets/botspot_mcp_callout.rst .. important:: **ThetaData is our preferred data partner and the service we recommend to most LumiBot users—sign up at** `ThetaData `_ **and use the promo code ``BotSpot10`` for 10% off the first order.** This section remains for teams that still need Polygon.io. If you require Polygon access you can create an account at `polygon.io `_. Polygon.io backtester allows for flexible and robust backtesting. It uses the polygon.io API to fetch pricing data for stocks, options, forex, and cryptocurrencies. This backtester simplifies the process of getting pricing data; simply use the PolygonDataSource and it will automatically fetch pricing data when you call `get_last_price()` or `get_historical_prices()`. As of this writing, polygon provides up to 2 years of historical data for free. If you pay for an API you can get many years of data and the backtesting will download data much faster because it won't be rate limited. This backtesting method caches the data on your computer making it faster for subsequent backtests. So even if it takes a bit of time the first time, the following backtests will be much faster. To use this feature, you need to obtain an API key from polygon.io, which is free and you can get in the Dashboard after you have created an account. You must then replace `YOUR_POLYGON_API_KEY` with your own key in the code. Start by importing the PolygonDataBacktesting, BacktestingBroker and other necessary classes: .. code-block:: python from datetime import datetime from lumibot.backtesting import BacktestingBroker, PolygonDataBacktesting from lumibot.strategies import Strategy from lumibot.traders import Trader Next, create a strategy class that inherits from the Strategy class. This class will be used to define the strategy that will be backtested. In this example, we will create a simple strategy that buys a stock on the first iteration and holds it until the end of the backtest. The strategy will be initialized with a symbol parameter that will be used to determine which stock to buy. The initialize method will be used to set the sleeptime to 1 day. The on_trading_iteration method will be used to buy the stock on the first iteration. The strategy will be run from 2025-01-01 to 2025-01-31. .. code-block:: python class MyStrategy(Strategy): parameters = { "symbol": "AAPL", } def initialize(self): self.sleeptime = "1D" def on_trading_iteration(self): if self.first_iteration: symbol = self.parameters["symbol"] price = self.get_last_price(symbol) qty = self.portfolio_value / price order = self.create_order(symbol, quantity=qty, side="buy") self.submit_order(order) Set the start and end dates for the backtest: .. code-block:: python backtesting_start = datetime(2025, 1, 1) backtesting_end = datetime(2025, 1, 31) Finally, run the backtest: .. code-block:: python result = MyStrategy.run_backtest( PolygonDataBacktesting, backtesting_start, backtesting_end, benchmark_asset="SPY") Here's the full code (with explicit dates): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Make sure to replace YOUR_POLYGON_API_KEY with your own API key from polygon.io (it's free)** .. code-block:: python from datetime import datetime from lumibot.backtesting import BacktestingBroker, PolygonDataBacktesting from lumibot.strategies import Strategy from lumibot.traders import Trader class MyStrategy(Strategy): parameters = { "symbol": "AAPL", } def initialize(self): self.sleeptime = "1D" def on_trading_iteration(self): if self.first_iteration: symbol = self.parameters["symbol"] price = self.get_last_price(symbol) qty = self.portfolio_value / price order = self.create_order(symbol, quantity=qty, side="buy") self.submit_order(order) if __name__ == "__main__": backtesting_start = datetime(2025, 1, 1) backtesting_end = datetime(2025, 1, 31) result = MyStrategy.run_backtest( PolygonDataBacktesting, backtesting_start, backtesting_end, benchmark_asset="SPY" ) .. important:: **ThetaData remains our recommended vendor (promo code ``BotSpot10`` at `thetadata.net `_). These Polygon instructions are provided for existing workflows that still rely on Polygon’s API.** Optional: Environment Variables ------------------------------- Instead of specifying `backtesting_start` and `backtesting_end` in your code, you can set these environment variables (along with `IS_BACKTESTING`). LumiBot will automatically detect them if they are present: .. list-table:: :header-rows: 1 :widths: 20 60 20 * - **Variable** - **Description** - **Example** * - IS_BACKTESTING - (Optional) Read only by startup code that checks it. It does not change a ``backtest()`` call into a broker run; see :doc:`strategy_run_modes`. - False * - BACKTESTING_START - (Optional) The start date (YYYY-MM-DD). - 2025-01-01 * - BACKTESTING_END - (Optional) The end date (YYYY-MM-DD). - 2025-01-31 Below is **the full code** that relies *entirely on environment variables*: .. code-block:: python from lumibot.backtesting import BacktestingBroker, PolygonDataBacktesting from lumibot.strategies import Strategy from lumibot.traders import Trader class MyStrategy(Strategy): parameters = { "symbol": "AAPL", } def initialize(self): self.sleeptime = "1D" def on_trading_iteration(self): if self.first_iteration: symbol = self.parameters["symbol"] price = self.get_last_price(symbol) qty = self.portfolio_value / price order = self.create_order(symbol, quantity=qty, side="buy") self.submit_order(order) if __name__ == "__main__": # No start/end dates in code. Rely on environment variables instead. result = MyStrategy.run_backtest( PolygonDataBacktesting, benchmark_asset="SPY" ) In summary, the polygon.io backtester is a powerful tool for fetching pricing data for backtesting various strategies. With its capability to cache data for faster subsequent backtesting and its easy integration with polygon.io API, it is a versatile choice for any backtesting needs. Frequently asked questions ~~~~~~~~~~~~~~~~~~~~~~~~~~ How do I backtest with Polygon.io in LumiBot? --------------------------------------------- Set ``POLYGON_API_KEY`` to your API key, then pass ``PolygonDataBacktesting`` to ``run_backtest()`` as shown above. You can also set ``BACKTESTING_DATA_SOURCE=polygon`` instead of passing the class. LumiBot stops with an error if the Polygon key is missing. Where do I get a Polygon API key? --------------------------------- Create an account and copy the key from your dashboard. The polygon.io website now redirects to `massive.com `__, where the plans are listed. Is the Polygon API free? ------------------------ There is a free plan. At the time of writing, the `Stocks Basic plan `__ is $0 a month with 5 API calls per minute, 2 years of historical data, end of day data and minute aggregates. Paid plans add more history and faster downloads. Check the pricing page for current details. Why is my first Polygon backtest slow? -------------------------------------- A free key allows only a few requests per minute, and LumiBot downloads data in chunks of up to 30 days. After the first run, LumiBot reads the data from its cache in the ``polygon`` folder inside ``LUMIBOT_CACHE_FOLDER``, so later backtests are much faster. How do I keep an options backtest under the free rate limit? ------------------------------------------------------------ Set ``LUMIBOT_OPTION_CHAIN_MAX_DAYS``, for example to ``21``. Each option chain request then only covers expirations up to that many days after the simulated date. See :ref:`environment_variables`. Which assets can I backtest with Polygon? ----------------------------------------- The Polygon backtester fetches prices for stocks, options, forex and crypto. What you can download depends on your Polygon plan. Should I use Polygon or ThetaData? ---------------------------------- ThetaData is LumiBot's preferred data partner and the one we recommend to most users, see :doc:`backtesting.thetadata`. This page stays for teams that already use Polygon. For simple daily stock tests, :doc:`Yahoo ` needs no API key. Can I set the backtest dates without changing code? --------------------------------------------------- Yes. Set ``BACKTESTING_START`` and ``BACKTESTING_END`` in the format YYYY-MM-DD and leave the dates out of ``run_backtest()``, as in the last example above.