Source-agnostic stock data prep utilities. Bring your own data fetcher.
Stock data comes from many sources (Yahoo Finance, Alpaca, CSVs, etc.), but the prep steps are always the same:
- Fetch price data
- Handle missing values (forward/backward fill)
- Normalize for comparison (start all at 1.0)
- Calculate returns
This package decouples the data source from the prep logic. You define how to fetch, it handles the rest.
from stock_data import StockData
# Define your fetcher - any function that returns a DataFrame
# with DatetimeIndex and symbols as columns
def my_fetcher(symbols, start, end):
import yfinance as yf
df = yf.download(symbols, start=start, end=end, progress=False)["Adj Close"]
return df if len(symbols) > 1 else df.to_frame(symbols[0])
# Use it
data = StockData(my_fetcher)
data.load(["AAPL", "GOOG", "SPY"], "2020-01-01", "2023-12-31")
# Normalized prices (all start at 1.0)
data.normalize()
# Daily returns
data.daily_returns()
# Cumulative returns
data.cumulative_returns()
# Access raw/cleaned data
data.raw # before cleaning
data.prices # after ffill/bfillA fetcher is any callable with this signature:
def fetcher(symbols: list, start: str, end: str) -> pd.DataFrame:
"""
Returns DataFrame with:
- DatetimeIndex (dates)
- Columns = symbol names
- Values = prices (typically Adj Close)
"""
passSee fetchers.py for ready-to-use examples:
| Fetcher | Source | Survivorship Bias Free |
|---|---|---|
yfinance_fetcher |
Yahoo Finance | No |
csv_fetcher(data_dir) |
Local CSV files | Depends on your data |
alpaca_fetcher(api_key, secret) |
Alpaca Markets | No |
nasdaqdatalink_fetcher(api_key) |
Sharadar | Yes |
tiingo_fetcher(api_key) |
Tiingo | Yes |
from fetchers import yfinance_fetcher, csv_fetcher
# Yahoo Finance (quick and free, but has survivorship bias)
data = StockData(yfinance_fetcher)
# Local CSVs
data = StockData(csv_fetcher("./my_data"))All example fetchers use adjusted close prices, which account for stock splits and dividends. This means daily_returns() gives you total return (price + dividends), not just price return. This is what you want for backtesting.
Free data sources (Yahoo Finance, Alpaca) only include stocks that currently exist. If you backtest 2008 using today's stock universe, you'll miss companies that went bankrupt (Lehman Brothers, etc.), making your results look artificially good.
Survivorship bias-free sources:
| Source | Cost | Notes |
|---|---|---|
| Sharadar (Nasdaq Data Link) | ~$50/mo | Includes delisted stocks |
| Tiingo | Free tier | Claims delisted coverage |
| CRSP | $$$ | Academic gold standard |
| Polygon.io | Paid tiers | Has delisted data |
from fetchers import nasdaqdatalink_fetcher, tiingo_fetcher
# Sharadar - paid, comprehensive
data = StockData(nasdaqdatalink_fetcher("your_api_key"))
# Tiingo - free tier available
data = StockData(tiingo_fetcher("your_api_key"))Just copy stock_data.py into your project. That's it.
For fetchers, install what you need:
pip install yfinance # for yfinance_fetcher
pip install alpaca-py # for alpaca_fetcher
pip install nasdaq-data-link # for nasdaqdatalink_fetcher
pip install requests # for tiingo_fetcherMIT