Polygon.io Backtesting

Choosing a data source? Compare free daily stock data, ThetaData for stocks and options, and Databento for market-data schemas before setting up Polygon.io. Availability, history, and pricing vary by provider and account.

What BotSpot MCP adds

Use Claude, ChatGPT, Codex, Cursor, VS Code or another MCP-compatible assistant? Connect the free BotSpot MCP for:

  • Skills: build LumiBot strategies.

  • Free data: news, economics and SEC filings.

  • Backtests: run tests and read charts.

  • Cloud hosting: low-cost scheduled bots.

  • Community: share bots and discuss trades.

  • Marketplace: discover and publish strategies.

  • Broker linking: connect accounts through BotSpot.

Connect your AI assistant

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:

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.

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:

backtesting_start = datetime(2025, 1, 1)
backtesting_end = datetime(2025, 1, 31)

Finally, run the backtest:

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)

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 <https://www.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:

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 Backtest, paper, or live: choose the runner.

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:

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 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 ThetaData Options and Stock Backtesting with LumiBot. This page stays for teams that already use Polygon. For simple daily stock tests, 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.