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.py–Tickerclass exposinghistory(),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 point –
yfinance/__init__.pyre‑exportsTicker,download, and helper functions. It is the hub module (36 inbound imports, 16 outbound) and sits in a circular import chain withyfinance/data.pyandyfinance/_http.py. - Call flow –
Ticker.__init__(inticker.py) builds aYfDataobject (yfinance/data.py). Subsequent calls such ashistory()delegate toBase.history(base.py), which invokes_lazy_load_price_history. That method eventually calls_fetch_and_parsein_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 referenceTicker, andhistoryis 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 inconst.py,exceptions.py, andutils.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 module –
yfinance/__init__.pyis a single point of failure; keep its API stable. - HIGH – Oversized files –
yfinance/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 density –
yfinance/screener/query.pycontains 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 manager –
yfinance/scrapers/quote.pyusesopen(...)directly.
Code‑Health audit (CI/ops):
- HIGH – CI does not run tests –
.github/workflows/*.ymllack a test step; addpytestinvocation. - MEDIUM – No dependency vulnerability scan – add
dependency-review-actionorosv-scanner. - LOW – Missing job timeouts – set
timeout-minutesin.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.