yfinance limitations for backtesting
yfinance is a great exploration tool and a poor research substrate. This guide covers exactly where it breaks down once a notebook becomes a repeatable backtest -- and which limitations you can live with.
Where yfinance fits
yfinance is free, ubiquitous, and takes one line to fetch a chart's worth of data. It is also a community wrapper around Yahoo Finance's web endpoints -- not a documented market-data service. For eyeballing a handful of large-cap symbols, checking a price on a date, or teaching, it is genuinely the right tool. The problems start when its output becomes the input to a strategy you intend to trust.
Problem 1: prices that change under you
Yahoo's Adj Close folds dividends into the entire price
history. That means every historical price your backtest sees
changes a little after every dividend -- run the same download
in January and July and the 2015 prices differ. Two consequences:
- Backtests are not reproducible from a fresh download; results drift for reasons unrelated to your strategy.
- Dividend-smoothed prices are not the prices that traded. Stops, limit fills, and "stock fell 5% today" logic execute at levels that never printed on the tape.
A static file archive inverts this: MarketParquet prices are split-adjusted traded levels, dividends deliberately left out of the series, and a downloaded file never changes underneath you. (Details and a worked AAPL example: methodology.)
Problem 2: survivorship bias
Yahoo serves listed companies. Tickers that were delisted, acquired, or went bankrupt mostly return nothing -- so a universe built from yfinance is a universe of survivors. The effect is not subtle: of the 21,427 stock, ETF, and futures symbols in the MarketParquet daily archive, roughly 10,000 no longer trade. The 2010-01-04 daily file contains 5,876 stock symbols; 3,516 of them -- 60% -- are gone today. A momentum or small-cap strategy backtested only on the 40% that survived will look dramatically better than it was.
Problem 3: intraday history barely exists
Yahoo's endpoints only serve 1-minute bars for roughly the last 30 calendar days, in request windows of about a week, and hourly data for about two years. There is no way to pull 1-minute data for 2015 -- or even for last quarter -- from yfinance at any price. Intraday backtesting over years of history requires an archive that was collected as it happened; MarketParquet's 1-minute files go back to 2000 for stocks and ETFs, and to the end of 2007 for futures.
Problem 4: shape and reliability of bulk access
- Ticker-at-a-time -- yfinance returns one symbol's history per logical request. Cross-sectional research ("rank all stocks by yesterday's volume") wants the transpose: every symbol for one day. MarketParquet files are already that shape -- one file per trading day, all ~6,500+ active symbols inside.
- Rate limits and breakage -- Yahoo throttles and reshapes its endpoints without notice; every few months a yfinance version stops working until patched. Fine for a hobby script, painful inside a nightly pipeline.
- No terms for it -- Yahoo's data is licensed for personal viewing; redistributing or building a product on scraped Yahoo data is a compliance risk vendors and funds will not accept.
What staying reproducible looks like
The fix is boring on purpose: download typed Parquet files once, keep them on disk, and make every backtest read the same bytes.
import pandas as pd
# Same file, same bytes, same backtest -- forever.
df = pd.read_parquet("stock_daily_2024-01-15.parquet")
day = df.set_index("symbol")
# The whole cross-section is already here:
# 6,500+ active symbols plus that era's now-delisted tickers
# (suffixed '-DELISTED'), split-adjusted OHLCV, typed columns.
liquid = df[df["volume"] > 1_000_000]
Column mapping if you are porting yfinance code:
yfinance Open/High/Low/Close/Volume map directly to
open/high/low/close/volume; there is no
Adj Close because prices are already split-adjusted, and
the index is a symbol/date pair rather than a
per-ticker DatetimeIndex.
Honest comparison
yfinance MarketParquet
price free $79 one-time archive
daily history ~listed life 2000-present incl. delisted
1-min history ~30 days 2000-present (stocks/ETFs)
delisted tickers mostly absent ~10,000 preserved
bulk shape per ticker per trading day (all symbols)
reproducible no (adj close yes (static Parquet files)
mutates)
rate limits opaque, shifting documented API, bulk download
If your work is a chart or a one-off look-up, keep using yfinance -- it is the better tool for that. The row that usually decides it for backtesters is reproducibility.
FAQ
Can I mix yfinance and MarketParquet data? For research convenience, yes -- but expect systematic price differences on dividend-paying names, since Yahoo's adjusted prices embed dividends and MarketParquet's deliberately do not.
Does yfinance data match MarketParquet on splits? Broadly yes for raw Close on active large caps; both are split-adjusted. Differences concentrate in Adj Close, thinly traded names, and anything delisted.
I only trade the S&P 500 -- do delisted tickers matter? Yes: index constituents change constantly, and the members that were removed (often after crashing) are exactly the ones a survivors-only dataset forgets. Point-in-time universes need the dead tickers too.
Related
MarketParquet vs Yahoo Finance · delisted stock data · data quality & methodology · browse stock daily · DuckDB guide · pricing