yfin-cli 0.1.1__py3-none-any.whl
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.
- src/__init__.py +1 -0
- src/cli.py +133 -0
- src/commands/__init__.py +0 -0
- src/commands/analysis.py +153 -0
- src/commands/calendar.py +84 -0
- src/commands/financials.py +83 -0
- src/commands/industry.py +44 -0
- src/commands/market.py +11 -0
- src/commands/screen.py +260 -0
- src/commands/sector.py +66 -0
- src/commands/stock.py +108 -0
- src/decorators.py +33 -0
- src/typer.py +223 -0
- src/utils.py +91 -0
- src/writer.py +43 -0
- yfin_cli-0.1.1.dist-info/METADATA +124 -0
- yfin_cli-0.1.1.dist-info/RECORD +20 -0
- yfin_cli-0.1.1.dist-info/WHEEL +4 -0
- yfin_cli-0.1.1.dist-info/entry_points.txt +2 -0
- yfin_cli-0.1.1.dist-info/licenses/LICENSE +21 -0
src/utils.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import typer
|
|
3
|
+
from typing import Any, Callable
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
from pandas import DataFrame, Series
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
console = Console()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def console_print(content: Any):
|
|
12
|
+
console.print(content)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def console_print_error(content: Any):
|
|
16
|
+
console.print(f"[red]{content}[/red]")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def console_print_warning(content: Any):
|
|
20
|
+
console.print(f"[yellow]{content}[/yellow]")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def count_specified(*args: str | None) -> int:
|
|
24
|
+
"""Count the number of specified arguments"""
|
|
25
|
+
return sum(x is not None for x in args)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def compact(**kwargs):
|
|
29
|
+
"""Return a dict with None values removed."""
|
|
30
|
+
return {k: v for k, v in kwargs.items() if v is not None}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_today_date_string() -> str:
|
|
34
|
+
return format_datetime(datetime.now())
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def default_end_from_start(start: str, end: str | None) -> str:
|
|
38
|
+
if end is not None:
|
|
39
|
+
return end
|
|
40
|
+
start_date = datetime.strptime(start, "%Y-%m-%d")
|
|
41
|
+
return format_datetime(start_date + timedelta(days=7))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def format_datetime(val: datetime) -> str:
|
|
45
|
+
return val.strftime("%Y-%m-%d")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def is_number(s: str) -> bool:
|
|
49
|
+
try:
|
|
50
|
+
float(s)
|
|
51
|
+
return True
|
|
52
|
+
except ValueError:
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def data_frame_to_list(data_frame: DataFrame, index_name: str | None = None) -> list:
|
|
57
|
+
if data_frame is None:
|
|
58
|
+
return None
|
|
59
|
+
return json.loads(
|
|
60
|
+
data_frame.reset_index(names=index_name).to_json(
|
|
61
|
+
orient="records", date_format="iso"
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def series_to_list(series: Series) -> list:
|
|
67
|
+
if series is None:
|
|
68
|
+
return None
|
|
69
|
+
return json.loads(series.reset_index().to_json(orient="records", date_format="iso"))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def validate_value_in_list(valid_values: list[str]) -> Callable[[str], str]:
|
|
73
|
+
def _validator(x: str) -> str:
|
|
74
|
+
if x in valid_values:
|
|
75
|
+
return x
|
|
76
|
+
raise typer.BadParameter(
|
|
77
|
+
f"Invalid value: {x}, should be one of {', '.join(valid_values)}"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return _validator
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def validate_date_string(value: str) -> str:
|
|
84
|
+
"""Validate date string format (YYYY-MM-DD)"""
|
|
85
|
+
try:
|
|
86
|
+
if len(value) != 10:
|
|
87
|
+
raise ValueError()
|
|
88
|
+
datetime.strptime(value, "%Y-%m-%d")
|
|
89
|
+
return value
|
|
90
|
+
except ValueError:
|
|
91
|
+
raise typer.BadParameter(f"Invalid date format: {value}. Use YYYY-MM-DD")
|
src/writer.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Protocol
|
|
3
|
+
from rich.table import Table
|
|
4
|
+
from .utils import console_print
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class OutputWriter(Protocol):
|
|
8
|
+
def write(self, data: dict | list) -> None: ...
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class WriterFactory:
|
|
12
|
+
@staticmethod
|
|
13
|
+
def get_writer(writer_type: str) -> OutputWriter:
|
|
14
|
+
if writer_type == "json":
|
|
15
|
+
return JsonWriter()
|
|
16
|
+
if writer_type == "table":
|
|
17
|
+
return TableWriter()
|
|
18
|
+
raise ValueError(f"Unsupported writer type: {writer_type}")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class JsonWriter(OutputWriter):
|
|
22
|
+
def write(self, data: dict | list) -> None:
|
|
23
|
+
console_print(json.dumps(data, indent=2, default=str))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TableWriter(OutputWriter):
|
|
27
|
+
def write(self, data: dict | list) -> None:
|
|
28
|
+
table = Table(header_style="bold cyan")
|
|
29
|
+
|
|
30
|
+
if isinstance(data, dict):
|
|
31
|
+
data = [data]
|
|
32
|
+
|
|
33
|
+
if len(data) == 0:
|
|
34
|
+
console_print(table)
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
for key, value in data[0].items():
|
|
38
|
+
table.add_column(key)
|
|
39
|
+
|
|
40
|
+
for record in data:
|
|
41
|
+
table.add_row(*[str(value) for value in record.values()])
|
|
42
|
+
|
|
43
|
+
console_print(table)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yfin-cli
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A command-line interface for Yahoo Finance data
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: lxml>=6.0.2
|
|
9
|
+
Requires-Dist: rich>=14.2.0
|
|
10
|
+
Requires-Dist: typer>=0.21.1
|
|
11
|
+
Requires-Dist: yfinance>=1.1.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# yfin-cli
|
|
15
|
+
|
|
16
|
+
A command-line interface for Yahoo Finance data, powered by [yfinance](https://github.com/ranaroussi/yfinance).
|
|
17
|
+
|
|
18
|
+
## Why yfin-cli
|
|
19
|
+
|
|
20
|
+
- Fast, scriptable access to Yahoo Finance data.
|
|
21
|
+
- JSON or table output for easy piping.
|
|
22
|
+
- Broad coverage: prices, financials, analyst data, screeners, sectors, industries.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
Requires Python 3.10+.
|
|
27
|
+
|
|
28
|
+
### From PyPI (recommended)
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pipx install yfin-cli
|
|
32
|
+
# or
|
|
33
|
+
pip install yfin-cli
|
|
34
|
+
# or
|
|
35
|
+
uv tool install yfin-cli
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### From source
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
git clone <your-repo-url>
|
|
42
|
+
cd yfin-cli
|
|
43
|
+
uv tool install .
|
|
44
|
+
# or
|
|
45
|
+
pip install .
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
### Quickstart
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Get historical stock data
|
|
54
|
+
yfin history AAPL
|
|
55
|
+
|
|
56
|
+
# Get quick summary metrics
|
|
57
|
+
yfin fast-info TSLA
|
|
58
|
+
|
|
59
|
+
# Check market status
|
|
60
|
+
yfin market-status
|
|
61
|
+
|
|
62
|
+
# View quarterly income statement
|
|
63
|
+
yfin income-stmt MSFT --frequency quarterly
|
|
64
|
+
|
|
65
|
+
# Get analyst recommendations
|
|
66
|
+
yfin recommendations NVDA
|
|
67
|
+
|
|
68
|
+
# Screen stocks
|
|
69
|
+
yfin screen --filter "sector eq Technology" --filter "intradaymarketcap gt 100000000000"
|
|
70
|
+
|
|
71
|
+
# Use a predefined screener
|
|
72
|
+
yfin screen --predefined day_gainers
|
|
73
|
+
|
|
74
|
+
# Output as a table
|
|
75
|
+
yfin --output table history AAPL --period 1y
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
> For the complete command reference with all options and parameters, see [COMMANDS.md](COMMANDS.md).
|
|
79
|
+
|
|
80
|
+
### Global options:
|
|
81
|
+
|
|
82
|
+
| Option | Description | Default |
|
|
83
|
+
| ---------- | ------------------------------- | ------- |
|
|
84
|
+
| `--output` | Output format (`json`, `table`) | `json` |
|
|
85
|
+
| `--help` | Show help message | — |
|
|
86
|
+
|
|
87
|
+
## Available Commands
|
|
88
|
+
|
|
89
|
+
| Category | Commands |
|
|
90
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
91
|
+
| **Stock** | `history`, `dividends`, `fast-info`, `news` |
|
|
92
|
+
| **Market** | `market-status` |
|
|
93
|
+
| **Calendar** | `calendar-earnings`, `calendar-economic-events`, `calendar-ipo` |
|
|
94
|
+
| **Financials** | `income-stmt`, `balance-sheet`, `cashflow`, `earnings-dates`, `sec-filings` |
|
|
95
|
+
| **Analysis** | `recommendations`, `upgrades-downgrades`, `price-targets`, `earnings-estimate`, `revenue-estimate`, `earnings-history`, `eps-trend`, `eps-revisions`, `growth-estimates` |
|
|
96
|
+
| **Holders** | `insider-purchases`, `insider-transactions`, `insider-roster-holders`, `major-holders`, `institutional-holders`, `mutualfund-holders` |
|
|
97
|
+
| **Sector** | `sector-keys`, `sector-industries`, `sector-overview`, `sector-research-reports`, `sector-top-companies`, `sector-top-etfs`, `sector-top-mutual-funds` |
|
|
98
|
+
| **Industry** | `industry-overview`, `industry-research-reports`, `industry-top-companies`, `industry-top-growth-companies`, `industry-top-performing-companies` |
|
|
99
|
+
| **Screen** | `screen`, `screen-query-fields`, `screen-query-values`, `screen-predefined-queries` |
|
|
100
|
+
|
|
101
|
+
## Development
|
|
102
|
+
|
|
103
|
+
This project uses uv for dependency management.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Install dependencies
|
|
107
|
+
uv sync
|
|
108
|
+
|
|
109
|
+
# Run the CLI
|
|
110
|
+
uv run yfin --help
|
|
111
|
+
|
|
112
|
+
# Run tests
|
|
113
|
+
uv run pytest
|
|
114
|
+
|
|
115
|
+
# Format code
|
|
116
|
+
uv run ruff format .
|
|
117
|
+
|
|
118
|
+
# Build package
|
|
119
|
+
uv build
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
src/__init__.py,sha256=-aMOnBZnHvEG2-LRhGUjTFdQae8v9izcodgdi7Ycq8E,49
|
|
2
|
+
src/cli.py,sha256=cvoI9BMRnXxwg-bjjdhu3lZxNzt7j2iukvZ58b-RLUo,4243
|
|
3
|
+
src/decorators.py,sha256=dDWAyhQpGStIGK1lOHbAPNEUBja6J5Fpfd3J0cFGg28,1030
|
|
4
|
+
src/typer.py,sha256=-_6iu6-6WuuPOJZLJfEa6n3EBdR0VevE2Oic2sNsFp0,4687
|
|
5
|
+
src/utils.py,sha256=i55BB3R0kV5RMt3iaLe-VMIzjANZLSfG4lEVKbPDfiM,2321
|
|
6
|
+
src/writer.py,sha256=2o_CBC-Zhis5vGAE_xzJMCk25xoWou_RkAkkwJjvA6c,1108
|
|
7
|
+
src/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
src/commands/analysis.py,sha256=9adDjdOZsYARk6EmXYIuZtDZ8VnlOWVK8vo9-rW-dtg,3753
|
|
9
|
+
src/commands/calendar.py,sha256=x2iXVyMIu6i_-VmFnrLzt8uHpkML-tseXRFJAK0ChII,1900
|
|
10
|
+
src/commands/financials.py,sha256=Ffc9a0PCSE61-c34QWWluIikRS5QLdeRuqpeMikhy6E,1922
|
|
11
|
+
src/commands/industry.py,sha256=2yPy8-UfFd7htvCZy81lIDaGDQ4f6UW-UKR9LNZ5nJ8,1083
|
|
12
|
+
src/commands/market.py,sha256=jomhVsXGTIIjTvJkJr8YtQ7CJpi4GQjPLkKlZb7A2V8,187
|
|
13
|
+
src/commands/screen.py,sha256=UDKkJzDYJMJBcc5pHKDPZiy9UZcmL-gi_A7Cdhnrgik,8884
|
|
14
|
+
src/commands/sector.py,sha256=hXQTxMOdCgtGUqxOVMjARgwxL0-8pEQALgvs2U0qiDQ,1442
|
|
15
|
+
src/commands/stock.py,sha256=tbaYNx0kqXbKVOuUAJTwLUGSQI0uHVKt0Suc5dsN5Jc,2706
|
|
16
|
+
yfin_cli-0.1.1.dist-info/METADATA,sha256=63mEwvQKOkQBPpn0u7afjwFaBTS8EU1NKd640uQN5nM,4187
|
|
17
|
+
yfin_cli-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
18
|
+
yfin_cli-0.1.1.dist-info/entry_points.txt,sha256=AeTQvaF0pSSs34VdOcoGsJ_-Fjb47cPbhjO65l46aDg,37
|
|
19
|
+
yfin_cli-0.1.1.dist-info/licenses/LICENSE,sha256=k22ZS3asvWad8Mv9mA1YBc5zg7K0t5Ip94gQWIfpxR0,1065
|
|
20
|
+
yfin_cli-0.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rayyamhk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|