finradar-cli 0.1.0__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.
Files changed (50) hide show
  1. finradar_cli/__init__.py +1 -0
  2. finradar_cli/__main__.py +4 -0
  3. finradar_cli/_generated/__init__.py +96 -0
  4. finradar_cli/_generated/cmd_13dg.py +23 -0
  5. finradar_cli/_generated/cmd_13f.py +291 -0
  6. finradar_cli/_generated/cmd_billing.py +68 -0
  7. finradar_cli/_generated/cmd_core.py +38 -0
  8. finradar_cli/_generated/cmd_cusip.py +124 -0
  9. finradar_cli/_generated/cmd_download.py +23 -0
  10. finradar_cli/_generated/cmd_facts.py +38 -0
  11. finradar_cli/_generated/cmd_financials.py +141 -0
  12. finradar_cli/_generated/cmd_holders.py +53 -0
  13. finradar_cli/_generated/cmd_index.py +21 -0
  14. finradar_cli/_generated/cmd_insider.py +322 -0
  15. finradar_cli/_generated/cmd_market.py +23 -0
  16. finradar_cli/_generated/cmd_ownership.py +111 -0
  17. finradar_cli/_generated/cmd_ownership_analytics.py +38 -0
  18. finradar_cli/_generated/cmd_positions.py +83 -0
  19. finradar_cli/_generated/cmd_pricing.py +53 -0
  20. finradar_cli/_generated/cmd_progress.py +23 -0
  21. finradar_cli/_generated/cmd_search.py +171 -0
  22. finradar_cli/_generated/cmd_sec.py +141 -0
  23. finradar_cli/_generated/cmd_status.py +36 -0
  24. finradar_cli/_generated/cmd_ticker.py +92 -0
  25. finradar_cli/_generated/cmd_user.py +21 -0
  26. finradar_cli/_generated/command_index.json +1308 -0
  27. finradar_cli/_generated/completion/commands.json +373 -0
  28. finradar_cli/app.py +103 -0
  29. finradar_cli/commands/__init__.py +0 -0
  30. finradar_cli/commands/api.py +124 -0
  31. finradar_cli/commands/auth_cmds.py +64 -0
  32. finradar_cli/commands/meta.py +83 -0
  33. finradar_cli/commands/update_cmd.py +234 -0
  34. finradar_cli/core/__init__.py +0 -0
  35. finradar_cli/core/auth.py +178 -0
  36. finradar_cli/core/client.py +95 -0
  37. finradar_cli/core/coerce.py +59 -0
  38. finradar_cli/core/config.py +80 -0
  39. finradar_cli/core/curation.py +17 -0
  40. finradar_cli/core/errors.py +44 -0
  41. finradar_cli/core/oauth.py +206 -0
  42. finradar_cli/core/output.py +49 -0
  43. finradar_cli/core/spec.py +42 -0
  44. finradar_cli/data/openapi.json +12797 -0
  45. finradar_cli/marquee.toml +37 -0
  46. finradar_cli/type_overrides.toml +6 -0
  47. finradar_cli-0.1.0.dist-info/METADATA +19 -0
  48. finradar_cli-0.1.0.dist-info/RECORD +50 -0
  49. finradar_cli-0.1.0.dist-info/WHEEL +4 -0
  50. finradar_cli-0.1.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,124 @@
1
+ """GENERATED by generate.py — do not edit. Run `make generate`."""
2
+ from __future__ import annotations
3
+
4
+ import typer
5
+ from finradar_cli.commands.api import run_request
6
+
7
+
8
+ def build(parent: typer.Typer) -> None:
9
+ _sub_batch = typer.Typer(no_args_is_help=True)
10
+ parent.add_typer(_sub_batch, name='batch')
11
+ _sub_chain = typer.Typer(no_args_is_help=True)
12
+ parent.add_typer(_sub_chain, name='chain')
13
+ _sub_cik = typer.Typer(no_args_is_help=True)
14
+ parent.add_typer(_sub_cik, name='cik')
15
+ _sub_cusip6 = typer.Typer(no_args_is_help=True)
16
+ parent.add_typer(_sub_cusip6, name='cusip6')
17
+ _sub_stats = typer.Typer(no_args_is_help=True)
18
+ parent.add_typer(_sub_stats, name='stats')
19
+ _sub_ticker = typer.Typer(no_args_is_help=True)
20
+ parent.add_typer(_sub_ticker, name='ticker')
21
+
22
+ @_sub_batch.command('create', help='CUSIP Batch [cost 5]')
23
+ def cmd_post_api_v1_cusip_batch(ctx: typer.Context, cusips: str = typer.Option(None, '--cusips', help='Array of 9-character CUSIP strings (max 1,000 per request — server returns 400 above the cap; for larger universes split into multiple batches client-side). 8-char CUSIPs (missing check digit) are zero-padded server-side. Whitespace and case are normalized. Order is preserved in the response so client code can zip results back to inputs.'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
24
+ url = '/api/v1/cusip/batch'
25
+ _pp: dict[str, str] = {}
26
+ for _k, _v in _pp.items():
27
+ url = url.replace("{" + _k + "}", str(_v))
28
+ _kv: list[str] = []
29
+ _body: dict[str, str | None] = {'cusips': cusips}
30
+ for _name, _val in _body.items():
31
+ if _val is not None:
32
+ _kv.append(f"{_name}={_val}")
33
+ run_request(ctx.obj, 'POST', url, _kv, data=data)
34
+
35
+ @_sub_chain.command('get', help='CUSIP Chain [cost 5]')
36
+ def cmd_get_api_v1_cusip_chain_cusip(ctx: typer.Context, cusip: str = typer.Argument(..., help='Any CUSIP in the chain (old or current). The endpoint walks the chain in BOTH directions from the input — pre-mergers AND post-mergers — to return the complete history. Examples: `G9442R126` (pre-SPAC RKLB) returns the same chain as `773121108` (current RKLB).; example: 773121108'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
37
+ url = '/api/v1/cusip/chain/{cusip}'
38
+ _pp: dict[str, str] = {'cusip': cusip}
39
+ for _k, _v in _pp.items():
40
+ url = url.replace("{" + _k + "}", str(_v))
41
+ _kv: list[str] = []
42
+ _body: dict[str, str | None] = {}
43
+ for _name, _val in _body.items():
44
+ if _val is not None:
45
+ _kv.append(f"{_name}={_val}")
46
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
47
+
48
+ @_sub_cik.command('get', help='CUSIP CIK [cost 1]')
49
+ def cmd_get_api_v1_cusip_cik_cik(ctx: typer.Context, cik: str = typer.Argument(..., help="SEC CIK number. Both formats accepted: 10-character zero-padded (`0000320193`) and short unpadded (`320193`). Server-side normalization handles both via `func.ltrim(cik, '0')` matching. Returns 404 when no CUSIP is registered to the CIK (rare; typically foreign-private issuers without ADR programs, or pre-registration shell companies).; example: 0000320193"), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
50
+ url = '/api/v1/cusip/cik/{cik}'
51
+ _pp: dict[str, str] = {'cik': cik}
52
+ for _k, _v in _pp.items():
53
+ url = url.replace("{" + _k + "}", str(_v))
54
+ _kv: list[str] = []
55
+ _body: dict[str, str | None] = {}
56
+ for _name, _val in _body.items():
57
+ if _val is not None:
58
+ _kv.append(f"{_name}={_val}")
59
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
60
+
61
+ @_sub_cusip6.command('get', help='CUSIP Cusip6 [cost 1]')
62
+ def cmd_get_api_v1_cusip_cusip6_cusip6(ctx: typer.Context, cusip6: str = typer.Argument(..., help='6-character CUSIP issuer prefix (case-insensitive). Real public-domain examples: `037833` (Apple), `594918` (Microsoft), `02079K` (Alphabet — covers both GOOGL `02079K305` and GOOG `02079K107`), `084670` (Berkshire — covers both BRK-A `084670108` and BRK-B `084670702`). Returns 404 when no securities are registered to the prefix.; example: 037833'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
63
+ url = '/api/v1/cusip/cusip6/{cusip6}'
64
+ _pp: dict[str, str] = {'cusip6': cusip6}
65
+ for _k, _v in _pp.items():
66
+ url = url.replace("{" + _k + "}", str(_v))
67
+ _kv: list[str] = []
68
+ _body: dict[str, str | None] = {}
69
+ for _name, _val in _body.items():
70
+ if _val is not None:
71
+ _kv.append(f"{_name}={_val}")
72
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
73
+
74
+ @parent.command('lookup', help='CUSIP Lookup [cost 1]')
75
+ def cmd_get_api_v1_cusip_lookup_cusip(ctx: typer.Context, cusip: str = typer.Argument(..., help='9-character CUSIP. Whitespace and case are normalized server-side; 8-character CUSIPs (missing the trailing check digit) are zero-padded to 9. Returns 404 when the CUSIP is not in `cusip_security_catalog` — for stale or successor-replaced CUSIPs try `/cusip/chain/{cusip}` to chase corporate-action history. Real public-domain examples: `037833100` (Apple), `594918104` (Microsoft), `02079K305` (Alphabet Class A), `02079K107` (Alphabet Class C), `084670702` (Berkshire Class B).; example: 037833100'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
76
+ url = '/api/v1/cusip/lookup/{cusip}'
77
+ _pp: dict[str, str] = {'cusip': cusip}
78
+ for _k, _v in _pp.items():
79
+ url = url.replace("{" + _k + "}", str(_v))
80
+ _kv: list[str] = []
81
+ _body: dict[str, str | None] = {}
82
+ for _name, _val in _body.items():
83
+ if _val is not None:
84
+ _kv.append(f"{_name}={_val}")
85
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
86
+
87
+ @parent.command('search', help='CUSIP Search [cost 1]')
88
+ def cmd_get_api_v1_cusip_search(ctx: typer.Context, q: str = typer.Option(None, '--q', help='Search query (matches CUSIP, ticker, or security name). Case-insensitive. Trigram fuzzy matching on the name field — `apple` matches `APPLE INC` AND `APPLE HOSPITALITY REIT`. CUSIP fragment matching is exact-prefix (so `037` matches `037833100` but `833` does not). Ticker matching is exact OR prefix on canonical hyphen form (`BRK` matches both `BRK-A` and `BRK-B`).; example: apple'), limit: str = typer.Option(None, '--limit', help='Max results returned (1-100). Server caps at 100 — values > 100 silently clamp to 100.; example: 20'), offset: str = typer.Option(None, '--offset', help='Pagination offset for chunked retrieval. Pair with `limit` for `total // limit` page count.; example: 0'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
89
+ url = '/api/v1/cusip/search'
90
+ _pp: dict[str, str] = {}
91
+ for _k, _v in _pp.items():
92
+ url = url.replace("{" + _k + "}", str(_v))
93
+ _kv: list[str] = []
94
+ _body: dict[str, str | None] = {'q': q, 'limit': limit, 'offset': offset}
95
+ for _name, _val in _body.items():
96
+ if _val is not None:
97
+ _kv.append(f"{_name}={_val}")
98
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
99
+
100
+ @_sub_stats.command('get', help='CUSIP Stats [cost 5]')
101
+ def cmd_get_api_v1_cusip_stats(ctx: typer.Context, data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
102
+ url = '/api/v1/cusip/stats'
103
+ _pp: dict[str, str] = {}
104
+ for _k, _v in _pp.items():
105
+ url = url.replace("{" + _k + "}", str(_v))
106
+ _kv: list[str] = []
107
+ _body: dict[str, str | None] = {}
108
+ for _name, _val in _body.items():
109
+ if _val is not None:
110
+ _kv.append(f"{_name}={_val}")
111
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
112
+
113
+ @_sub_ticker.command('get', help='CUSIP Ticker [cost 1]')
114
+ def cmd_get_api_v1_cusip_ticker_ticker(ctx: typer.Context, ticker: str = typer.Argument(..., help='Canonical hyphen-form ticker (`BRK-A` not `BRK.A`). Server-side normalization applies the v3.14.2 ticker-coercion shim — `BRK.A`, `BRK/A`, `BRKA` all coerce to `BRK-A`. Returns 404 when no active CUSIP mapping exists (delisted issuers, foreign tickers without OpenFIGI coverage).; example: AAPL'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
115
+ url = '/api/v1/cusip/ticker/{ticker}'
116
+ _pp: dict[str, str] = {'ticker': ticker}
117
+ for _k, _v in _pp.items():
118
+ url = url.replace("{" + _k + "}", str(_v))
119
+ _kv: list[str] = []
120
+ _body: dict[str, str | None] = {}
121
+ for _name, _val in _body.items():
122
+ if _val is not None:
123
+ _kv.append(f"{_name}={_val}")
124
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
@@ -0,0 +1,23 @@
1
+ """GENERATED by generate.py — do not edit. Run `make generate`."""
2
+ from __future__ import annotations
3
+
4
+ import typer
5
+ from finradar_cli.commands.api import run_request
6
+
7
+
8
+ def build(parent: typer.Typer) -> None:
9
+ _sub_pdf = typer.Typer(no_args_is_help=True)
10
+ parent.add_typer(_sub_pdf, name='pdf')
11
+
12
+ @_sub_pdf.command('create', help='Scrapping Pdf [cost 25]')
13
+ def cmd_post_api_v1_scrapping_pdf(ctx: typer.Context, link: str = typer.Option(None, '--link', help="Direct SEC document URL — the filing's primary HTML document (NOT the index page; must be the actual content URL). Server-side rejected if not on the `sec.gov` domain. iXBRL viewer URLs (`https://www.sec.gov/ix?doc=/...`) are auto-normalized to the underlying document URL — pass either form."), type_: str = typer.Option(None, '--type', help='Output format. `pdf` (default) — full headless-Chrome render with embedded fonts, tables, and images preserved. `html` — cleaned HTML with embedded styles inlined (lighter weight, smaller files). `txt` — plain-text strip-down for NLP pipelines and LLM grounding (loses table structure but tiny files).'), fileName: str = typer.Option(None, '--fileName', help='Custom filename for the download (extension auto-appended based on `type`). Useful for archival workflows where you want predictable filenames (e.g. `{ticker}-{form_type}-{fiscal_year}`). Defaults to a hash-based filename derived from the source URL when omitted.'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
14
+ url = '/api/v1/scrapping/pdf'
15
+ _pp: dict[str, str] = {}
16
+ for _k, _v in _pp.items():
17
+ url = url.replace("{" + _k + "}", str(_v))
18
+ _kv: list[str] = []
19
+ _body: dict[str, str | None] = {'link': link, 'type': type_, 'fileName': fileName}
20
+ for _name, _val in _body.items():
21
+ if _val is not None:
22
+ _kv.append(f"{_name}={_val}")
23
+ run_request(ctx.obj, 'POST', url, _kv, data=data)
@@ -0,0 +1,38 @@
1
+ """GENERATED by generate.py — do not edit. Run `make generate`."""
2
+ from __future__ import annotations
3
+
4
+ import typer
5
+ from finradar_cli.commands.api import run_request
6
+
7
+
8
+ def build(parent: typer.Typer) -> None:
9
+ _sub_aggregate = typer.Typer(no_args_is_help=True)
10
+ parent.add_typer(_sub_aggregate, name='aggregate')
11
+ _sub_by_concept = typer.Typer(no_args_is_help=True)
12
+ parent.add_typer(_sub_by_concept, name='by-concept')
13
+
14
+ @_sub_aggregate.command('create', help='Facts Aggregate [cost 10]')
15
+ def cmd_post_api_v1_facts_aggregate(ctx: typer.Context, concept: str = typer.Option(None, '--concept', help='XBRL concept name in `prefix:LocalName` format (e.g. `us-gaap:Revenues`, `us-gaap:NetIncomeLoss`). Same concept syntax as `/facts/by-concept`. Returns 400 for unknown concepts.'), period: str = typer.Option(None, '--period', help='Period filter — accepts (a) exact ISO `YYYY-MM-DD` for a specific period-end date, or (b) `YYYY-Q1`/`YYYY-Q2`/`YYYY-Q3`/`YYYY-Q4` for fiscal-quarter rollup, or (c) `YYYY` for full-year (annual filings only). Calendar-year scope; for fiscal-year offsets (companies with non-calendar fiscal years) use `/facts/by-concept` and aggregate client-side.'), sector: str = typer.Option(None, '--sector', help='Sharadar sector filter (e.g. `Technology`, `Healthcare`, `Industrials`). Default: aggregate across all sectors. Excludes companies unmapped in Sharadar (~5% of universe).'), tickers: str = typer.Option(None, '--tickers', help="Array of ticker strings to restrict the aggregation to (max 500 entries). Useful for index-defined cohort aggregations (e.g. 'aggregate over the S&P 500'). Mutually exclusive with `sector` — pass one or the other, not both."), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
16
+ url = '/api/v1/facts/aggregate'
17
+ _pp: dict[str, str] = {}
18
+ for _k, _v in _pp.items():
19
+ url = url.replace("{" + _k + "}", str(_v))
20
+ _kv: list[str] = []
21
+ _body: dict[str, str | None] = {'concept': concept, 'period': period, 'sector': sector, 'tickers': tickers}
22
+ for _name, _val in _body.items():
23
+ if _val is not None:
24
+ _kv.append(f"{_name}={_val}")
25
+ run_request(ctx.obj, 'POST', url, _kv, data=data)
26
+
27
+ @_sub_by_concept.command('get', help='Facts By Concept [cost 10]')
28
+ def cmd_get_api_v1_facts_by_concept(ctx: typer.Context, concept: str = typer.Option(None, '--concept', help="XBRL concept name in `prefix:LocalName` format. Standard concepts use `us-gaap:` prefix (e.g. `us-gaap:Revenues`, `us-gaap:NetIncomeLoss`, `us-gaap:Assets`); company-specific extensions use the company's own prefix. Case-sensitive. Returns 400 for unknown concepts; use the SEC EDGAR taxonomy browser to verify concept names before query.; example: us-gaap:Revenues"), ticker: str = typer.Option(None, '--ticker', help="Restrict to one issuer ticker (case-insensitive, canonical hyphen normalization). Default: returns facts across all ~7,000 companies. Useful for per-ticker concept time-series ('show me every reported `us-gaap:Revenues` for AAPL across all quarters').; example: AAPL"), date_from: str = typer.Option(None, '--date_from', help='Inclusive lower bound on `period_end` (ISO `YYYY-MM-DD`). Useful for limiting to recent fiscal periods on high-cardinality concepts.; example: 2024-01-01'), date_to: str = typer.Option(None, '--date_to', help='Inclusive upper bound on `period_end` (ISO `YYYY-MM-DD`). Combine with `date_from` for closed ranges.; example: 2025-12-31'), limit: str = typer.Option(None, '--limit', help='Maximum fact rows returned, capped at 1000 server-side. For bulk concept exports use date-range chunking + `limit=1000`.; example: 100'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
29
+ url = '/api/v1/facts/by-concept'
30
+ _pp: dict[str, str] = {}
31
+ for _k, _v in _pp.items():
32
+ url = url.replace("{" + _k + "}", str(_v))
33
+ _kv: list[str] = []
34
+ _body: dict[str, str | None] = {'concept': concept, 'ticker': ticker, 'date_from': date_from, 'date_to': date_to, 'limit': limit}
35
+ for _name, _val in _body.items():
36
+ if _val is not None:
37
+ _kv.append(f"{_name}={_val}")
38
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
@@ -0,0 +1,141 @@
1
+ """GENERATED by generate.py — do not edit. Run `make generate`."""
2
+ from __future__ import annotations
3
+
4
+ import typer
5
+ from finradar_cli.commands.api import run_request
6
+
7
+
8
+ def build(parent: typer.Typer) -> None:
9
+ _sub_changes = typer.Typer(no_args_is_help=True)
10
+ parent.add_typer(_sub_changes, name='changes')
11
+ _sub_financials = typer.Typer(no_args_is_help=True)
12
+ parent.add_typer(_sub_financials, name='financials')
13
+ _sub_metrics = typer.Typer(no_args_is_help=True)
14
+ parent.add_typer(_sub_metrics, name='metrics')
15
+ _sub_ratios = typer.Typer(no_args_is_help=True)
16
+ parent.add_typer(_sub_ratios, name='ratios')
17
+ _sub_status = typer.Typer(no_args_is_help=True)
18
+ parent.add_typer(_sub_status, name='status')
19
+ _sub_stock_overview = typer.Typer(no_args_is_help=True)
20
+ parent.add_typer(_sub_stock_overview, name='stock-overview')
21
+ _sub_tickers = typer.Typer(no_args_is_help=True)
22
+ parent.add_typer(_sub_tickers, name='tickers')
23
+ _sub_financials_changes = typer.Typer(no_args_is_help=True)
24
+ _sub_financials.add_typer(_sub_financials_changes, name='changes')
25
+
26
+ @_sub_changes.command('get', help='Financials Changes [cost 5]')
27
+ def cmd_get_api_v1_financials_changes(ctx: typer.Context, since: str = typer.Option(None, '--since', help='ISO-8601 UTC timestamp lower-bound on `last_updated_at`. Default = 7 days ago. Pass the most-recent `last_updated_at` from your last poll to receive only newly-updated tickers (incremental delta-feed pattern).; example: 2026-04-25T00:00:00Z'), limit: str = typer.Option(None, '--limit', help='Maximum tickers returned (capped at 200 server-side). For typical delta-feed cadence (every 6-24 hours) 50-100 is plenty; for rare large-batch backfill catch-ups use 200 + paginated walks.; example: 50'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
28
+ url = '/api/v1/financials/changes'
29
+ _pp: dict[str, str] = {}
30
+ for _k, _v in _pp.items():
31
+ url = url.replace("{" + _k + "}", str(_v))
32
+ _kv: list[str] = []
33
+ _body: dict[str, str | None] = {'since': since, 'limit': limit}
34
+ for _name, _val in _body.items():
35
+ if _val is not None:
36
+ _kv.append(f"{_name}={_val}")
37
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
38
+
39
+ @_sub_financials_changes.command('get', help='Financials Changes [cost 5]')
40
+ def cmd_get_api_v1_financials_changes_ticker(ctx: typer.Context, ticker: str = typer.Argument(..., help='Stock ticker symbol (canonical hyphen form). Returns 404 if ticker has no XBRL coverage; returns empty `periods_updated[]` if ticker exists but has no recent updates.; example: AAPL'), since: str = typer.Option(None, '--since', help='ISO-8601 UTC timestamp lower-bound on `updated_at`. Default = 7 days ago. Pass a wider window to surface restatement events that backfill historical periods.; example: 2026-04-25T00:00:00Z'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
41
+ url = '/api/v1/financials/changes/{ticker}'
42
+ _pp: dict[str, str] = {'ticker': ticker}
43
+ for _k, _v in _pp.items():
44
+ url = url.replace("{" + _k + "}", str(_v))
45
+ _kv: list[str] = []
46
+ _body: dict[str, str | None] = {'since': since}
47
+ for _name, _val in _body.items():
48
+ if _val is not None:
49
+ _kv.append(f"{_name}={_val}")
50
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
51
+
52
+ @_sub_metrics.command('get', help='Financials Metrics [cost 5]')
53
+ def cmd_get_api_v1_financials_metrics(ctx: typer.Context, ticker: str = typer.Option(None, '--ticker', help='Stock ticker symbol (e.g., AAPL). Canonical hyphen form (`BRK-A`); server normalizes `BRK.A`/`BRK/A`/`BRKA` → `BRK-A`. Returns 404 if no XBRL coverage — gate via `/api/v1/financials/tickers` for the ~7,000-ticker covered universe.; example: AAPL'), period: str = typer.Option(None, '--period', help='Period type: `Q` (quarterly) returns one row per fiscal quarter; `Y` (annual) returns one row per fiscal year (10-K-anchored, fiscal-year-end-aware so non-calendar issuers like AAPL/September-end and ORCL/May-end report against their actual fiscal year boundaries — NOT calendar).; example: Q'), years: str = typer.Option(None, '--years', help="Number of years of history (1-20). Quarterly returns up to `years × 4` rows; annual up to `years` rows. FinRadar's XBRL ETL coverage starts ~2013 for most issuers, ~2009 for large-caps that filed early-XBRL voluntarily.; example: 5"), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
54
+ url = '/api/v1/financials/metrics'
55
+ _pp: dict[str, str] = {}
56
+ for _k, _v in _pp.items():
57
+ url = url.replace("{" + _k + "}", str(_v))
58
+ _kv: list[str] = []
59
+ _body: dict[str, str | None] = {'ticker': ticker, 'period': period, 'years': years}
60
+ for _name, _val in _body.items():
61
+ if _val is not None:
62
+ _kv.append(f"{_name}={_val}")
63
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
64
+
65
+ @_sub_ratios.command('get', help='Financials Ratios [cost 5]')
66
+ def cmd_get_api_v1_financials_ratios(ctx: typer.Context, ticker: str = typer.Option(None, '--ticker', help='Stock ticker symbol (canonical hyphen form). Returns 404 on tickers without XBRL coverage; gate via `/api/v1/financials/tickers`.; example: AAPL'), period: str = typer.Option(None, '--period', help='Period type: `Q` (quarterly point-in-time ratios), `Y` (annual), or `TTM` (trailing-twelve-month rolling — most useful for cross-issuer comparison since it smooths quarterly seasonality). For most fundamental-screener UIs, default to `TTM`.; example: TTM'), years: str = typer.Option(None, '--years', help='Years of history (1-20). With `period=Q` returns up to `years × 4` rows; with `period=Y` or `period=TTM` returns up to `years` rows. TTM rolls the 4-quarter window forward by one quarter per row.; example: 5'), category: str = typer.Option(None, '--category', help='Filter to one ratio category: `profitability` (margins + ROE/ROA/ROIC), `leverage` (debt-to-equity, interest coverage), `liquidity` (current/quick ratios), `efficiency` (asset/inventory turnover), `per_share` (EPS, BVPS, FCFPS), `runway` (cash-runway-status tier), `growth` (YoY/QoQ revenue + EPS), or `all` (everything). Use `category=profitability` for margin-trend charts; `category=leverage` for credit-risk dashboards.; example: profitability'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
67
+ url = '/api/v1/financials/ratios'
68
+ _pp: dict[str, str] = {}
69
+ for _k, _v in _pp.items():
70
+ url = url.replace("{" + _k + "}", str(_v))
71
+ _kv: list[str] = []
72
+ _body: dict[str, str | None] = {'ticker': ticker, 'period': period, 'years': years, 'category': category}
73
+ for _name, _val in _body.items():
74
+ if _val is not None:
75
+ _kv.append(f"{_name}={_val}")
76
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
77
+
78
+ @parent.command('screen', help='Financials Screen [cost 10]')
79
+ def cmd_get_api_v1_financials_screen(ctx: typer.Context, gross_margin_gt: str = typer.Option(None, '--gross_margin_gt', help='Keep only companies whose gross margin is greater than this value (decimal, e.g. 0.15 = 15%).; example: 0.35'), gross_margin_lt: str = typer.Option(None, '--gross_margin_lt', help='Keep only companies whose gross margin is less than this value (decimal, e.g. 0.15 = 15%).; example: 0.35'), operating_margin_gt: str = typer.Option(None, '--operating_margin_gt', help='Keep only companies whose operating margin is greater than this value (decimal, e.g. 0.15 = 15%).; example: 0.15'), operating_margin_lt: str = typer.Option(None, '--operating_margin_lt', help='Keep only companies whose operating margin is less than this value (decimal, e.g. 0.15 = 15%).; example: 0.15'), net_margin_gt: str = typer.Option(None, '--net_margin_gt', help='Keep only companies whose net margin is greater than this value (decimal, e.g. 0.15 = 15%).; example: 0.10'), net_margin_lt: str = typer.Option(None, '--net_margin_lt', help='Keep only companies whose net margin is less than this value (decimal, e.g. 0.15 = 15%).; example: 0.10'), fcf_margin_gt: str = typer.Option(None, '--fcf_margin_gt', help='Keep only companies whose free-cash-flow margin is greater than this value (decimal, e.g. 0.15 = 15%).; example: 0.10'), fcf_margin_lt: str = typer.Option(None, '--fcf_margin_lt', help='Keep only companies whose free-cash-flow margin is less than this value (decimal, e.g. 0.15 = 15%).; example: 0.10'), roe_gt: str = typer.Option(None, '--roe_gt', help='Keep only companies whose ROE is greater than this value (decimal, e.g. 0.15 = 15%).; example: 0.15'), roe_lt: str = typer.Option(None, '--roe_lt', help='Keep only companies whose ROE is less than this value (decimal, e.g. 0.15 = 15%).; example: 0.15'), roa_gt: str = typer.Option(None, '--roa_gt', help='Keep only companies whose ROA is greater than this value (decimal, e.g. 0.15 = 15%).; example: 0.08'), roa_lt: str = typer.Option(None, '--roa_lt', help='Keep only companies whose ROA is less than this value (decimal, e.g. 0.15 = 15%).; example: 0.08'), roic_gt: str = typer.Option(None, '--roic_gt', help='Keep only companies whose ROIC is greater than this value (decimal, e.g. 0.15 = 15%).; example: 0.12'), roic_lt: str = typer.Option(None, '--roic_lt', help='Keep only companies whose ROIC is less than this value (decimal, e.g. 0.15 = 15%).; example: 0.12'), debt_to_equity_gt: str = typer.Option(None, '--debt_to_equity_gt', help='Keep only companies whose debt-to-equity ratio is greater than this value (ratio).; example: 1.0'), debt_to_equity_lt: str = typer.Option(None, '--debt_to_equity_lt', help='Keep only companies whose debt-to-equity ratio is less than this value (ratio).; example: 1.0'), debt_to_assets_gt: str = typer.Option(None, '--debt_to_assets_gt', help='Keep only companies whose debt-to-assets ratio is greater than this value (ratio).; example: 0.5'), debt_to_assets_lt: str = typer.Option(None, '--debt_to_assets_lt', help='Keep only companies whose debt-to-assets ratio is less than this value (ratio).; example: 0.5'), current_ratio_gt: str = typer.Option(None, '--current_ratio_gt', help='Keep only companies whose current ratio is greater than this value (ratio).; example: 1.5'), current_ratio_lt: str = typer.Option(None, '--current_ratio_lt', help='Keep only companies whose current ratio is less than this value (ratio).; example: 1.5'), quick_ratio_gt: str = typer.Option(None, '--quick_ratio_gt', help='Keep only companies whose quick ratio is greater than this value (ratio).; example: 1.0'), quick_ratio_lt: str = typer.Option(None, '--quick_ratio_lt', help='Keep only companies whose quick ratio is less than this value (ratio).; example: 1.0'), asset_turnover_gt: str = typer.Option(None, '--asset_turnover_gt', help='Keep only companies whose asset turnover is greater than this value (ratio).; example: 1.0'), asset_turnover_lt: str = typer.Option(None, '--asset_turnover_lt', help='Keep only companies whose asset turnover is less than this value (ratio).; example: 1.0'), interest_coverage_gt: str = typer.Option(None, '--interest_coverage_gt', help='Keep only companies whose interest coverage is greater than this value (ratio).; example: 5.0'), interest_coverage_lt: str = typer.Option(None, '--interest_coverage_lt', help='Keep only companies whose interest coverage is less than this value (ratio).; example: 5.0'), inventory_turnover_gt: str = typer.Option(None, '--inventory_turnover_gt', help='Keep only companies whose inventory turnover is greater than this value (ratio).; example: 5.0'), inventory_turnover_lt: str = typer.Option(None, '--inventory_turnover_lt', help='Keep only companies whose inventory turnover is less than this value (ratio).; example: 5.0'), receivables_turnover_gt: str = typer.Option(None, '--receivables_turnover_gt', help='Keep only companies whose receivables turnover is greater than this value (ratio).; example: 8.0'), receivables_turnover_lt: str = typer.Option(None, '--receivables_turnover_lt', help='Keep only companies whose receivables turnover is less than this value (ratio).; example: 8.0'), revenue_growth_yoy_gt: str = typer.Option(None, '--revenue_growth_yoy_gt', help='Keep only companies whose YoY revenue growth is greater than this value (decimal, e.g. 0.15 = 15%).; example: 0.20'), revenue_growth_yoy_lt: str = typer.Option(None, '--revenue_growth_yoy_lt', help='Keep only companies whose YoY revenue growth is less than this value (decimal, e.g. 0.15 = 15%).; example: 0.20'), eps_growth_yoy_gt: str = typer.Option(None, '--eps_growth_yoy_gt', help='Keep only companies whose YoY EPS growth is greater than this value (decimal, e.g. 0.15 = 15%).; example: 0.15'), eps_growth_yoy_lt: str = typer.Option(None, '--eps_growth_yoy_lt', help='Keep only companies whose YoY EPS growth is less than this value (decimal, e.g. 0.15 = 15%).; example: 0.15'), revenue_growth_qoq_gt: str = typer.Option(None, '--revenue_growth_qoq_gt', help='Keep only companies whose QoQ revenue growth is greater than this value (decimal, e.g. 0.15 = 15%).; example: 0.05'), revenue_growth_qoq_lt: str = typer.Option(None, '--revenue_growth_qoq_lt', help='Keep only companies whose QoQ revenue growth is less than this value (decimal, e.g. 0.15 = 15%).; example: 0.05'), eps_growth_qoq_gt: str = typer.Option(None, '--eps_growth_qoq_gt', help='Keep only companies whose QoQ EPS growth is greater than this value (decimal, e.g. 0.15 = 15%).; example: 0.05'), eps_growth_qoq_lt: str = typer.Option(None, '--eps_growth_qoq_lt', help='Keep only companies whose QoQ EPS growth is less than this value (decimal, e.g. 0.15 = 15%).; example: 0.05'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
80
+ url = '/api/v1/financials/screen'
81
+ _pp: dict[str, str] = {}
82
+ for _k, _v in _pp.items():
83
+ url = url.replace("{" + _k + "}", str(_v))
84
+ _kv: list[str] = []
85
+ _body: dict[str, str | None] = {'gross_margin_gt': gross_margin_gt, 'gross_margin_lt': gross_margin_lt, 'operating_margin_gt': operating_margin_gt, 'operating_margin_lt': operating_margin_lt, 'net_margin_gt': net_margin_gt, 'net_margin_lt': net_margin_lt, 'fcf_margin_gt': fcf_margin_gt, 'fcf_margin_lt': fcf_margin_lt, 'roe_gt': roe_gt, 'roe_lt': roe_lt, 'roa_gt': roa_gt, 'roa_lt': roa_lt, 'roic_gt': roic_gt, 'roic_lt': roic_lt, 'debt_to_equity_gt': debt_to_equity_gt, 'debt_to_equity_lt': debt_to_equity_lt, 'debt_to_assets_gt': debt_to_assets_gt, 'debt_to_assets_lt': debt_to_assets_lt, 'current_ratio_gt': current_ratio_gt, 'current_ratio_lt': current_ratio_lt, 'quick_ratio_gt': quick_ratio_gt, 'quick_ratio_lt': quick_ratio_lt, 'asset_turnover_gt': asset_turnover_gt, 'asset_turnover_lt': asset_turnover_lt, 'interest_coverage_gt': interest_coverage_gt, 'interest_coverage_lt': interest_coverage_lt, 'inventory_turnover_gt': inventory_turnover_gt, 'inventory_turnover_lt': inventory_turnover_lt, 'receivables_turnover_gt': receivables_turnover_gt, 'receivables_turnover_lt': receivables_turnover_lt, 'revenue_growth_yoy_gt': revenue_growth_yoy_gt, 'revenue_growth_yoy_lt': revenue_growth_yoy_lt, 'eps_growth_yoy_gt': eps_growth_yoy_gt, 'eps_growth_yoy_lt': eps_growth_yoy_lt, 'revenue_growth_qoq_gt': revenue_growth_qoq_gt, 'revenue_growth_qoq_lt': revenue_growth_qoq_lt, 'eps_growth_qoq_gt': eps_growth_qoq_gt, 'eps_growth_qoq_lt': eps_growth_qoq_lt}
86
+ for _name, _val in _body.items():
87
+ if _val is not None:
88
+ _kv.append(f"{_name}={_val}")
89
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
90
+
91
+ @parent.command('snapshot', help='Financials Snapshot [cost 5]')
92
+ def cmd_get_api_v1_financials_snapshot(ctx: typer.Context, ticker: str = typer.Option(None, '--ticker', help='Stock ticker symbol (canonical hyphen form). Returns 404 on tickers without XBRL coverage; gate via `/api/v1/financials/tickers`.; example: AAPL'), period: str = typer.Option(None, '--period', help="Period basis: `TTM` (trailing-twelve-month rolling — default; smooths quarterly seasonality, comparable across issuers) or `Q` (latest quarterly point-in-time — useful when you specifically want the most-recent fiscal quarter's results, not a smoothed view). For company-overview cards default to `TTM`; for earnings-watch dashboards use `Q`.; example: TTM"), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
93
+ url = '/api/v1/financials/snapshot'
94
+ _pp: dict[str, str] = {}
95
+ for _k, _v in _pp.items():
96
+ url = url.replace("{" + _k + "}", str(_v))
97
+ _kv: list[str] = []
98
+ _body: dict[str, str | None] = {'ticker': ticker, 'period': period}
99
+ for _name, _val in _body.items():
100
+ if _val is not None:
101
+ _kv.append(f"{_name}={_val}")
102
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
103
+
104
+ @_sub_status.command('get', help='Financials Status [cost 0]')
105
+ def cmd_get_api_v1_financials_status(ctx: typer.Context, data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
106
+ url = '/api/v1/financials/status'
107
+ _pp: dict[str, str] = {}
108
+ for _k, _v in _pp.items():
109
+ url = url.replace("{" + _k + "}", str(_v))
110
+ _kv: list[str] = []
111
+ _body: dict[str, str | None] = {}
112
+ for _name, _val in _body.items():
113
+ if _val is not None:
114
+ _kv.append(f"{_name}={_val}")
115
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
116
+
117
+ @_sub_stock_overview.command('get', help='Financials Stock Overview [cost 5]')
118
+ def cmd_get_api_v1_financials_stock_overview(ctx: typer.Context, ticker: str = typer.Option(None, '--ticker', help='Stock ticker symbol (canonical hyphen form). Returns 404 if ticker has no Yahoo Finance / yfinance daily-data coverage. Cache: 24h server-side.; example: AAPL'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
119
+ url = '/api/v1/financials/stock-overview'
120
+ _pp: dict[str, str] = {}
121
+ for _k, _v in _pp.items():
122
+ url = url.replace("{" + _k + "}", str(_v))
123
+ _kv: list[str] = []
124
+ _body: dict[str, str | None] = {'ticker': ticker}
125
+ for _name, _val in _body.items():
126
+ if _val is not None:
127
+ _kv.append(f"{_name}={_val}")
128
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
129
+
130
+ @_sub_tickers.command('get', help='Financials Tickers [cost 10]')
131
+ def cmd_get_api_v1_financials_tickers(ctx: typer.Context, q: str = typer.Option(None, '--q', help="Free-text search across (1) ticker (exact match boost) and (2) company name (partial match `ILIKE '%name%'`). Case-insensitive. Empty `q` returns the full list ordered by `fact_count DESC` (most-data-rich tickers first) — useful for default-state autocomplete dropdowns.; example: apple"), limit: str = typer.Option(None, '--limit', help='Maximum tickers returned (capped at 200 server-side). For typical autocomplete dropdowns, 10-20 is enough; for full-list preload (all 7000 tickers for offline indexing) use 200 + paginated walks.; example: 20'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
132
+ url = '/api/v1/financials/tickers'
133
+ _pp: dict[str, str] = {}
134
+ for _k, _v in _pp.items():
135
+ url = url.replace("{" + _k + "}", str(_v))
136
+ _kv: list[str] = []
137
+ _body: dict[str, str | None] = {'q': q, 'limit': limit}
138
+ for _name, _val in _body.items():
139
+ if _val is not None:
140
+ _kv.append(f"{_name}={_val}")
141
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
@@ -0,0 +1,53 @@
1
+ """GENERATED by generate.py — do not edit. Run `make generate`."""
2
+ from __future__ import annotations
3
+
4
+ import typer
5
+ from finradar_cli.commands.api import run_request
6
+
7
+
8
+ def build(parent: typer.Typer) -> None:
9
+ _sub_filing_history = typer.Typer(no_args_is_help=True)
10
+ parent.add_typer(_sub_filing_history, name='filing-history')
11
+ _sub_ownership = typer.Typer(no_args_is_help=True)
12
+ parent.add_typer(_sub_ownership, name='ownership')
13
+ _sub_ownership_holders = typer.Typer(no_args_is_help=True)
14
+ _sub_ownership.add_typer(_sub_ownership_holders, name='holders')
15
+
16
+ @_sub_filing_history.command('get', help='Ownership Holders Filing History [cost 10]')
17
+ def cmd_get_api_v1_ownership_holders_uuid_filing_history(ctx: typer.Context, uuid: str = typer.Argument(..., help='Unified holder UUID — internally generated by the Phase 31 holder-linker, NOT a SEC-issued identifier. Lookup the UUID by CIK or name via `GET /api/v1/ownership/holders` (which has a `q` search parameter). UUIDs are stable across re-runs of the linker for the same `(cik, name)` cluster.; example: 7f1a2b30-c4d5-46e7-9f01-23456789abcd'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
18
+ url = '/api/v1/ownership/holders/{uuid}/filing-history'
19
+ _pp: dict[str, str] = {'uuid': uuid}
20
+ for _k, _v in _pp.items():
21
+ url = url.replace("{" + _k + "}", str(_v))
22
+ _kv: list[str] = []
23
+ _body: dict[str, str | None] = {}
24
+ for _name, _val in _body.items():
25
+ if _val is not None:
26
+ _kv.append(f"{_name}={_val}")
27
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
28
+
29
+ @parent.command('get', help='Ownership Holders [cost 5]')
30
+ def cmd_get_api_v1_ownership_holders(ctx: typer.Context, q: str = typer.Option(None, '--q', help='Name search (partial, case-insensitive). Trigram fuzzy matching on the holder_name field — `berkshire` matches `BERKSHIRE HATHAWAY INC`, `BERKSHIRE GROUP LP`, etc. Either `q` or `cik` must be provided (server returns 400 when both are omitted).; example: berkshire'), cik: str = typer.Option(None, '--cik', help="CIK exact match. Both 10-character zero-padded (`0001067983`) and short unpadded (`1067983`) formats accepted via server-side `ltrim('0')` normalization. Either `q` or `cik` must be provided.; example: 0001067983"), type_: str = typer.Option(None, '--type', help='Filter holders by primary type: `insider` (Form 4 / Section 16 filers), `institutional` (13F filers), `beneficial` (13D/13G filers). Omit for all types. Multi-type holders (e.g. Berkshire — both institutional AND beneficial) appear under their primary type but expose all flags.; example: institutional'), page: str = typer.Option(None, '--page', help='1-indexed page number for pagination.'), size: str = typer.Option(None, '--size', help='Page size (max 200). Values > 200 silently clamp to 200.'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
31
+ url = '/api/v1/ownership/holders'
32
+ _pp: dict[str, str] = {}
33
+ for _k, _v in _pp.items():
34
+ url = url.replace("{" + _k + "}", str(_v))
35
+ _kv: list[str] = []
36
+ _body: dict[str, str | None] = {'q': q, 'cik': cik, 'type': type_, 'page': page, 'size': size}
37
+ for _name, _val in _body.items():
38
+ if _val is not None:
39
+ _kv.append(f"{_name}={_val}")
40
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
41
+
42
+ @_sub_ownership_holders.command('get', help='Ownership Holders [cost 5]')
43
+ def cmd_get_api_v1_ownership_holders_uuid(ctx: typer.Context, uuid: str = typer.Argument(..., help="Holder UUID — the stable Phase-47 identifier. Look up via [GET /api/v1/ownership/holders](/docs/ownership-and-positions/unified-holders/get-ownership-holders) (`q` or `cik` search). UUIDs are deterministic across linker re-runs for the same `(canonical_cik, normalized_name)` cluster, so they're safe to persist client-side. Returns 404 on unknown UUIDs.; example: 7f1a2b30-c4d5-46e7-9f01-23456789abcd"), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
44
+ url = '/api/v1/ownership/holders/{uuid}'
45
+ _pp: dict[str, str] = {'uuid': uuid}
46
+ for _k, _v in _pp.items():
47
+ url = url.replace("{" + _k + "}", str(_v))
48
+ _kv: list[str] = []
49
+ _body: dict[str, str | None] = {}
50
+ for _name, _val in _body.items():
51
+ if _val is not None:
52
+ _kv.append(f"{_name}={_val}")
53
+ run_request(ctx.obj, 'GET', url, _kv, data=data)
@@ -0,0 +1,21 @@
1
+ """GENERATED by generate.py — do not edit. Run `make generate`."""
2
+ from __future__ import annotations
3
+
4
+ import typer
5
+ from finradar_cli.commands.api import run_request
6
+
7
+
8
+ def build(parent: typer.Typer) -> None:
9
+
10
+ @parent.command('get', help='Index [cost 1]')
11
+ def cmd_get_api_v1_index(ctx: typer.Context, date: str = typer.Option(None, '--date', help="ISO `YYYY-MM-DD` date for which to retrieve the master-index. Returns 404 for future dates and dates before SEC EDGAR's electronic-filing era (1993). Note: weekends + federal holidays return empty `accessions` arrays (SEC doesn't accept filings on those days) — your sync code should handle this without erroring.; example: 2026-01-15"), format: str = typer.Option(None, '--format', help='Output format — `json` (default; structured response with `date` + `accessions[]` + `count`) or `csv` (newline-delimited rows: `accession_number,cik,form_type,accepted_at`). Use CSV for streaming to file or piping to Postgres `\\COPY`; use JSON for in-process consumption.; example: json'), data: str = typer.Option(None, "--data", help="JSON body or @file.json")):
12
+ url = '/api/v1/index'
13
+ _pp: dict[str, str] = {}
14
+ for _k, _v in _pp.items():
15
+ url = url.replace("{" + _k + "}", str(_v))
16
+ _kv: list[str] = []
17
+ _body: dict[str, str | None] = {'date': date, 'format': format}
18
+ for _name, _val in _body.items():
19
+ if _val is not None:
20
+ _kv.append(f"{_name}={_val}")
21
+ run_request(ctx.obj, 'GET', url, _kv, data=data)