The Problem

Clients need programmatic access to historical and realtime market data without negotiating Yahoo!’s undocumented API contracts. Existing wrappers are either stale or tightly coupled to a single request style, making it hard to integrate into automated pipelines that require reliable error handling and configurable caching.

What This Does

yfinance provides a thin Pythonic layer over Yahoo! Finance’s public endpoints. Core symbols live in the yfinance/ package:

  • yfinance/ticker.pyTicker class exposing history(), info, options, etc.
  • yfinance/base.py – shared logic for lazy‑loading price history and timezone handling.
  • yfinance/_http.py – low‑level request handling, cookie/CSRF management, and retry policies.
  • yfinance/config.py, yfinance/const.py, yfinance/exceptions.py – global settings, constant definitions, and domain‑specific error types.

The tests/ directory (≈100 files) validates end‑to‑end data retrieval, caching, and edge‑case handling (e.g., dividend‑adjustment fixes). Documentation lives under doc/ and is built with the Makefile in that folder.

How It Is Wired

Execution typically starts with user code that imports yfinance and creates a Ticker:

from yfinance import Ticker
ts = Ticker("AAPL")
df = ts.history(period="1mo")
  • Entry pointyfinance/__init__.py re‑exports Ticker, download, and helper functions. It is the hub module (36 inbound imports, 16 outbound) and sits in a circular import chain with yfinance/data.py and yfinance/_http.py.
  • Call flowTicker.__init__ (in ticker.py) builds a YfData object (yfinance/data.py). Subsequent calls such as history() delegate to Base.history (base.py), which invokes _lazy_load_price_history. That method eventually calls _fetch_and_parse in _http.py, which performs the HTTP GET, applies retry logic (_is_transient_error), and parses CSV/JSON into pandas structures.
  • Blast radius – The hub (__init__) and base (base.py) modules are the widest impact points: 74 call sites reference Ticker, and history is invoked from 43 locations. Changing their signatures will ripple through most of the library and the test suite.
  • Cycles & nesting – The import cycle (__init__data_http) adds maintenance friction; deep nesting (max depth 8) appears in const.py, exceptions.py, and utils.py, making the control flow harder to follow.
  • External touch – Only file I/O occurs in yfinance/scrapers/quote.py (raw CSV open without a context manager). No database or network sockets are opened directly by the library; all network traffic is encapsulated in _http.py.

How To Use It

# Clone and install
git clone https://github.com/moses-y/yfinance
cd yfinance
pip install -e .   # reads requirements.txt and setup.cfg

No additional configuration is required; the library reads proxy settings from environment variables (e.g., HTTP_PROXY) as handled in yfinance/data.py. To fetch data:

from yfinance import download, Ticker

# Bulk download
df = download(["MSFT", "GOOG"], start="2023-01-01", end="2023-12-31")

# Single ticker
ticker = Ticker("TSLA")
prices = ticker.history(period="5d")

The download helper lives in yfinance/__init__.py; Ticker lives in yfinance/ticker.py.

Real‑World Use

A quant team can embed the library in a nightly ETL job:

import pandas as pd
from yfinance import download

symbols = ["AAPL", "AMZN", "NVDA"]
prices = download(symbols, period="1d", interval="1m")
prices.to_parquet("s3://data/market/quotes.parquet")

The call chain is download → _http._fetch_and_parse → pandas.read_csv, yielding a DataFrame ready for downstream analysis.

Code Health & Issues

Measured static analysis (71 modules, 178 import edges, 26 cycles):

  • HIGH – Import cycles (26 files) – e.g., yfinance/__init__.py, yfinance/data.py, yfinance/_http.py. Break cycles by extracting shared types or lazy imports.
  • HIGH – Hub moduleyfinance/__init__.py is a single point of failure; keep its API stable.
  • HIGH – Oversized filesyfinance/const.py, yfinance/utils.py, yfinance/scrapers/history.py (>1 k lines each). Split into logical sub‑modules.
  • HIGH – Deep nesting (max depth 8)yfinance/const.py, yfinance/exceptions.py, yfinance/utils.py. Refactor with early returns.
  • MEDIUM – Broad exception handling – many except Exception: blocks swallow errors (e.g., yfinance/data.py). Replace with specific catches.
  • MEDIUM – High branching densityyfinance/screener/query.py contains 60 branches in 203 lines; consider strategy dispatch.
  • MEDIUM – Duplicated test code – 27 identical 6‑line blocks across 15 test files; extract helpers.
  • LOW – File opened without context manageryfinance/scrapers/quote.py uses open(...) directly.

Code‑Health audit (CI/ops):

  • HIGH – CI does not run tests.github/workflows/*.yml lack a test step; add pytest invocation.
  • MEDIUM – No dependency vulnerability scan – add dependency-review-action or osv-scanner.
  • LOW – Missing job timeouts – set timeout-minutes in .github/workflows/deploy_doc.yml.

No lockfile (requirements.txt only) means reproducible builds are not guaranteed.

The Bottom Line

yfinance delivers a functional, well‑documented wrapper for Yahoo! Finance data with a clear public API (Ticker, download). The codebase is heavily tested but suffers from import cycles, large hub modules, and deep nesting that raise the maintenance burden. It is suitable for internal tooling or research pipelines, provided the team budgets time for refactoring the identified hotspots and tightening CI.