Files
yfinance-fork/cli/yfin/commands/tickers.py
T
goldyard 057bc4493e Add yfin CLI + tool catalog + CLAUDE.md
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>
2026-06-08 23:36:56 +08:00

54 lines
1.9 KiB
Python

"""`yfin tickers ...` — Tickers-collection operations."""
from __future__ import annotations
from typing import List, Optional
import typer
import yfinance as yf
from .._shared import parse_symbols, render_via_state, get_state
from .download import GroupBy, Period, Interval
app = typer.Typer(no_args_is_help=True, help="Tickers-collection operations.")
@app.command("history")
def history(
ctx: typer.Context,
symbols: List[str] = typer.Argument(...),
period: Optional[Period] = typer.Option(None, "--period"),
interval: Interval = typer.Option(Interval.d1, "--interval"),
start: Optional[str] = typer.Option(None, "--start"),
end: Optional[str] = typer.Option(None, "--end"),
prepost: bool = typer.Option(False, "--prepost/--no-prepost"),
actions: bool = typer.Option(True, "--actions/--no-actions"),
auto_adjust: bool = typer.Option(True, "--auto-adjust/--no-auto-adjust"),
repair: bool = typer.Option(False, "--repair/--no-repair"),
threads: bool = typer.Option(True, "--threads/--no-threads"),
group_by: GroupBy = typer.Option(GroupBy.column, "--group-by"),
progress: bool = typer.Option(True, "--progress/--no-progress"),
):
"""Multi-symbol history via the Tickers collection."""
st = get_state(ctx)
tickers = parse_symbols(symbols)
t = yf.Tickers(" ".join(tickers))
kw = dict(
interval=interval.value, start=start, end=end,
prepost=prepost, actions=actions, auto_adjust=auto_adjust,
repair=repair, threads=threads, group_by=group_by.value,
progress=progress and not st.quiet, timeout=st.timeout,
)
if period and not (start or end):
kw["period"] = period.value
df = t.history(**kw)
render_via_state(ctx, df)
@app.command("news")
def news(ctx: typer.Context, symbols: List[str] = typer.Argument(...)):
"""Per-symbol news dict."""
tickers = parse_symbols(symbols)
out = yf.Tickers(" ".join(tickers)).news()
render_via_state(ctx, out)