ceorater 0.1.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.
- ceorater-0.1.0/PKG-INFO +126 -0
- ceorater-0.1.0/README.md +105 -0
- ceorater-0.1.0/ceorater/__init__.py +3 -0
- ceorater-0.1.0/ceorater/__main__.py +5 -0
- ceorater-0.1.0/ceorater/cli.py +262 -0
- ceorater-0.1.0/ceorater/client.py +52 -0
- ceorater-0.1.0/ceorater/config.py +31 -0
- ceorater-0.1.0/ceorater.egg-info/PKG-INFO +126 -0
- ceorater-0.1.0/ceorater.egg-info/SOURCES.txt +13 -0
- ceorater-0.1.0/ceorater.egg-info/dependency_links.txt +1 -0
- ceorater-0.1.0/ceorater.egg-info/entry_points.txt +2 -0
- ceorater-0.1.0/ceorater.egg-info/requires.txt +3 -0
- ceorater-0.1.0/ceorater.egg-info/top_level.txt +1 -0
- ceorater-0.1.0/pyproject.toml +33 -0
- ceorater-0.1.0/setup.cfg +4 -0
ceorater-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ceorater
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI for CEORater — institutional-grade CEO performance analytics
|
|
5
|
+
Author-email: Jon Maietta <support@ceorater.com>
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://www.ceorater.com
|
|
8
|
+
Project-URL: Documentation, https://www.ceorater.com/api-docs.html
|
|
9
|
+
Project-URL: Support, https://www.ceorater.com/support.html
|
|
10
|
+
Keywords: ceo,finance,investing,analytics,sec
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: click>=8.0
|
|
19
|
+
Requires-Dist: requests>=2.28
|
|
20
|
+
Requires-Dist: rich>=13.0
|
|
21
|
+
|
|
22
|
+
# CEORater CLI
|
|
23
|
+
|
|
24
|
+
Command-line interface for [CEORater](https://www.ceorater.com) — institutional-grade CEO performance analytics covering 500+ S&P 500 CEOs.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install ceorater
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Setup
|
|
33
|
+
|
|
34
|
+
Get your API key at [ceorater.com/api-docs.html](https://www.ceorater.com/api-docs.html), then:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
ceorater configure
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or set the `CEORATER_API_KEY` environment variable.
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
### Look up a CEO by ticker
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ceorater lookup NVDA
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
NVIDIA (NVDA)
|
|
52
|
+
CEO: Jensen Huang | Founder: Yes | Tenure: 27.3 yrs
|
|
53
|
+
Sector: Technology | Industry: Semiconductors
|
|
54
|
+
|
|
55
|
+
Metric Value
|
|
56
|
+
CEORaterScore 99
|
|
57
|
+
AlphaScore 100
|
|
58
|
+
RevCAGR Score 100
|
|
59
|
+
CompScore A
|
|
60
|
+
|
|
61
|
+
TSR Multiple 589,250%
|
|
62
|
+
Avg Annual TSR 21,578%
|
|
63
|
+
TSR vs SPY 588,370%
|
|
64
|
+
Avg Annual vs SPY 21,546%
|
|
65
|
+
|
|
66
|
+
Compensation ($M) $49.9M
|
|
67
|
+
Cost/1% TSR ($M) $0.0M
|
|
68
|
+
Revenue CAGR 69.3%
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Search by company, CEO, sector, or industry
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
ceorater search "technology"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### List all CEOs (paginated)
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
ceorater list --limit 50
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Check data freshness
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
ceorater meta
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### JSON output for agents and scripts
|
|
90
|
+
|
|
91
|
+
Every data command supports `--json` for raw, machine-readable output:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
ceorater lookup AAPL --json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"companyName": "Apple Inc.",
|
|
100
|
+
"ticker": "AAPL",
|
|
101
|
+
"ceoraterScore": 76.2,
|
|
102
|
+
"alphaScore": 93.9,
|
|
103
|
+
"compScore": "C",
|
|
104
|
+
...
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Metrics
|
|
109
|
+
|
|
110
|
+
| Metric | Description |
|
|
111
|
+
|--------|-------------|
|
|
112
|
+
| CEORaterScore | Composite CEO effectiveness rating (0-100) |
|
|
113
|
+
| AlphaScore | Market outperformance score (0-100) |
|
|
114
|
+
| RevCAGR Score | Tenure-adjusted revenue growth percentile (0-100) |
|
|
115
|
+
| CompScore | Compensation efficiency grade (A-F) |
|
|
116
|
+
| TSR Multiple | Total Shareholder Return during tenure |
|
|
117
|
+
| Cost/1% TSR | CEO compensation cost per 1% of average annual TSR |
|
|
118
|
+
|
|
119
|
+
## Requirements
|
|
120
|
+
|
|
121
|
+
- Python 3.9+
|
|
122
|
+
- CEORater API subscription ($99/month) — [subscribe here](https://www.ceorater.com/api-docs.html)
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
Proprietary. See [terms of service](https://www.ceorater.com/terms.html).
|
ceorater-0.1.0/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# CEORater CLI
|
|
2
|
+
|
|
3
|
+
Command-line interface for [CEORater](https://www.ceorater.com) — institutional-grade CEO performance analytics covering 500+ S&P 500 CEOs.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install ceorater
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
Get your API key at [ceorater.com/api-docs.html](https://www.ceorater.com/api-docs.html), then:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
ceorater configure
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or set the `CEORATER_API_KEY` environment variable.
|
|
20
|
+
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
### Look up a CEO by ticker
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
ceorater lookup NVDA
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
NVIDIA (NVDA)
|
|
31
|
+
CEO: Jensen Huang | Founder: Yes | Tenure: 27.3 yrs
|
|
32
|
+
Sector: Technology | Industry: Semiconductors
|
|
33
|
+
|
|
34
|
+
Metric Value
|
|
35
|
+
CEORaterScore 99
|
|
36
|
+
AlphaScore 100
|
|
37
|
+
RevCAGR Score 100
|
|
38
|
+
CompScore A
|
|
39
|
+
|
|
40
|
+
TSR Multiple 589,250%
|
|
41
|
+
Avg Annual TSR 21,578%
|
|
42
|
+
TSR vs SPY 588,370%
|
|
43
|
+
Avg Annual vs SPY 21,546%
|
|
44
|
+
|
|
45
|
+
Compensation ($M) $49.9M
|
|
46
|
+
Cost/1% TSR ($M) $0.0M
|
|
47
|
+
Revenue CAGR 69.3%
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Search by company, CEO, sector, or industry
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
ceorater search "technology"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### List all CEOs (paginated)
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
ceorater list --limit 50
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Check data freshness
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
ceorater meta
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### JSON output for agents and scripts
|
|
69
|
+
|
|
70
|
+
Every data command supports `--json` for raw, machine-readable output:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
ceorater lookup AAPL --json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"companyName": "Apple Inc.",
|
|
79
|
+
"ticker": "AAPL",
|
|
80
|
+
"ceoraterScore": 76.2,
|
|
81
|
+
"alphaScore": 93.9,
|
|
82
|
+
"compScore": "C",
|
|
83
|
+
...
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Metrics
|
|
88
|
+
|
|
89
|
+
| Metric | Description |
|
|
90
|
+
|--------|-------------|
|
|
91
|
+
| CEORaterScore | Composite CEO effectiveness rating (0-100) |
|
|
92
|
+
| AlphaScore | Market outperformance score (0-100) |
|
|
93
|
+
| RevCAGR Score | Tenure-adjusted revenue growth percentile (0-100) |
|
|
94
|
+
| CompScore | Compensation efficiency grade (A-F) |
|
|
95
|
+
| TSR Multiple | Total Shareholder Return during tenure |
|
|
96
|
+
| Cost/1% TSR | CEO compensation cost per 1% of average annual TSR |
|
|
97
|
+
|
|
98
|
+
## Requirements
|
|
99
|
+
|
|
100
|
+
- Python 3.9+
|
|
101
|
+
- CEORater API subscription ($99/month) — [subscribe here](https://www.ceorater.com/api-docs.html)
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
Proprietary. See [terms of service](https://www.ceorater.com/terms.html).
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"""CEORater CLI — command definitions."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import math
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from ceorater.client import CEORaterError, Client
|
|
12
|
+
from ceorater.config import load_key, save_key
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
SCORE_FIELDS = [
|
|
17
|
+
("ceoraterScore", "CEORaterScore", "0-100"),
|
|
18
|
+
("alphaScore", "AlphaScore", "0-100"),
|
|
19
|
+
("revenueCagrScore", "RevCAGR Score", "0-100"),
|
|
20
|
+
("compScore", "CompScore", "A-F"),
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
PERF_FIELDS = [
|
|
24
|
+
("tsrMultiple", "TSR Multiple"),
|
|
25
|
+
("avgAnnualTsrRatio", "Avg Annual TSR"),
|
|
26
|
+
("tsrVsSpyRatio", "TSR vs SPY"),
|
|
27
|
+
("avgAnnualVsSpyRatio", "Avg Annual vs SPY"),
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
COMP_FIELDS = [
|
|
31
|
+
("compensationMM", "Compensation ($M)"),
|
|
32
|
+
("compPer1PctTsrMM", "Cost/1% TSR ($M)"),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _get_client() -> Client:
|
|
37
|
+
key = load_key()
|
|
38
|
+
if not key:
|
|
39
|
+
console.print(
|
|
40
|
+
"[red]No API key configured.[/red] Run [bold]ceorater configure[/bold] first."
|
|
41
|
+
)
|
|
42
|
+
sys.exit(1)
|
|
43
|
+
return Client(key)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _handle_error(e: CEORaterError) -> None:
|
|
47
|
+
if e.status == 401:
|
|
48
|
+
console.print("[red]Unauthorized.[/red] Check your API key with [bold]ceorater configure[/bold].")
|
|
49
|
+
elif e.status == 403:
|
|
50
|
+
console.print(f"[red]Subscription required.[/red] {e}")
|
|
51
|
+
elif e.status == 404:
|
|
52
|
+
console.print(f"[yellow]Not found.[/yellow] {e}")
|
|
53
|
+
else:
|
|
54
|
+
console.print(f"[red]Error {e.status}:[/red] {e}")
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _fmt_pct(val) -> str:
|
|
59
|
+
if val is None:
|
|
60
|
+
return "-"
|
|
61
|
+
return f"{val * 100:,.0f}%"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _fmt_score(val) -> str:
|
|
65
|
+
if val is None:
|
|
66
|
+
return "-"
|
|
67
|
+
if isinstance(val, str):
|
|
68
|
+
return val
|
|
69
|
+
return str(math.floor(val + 0.5))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _fmt_money(val) -> str:
|
|
73
|
+
if val is None:
|
|
74
|
+
return "-"
|
|
75
|
+
return f"${val:,.1f}M"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _fmt_years(val) -> str:
|
|
79
|
+
if val is None:
|
|
80
|
+
return "-"
|
|
81
|
+
return f"{val:.1f} yrs"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _print_ceo_card(ceo: dict) -> None:
|
|
85
|
+
console.print()
|
|
86
|
+
console.print(f" [bold]{ceo.get('companyName', '')}[/bold] ({ceo.get('ticker', '')})")
|
|
87
|
+
console.print(f" CEO: {ceo.get('ceo', '')} | Founder: {'Yes' if ceo.get('founderCEO') else 'No'} | Tenure: {_fmt_years(ceo.get('tenureYears'))}")
|
|
88
|
+
console.print(f" Sector: {ceo.get('sector', '')} | Industry: {ceo.get('industry', '')}")
|
|
89
|
+
console.print()
|
|
90
|
+
|
|
91
|
+
t = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
92
|
+
t.add_column("Metric")
|
|
93
|
+
t.add_column("Value", justify="right")
|
|
94
|
+
|
|
95
|
+
for key, label, _ in SCORE_FIELDS:
|
|
96
|
+
t.add_row(label, _fmt_score(ceo.get(key)))
|
|
97
|
+
|
|
98
|
+
t.add_row("", "")
|
|
99
|
+
for key, label in PERF_FIELDS:
|
|
100
|
+
t.add_row(label, _fmt_pct(ceo.get(key)))
|
|
101
|
+
|
|
102
|
+
t.add_row("", "")
|
|
103
|
+
for key, label in COMP_FIELDS:
|
|
104
|
+
t.add_row(label, _fmt_money(ceo.get(key)))
|
|
105
|
+
|
|
106
|
+
rev_cagr = ceo.get("revenueCagr")
|
|
107
|
+
if rev_cagr is not None:
|
|
108
|
+
t.add_row("Revenue CAGR", f"{rev_cagr * 100:.1f}%")
|
|
109
|
+
|
|
110
|
+
console.print(t)
|
|
111
|
+
console.print()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _print_ceo_table(items: list[dict]) -> None:
|
|
115
|
+
t = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
116
|
+
t.add_column("Ticker", style="bold")
|
|
117
|
+
t.add_column("Company")
|
|
118
|
+
t.add_column("CEO")
|
|
119
|
+
t.add_column("Score", justify="right")
|
|
120
|
+
t.add_column("Alpha", justify="right")
|
|
121
|
+
t.add_column("RevCAGR", justify="right")
|
|
122
|
+
t.add_column("Comp", justify="center")
|
|
123
|
+
t.add_column("Tenure", justify="right")
|
|
124
|
+
|
|
125
|
+
for ceo in items:
|
|
126
|
+
t.add_row(
|
|
127
|
+
ceo.get("ticker", ""),
|
|
128
|
+
(ceo.get("companyName") or "")[:30],
|
|
129
|
+
(ceo.get("ceo") or "")[:25],
|
|
130
|
+
_fmt_score(ceo.get("ceoraterScore")),
|
|
131
|
+
_fmt_score(ceo.get("alphaScore")),
|
|
132
|
+
_fmt_score(ceo.get("revenueCagrScore")),
|
|
133
|
+
ceo.get("compScore") or "-",
|
|
134
|
+
_fmt_years(ceo.get("tenureYears")),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
console.print()
|
|
138
|
+
console.print(t)
|
|
139
|
+
console.print()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# ── CLI Group ────────────────────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@click.group()
|
|
146
|
+
@click.version_option(package_name="ceorater")
|
|
147
|
+
def main():
|
|
148
|
+
"""CEORater — CEO performance analytics from the command line."""
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@main.command()
|
|
153
|
+
def configure():
|
|
154
|
+
"""Save your CEORater API key."""
|
|
155
|
+
key = click.prompt("Enter your CEORater API key", hide_input=True)
|
|
156
|
+
key = key.strip()
|
|
157
|
+
|
|
158
|
+
if not key:
|
|
159
|
+
console.print("[red]No key entered.[/red]")
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
console.print("Testing connection...", end=" ")
|
|
163
|
+
try:
|
|
164
|
+
client = Client(key)
|
|
165
|
+
meta = client.meta()
|
|
166
|
+
save_key(key)
|
|
167
|
+
count = meta.get("count", "?")
|
|
168
|
+
version = meta.get("api_version", "?")
|
|
169
|
+
console.print(f"[green]Connected.[/green] {count} CEOs available. API v{version}.")
|
|
170
|
+
except CEORaterError as e:
|
|
171
|
+
console.print(f"[red]Failed.[/red] {e}")
|
|
172
|
+
except Exception as e:
|
|
173
|
+
console.print(f"[red]Connection error.[/red] {e}")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@main.command()
|
|
177
|
+
@click.argument("ticker")
|
|
178
|
+
@click.option("--json", "as_json", is_flag=True, help="Output raw JSON")
|
|
179
|
+
def lookup(ticker: str, as_json: bool):
|
|
180
|
+
"""Look up a CEO by ticker symbol."""
|
|
181
|
+
client = _get_client()
|
|
182
|
+
try:
|
|
183
|
+
data = client.lookup(ticker.upper())
|
|
184
|
+
except CEORaterError as e:
|
|
185
|
+
_handle_error(e)
|
|
186
|
+
|
|
187
|
+
if as_json:
|
|
188
|
+
click.echo(json.dumps(data, indent=2))
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
if isinstance(data, list):
|
|
192
|
+
for ceo in data:
|
|
193
|
+
_print_ceo_card(ceo)
|
|
194
|
+
else:
|
|
195
|
+
_print_ceo_card(data)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@main.command()
|
|
199
|
+
@click.argument("query")
|
|
200
|
+
@click.option("--json", "as_json", is_flag=True, help="Output raw JSON")
|
|
201
|
+
def search(query: str, as_json: bool):
|
|
202
|
+
"""Search by company name, ticker, CEO, sector, or industry."""
|
|
203
|
+
client = _get_client()
|
|
204
|
+
try:
|
|
205
|
+
data = client.search(query)
|
|
206
|
+
except CEORaterError as e:
|
|
207
|
+
_handle_error(e)
|
|
208
|
+
|
|
209
|
+
if as_json:
|
|
210
|
+
click.echo(json.dumps(data, indent=2))
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
items = data.get("items", [])
|
|
214
|
+
count = data.get("count", len(items))
|
|
215
|
+
console.print(f"\n [bold]{count}[/bold] result(s) for \"{query}\"")
|
|
216
|
+
if items:
|
|
217
|
+
_print_ceo_table(items)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@main.command("list")
|
|
221
|
+
@click.option("--limit", default=20, show_default=True, help="Number of results")
|
|
222
|
+
@click.option("--offset", default=0, show_default=True, help="Starting position")
|
|
223
|
+
@click.option("--json", "as_json", is_flag=True, help="Output raw JSON")
|
|
224
|
+
def list_ceos(limit: int, offset: int, as_json: bool):
|
|
225
|
+
"""List CEOs (paginated)."""
|
|
226
|
+
client = _get_client()
|
|
227
|
+
try:
|
|
228
|
+
data = client.list_ceos(limit=limit, offset=offset)
|
|
229
|
+
except CEORaterError as e:
|
|
230
|
+
_handle_error(e)
|
|
231
|
+
|
|
232
|
+
if as_json:
|
|
233
|
+
click.echo(json.dumps(data, indent=2))
|
|
234
|
+
return
|
|
235
|
+
|
|
236
|
+
items = data.get("items", [])
|
|
237
|
+
total = data.get("total", "?")
|
|
238
|
+
console.print(f"\n Showing {offset + 1}-{offset + len(items)} of [bold]{total}[/bold]")
|
|
239
|
+
if items:
|
|
240
|
+
_print_ceo_table(items)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@main.command()
|
|
244
|
+
@click.option("--json", "as_json", is_flag=True, help="Output raw JSON")
|
|
245
|
+
def meta(as_json: bool):
|
|
246
|
+
"""Show API metadata and data freshness."""
|
|
247
|
+
client = _get_client()
|
|
248
|
+
try:
|
|
249
|
+
data = client.meta()
|
|
250
|
+
except CEORaterError as e:
|
|
251
|
+
_handle_error(e)
|
|
252
|
+
|
|
253
|
+
if as_json:
|
|
254
|
+
click.echo(json.dumps(data, indent=2))
|
|
255
|
+
return
|
|
256
|
+
|
|
257
|
+
console.print()
|
|
258
|
+
console.print(f" CEOs available : [bold]{data.get('count', '?')}[/bold]")
|
|
259
|
+
console.print(f" Last updated : {data.get('last_loaded', '?')}")
|
|
260
|
+
console.print(f" API version : {data.get('api_version', '?')}")
|
|
261
|
+
console.print(f" Base URL : {data.get('base_url', '?')}")
|
|
262
|
+
console.print()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""HTTP client for the CEORater API."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
from ceorater import __version__
|
|
8
|
+
|
|
9
|
+
BASE_URL = "https://api.ceorater.com"
|
|
10
|
+
TIMEOUT = 15
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CEORaterError(Exception):
|
|
14
|
+
def __init__(self, status: int, code: str, message: str):
|
|
15
|
+
self.status = status
|
|
16
|
+
self.code = code
|
|
17
|
+
super().__init__(message)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Client:
|
|
21
|
+
def __init__(self, api_key: str):
|
|
22
|
+
self.session = requests.Session()
|
|
23
|
+
self.session.headers.update({
|
|
24
|
+
"Authorization": f"Bearer {api_key}",
|
|
25
|
+
"User-Agent": f"ceorater-cli/{__version__}",
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
def _get(self, path: str, params: dict | None = None) -> dict | list:
|
|
29
|
+
resp = self.session.get(f"{BASE_URL}{path}", params=params, timeout=TIMEOUT)
|
|
30
|
+
if not resp.ok:
|
|
31
|
+
try:
|
|
32
|
+
body = resp.json()
|
|
33
|
+
raise CEORaterError(
|
|
34
|
+
resp.status_code,
|
|
35
|
+
body.get("code", "UNKNOWN"),
|
|
36
|
+
body.get("error", resp.text),
|
|
37
|
+
)
|
|
38
|
+
except (ValueError, KeyError):
|
|
39
|
+
raise CEORaterError(resp.status_code, "UNKNOWN", resp.text)
|
|
40
|
+
return resp.json()
|
|
41
|
+
|
|
42
|
+
def meta(self) -> dict:
|
|
43
|
+
return self._get("/v1/meta")
|
|
44
|
+
|
|
45
|
+
def lookup(self, ticker: str, fmt: str = "raw") -> dict | list:
|
|
46
|
+
return self._get(f"/v1/ceo/{ticker}", {"format": fmt})
|
|
47
|
+
|
|
48
|
+
def search(self, query: str, fmt: str = "raw") -> dict:
|
|
49
|
+
return self._get("/v1/search", {"q": query, "format": fmt})
|
|
50
|
+
|
|
51
|
+
def list_ceos(self, limit: int = 50, offset: int = 0, fmt: str = "raw") -> dict:
|
|
52
|
+
return self._get("/v1/ceos", {"limit": limit, "offset": offset, "format": fmt})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""API key storage and retrieval."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
CONFIG_DIR = Path.home() / ".ceorater"
|
|
8
|
+
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
9
|
+
|
|
10
|
+
ENV_VAR = "CEORATER_API_KEY"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def save_key(api_key: str) -> None:
|
|
14
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
15
|
+
CONFIG_FILE.write_text(
|
|
16
|
+
json.dumps({"api_key": api_key}, indent=2),
|
|
17
|
+
encoding="utf-8",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def load_key() -> str | None:
|
|
22
|
+
key = os.environ.get(ENV_VAR)
|
|
23
|
+
if key:
|
|
24
|
+
return key
|
|
25
|
+
if not CONFIG_FILE.exists():
|
|
26
|
+
return None
|
|
27
|
+
try:
|
|
28
|
+
data = json.loads(CONFIG_FILE.read_text(encoding="utf-8"))
|
|
29
|
+
return data.get("api_key") or None
|
|
30
|
+
except (json.JSONDecodeError, OSError):
|
|
31
|
+
return None
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ceorater
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI for CEORater — institutional-grade CEO performance analytics
|
|
5
|
+
Author-email: Jon Maietta <support@ceorater.com>
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://www.ceorater.com
|
|
8
|
+
Project-URL: Documentation, https://www.ceorater.com/api-docs.html
|
|
9
|
+
Project-URL: Support, https://www.ceorater.com/support.html
|
|
10
|
+
Keywords: ceo,finance,investing,analytics,sec
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: click>=8.0
|
|
19
|
+
Requires-Dist: requests>=2.28
|
|
20
|
+
Requires-Dist: rich>=13.0
|
|
21
|
+
|
|
22
|
+
# CEORater CLI
|
|
23
|
+
|
|
24
|
+
Command-line interface for [CEORater](https://www.ceorater.com) — institutional-grade CEO performance analytics covering 500+ S&P 500 CEOs.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install ceorater
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Setup
|
|
33
|
+
|
|
34
|
+
Get your API key at [ceorater.com/api-docs.html](https://www.ceorater.com/api-docs.html), then:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
ceorater configure
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or set the `CEORATER_API_KEY` environment variable.
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
### Look up a CEO by ticker
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ceorater lookup NVDA
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
NVIDIA (NVDA)
|
|
52
|
+
CEO: Jensen Huang | Founder: Yes | Tenure: 27.3 yrs
|
|
53
|
+
Sector: Technology | Industry: Semiconductors
|
|
54
|
+
|
|
55
|
+
Metric Value
|
|
56
|
+
CEORaterScore 99
|
|
57
|
+
AlphaScore 100
|
|
58
|
+
RevCAGR Score 100
|
|
59
|
+
CompScore A
|
|
60
|
+
|
|
61
|
+
TSR Multiple 589,250%
|
|
62
|
+
Avg Annual TSR 21,578%
|
|
63
|
+
TSR vs SPY 588,370%
|
|
64
|
+
Avg Annual vs SPY 21,546%
|
|
65
|
+
|
|
66
|
+
Compensation ($M) $49.9M
|
|
67
|
+
Cost/1% TSR ($M) $0.0M
|
|
68
|
+
Revenue CAGR 69.3%
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Search by company, CEO, sector, or industry
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
ceorater search "technology"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### List all CEOs (paginated)
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
ceorater list --limit 50
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Check data freshness
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
ceorater meta
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### JSON output for agents and scripts
|
|
90
|
+
|
|
91
|
+
Every data command supports `--json` for raw, machine-readable output:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
ceorater lookup AAPL --json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"companyName": "Apple Inc.",
|
|
100
|
+
"ticker": "AAPL",
|
|
101
|
+
"ceoraterScore": 76.2,
|
|
102
|
+
"alphaScore": 93.9,
|
|
103
|
+
"compScore": "C",
|
|
104
|
+
...
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Metrics
|
|
109
|
+
|
|
110
|
+
| Metric | Description |
|
|
111
|
+
|--------|-------------|
|
|
112
|
+
| CEORaterScore | Composite CEO effectiveness rating (0-100) |
|
|
113
|
+
| AlphaScore | Market outperformance score (0-100) |
|
|
114
|
+
| RevCAGR Score | Tenure-adjusted revenue growth percentile (0-100) |
|
|
115
|
+
| CompScore | Compensation efficiency grade (A-F) |
|
|
116
|
+
| TSR Multiple | Total Shareholder Return during tenure |
|
|
117
|
+
| Cost/1% TSR | CEO compensation cost per 1% of average annual TSR |
|
|
118
|
+
|
|
119
|
+
## Requirements
|
|
120
|
+
|
|
121
|
+
- Python 3.9+
|
|
122
|
+
- CEORater API subscription ($99/month) — [subscribe here](https://www.ceorater.com/api-docs.html)
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
Proprietary. See [terms of service](https://www.ceorater.com/terms.html).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
ceorater/__init__.py
|
|
4
|
+
ceorater/__main__.py
|
|
5
|
+
ceorater/cli.py
|
|
6
|
+
ceorater/client.py
|
|
7
|
+
ceorater/config.py
|
|
8
|
+
ceorater.egg-info/PKG-INFO
|
|
9
|
+
ceorater.egg-info/SOURCES.txt
|
|
10
|
+
ceorater.egg-info/dependency_links.txt
|
|
11
|
+
ceorater.egg-info/entry_points.txt
|
|
12
|
+
ceorater.egg-info/requires.txt
|
|
13
|
+
ceorater.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ceorater
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ceorater"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "CLI for CEORater — institutional-grade CEO performance analytics"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "Proprietary"}
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [{name = "Jon Maietta", email = "support@ceorater.com"}]
|
|
13
|
+
keywords = ["ceo", "finance", "investing", "analytics", "sec"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
18
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"click>=8.0",
|
|
23
|
+
"requests>=2.28",
|
|
24
|
+
"rich>=13.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://www.ceorater.com"
|
|
29
|
+
Documentation = "https://www.ceorater.com/api-docs.html"
|
|
30
|
+
Support = "https://www.ceorater.com/support.html"
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
ceorater = "ceorater.cli:main"
|
ceorater-0.1.0/setup.cfg
ADDED