Quant Buffet API

Data API

load_daily_prices, load_price_panel, the disk cache, and price panel conventions.

Market data lives in `backtest.data`. The lab uses adjusted daily closes from Yahoo Finance (auto_adjust=True), cached as CSV under backtest/data_cache/ so repeat runs are fast and reproducible.

load_daily_prices

python
prices = load_daily_prices(
    ["SPY", "TLT", "GLD"],   # symbols: list[str]
    "2010-01-01",            # start: str = "2000-01-01"
    None,                    # end: str | None = None
    use_cache=True,          # keyword-only: read/write backtest/data_cache/
    strict=False,            # keyword-only: raise on first failure
)
# -> pd.DataFrame, DatetimeIndex rows, one float column per loaded ticker
ParameterTypeDefaultDescription
symbolslist[str]Tickers to load, e.g. ["SPY", "TLT"].
startstr"2000-01-01"Inclusive ISO start date.
endstr | NoneNoneOptional end date; None means latest available.
use_cachebool (keyword-only)TrueRead and write the CSV cache.
strictbool (keyword-only)FalseRaise on the first symbol failure instead of skipping it.

Return value

  • A `pd.DataFrame` indexed by a sorted, timezone-naive `DatetimeIndex`.
  • One float column per symbol that loaded successfully — failed symbols are simply absent unless strict=True.
  • Each column is forward-filled independently, so a symbol's gaps are patched but its pre-IPO history stays NaN.
  • When some symbols fail, the reasons are attached to `prices.attrs["load_errors"]`.
  • If no symbol loads, a RuntimeError lists the first few errors.

The disk cache

AspectBehaviour
Locationbacktest/data_cache/
Filename{SYMBOL}_{start}_{end or 'latest'}.csv
ColumnsDate, AdjClose
Reuse conditionFile exists and is larger than 50 bytes
Cache key caveatA different start produces a different file — and a fresh download

load_price_panel

python
from backtest.data import load_price_panel

prices, missing = load_price_panel(["SPY", "TLT", "XYZ"], start="2010-01-01")
# prices:  pd.DataFrame of everything that loaded
# missing: ["XYZ"] — symbols with no data

load_price_panel de-duplicates the symbol list, always runs non-strict, and hands back the missing symbols explicitly. Use it in scripts where you want to report gaps rather than crash.

Panel conventions to rely on

GuaranteeConsequence for your code
Index is sorted ascendingrolling and shift behave as expected
Index is timezone-naiveNo tz_localize needed for comparisons
Columns follow ASSETS orderBut may be shorter if a symbol failed
Values are adjusted closesSplits and dividends are already handled
All-NaN rows are dropped by the runnerThe first panel row has at least one real price
python
# Defensive opening lines that belong in every strategy
cols = [c for c in ASSETS if c in prices.columns]
if not cols:
    raise ValueError("No ASSETS loaded")

first_valid = {s: prices[s].first_valid_index() for s in cols}
# Inspect this when a backtest starts much later than you expected