yfd 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- yfd-0.3.0/.github/workflows/fundamentals.yml +35 -0
- yfd-0.3.0/.github/workflows/nightly.yml +35 -0
- yfd-0.3.0/.github/workflows/pip-publish.yml +62 -0
- yfd-0.3.0/.github/workflows/test-yfinance.yml +43 -0
- yfd-0.3.0/.gitignore +8 -0
- yfd-0.3.0/PKG-INFO +12 -0
- yfd-0.3.0/README.md +134 -0
- yfd-0.3.0/llms.txt +121 -0
- yfd-0.3.0/pyproject.toml +24 -0
- yfd-0.3.0/reports/price/2026-03-28.txt +6 -0
- yfd-0.3.0/src/yfd/__init__.py +1 -0
- yfd-0.3.0/src/yfd/cli.py +77 -0
- yfd-0.3.0/src/yfd/fetcher.py +62 -0
- yfd-0.3.0/src/yfd/fundamentals.py +471 -0
- yfd-0.3.0/src/yfd/r2.py +39 -0
- yfd-0.3.0/src/yfd/store.py +124 -0
- yfd-0.3.0/src/yfd/symbols.py +26 -0
- yfd-0.3.0/src/yfd/updater.py +189 -0
- yfd-0.3.0/src/yfd/yahoo.py +434 -0
- yfd-0.3.0/uv.lock +661 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Fundamentals data fetch
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
# Midnight ET (05:00 UTC) Mon-Fri, after price update
|
|
6
|
+
- cron: "0 5 * * 1-5"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
fetch:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
timeout-minutes: 150
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write
|
|
15
|
+
env:
|
|
16
|
+
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
|
|
17
|
+
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
|
|
18
|
+
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
|
|
19
|
+
R2_BUCKET: ${{ secrets.R2_BUCKET }}
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- uses: astral-sh/setup-uv@v4
|
|
25
|
+
|
|
26
|
+
- run: uv sync
|
|
27
|
+
|
|
28
|
+
- run: uv run python -m yfd.fundamentals
|
|
29
|
+
|
|
30
|
+
- name: Commit report
|
|
31
|
+
run: |
|
|
32
|
+
git config user.name "github-actions[bot]"
|
|
33
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
34
|
+
git add reports/fundamentals/
|
|
35
|
+
git diff --cached --quiet || git commit -m "report: fundamentals $(date +%Y-%m-%d)" && git push
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Nightly data update
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
# 10 PM ET (03:00 UTC) — after post-market close, before pre-market
|
|
6
|
+
- cron: "0 3 * * 1-5"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
update:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
timeout-minutes: 120
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write
|
|
15
|
+
env:
|
|
16
|
+
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
|
|
17
|
+
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
|
|
18
|
+
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
|
|
19
|
+
R2_BUCKET: ${{ secrets.R2_BUCKET }}
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- uses: astral-sh/setup-uv@v4
|
|
25
|
+
|
|
26
|
+
- run: uv sync
|
|
27
|
+
|
|
28
|
+
- run: uv run python -m yfd.updater
|
|
29
|
+
|
|
30
|
+
- name: Commit report
|
|
31
|
+
run: |
|
|
32
|
+
git config user.name "github-actions[bot]"
|
|
33
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
34
|
+
git add reports/price/
|
|
35
|
+
git diff --cached --quiet || git commit -m "report: nightly price $(date +%Y-%m-%d)" && git push
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: Build and Publish Python package to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
name: Build distribution
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Check out code
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.11"
|
|
23
|
+
|
|
24
|
+
- name: Install build dependencies
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip
|
|
27
|
+
pip install build
|
|
28
|
+
|
|
29
|
+
- name: Build package
|
|
30
|
+
run: python -m build
|
|
31
|
+
|
|
32
|
+
- name: Test built package
|
|
33
|
+
run: |
|
|
34
|
+
pip install dist/*.whl
|
|
35
|
+
python -c "from yfd import get, symbols, types; print('Package imported successfully!')"
|
|
36
|
+
|
|
37
|
+
- name: Store the distribution packages
|
|
38
|
+
uses: actions/upload-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: python-package-distributions
|
|
41
|
+
path: dist/
|
|
42
|
+
|
|
43
|
+
publish-to-pypi:
|
|
44
|
+
name: Publish to PyPI
|
|
45
|
+
needs:
|
|
46
|
+
- build
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
environment:
|
|
49
|
+
name: pypi
|
|
50
|
+
url: https://pypi.org/p/yfd
|
|
51
|
+
permissions:
|
|
52
|
+
id-token: write
|
|
53
|
+
|
|
54
|
+
steps:
|
|
55
|
+
- name: Download all the dists
|
|
56
|
+
uses: actions/download-artifact@v4
|
|
57
|
+
with:
|
|
58
|
+
name: python-package-distributions
|
|
59
|
+
path: dist/
|
|
60
|
+
|
|
61
|
+
- name: Publish distribution to PyPI
|
|
62
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Test Yahoo Finance API
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
timeout-minutes: 5
|
|
10
|
+
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- uses: astral-sh/setup-uv@v4
|
|
15
|
+
|
|
16
|
+
- run: uv sync
|
|
17
|
+
|
|
18
|
+
- name: Test direct Yahoo Finance API
|
|
19
|
+
run: |
|
|
20
|
+
uv run python -c "
|
|
21
|
+
from yfd.yahoo import Yahoo, INCOME_KEYS
|
|
22
|
+
|
|
23
|
+
y = Yahoo()
|
|
24
|
+
print(f'Auth OK, crumb: {y._crumb[:10]}...')
|
|
25
|
+
|
|
26
|
+
# Chart API
|
|
27
|
+
df, events, meta = y.chart('AAPL', start='2026-01-01')
|
|
28
|
+
print(f'AAPL chart: {len(df)} rows, currency={meta.get(\"currency\")}')
|
|
29
|
+
|
|
30
|
+
# Quote summary
|
|
31
|
+
qs = y.quote_summary('AAPL', ['financialData', 'majorHoldersBreakdown'])
|
|
32
|
+
print(f'quoteSummary modules: {list(qs.keys())}')
|
|
33
|
+
|
|
34
|
+
# Timeseries
|
|
35
|
+
ts = y.timeseries('AAPL', INCOME_KEYS, 'annual')
|
|
36
|
+
print(f'Income statement: {ts.shape}')
|
|
37
|
+
|
|
38
|
+
# Quote
|
|
39
|
+
q = y.quote('AAPL')
|
|
40
|
+
print(f'Quote price: {q.get(\"regularMarketPrice\")}')
|
|
41
|
+
|
|
42
|
+
print('\nAll tests passed!')
|
|
43
|
+
"
|
yfd-0.3.0/.gitignore
ADDED
yfd-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yfd
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Requires-Python: >=3.11
|
|
5
|
+
Requires-Dist: boto3
|
|
6
|
+
Requires-Dist: curl-cffi
|
|
7
|
+
Requires-Dist: lxml
|
|
8
|
+
Requires-Dist: pandas
|
|
9
|
+
Requires-Dist: pyarrow
|
|
10
|
+
Requires-Dist: python-dotenv
|
|
11
|
+
Requires-Dist: requests
|
|
12
|
+
Requires-Dist: tvscreener>=0.2.2
|
yfd-0.3.0/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# yfd
|
|
2
|
+
|
|
3
|
+
Offline Yahoo Finance data for 17,000+ US-listed securities (stocks, ETFs, REITs). Daily OHLCV prices, financial statements, holders, analyst estimates, and 39 data types — served from Cloudflare R2, updated nightly.
|
|
4
|
+
|
|
5
|
+
No API keys. No rate limits. No Yahoo dependency at query time.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
pip install yfd
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Python API
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import yfd
|
|
17
|
+
|
|
18
|
+
# Get price history
|
|
19
|
+
df = yfd.get("AAPL", "price", start="2026-01-01")
|
|
20
|
+
|
|
21
|
+
# Get annual income statement
|
|
22
|
+
inc = yfd.get("AAPL", "income_stmt")
|
|
23
|
+
|
|
24
|
+
# Get company info
|
|
25
|
+
info = yfd.get("AAPL", "info")
|
|
26
|
+
|
|
27
|
+
# Works for ETFs too
|
|
28
|
+
spy = yfd.get("SPY", "price", start="2026-03-01")
|
|
29
|
+
|
|
30
|
+
# List all data types
|
|
31
|
+
yfd.types()
|
|
32
|
+
|
|
33
|
+
# List all symbols
|
|
34
|
+
yfd.symbols() # ["A", "AA", "AAPL", ..., "SPY", "QQQ", ...]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`get()` returns a DataFrame for tabular data, a dict/list for JSON data, or `None` if not found.
|
|
38
|
+
|
|
39
|
+
## CLI
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
yfd price AAPL --start 2026-03-01
|
|
43
|
+
yfd income-stmt AAPL
|
|
44
|
+
yfd info AAPL --json
|
|
45
|
+
yfd balance-sheet MSFT
|
|
46
|
+
yfd types # list all 39 data types
|
|
47
|
+
yfd symbols # list all symbols
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Hyphens and underscores both work: `income-stmt` = `income_stmt`.
|
|
51
|
+
|
|
52
|
+
## Data Types
|
|
53
|
+
|
|
54
|
+
### Price & Actions
|
|
55
|
+
| Type | Description |
|
|
56
|
+
|------|-------------|
|
|
57
|
+
| `price` | Daily OHLCV (Open, High, Low, Close, Volume) |
|
|
58
|
+
| `dividends` | Dividend payment history |
|
|
59
|
+
| `splits` | Stock split history |
|
|
60
|
+
| `actions` | Dividends and splits combined |
|
|
61
|
+
| `capital_gains` | Capital gains distributions (ETFs/funds) |
|
|
62
|
+
|
|
63
|
+
### Financial Statements
|
|
64
|
+
| Type | Description |
|
|
65
|
+
|------|-------------|
|
|
66
|
+
| `income_stmt` | Annual income statement |
|
|
67
|
+
| `income_stmt_quarterly` | Quarterly income statement |
|
|
68
|
+
| `income_stmt_ttm` | Trailing 12-month income statement |
|
|
69
|
+
| `balance_sheet` | Annual balance sheet |
|
|
70
|
+
| `balance_sheet_quarterly` | Quarterly balance sheet |
|
|
71
|
+
| `cashflow` | Annual cash flow statement |
|
|
72
|
+
| `cashflow_quarterly` | Quarterly cash flow statement |
|
|
73
|
+
| `cashflow_ttm` | Trailing 12-month cash flow |
|
|
74
|
+
|
|
75
|
+
### Holders
|
|
76
|
+
| Type | Description |
|
|
77
|
+
|------|-------------|
|
|
78
|
+
| `major_holders` | Insider/institution ownership % |
|
|
79
|
+
| `institutional_holders` | Top institutional holders |
|
|
80
|
+
| `mutualfund_holders` | Top mutual fund holders |
|
|
81
|
+
| `insider_transactions` | Insider buy/sell transactions |
|
|
82
|
+
| `insider_purchases` | Net insider purchase activity |
|
|
83
|
+
| `insider_roster` | Insider roster with positions |
|
|
84
|
+
|
|
85
|
+
### Analyst & Estimates
|
|
86
|
+
| Type | Description |
|
|
87
|
+
|------|-------------|
|
|
88
|
+
| `recommendations` | Analyst recommendation trend |
|
|
89
|
+
| `upgrades_downgrades` | Analyst upgrade/downgrade history |
|
|
90
|
+
| `earnings_estimate` | EPS estimates by period |
|
|
91
|
+
| `revenue_estimate` | Revenue estimates by period |
|
|
92
|
+
| `earnings_history` | Historical EPS vs estimates |
|
|
93
|
+
| `eps_trend` | EPS trend |
|
|
94
|
+
| `eps_revisions` | EPS revision history |
|
|
95
|
+
| `growth_estimates` | Growth estimates (stock/industry/sector) |
|
|
96
|
+
| `analyst_price_targets` | Target high/low/mean/median price |
|
|
97
|
+
|
|
98
|
+
### Company
|
|
99
|
+
| Type | Description |
|
|
100
|
+
|------|-------------|
|
|
101
|
+
| `info` | Full company information |
|
|
102
|
+
| `fast_info` | Quick price/volume stats |
|
|
103
|
+
| `sustainability` | ESG scores |
|
|
104
|
+
| `calendar` | Upcoming earnings/dividend dates |
|
|
105
|
+
| `history_metadata` | Exchange, currency, trading hours |
|
|
106
|
+
| `news` | Latest news articles |
|
|
107
|
+
| `sec_filings` | SEC 10-K/10-Q filing history |
|
|
108
|
+
|
|
109
|
+
### Other
|
|
110
|
+
| Type | Description |
|
|
111
|
+
|------|-------------|
|
|
112
|
+
| `earnings` | Earnings dates with EPS surprise |
|
|
113
|
+
| `options` | Full options chain (all expiries) |
|
|
114
|
+
| `isin` | ISIN identifier |
|
|
115
|
+
| `funds_data` | ETF/mutual fund details |
|
|
116
|
+
|
|
117
|
+
## How It Works
|
|
118
|
+
|
|
119
|
+
Two GitHub Actions pipelines run every weeknight:
|
|
120
|
+
|
|
121
|
+
1. **Price update** (10 PM ET) — fetches daily OHLCV for all 17,000+ symbols via Yahoo Finance chart API, merges into R2
|
|
122
|
+
2. **Fundamentals update** (midnight ET) — fetches 39 data types per symbol via Yahoo Finance quoteSummary/timeseries APIs, uploads to R2
|
|
123
|
+
|
|
124
|
+
The client library reads directly from R2 public URLs. No Yahoo calls at query time.
|
|
125
|
+
|
|
126
|
+
## Architecture
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
Yahoo Finance APIs ──→ GitHub Actions (nightly) ──→ Cloudflare R2
|
|
130
|
+
│
|
|
131
|
+
yfd CLI / Python ◄───┘
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Symbol universe sourced from [TradingView screener](https://github.com/deepentropy/tvscreener) covering NYSE, NASDAQ, AMEX, CBOE, and OTC markets.
|
yfd-0.3.0/llms.txt
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# yfd
|
|
2
|
+
|
|
3
|
+
Offline Yahoo Finance data for 17,000+ US-listed securities (stocks, ETFs, REITs). Daily OHLCV prices, financial statements, holders, analyst estimates, and 39 data types — served from Cloudflare R2, updated nightly. No API keys needed.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pip install yfd
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Python API
|
|
12
|
+
|
|
13
|
+
Three functions:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import yfd
|
|
17
|
+
|
|
18
|
+
# Fetch any data type for a symbol
|
|
19
|
+
yfd.get(symbol, data_type="price", start=None, end=None)
|
|
20
|
+
# Returns: DataFrame (parquet types), dict/list/str (JSON types), or None
|
|
21
|
+
|
|
22
|
+
# List all 39 available data types
|
|
23
|
+
yfd.types()
|
|
24
|
+
# Returns: {"price": "Daily OHLCV price history", ...}
|
|
25
|
+
|
|
26
|
+
# List all available ticker symbols
|
|
27
|
+
yfd.symbols()
|
|
28
|
+
# Returns: ["A", "AA", "AAPL", ...]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## CLI
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
yfd <data_type> <SYMBOL> [--start YYYY-MM-DD] [--end YYYY-MM-DD] [--json]
|
|
35
|
+
yfd types
|
|
36
|
+
yfd symbols
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
CLI accepts hyphens: `income-stmt` = `income_stmt`.
|
|
40
|
+
|
|
41
|
+
## Data Types
|
|
42
|
+
|
|
43
|
+
### Price & Actions
|
|
44
|
+
- `price` — Daily OHLCV price history (columns: Open, High, Low, Close, Volume)
|
|
45
|
+
- `dividends` — Dividend payment history
|
|
46
|
+
- `splits` — Stock split history
|
|
47
|
+
- `actions` — Dividends and splits combined
|
|
48
|
+
- `capital_gains` — Capital gains distributions (ETFs/funds only)
|
|
49
|
+
|
|
50
|
+
### Financial Statements
|
|
51
|
+
- `income_stmt` — Annual income statement
|
|
52
|
+
- `income_stmt_quarterly` — Quarterly income statement
|
|
53
|
+
- `income_stmt_ttm` — Trailing 12-month income statement
|
|
54
|
+
- `balance_sheet` — Annual balance sheet
|
|
55
|
+
- `balance_sheet_quarterly` — Quarterly balance sheet
|
|
56
|
+
- `cashflow` — Annual cash flow statement
|
|
57
|
+
- `cashflow_quarterly` — Quarterly cash flow statement
|
|
58
|
+
- `cashflow_ttm` — Trailing 12-month cash flow
|
|
59
|
+
|
|
60
|
+
### Holders
|
|
61
|
+
- `major_holders` — Insider/institution ownership %
|
|
62
|
+
- `institutional_holders` — Top institutional holders
|
|
63
|
+
- `mutualfund_holders` — Top mutual fund holders
|
|
64
|
+
- `insider_transactions` — Insider buy/sell transactions
|
|
65
|
+
- `insider_purchases` — Net insider purchase activity
|
|
66
|
+
- `insider_roster` — Insider roster with positions
|
|
67
|
+
|
|
68
|
+
### Analyst & Estimates
|
|
69
|
+
- `recommendations` — Analyst recommendation trend (buy/hold/sell counts)
|
|
70
|
+
- `upgrades_downgrades` — Analyst upgrade/downgrade history
|
|
71
|
+
- `earnings_estimate` — EPS estimates by period
|
|
72
|
+
- `revenue_estimate` — Revenue estimates by period
|
|
73
|
+
- `earnings_history` — Historical EPS vs estimates (surprise %)
|
|
74
|
+
- `eps_trend` — EPS trend
|
|
75
|
+
- `eps_revisions` — EPS revision history
|
|
76
|
+
- `growth_estimates` — Growth estimates (stock/industry/sector)
|
|
77
|
+
- `analyst_price_targets` — Target high/low/mean/median price
|
|
78
|
+
|
|
79
|
+
### Company
|
|
80
|
+
- `info` — Full company information (sector, industry, officers, description, ...)
|
|
81
|
+
- `fast_info` — Quick price/volume stats (market cap, 52-week range, averages)
|
|
82
|
+
- `sustainability` — ESG scores
|
|
83
|
+
- `calendar` — Upcoming earnings and dividend dates
|
|
84
|
+
- `history_metadata` — Exchange, currency, trading hours
|
|
85
|
+
- `news` — Latest news articles
|
|
86
|
+
- `sec_filings` — SEC 10-K/10-Q filing history
|
|
87
|
+
|
|
88
|
+
### Other
|
|
89
|
+
- `earnings` — Earnings dates with EPS surprise
|
|
90
|
+
- `options` — Full options chain (all expiries, calls and puts)
|
|
91
|
+
- `isin` — ISIN identifier string
|
|
92
|
+
- `funds_data` — ETF/mutual fund details (holdings, allocations)
|
|
93
|
+
|
|
94
|
+
## Examples
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
import yfd
|
|
98
|
+
|
|
99
|
+
# Get AAPL price for March 2026
|
|
100
|
+
df = yfd.get("AAPL", "price", start="2026-03-01", end="2026-03-31")
|
|
101
|
+
|
|
102
|
+
# Get annual income statement
|
|
103
|
+
inc = yfd.get("AAPL", "income_stmt")
|
|
104
|
+
# DataFrame: rows = line items (TotalRevenue, NetIncome, ...), columns = fiscal year dates
|
|
105
|
+
|
|
106
|
+
# Get company info
|
|
107
|
+
info = yfd.get("AAPL", "info")
|
|
108
|
+
# dict with 180+ keys: sector, industry, marketCap, fullTimeEmployees, ...
|
|
109
|
+
|
|
110
|
+
# Get analyst price targets
|
|
111
|
+
targets = yfd.get("AAPL", "analyst_price_targets")
|
|
112
|
+
# {"currentPrice": 248.8, "highprice": 350.0, "lowprice": 205.0, "meanprice": 295.3, ...}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# CLI equivalents
|
|
117
|
+
yfd price AAPL --start 2026-03-01
|
|
118
|
+
yfd income-stmt AAPL
|
|
119
|
+
yfd info AAPL --json
|
|
120
|
+
yfd analyst-price-targets AAPL
|
|
121
|
+
```
|
yfd-0.3.0/pyproject.toml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "yfd"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
requires-python = ">=3.11"
|
|
5
|
+
dependencies = [
|
|
6
|
+
"boto3",
|
|
7
|
+
"curl-cffi",
|
|
8
|
+
"lxml",
|
|
9
|
+
"pandas",
|
|
10
|
+
"pyarrow",
|
|
11
|
+
"python-dotenv",
|
|
12
|
+
"requests",
|
|
13
|
+
"tvscreener>=0.2.2",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
yfd = "yfd.cli:main"
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["hatchling"]
|
|
21
|
+
build-backend = "hatchling.build"
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["src/yfd"]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
NIGHTLY PRICE REPORT — 2026-03-28
|
|
2
|
+
Symbols: 6697, Start date: 2026-03-28
|
|
3
|
+
Fetched: 6432, No data: 265, Uploaded: 6432, Upload failed: 0
|
|
4
|
+
|
|
5
|
+
NO DATA (265 symbols, likely delisted):
|
|
6
|
+
AACBR, AACI, ABLV, ACAAU, ADACU, AEAQU, AHL, AIIA, AIMDW, AIRT, AIRTP, AKO, ALCYU, ALFUU, ALIS, ALISR, ALISU, ALOV, ALOVW, ALUB, ALVOW, ANG, APACU, ARCIU, ARTC, ATH, BACC, BANFP, BBLGW, BCGWW, BDMDW, BEBE, BENFW, BF, BFRGW, BFRIW, BHAVU, BIII, BIXIW, BKHA, BKHAR, BLRKU, BLUWU, BLUWW, BML, BPACU, BPOPM, BRK, BRLSW, BRRWW, BSAAR, BTBDW, BZFDW, CAPTW, CCXIW, CCZ, CDR, CGCTW, CHAR, CMIIW, COLAU, CRACR, CRACU, CRACW, CRAQ, CRD, CTAAU, CUBWU, CUBWW, DFSCW, DMAA, DSYWW, DTSQU, DYOR, ETI, EURKR, EVOX, FACTW, FBYDW, FERAR, FERAU, FGII, FGMCU, FORTY, FSHP, FUSEW, GECCG, GECCO, GGROW, GIGGU, GIPRW, GIW, GIWWR, GJR, GJT, GLOP, GPACW, GSRFU, GTERU, HAVA, HAVAU, HCACU, HCICU, HSCSW, HSPTU, HVMC, HVMCW, I, ICR, ICUCW, INACR, IRHOR, ITHA, IVDAW, JBK, JENA, JFBRW, JWEL, K, KBONW, KCHV, KFII, KRAQW, KTH, KTWOU, KVAC, LAFAU, LEGO, LIMNW, LKSPU, LOTWW, LPAA, LPBBW, LPCV, LPCVW, LUCYW, MBAVU, MCGAU, MDRR, MER, MEVO, MKLYR, MKLYU, MLAA, MLAAW, MMTX, MTEX, MYPSW, N, NBRGR, NCPLW, NHIC, NIVFW, NIXXW, NMP, NOEMR, NOEMW, NPACW, NTRBW, NTWO, NTWOW, NVAWW, NVNIW, OABIW, OACCW, OAK, OBA, OCCIO, OCSAW, OIM, ONMDW, ORIQU, OTGAW, OXBRW, OXSQG, P, PAACU, PACH, PBHC, PDPA, PFX, PGACR, PHXE, PIIIW, PLBL, PMTRW, POLEW, PRENW, PRHIZ, PRIF, PTORU, QADRU, QSEAR, QUMSU, RAINW, RANG, RDACU, RDIB, RDZNW, REGCO, RFAMU, RSVRW, SAAQW, SBXD, SCE, SCII, SCIIU, SCPQ, SDHI, SDHIR, SEAL, SENEB, SIM, SOCA, SOHOB, SPEG, SPEGU, SSEA, SSEAU, SVACU, SVAQU, SVAQW, SVCC, SVIV, SWVLW, SXTPW, TACH, TACOW, TAVI, TAVIU, TC, TLNCU, TNONW, TOIIW, TPTA, TRGSU, TRTN, TVACU, TVAI, UHGWW, UYSCR, UYSCU, VACHU, VACHW, VACI, VCICU, VHCPW, VSECU, WALDW, WHLRD, WHLRL, WLACU, WLDSW, WLYB, WSBK, WSTNU, WTG, X, XBPEW, XCBEW, XFLH, XSLL, Y, YCY, ZKPU
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from yfd.store import DATA_TYPES, get, symbols, types
|
yfd-0.3.0/src/yfd/cli.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""CLI entry point: ``yfd <data_type> <symbol> [--start] [--end] [--json]``."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
from yfd.store import DATA_TYPES, get, symbols, types
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _format_df(df: pd.DataFrame, as_json: bool) -> str:
|
|
13
|
+
if as_json:
|
|
14
|
+
return df.to_json(orient="split", date_format="iso", indent=2)
|
|
15
|
+
return df.to_string()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _format_value(val, as_json: bool) -> str:
|
|
19
|
+
if as_json:
|
|
20
|
+
return json.dumps(val, default=str, indent=2)
|
|
21
|
+
if isinstance(val, dict):
|
|
22
|
+
return "\n".join(f"{k}: {v}" for k, v in val.items())
|
|
23
|
+
if isinstance(val, list):
|
|
24
|
+
return json.dumps(val, default=str, indent=2)
|
|
25
|
+
return str(val)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def main():
|
|
29
|
+
parser = argparse.ArgumentParser(
|
|
30
|
+
prog="yfd",
|
|
31
|
+
description="Yahoo Finance data — read from R2 storage",
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"command",
|
|
35
|
+
help="Data type (e.g. price, income-stmt, info) or: types, symbols",
|
|
36
|
+
)
|
|
37
|
+
parser.add_argument("symbol", nargs="?", help="Ticker symbol (e.g. AAPL)")
|
|
38
|
+
parser.add_argument("--start", help="Start date filter (price only)")
|
|
39
|
+
parser.add_argument("--end", help="End date filter (price only)")
|
|
40
|
+
parser.add_argument("--json", action="store_true", dest="as_json", help="JSON output")
|
|
41
|
+
|
|
42
|
+
args = parser.parse_args()
|
|
43
|
+
|
|
44
|
+
if args.command == "types":
|
|
45
|
+
for name, desc in sorted(types().items()):
|
|
46
|
+
print(f" {name:<25} {desc}")
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
if args.command == "symbols":
|
|
50
|
+
syms = symbols()
|
|
51
|
+
print(f"{len(syms)} symbols: {', '.join(syms[:20])}{'...' if len(syms) > 20 else ''}")
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
data_type = args.command.replace("-", "_")
|
|
55
|
+
|
|
56
|
+
if data_type not in DATA_TYPES:
|
|
57
|
+
print(f"Unknown: {args.command}. Run 'yfd types' for valid types.", file=sys.stderr)
|
|
58
|
+
sys.exit(1)
|
|
59
|
+
|
|
60
|
+
if not args.symbol:
|
|
61
|
+
print(f"Usage: yfd {args.command} <SYMBOL>", file=sys.stderr)
|
|
62
|
+
sys.exit(1)
|
|
63
|
+
|
|
64
|
+
result = get(args.symbol.upper(), data_type, start=args.start, end=args.end)
|
|
65
|
+
|
|
66
|
+
if result is None:
|
|
67
|
+
print(f"No data for {args.symbol.upper()}/{data_type}", file=sys.stderr)
|
|
68
|
+
sys.exit(1)
|
|
69
|
+
|
|
70
|
+
if isinstance(result, pd.DataFrame):
|
|
71
|
+
print(_format_df(result, args.as_json))
|
|
72
|
+
else:
|
|
73
|
+
print(_format_value(result, args.as_json))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
if __name__ == "__main__":
|
|
77
|
+
main()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Fetch full daily OHLCV history and save as per-symbol parquet files."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from yfd.yahoo import Yahoo
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def fetch_daily_history(
|
|
11
|
+
symbols: list[str],
|
|
12
|
+
output_dir: Path,
|
|
13
|
+
batch_size: int = 50,
|
|
14
|
+
pause: float = 1.0,
|
|
15
|
+
end: str | None = None,
|
|
16
|
+
workers: int = 10,
|
|
17
|
+
) -> list[str]:
|
|
18
|
+
"""Fetch full daily OHLCV history and save as per-symbol parquet files.
|
|
19
|
+
|
|
20
|
+
Returns list of symbols that failed.
|
|
21
|
+
"""
|
|
22
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
23
|
+
existing = {p.stem for p in output_dir.glob("*.parquet")}
|
|
24
|
+
remaining = [s for s in symbols if s not in existing]
|
|
25
|
+
if existing:
|
|
26
|
+
print(f"Skipping {len(existing)} already fetched, {len(remaining)} remaining")
|
|
27
|
+
|
|
28
|
+
yahoo = Yahoo()
|
|
29
|
+
failed: list[str] = []
|
|
30
|
+
total_batches = -(-len(remaining) // batch_size)
|
|
31
|
+
|
|
32
|
+
for i in range(0, len(remaining), batch_size):
|
|
33
|
+
batch = remaining[i : i + batch_size]
|
|
34
|
+
batch_num = i // batch_size + 1
|
|
35
|
+
print(f"[{batch_num}/{total_batches}] Fetching {len(batch)} symbols...")
|
|
36
|
+
|
|
37
|
+
saved = 0
|
|
38
|
+
|
|
39
|
+
def _fetch(sym):
|
|
40
|
+
df, _, _ = yahoo.chart(sym, end=end, period="max")
|
|
41
|
+
return sym, df
|
|
42
|
+
|
|
43
|
+
with ThreadPoolExecutor(max_workers=workers) as pool:
|
|
44
|
+
futures = {pool.submit(_fetch, s): s for s in batch}
|
|
45
|
+
for fut in as_completed(futures):
|
|
46
|
+
sym = futures[fut]
|
|
47
|
+
try:
|
|
48
|
+
_, df = fut.result()
|
|
49
|
+
if df is not None and not df.empty:
|
|
50
|
+
df.to_parquet(output_dir / f"{sym}.parquet")
|
|
51
|
+
saved += 1
|
|
52
|
+
else:
|
|
53
|
+
failed.append(sym)
|
|
54
|
+
except Exception:
|
|
55
|
+
failed.append(sym)
|
|
56
|
+
|
|
57
|
+
print(f" Saved {saved}/{len(batch)} symbols")
|
|
58
|
+
|
|
59
|
+
if batch_num < total_batches:
|
|
60
|
+
time.sleep(pause)
|
|
61
|
+
|
|
62
|
+
return failed
|