
First published on Substack
This ORB strategy backtest using Alpaca was originally published on Substack before appearing on the site. Subscribe there to get new quantitative trading research, backtesting studies, and data quality investigations early.
Introduction
This article shows how to run an ORB strategy backtest in Python using Alpaca, using over 10 years of free intraday data.
In our previous article, we published a Python implementation for backtesting the Opening Range Breakout (ORB) strategy from our paper Can Day Trading Really Be Profitable? (Zarattini & Aziz, 2023). That version used Polygon.io as the data source, but Polygon’s free tier only gives access to the last 2 years of history, which limited how far back you could test.
We wanted to fix that. While working on it, we noticed something unexpected: the same strategy, with identical code and parameters, produced materially different results depending on which data provider we used. That finding turned into its own investigation, which we published separately:
With that resolved, we can now share what we originally set out to build: a complete, free, reproducible backtest covering 10+ years of SIP-quality intraday data using Alpaca.
This ORB strategy backtest in Python using Alpaca is fully reproducible and runs entirely in the browser.
Why Alpaca?
The previous article used Polygon.io, which works well but limits free-tier users to 2 years of historical data. For a strategy that was originally tested from 2016 to 2023, that’s not enough.
Alpaca solves this:
- Free SIP data since 2016. Their Basic plan includes historical 1-minute bars sourced from the CTA (NYSE) and UTP (Nasdaq) feeds, covering 100% of US market volume. No paid subscription required.
- Simple REST API. Just HTTP requests with your API key. No proprietary software, no desktop clients.
- Built-in adjustments. Request
adjustment=allon daily bars and Alpaca returns prices fully adjusted for splits and dividends. Intraday bars come unadjusted (adjustment=raw), which is exactly what you want for realistic trade simulation. This eliminates the need for a separate dividend adjustment pipeline. It is a meaningful simplification over the Polygon version. - Rate limits are generous. The free tier allows 200 requests per minute for historical data, with 10+ years of 1-minute bars downloadable in about 5 minutes.
What Changed from the Polygon Version
If you’re coming from the Polygon article, here’s what’s different:
| Polygon Version | Alpaca Version | |
|---|---|---|
| Data source | Polygon.io | Alpaca |
| Free history | ~2 years | 10+ years (since 2016) |
| Dividend adjustment | Manual (fetch dividends separately, apply backward adjustment) | Built-in (adjustment=all on daily bars) |
| Date range | 2024–2026 (free tier) | 2016–2026 |
| Stop modes | H/L only | Both H/L and ATR |
The core strategy logic is identical. It uses the same entry rules, the same position sizing, and the same risk management. The improvements are in data coverage, adjustment handling, and ATR implementation.
ORB Strategy Backtest Logic
For those new here, the ORB strategy is straightforward:
- Determine direction. If the close of the 5th bar is above the open of the 1st bar, go Long. Otherwise, go Short.
- Enter at the open of the 6th bar.
- Stop loss has two modes:
- H/L: the low of the opening range for longs or the high for shorts.
- ATR: a fraction of the previous day’s ATR, reconverted to current-day dollars.
- Target: hold until stop or market close. There is no profit target.
- Position sizing: risk 1% of equity per trade, capped at 4x leverage.

The parameters match the paper exactly:
| Parameter | Value |
|---|---|
| Opening range | 5 minutes |
| Stop (H/L mode) | High/Low of the range |
| Stop (ATR mode) | 5% of 14-day ATR |
| Profit target (H/L mode) | 10R |
| Profit target (ATR mode) | None (hold to close) |
| Risk per trade | 1% of equity |
| Max leverage | 4x |
| Starting capital | $25,000 |
| Commission | $0.0005/share per side |
Walkthrough of the Notebook
The notebook implements a complete ORB strategy backtest in Python using Alpaca, from data download to performance analysis.
Section 1: Setup & Configuration
This is the only cell you need to edit. It contains your Alpaca API keys, the ticker symbol (TQQQ), the date range, and all strategy parameters.
API_KEY_ID = os.environ.get("ALPACA_API_KEY_ID", "")
API_SECRET_KEY = os.environ.get("ALPACA_API_SECRET_KEY", "")
TICKER = "TQQQ"
START_DATE = "2016-01-01"
END_DATE = "2026-04-20"
Everything else, including data download, processing, backtesting, and analysis, runs automatically from here.
Section 2: Data Download
Two datasets are fetched from Alpaca’s REST API:
- Intraday bars: 1-minute OHLCV, unadjusted (
adjustment=raw), SIP feed, filtered to regular trading hours (9:30 AM to 3:59 PM ET). - Daily bars: daily OHLC, fully adjusted (
adjustment=all). Used to compute the ATR lookups.
Both are cached to CSV on first run. Subsequent executions skip the download entirely.

Section 3: Load Data & Compute ATR
The adjusted daily data is used to build a lookup table of ATR% values, defined as the 14-day ATR divided by the daily open and lagged by one day.

First, we compute the 14-day ATR using adjusted daily price data. We then divide this value by the adjusted daily open to obtain ATR%, a normalized volatility measure that is not affected by splits or dividends.
At trade time, this ratio is converted back into current-day dollar terms by multiplying it by the intraday open price.
The stop is defined as a dollar distance from entry (stop width) and is computed as:
The stop is then calculated by taking the chosen ATR scaling factor (stop_atr), multiplying it by the ATR percentage, and adjusting it based on the relationship between the intraday open and the entry price.
This approach is cleaner than the Polygon version, which required fetching dividends separately and applying a manual backward adjustment.
Section 4: Filter Early-Close Days
On days like Black Friday, Christmas Eve, and July 3rd, the NYSE closes at 1:00 PM instead of 4:00 PM. Some data feeds include bars after the real close. We use the exchange_calendars library to identify these half-days and remove any post-close bars.
Sections 5–6: Strategy Logic & Backtest Engine
The backtest loops day by day:
- Extract the day’s 1-minute OHLC matrix.
- Determine trade direction from the opening range.
- Compute entry price (open of the 6th bar).
- Calculate the stop distance (H/L or ATR mode).
- Size the position:
shares = min(AUM × risk / (entry × stop), max_lev × AUM / entry). - Walk forward through remaining bars to detect stop hits.
- Close at stop price, gap-open price, or end-of-day close.
- Update the equity curve.
Both stop modes (H/L and ATR) are run back-to-back for comparison.

Section 7: Performance Analysis
Utility functions compute:
- Summary statistics: CAGR, annualized volatility, Sharpe ratio, max drawdown, and hit ratio.
- Monthly returns heatmap: a year by month grid of compounded returns.
- Equity curve: a log-scale chart with the out-of-sample region (post February 2023) shown in green.
Sections 8–9: Run & Results
The backtests execute and the results are displayed: equity curves, performance tables, and monthly breakdowns for both H/L and ATR stop modes. The out-of-sample period is clearly marked. Strategy lines turn green after February 17, 2023 (the paper’s data cutoff), so you can visually assess out-of-sample performance.

How to Run It Yourself
This ORB strategy backtest in Python using Alpaca requires only a free API key and a browser.
- Create a free Alpaca account at alpaca.markets.
- Get your API keys from the dashboard (Paper trading keys work).
- Open the notebook → Google Colab link
- Paste your API keys into Section 1.
- Run All, data downloads in ~5 minutes (cached afterward), backtest runs in under a minute.
No paid subscriptions. No local installs. Just a browser and a free API key.

Conclusion
This article extends our original Polygon-based backtest with a significantly longer data history (10+ years vs 2 years), a cleaner data pipeline (no manual dividend adjustments), and a faithful implementation of the ATR stop from the paper.
If you’re interested in how data quality affects backtesting results and why the same code can produce wildly different outcomes depending on the provider, see our companion article: Backtesting Data Quality: Can Your Data Provider Be Trusted?.
The full code is open and reproducible. Fork it, change the ticker, adjust the parameters, and see what you find.

