# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project `yfinance` is a Python library (v1.4.1) that downloads market data from Yahoo! Finance's public web APIs. It is not affiliated with Yahoo — it reverse-engineers their v7/v10/v8 chart, quote, options, fundamentals, and websocket streaming endpoints. The package is published on PyPI and conda-forge. ## Commands ### Tests Tests use the stdlib `unittest` runner. They hit real Yahoo endpoints, so a network connection is required and individual tests can be flaky against live data. ```bash # Run everything python -m unittest discover -s tests # Single module python -m unittest tests.test_ticker # Single test python -m unittest tests.test_ticker.TestTicker.test_history # With coverage (mirrors CI) pytest --cov=yfinance/ ``` All test modules import `tests/context.py` first — it inserts the repo root onto `sys.path` and configures a dedicated test cache at `/py-yfinance-testing` that auto-clears once per day. `tests/context.py` also exposes `session_gbl` (currently `None`); requests_cache/requests_ratelimiter sessions don't compose with `curl_cffi` so the rate-limiter examples are kept commented out as reference. ### Local install ```bash pip install -e . ``` `extras_require`: `nospam` (requests_cache + requests_ratelimiter) and `repair` (scipy, needed for `Ticker.history(repair=True)`). ### Type check Pyright is configured via `pyrightconfig.json` in basic mode with most checks at `warning` level. ### Branching for PRs Two-layer model (per `CONTRIBUTING.md`): submit PRs against **`dev`**. The maintainer batches dev → **`main`** for each PyPI release. Do not target `main` directly. CI (`.travis.yml`) only runs on `main`. ## Architecture ### Public surface `yfinance/__init__.py` is the single entry point that re-exports the entire public API. Anything not exported there is private. The public types are: `Ticker`, `Tickers`, `download`, `Market`/`MarketRegion`, `Search`, `Lookup`, `Calendars`, `Sector`, `Industry`, `WebSocket`/`AsyncWebSocket`, `EquityQuery`/`FundQuery`/`ETFQuery`, `screen`, `PREDEFINED_SCREENER_QUERIES`, `Auth`, `config`, `enable_debug_mode`, `set_tz_cache_location`. ### Ticker is a thin facade over scrapers `Ticker` (yfinance/ticker.py) inherits from `TickerBase` (yfinance/base.py) and exposes ~50 `@property` accessors (`info`, `financials`, `balance_sheet`, `news`, `options`, `funds_data`, etc.). Each property just calls a `get_*()` method on `TickerBase`. The actual fetching/parsing logic for each property lives in a dedicated scraper module under `yfinance/scrapers/`: - `quote.py` — `info`, `fast_info`, `calendar`, `sec_filings`, `news`, ISIN - `fundamentals.py` — income statement, balance sheet, cash flow, earnings - `analysis.py` — analyst estimates, price targets, revisions, growth - `holders.py` — major/institutional/mutualfund/insider holders - `history.py` — OHLCV history, splits, dividends, price-repair (the largest file: ~164k) - `funds.py` — `FundsData` for ETFs/mutual funds When adding a new ticker field, follow the established pattern: implement `get_()` in the relevant scraper, wire it through `TickerBase`, then add a `@property` on `Ticker`. ### Multi-ticker downloads `Tickers` (tickers.py) holds a dict of `Ticker` instances. `download()` (multi.py) is the heavy-lift batch path used for portfolio history — it parallelizes via the `multitasking` package and reshapes results into a single (multi-indexed) DataFrame. Don't extend `Ticker.history()` with batching logic; that belongs in `multi.py`. ### HTTP and caching - `yfinance/data.py` — `YfData` (the per-session HTTP wrapper) and the `Auth` helper that solves Yahoo's crumb/cookie challenge. Every scraper goes through `self._data.get(...)`. - `yfinance/_http.py` — low-level HTTP utilities. - `curl_cffi` is the default backend (it impersonates a browser TLS fingerprint, which is required to get past Yahoo's Cloudflare). Plain `requests`/`requests_cache`/`requests_ratelimiter` are not compatible — that's why `tests/context.py` keeps `session_gbl = None` and why the `nospam` extra is opt-in only for users supplying their own `requests`-based session. - `yfinance/cache.py` — peewee + SQLite caches for tz lookups and the IPO/cookie cache. User-facing location is set via `yf.set_tz_cache_location(path)`. ### Live streaming `yfinance/live.py` provides `WebSocket` and `AsyncWebSocket` for Yahoo's real-time pricing feed. Wire-format is protobuf — `yfinance/pricing.proto` is the schema and `yfinance/pricing_pb2.py` is the generated code (both ship in the wheel via `setup.py` `package_data`). Regenerate `pricing_pb2.py` with `protoc` if the schema changes. ### Domain / screener - `yfinance/domain/` — `Sector`, `Industry`, `Market`/`MarketRegion` (sector overviews, industry summaries, market summary endpoints). They share a `domain.py` base. - `yfinance/screener/` — `query.py` defines the typed `EquityQuery`/`FundQuery`/`ETFQuery` builders; `screener.py` defines `screen()` and `PREDEFINED_SCREENER_QUERIES`. New screener fields go in `query.py` keyed off Yahoo's screenable-field metadata. ### Constants & utilities - `yfinance/const.py` (~48k) — every magic string from Yahoo: financial-statement field name maps (`fundamentals_keys`), pretty-name translation tables, user-agents, base URLs (`_BASE_URL_`, `_ROOT_URL_`, `_SCRAPE_URL_`), screener-field enums. Look here first before hard-coding any Yahoo field name. - `yfinance/utils.py` (~47k) — shared parsers, the project-wide `get_yf_logger()`, decorators, DataFrame helpers (`empty_df`, `_pd_timedelta_to_interval`), etc. ### Config `yfinance/config.py` defines `YfConfig` (exported as `yfinance.config`), a tiny nested-attribute store with auto-vivifying namespaces. The defaults are `config.network.proxy`, `config.network.retries`, `config.debug.hide_exceptions`, `config.debug.logging`, `config.locale.lang`, `config.locale.region`. The legacy `set_config(proxy=, retries=)` is a deprecated shim that forwards to `config.network.*`. ## Conventions and gotchas - **Version bump**: edit `yfinance/version.py` (`setup.py` reads it textually — keep the `version = "x.y.z"` line shape) and `meta.yaml` (`{% set version = "..." %}`) together. `meta.yaml` is for conda-forge and includes a `sha256` that the conda-forge bot updates after PyPI release. - **Versioning is loose**: bumps are not always semver-aligned with API changes. Check `CHANGELOG.rst` (the canonical changelog; `CHANGELOG.md` is the short rolling extract). - **Logging**: scrapers should call `utils.get_yf_logger()` rather than the stdlib `logging` module directly. End-users opt in with `yf.enable_debug_mode()`. - **Frequencies on financials**: `freq` is one of `'yearly'`, `'quarterly'`, `'trailing'` (TTM). The `pretty=True` flag on `get_income_stmt`/`get_balance_sheet`/`get_cash_flow` swaps Yahoo's raw camelCase keys for human-readable row labels via maps in `const.py`. - **Price repair**: `Ticker.history(..., repair=True)` runs the heuristics in `scrapers/history.py` against bad-tick patterns Yahoo emits (100x/0.01x splits, missing dividends). It needs `scipy` — guard new repair code behind the existing optional-import pattern. - **Tests hit the network**: there is no recorded-fixture infrastructure. When debugging a failing test, expect that the underlying Yahoo endpoint may have changed shape; check `scrapers/yahoo-keys.txt` for the latest observed key set.