Wraps the entire yfinance public API behind a single binary `yfin` with
51 subcommands across 13 namespaces (ticker, download, tickers, search,
lookup, market, sector, industry, calendars, screen, live, auth, config).
Output renders as Rich tables on TTY, JSON when piped; --format / --out
support table | json | ndjson | csv | tsv | parquet | yaml.
Highlights:
- 1857 LOC across 18 Python files under cli/yfin/
- Typer + Rich, packaged via hatchling + uv
- Install globally: `cd cli && uv tool install -e .`
- ~/.yfin/config.toml for persistent proxy/cookies/locale defaults
- Friendly YFRateLimitError handler (exit 2, no Rich traceback)
- Validated end-to-end with 33 live tests against Yahoo from a
residential exit (AAPL, MSFT, NVDA, options, screens, calendars,
financials, news, holders, parquet round-trip)
yfinance-tools.yaml is the single source of truth:
- tools (49) — every public yfinance entry point with parameters
- cli (13 groups) — exact CLI invocations, mapped back to tool names
- outputSchemas (27) — actual return shapes captured from live calls
- learnings (19) — IP rate-limit, crumb handshake, Typer gotchas,
screen-build wrapping rules, upstream stubs, etc.
CLAUDE.md documents the upstream codebase layout for future agents.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
reindex_dfs() rebuilt the combined index via sorted(set(...)) +
pd.to_datetime, dropping the index name set by history.py ('Date' for
daily, 'Datetime' for intraday). After concat, data.index.name was
None, so users doing data.stack().reset_index() got a 'level_0' column
instead of 'Date' / 'Datetime'.
Use Index.union, which preserves the name natively when sources agree.
Drive-by: narrow data.columns to MultiIndex before swaplevel to silence
a pyright attribute warning.
Some platforms (e.g. older macOS, exotic Linux distros) cannot build the
curl-impersonate binary that backs curl_cffi, leaving yfinance unusable.
This change keeps curl_cffi as the preferred and default backend but no
longer hard-requires it at runtime.
- New `yfinance/_http.py` abstracts the HTTP backend. If `curl_cffi` is
importable it is used as before; otherwise yfinance falls back to plain
`requests` with a realistic Chrome User-Agent and logs a warning so the
downgrade is explicit.
- `data.py`, `base.py`, `multi.py`, `scrapers/history.py` build sessions
via `_http.new_session()`.
- Scrapers and screener catch `_http.HTTPError` instead of importing
`curl_cffi.requests.exceptions.HTTPError` directly.
- `is_supported_session()` accepts either backend; `cookie_jar()` papers
over the small API difference between curl_cffi (`cookies.jar`) and
requests (`cookies` is itself the jar).
- New Advanced > Installation page documents the curl_cffi-free install
recipe; README links to it just under the install instruction.
Empirically verified that plain `requests` with a Chrome UA does not hit
the 401-after-~10-requests issue that the earlier `curl_adapter` approach
ran into; 15/15 quoteSummary calls succeed without rate limiting.
`setup.py` is unchanged -- `curl_cffi>=0.15` remains the default install
requirement (CVE-pinned). The fallback only activates when the import
fails at runtime; moving curl_cffi to extras_require can be a follow-up
decision for the maintainer.
- history.py: drop the hardcoded ['KAP.IL', 'SAND'] ticker whitelist
from the phantom-dividend approval in _fix_bad_div_adjust. Per
maintainer feedback on the original PR, the whitelist was leakage
from a custom debug build and was never meant to be in main; phantom
detections are safe to approve unconditionally in this branch.
- history.py: split a semicolon-joined statement that was failing
ruff E702 in CI.
- domain/industry.py: fix `compnaies` → `companies` typo (8
occurrences, local-variable rename only, no behavioural impact).
Yahoo's v7 (`/v7/quote`) and v10 (`/quoteSummary`) endpoints accept
`lang` and `region` parameters and return localized fields (`longName`,
`shortName`, ...) when the ticker has been translated for that locale —
e.g. 1810.HK returns '小米集團-W' under `lang=zh-Hant-HK`, GAZP.ME
returns 'Публичное акционерное общество Газпром' under `lang=ru-RU`.
Previously these params were not exposed and `Ticker.info["longName"]`
always came back in U.S. English.
Surfaced as global config (`YfConfig.locale.lang` / `YfConfig.locale.region`)
rather than per-Ticker constructor params: a typical user wants the
same locale for every call in their session, and the config approach
also covers the v10 calendar/earnings endpoint in `_fetch_history_metadata`
without threading kwargs through three layers.
- Add `YfConfig.locale.lang` (default `"en-US"`) and `YfConfig.locale.region`
(default `"US"`).
- `Quote._fetch` (v10/quoteSummary) and `Quote._fetch_additional_info`
(v7/quote) read the locale from `YfConfig` on every call.
- `_fetch_history_metadata` (v1 calendar) reads the same locale, replacing
a hardcoded `lang=en-US, region=US`.
- The `v8/chart` endpoint ignores `lang` server-side, so we don't forward
there — would only generate noise.
- Verified live for Latin/diacritics (it-IT), CJK (zh-Hant-HK, ja-JP),
Cyrillic (ru-RU); Apple under ja-JP correctly stays "Apple Inc." since
it's a U.S. listing.
Closes#2582