arkleon 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.
- arkleon-0.1.0/.gitignore +12 -0
- arkleon-0.1.0/LICENSE +21 -0
- arkleon-0.1.0/PKG-INFO +206 -0
- arkleon-0.1.0/README.md +174 -0
- arkleon-0.1.0/pyproject.toml +60 -0
- arkleon-0.1.0/src/arkleon/__init__.py +48 -0
- arkleon-0.1.0/src/arkleon/api/__init__.py +49 -0
- arkleon-0.1.0/src/arkleon/api/auth.py +58 -0
- arkleon-0.1.0/src/arkleon/api/client.py +378 -0
- arkleon-0.1.0/src/arkleon/api/errors.py +202 -0
- arkleon-0.1.0/src/arkleon/api/pagination.py +51 -0
- arkleon-0.1.0/src/arkleon/edgar/__init__.py +30 -0
- arkleon-0.1.0/src/arkleon/edgar/fetch.py +134 -0
- arkleon-0.1.0/src/arkleon/edgar/frames.py +43 -0
- arkleon-0.1.0/src/arkleon/edgar/fsds.py +61 -0
- arkleon-0.1.0/src/arkleon/edgar/identifiers.py +47 -0
- arkleon-0.1.0/src/arkleon/edgar/models.py +39 -0
- arkleon-0.1.0/src/arkleon/edgar/parse.py +160 -0
- arkleon-0.1.0/src/arkleon/edgar/useragent.py +17 -0
- arkleon-0.1.0/src/arkleon/mcp/__init__.py +31 -0
- arkleon-0.1.0/src/arkleon/mcp/server.py +121 -0
- arkleon-0.1.0/src/arkleon/mcp/tools.py +466 -0
- arkleon-0.1.0/src/arkleon/py.typed +0 -0
arkleon-0.1.0/.gitignore
ADDED
arkleon-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arkleon LLC
|
|
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.
|
arkleon-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: arkleon
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Point-in-time SEC EDGAR fundamentals: free fetch/parse helpers, an optional certified /v1 client, and a built-in MCP server.
|
|
5
|
+
Project-URL: Homepage, https://github.com/arkleon/arkleon
|
|
6
|
+
Project-URL: Repository, https://github.com/arkleon/arkleon
|
|
7
|
+
Project-URL: SEC fair access, https://www.sec.gov/os/accessing-edgar-data
|
|
8
|
+
Author-email: Arkleon LLC <founder@arkleon.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: 10-K,10-Q,as-reported,edgar,filings,financial-data,financial-statement-data-sets,fundamentals,mcp,point-in-time,sec,xbrl
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: httpx>=0.27
|
|
21
|
+
Provides-Extra: all
|
|
22
|
+
Requires-Dist: httpx>=0.27; extra == 'all'
|
|
23
|
+
Requires-Dist: mcp>=1.9; extra == 'all'
|
|
24
|
+
Requires-Dist: pandas>=2.0; extra == 'all'
|
|
25
|
+
Provides-Extra: api
|
|
26
|
+
Requires-Dist: httpx>=0.27; extra == 'api'
|
|
27
|
+
Provides-Extra: mcp
|
|
28
|
+
Requires-Dist: mcp>=1.9; extra == 'mcp'
|
|
29
|
+
Provides-Extra: pandas
|
|
30
|
+
Requires-Dist: pandas>=2.0; extra == 'pandas'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# arkleon
|
|
34
|
+
|
|
35
|
+
Point-in-time SEC EDGAR fundamentals: free EDGAR fetch/parse helpers with zero
|
|
36
|
+
credentials, plus an optional certified `/v1` client and a built-in MCP server.
|
|
37
|
+
|
|
38
|
+
The free EDGAR core requires no account and no API key. The optional paid `/v1`
|
|
39
|
+
client and the corpus-backed point-in-time features are the only parts that need
|
|
40
|
+
a key.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install arkleon
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quickstart: free, no API key
|
|
49
|
+
|
|
50
|
+
The EDGAR helpers work on the first line of code with no Arkleon account and no
|
|
51
|
+
key. The only string they require is an SEC fair-access `User-Agent`, which you
|
|
52
|
+
write yourself.
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from arkleon.edgar import EdgarClient
|
|
56
|
+
|
|
57
|
+
# SEC fair-access User-Agent: a self-declared contact string, NOT an Arkleon key.
|
|
58
|
+
edgar = EdgarClient(user_agent="Acme Research contact@acme.com")
|
|
59
|
+
|
|
60
|
+
# Fetch a company's 10-K filings by CIK (Apple = 320193):
|
|
61
|
+
for filing in edgar.filings(cik=320193, form="10-K"):
|
|
62
|
+
print(filing.filed, filing.accession, filing.form)
|
|
63
|
+
|
|
64
|
+
# Parse as-reported numeric facts, filtered best-effort to a date:
|
|
65
|
+
assets = edgar.concept(cik=320193, tag="Assets", taxonomy="us-gaap")
|
|
66
|
+
for fact in assets.as_of("2020-01-01"): # keep only filed <= 2020-01-01
|
|
67
|
+
print(fact.period_end, fact.value, fact.unit, fact.filed)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
*No account, no key, no Arkleon network. Live-EDGAR, best-effort point-in-time.*
|
|
71
|
+
|
|
72
|
+
## Quickstart: optional paid `/v1` client (certified point-in-time)
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
# pip install "arkleon[api]"
|
|
76
|
+
from arkleon.api import DataClient
|
|
77
|
+
|
|
78
|
+
client = DataClient(api_key="ak_...") # or ARKLEON_API_KEY env
|
|
79
|
+
page = client.facts(cik=320193, tag="Assets", as_of="2020-01-01")
|
|
80
|
+
for fact in page.data:
|
|
81
|
+
print(fact["period_end"], fact["value"], fact.get("source_url"))
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
*Certified corpus. `as_of` required, filters on filing date, identical query +
|
|
85
|
+
`as_of` returns identical data permanently.*
|
|
86
|
+
|
|
87
|
+
## The free/paid boundary
|
|
88
|
+
|
|
89
|
+
The `arkleon.edgar` helpers never require a key and never contact Arkleon: their
|
|
90
|
+
only remote hosts are `data.sec.gov` and `www.sec.gov`. Only the `arkleon[api]`
|
|
91
|
+
client and the corpus-backed point-in-time features call Arkleon, and they refuse
|
|
92
|
+
to operate without an `ak_`-prefixed key. Installing `arkleon[api]` does not by
|
|
93
|
+
itself activate the paid path; a key must still be supplied at runtime.
|
|
94
|
+
|
|
95
|
+
## MCP server
|
|
96
|
+
|
|
97
|
+
Install the server extra and launch the stdio server with the console script:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pip install "arkleon[mcp]"
|
|
101
|
+
arkleon-mcp
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
An agent host launches it the same way. Set `ARKLEON_API_KEY` in the launch
|
|
105
|
+
environment to unlock the paid tools:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"mcpServers": {
|
|
110
|
+
"arkleon": {
|
|
111
|
+
"command": "arkleon-mcp",
|
|
112
|
+
"env": { "ARKLEON_API_KEY": "ak_..." }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The free EDGAR tools register **unconditionally**; the paid tools register **only
|
|
119
|
+
when an `ak_` key is present**. An agent with no key sees exactly the free set.
|
|
120
|
+
|
|
121
|
+
| Tool | Backed by | Key required? |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| `edgar_list_filings` | free EDGAR core | No |
|
|
124
|
+
| `edgar_company_facts` | free EDGAR core | No |
|
|
125
|
+
| `edgar_concept` | free EDGAR core | No |
|
|
126
|
+
| `edgar_facts_as_of` (best-effort, live EDGAR) | free EDGAR core | No |
|
|
127
|
+
| `edgar_resolve_cik` (current-snapshot, non-point-in-time) | free EDGAR core | No |
|
|
128
|
+
| `pit_facts` (certified point-in-time) | paid `/v1` client | Yes |
|
|
129
|
+
| `pit_filings` | paid `/v1` client | Yes |
|
|
130
|
+
| `resolve_company` | paid `/v1` client | Yes |
|
|
131
|
+
| `pit_revision_history` | paid `/v1` client | Yes |
|
|
132
|
+
|
|
133
|
+
## What it returns, and what it does not
|
|
134
|
+
|
|
135
|
+
**Returns:** as-reported numeric XBRL facts, filing and company metadata, and the
|
|
136
|
+
lineage linking each figure to its source filing.
|
|
137
|
+
|
|
138
|
+
**Does not return:** market prices, analyst estimates, narrative disclosure
|
|
139
|
+
(MD&A, risk factors, footnotes), or derived analytics, ratios, scores, or
|
|
140
|
+
signals. Neither layer serves these. It is a research corpus, not a real-time
|
|
141
|
+
filing feed: the paid corpus refreshes on the SEC's quarterly cadence.
|
|
142
|
+
|
|
143
|
+
## Point-in-time, in two sentences
|
|
144
|
+
|
|
145
|
+
`as_of` filters on the **filing date** (`filed <= as_of`), never on the period a
|
|
146
|
+
fact describes, so a query sees only values that were already public on that
|
|
147
|
+
date. Because a fact from a later filing never appears, a historical query cannot
|
|
148
|
+
observe information that did not yet exist, which is what keeps a study free of
|
|
149
|
+
look-ahead bias.
|
|
150
|
+
|
|
151
|
+
## The certified corpus (paid)
|
|
152
|
+
|
|
153
|
+
The paid `/v1` client serves the certified corpus. Per
|
|
154
|
+
`docs/data-layer/api-v1-contract.md` Appendix A, which is the sole authority for
|
|
155
|
+
what any surface may state, the claimable figures are:
|
|
156
|
+
|
|
157
|
+
| Quantity | Figure | Kind |
|
|
158
|
+
|---|---|---|
|
|
159
|
+
| Filings | 426,003 | EXACT |
|
|
160
|
+
| Facts | 181,350,662 | EXACT |
|
|
161
|
+
| Distinct companies (CIK) | 16,811 | EXACT |
|
|
162
|
+
| Quarters spanned | 69 (2009q1–2026q1) | exact |
|
|
163
|
+
| Quarters populated | 68 | exact |
|
|
164
|
+
|
|
165
|
+
The EXACT/exact label travels with each number and is not presentational. These
|
|
166
|
+
figures are keyed to the date each filing became public, include values that
|
|
167
|
+
later filings revised, and trace to their source filing. The earliest filed date
|
|
168
|
+
in the corpus is 2009-04-15, so a query with an `as_of` earlier than that date
|
|
169
|
+
correctly returns empty. Treat the contract's Appendix A, not this table, as the
|
|
170
|
+
authority: a later corpus revision updates the contract, and any figure that
|
|
171
|
+
disagrees with it is stale.
|
|
172
|
+
|
|
173
|
+
## Identifiers
|
|
174
|
+
|
|
175
|
+
**CIK is the addressable identifier** across both layers. In the free layer,
|
|
176
|
+
`resolve_cik(ticker)` maps a symbol to a CIK from SEC's current ticker snapshot;
|
|
177
|
+
it is a convenience, not a reproducible identity resolver, because it has no
|
|
178
|
+
ticker history, a symbol is reassigned over time, and one CIK carries several
|
|
179
|
+
symbols at once (`GOOG` and `GOOGL`, `BRK.A` and `BRK.B`). The `/v1` API does not
|
|
180
|
+
serve the `ticker` filter today: a `ticker` request returns a distinct
|
|
181
|
+
"not served in this release" error rather than silently resolving through the
|
|
182
|
+
free snapshot. Address facts by CIK.
|
|
183
|
+
|
|
184
|
+
## Install matrix
|
|
185
|
+
|
|
186
|
+
| Install | Adds |
|
|
187
|
+
|---|---|
|
|
188
|
+
| `arkleon` | The free EDGAR fetch/parse core. No key, no Arkleon network. |
|
|
189
|
+
| `arkleon[api]` | The optional paid `/v1` `DataClient`. Requires an `ak_` key at runtime. |
|
|
190
|
+
| `arkleon[mcp]` | The built-in MCP server and the `arkleon-mcp` console script. |
|
|
191
|
+
| `arkleon[pandas]` | Optional pandas DataFrame adapters for the free core. Never required. |
|
|
192
|
+
| `arkleon[all]` | Convenience union of `api`, `mcp`, and `pandas`. |
|
|
193
|
+
|
|
194
|
+
Base plus `[pandas]` stays fully free and credential-free. `[api]` and the paid
|
|
195
|
+
tools in `[mcp]` are the only parts that consume an `ak_` key.
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
MIT. See [LICENSE](LICENSE).
|
|
200
|
+
|
|
201
|
+
## Links
|
|
202
|
+
|
|
203
|
+
- **`/v1` API contract:** `docs/data-layer/api-v1-contract.md` (Appendix A is the
|
|
204
|
+
authority for the corpus figures above).
|
|
205
|
+
- **Issues and contributing:** https://github.com/arkleon/arkleon
|
|
206
|
+
- **SEC fair-access policy:** https://www.sec.gov/os/accessing-edgar-data
|
arkleon-0.1.0/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# arkleon
|
|
2
|
+
|
|
3
|
+
Point-in-time SEC EDGAR fundamentals: free EDGAR fetch/parse helpers with zero
|
|
4
|
+
credentials, plus an optional certified `/v1` client and a built-in MCP server.
|
|
5
|
+
|
|
6
|
+
The free EDGAR core requires no account and no API key. The optional paid `/v1`
|
|
7
|
+
client and the corpus-backed point-in-time features are the only parts that need
|
|
8
|
+
a key.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install arkleon
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quickstart: free, no API key
|
|
17
|
+
|
|
18
|
+
The EDGAR helpers work on the first line of code with no Arkleon account and no
|
|
19
|
+
key. The only string they require is an SEC fair-access `User-Agent`, which you
|
|
20
|
+
write yourself.
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from arkleon.edgar import EdgarClient
|
|
24
|
+
|
|
25
|
+
# SEC fair-access User-Agent: a self-declared contact string, NOT an Arkleon key.
|
|
26
|
+
edgar = EdgarClient(user_agent="Acme Research contact@acme.com")
|
|
27
|
+
|
|
28
|
+
# Fetch a company's 10-K filings by CIK (Apple = 320193):
|
|
29
|
+
for filing in edgar.filings(cik=320193, form="10-K"):
|
|
30
|
+
print(filing.filed, filing.accession, filing.form)
|
|
31
|
+
|
|
32
|
+
# Parse as-reported numeric facts, filtered best-effort to a date:
|
|
33
|
+
assets = edgar.concept(cik=320193, tag="Assets", taxonomy="us-gaap")
|
|
34
|
+
for fact in assets.as_of("2020-01-01"): # keep only filed <= 2020-01-01
|
|
35
|
+
print(fact.period_end, fact.value, fact.unit, fact.filed)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
*No account, no key, no Arkleon network. Live-EDGAR, best-effort point-in-time.*
|
|
39
|
+
|
|
40
|
+
## Quickstart: optional paid `/v1` client (certified point-in-time)
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
# pip install "arkleon[api]"
|
|
44
|
+
from arkleon.api import DataClient
|
|
45
|
+
|
|
46
|
+
client = DataClient(api_key="ak_...") # or ARKLEON_API_KEY env
|
|
47
|
+
page = client.facts(cik=320193, tag="Assets", as_of="2020-01-01")
|
|
48
|
+
for fact in page.data:
|
|
49
|
+
print(fact["period_end"], fact["value"], fact.get("source_url"))
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
*Certified corpus. `as_of` required, filters on filing date, identical query +
|
|
53
|
+
`as_of` returns identical data permanently.*
|
|
54
|
+
|
|
55
|
+
## The free/paid boundary
|
|
56
|
+
|
|
57
|
+
The `arkleon.edgar` helpers never require a key and never contact Arkleon: their
|
|
58
|
+
only remote hosts are `data.sec.gov` and `www.sec.gov`. Only the `arkleon[api]`
|
|
59
|
+
client and the corpus-backed point-in-time features call Arkleon, and they refuse
|
|
60
|
+
to operate without an `ak_`-prefixed key. Installing `arkleon[api]` does not by
|
|
61
|
+
itself activate the paid path; a key must still be supplied at runtime.
|
|
62
|
+
|
|
63
|
+
## MCP server
|
|
64
|
+
|
|
65
|
+
Install the server extra and launch the stdio server with the console script:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install "arkleon[mcp]"
|
|
69
|
+
arkleon-mcp
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
An agent host launches it the same way. Set `ARKLEON_API_KEY` in the launch
|
|
73
|
+
environment to unlock the paid tools:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"arkleon": {
|
|
79
|
+
"command": "arkleon-mcp",
|
|
80
|
+
"env": { "ARKLEON_API_KEY": "ak_..." }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The free EDGAR tools register **unconditionally**; the paid tools register **only
|
|
87
|
+
when an `ak_` key is present**. An agent with no key sees exactly the free set.
|
|
88
|
+
|
|
89
|
+
| Tool | Backed by | Key required? |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| `edgar_list_filings` | free EDGAR core | No |
|
|
92
|
+
| `edgar_company_facts` | free EDGAR core | No |
|
|
93
|
+
| `edgar_concept` | free EDGAR core | No |
|
|
94
|
+
| `edgar_facts_as_of` (best-effort, live EDGAR) | free EDGAR core | No |
|
|
95
|
+
| `edgar_resolve_cik` (current-snapshot, non-point-in-time) | free EDGAR core | No |
|
|
96
|
+
| `pit_facts` (certified point-in-time) | paid `/v1` client | Yes |
|
|
97
|
+
| `pit_filings` | paid `/v1` client | Yes |
|
|
98
|
+
| `resolve_company` | paid `/v1` client | Yes |
|
|
99
|
+
| `pit_revision_history` | paid `/v1` client | Yes |
|
|
100
|
+
|
|
101
|
+
## What it returns, and what it does not
|
|
102
|
+
|
|
103
|
+
**Returns:** as-reported numeric XBRL facts, filing and company metadata, and the
|
|
104
|
+
lineage linking each figure to its source filing.
|
|
105
|
+
|
|
106
|
+
**Does not return:** market prices, analyst estimates, narrative disclosure
|
|
107
|
+
(MD&A, risk factors, footnotes), or derived analytics, ratios, scores, or
|
|
108
|
+
signals. Neither layer serves these. It is a research corpus, not a real-time
|
|
109
|
+
filing feed: the paid corpus refreshes on the SEC's quarterly cadence.
|
|
110
|
+
|
|
111
|
+
## Point-in-time, in two sentences
|
|
112
|
+
|
|
113
|
+
`as_of` filters on the **filing date** (`filed <= as_of`), never on the period a
|
|
114
|
+
fact describes, so a query sees only values that were already public on that
|
|
115
|
+
date. Because a fact from a later filing never appears, a historical query cannot
|
|
116
|
+
observe information that did not yet exist, which is what keeps a study free of
|
|
117
|
+
look-ahead bias.
|
|
118
|
+
|
|
119
|
+
## The certified corpus (paid)
|
|
120
|
+
|
|
121
|
+
The paid `/v1` client serves the certified corpus. Per
|
|
122
|
+
`docs/data-layer/api-v1-contract.md` Appendix A, which is the sole authority for
|
|
123
|
+
what any surface may state, the claimable figures are:
|
|
124
|
+
|
|
125
|
+
| Quantity | Figure | Kind |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| Filings | 426,003 | EXACT |
|
|
128
|
+
| Facts | 181,350,662 | EXACT |
|
|
129
|
+
| Distinct companies (CIK) | 16,811 | EXACT |
|
|
130
|
+
| Quarters spanned | 69 (2009q1–2026q1) | exact |
|
|
131
|
+
| Quarters populated | 68 | exact |
|
|
132
|
+
|
|
133
|
+
The EXACT/exact label travels with each number and is not presentational. These
|
|
134
|
+
figures are keyed to the date each filing became public, include values that
|
|
135
|
+
later filings revised, and trace to their source filing. The earliest filed date
|
|
136
|
+
in the corpus is 2009-04-15, so a query with an `as_of` earlier than that date
|
|
137
|
+
correctly returns empty. Treat the contract's Appendix A, not this table, as the
|
|
138
|
+
authority: a later corpus revision updates the contract, and any figure that
|
|
139
|
+
disagrees with it is stale.
|
|
140
|
+
|
|
141
|
+
## Identifiers
|
|
142
|
+
|
|
143
|
+
**CIK is the addressable identifier** across both layers. In the free layer,
|
|
144
|
+
`resolve_cik(ticker)` maps a symbol to a CIK from SEC's current ticker snapshot;
|
|
145
|
+
it is a convenience, not a reproducible identity resolver, because it has no
|
|
146
|
+
ticker history, a symbol is reassigned over time, and one CIK carries several
|
|
147
|
+
symbols at once (`GOOG` and `GOOGL`, `BRK.A` and `BRK.B`). The `/v1` API does not
|
|
148
|
+
serve the `ticker` filter today: a `ticker` request returns a distinct
|
|
149
|
+
"not served in this release" error rather than silently resolving through the
|
|
150
|
+
free snapshot. Address facts by CIK.
|
|
151
|
+
|
|
152
|
+
## Install matrix
|
|
153
|
+
|
|
154
|
+
| Install | Adds |
|
|
155
|
+
|---|---|
|
|
156
|
+
| `arkleon` | The free EDGAR fetch/parse core. No key, no Arkleon network. |
|
|
157
|
+
| `arkleon[api]` | The optional paid `/v1` `DataClient`. Requires an `ak_` key at runtime. |
|
|
158
|
+
| `arkleon[mcp]` | The built-in MCP server and the `arkleon-mcp` console script. |
|
|
159
|
+
| `arkleon[pandas]` | Optional pandas DataFrame adapters for the free core. Never required. |
|
|
160
|
+
| `arkleon[all]` | Convenience union of `api`, `mcp`, and `pandas`. |
|
|
161
|
+
|
|
162
|
+
Base plus `[pandas]` stays fully free and credential-free. `[api]` and the paid
|
|
163
|
+
tools in `[mcp]` are the only parts that consume an `ak_` key.
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT. See [LICENSE](LICENSE).
|
|
168
|
+
|
|
169
|
+
## Links
|
|
170
|
+
|
|
171
|
+
- **`/v1` API contract:** `docs/data-layer/api-v1-contract.md` (Appendix A is the
|
|
172
|
+
authority for the corpus figures above).
|
|
173
|
+
- **Issues and contributing:** https://github.com/arkleon/arkleon
|
|
174
|
+
- **SEC fair-access policy:** https://www.sec.gov/os/accessing-edgar-data
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "arkleon"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Point-in-time SEC EDGAR fundamentals: free fetch/parse helpers, an optional certified /v1 client, and a built-in MCP server."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "Arkleon LLC", email = "founder@arkleon.com" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"sec",
|
|
15
|
+
"edgar",
|
|
16
|
+
"xbrl",
|
|
17
|
+
"fundamentals",
|
|
18
|
+
"point-in-time",
|
|
19
|
+
"as-reported",
|
|
20
|
+
"financial-data",
|
|
21
|
+
"filings",
|
|
22
|
+
"10-K",
|
|
23
|
+
"10-Q",
|
|
24
|
+
"mcp",
|
|
25
|
+
"financial-statement-data-sets",
|
|
26
|
+
]
|
|
27
|
+
dependencies = ["httpx>=0.27"]
|
|
28
|
+
classifiers = [
|
|
29
|
+
"License :: OSI Approved :: MIT License",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Development Status :: 3 - Alpha",
|
|
34
|
+
"Intended Audience :: Developers",
|
|
35
|
+
"Topic :: Office/Business :: Financial",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
api = ["httpx>=0.27"]
|
|
40
|
+
mcp = ["mcp>=1.9"]
|
|
41
|
+
pandas = ["pandas>=2.0"]
|
|
42
|
+
all = ["httpx>=0.27", "mcp>=1.9", "pandas>=2.0"]
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
Homepage = "https://github.com/arkleon/arkleon"
|
|
46
|
+
Repository = "https://github.com/arkleon/arkleon"
|
|
47
|
+
"SEC fair access" = "https://www.sec.gov/os/accessing-edgar-data"
|
|
48
|
+
|
|
49
|
+
[project.scripts]
|
|
50
|
+
arkleon-mcp = "arkleon.mcp.server:main"
|
|
51
|
+
|
|
52
|
+
[tool.hatch.build.targets.wheel]
|
|
53
|
+
packages = ["src/arkleon"]
|
|
54
|
+
|
|
55
|
+
[tool.hatch.build.targets.sdist]
|
|
56
|
+
include = [
|
|
57
|
+
"/src/arkleon",
|
|
58
|
+
"/README.md",
|
|
59
|
+
"/LICENSE",
|
|
60
|
+
]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Free EDGAR core imported by default; api and mcp are optional extras."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__version__ = "0.1.0"
|
|
6
|
+
|
|
7
|
+
from .edgar import (
|
|
8
|
+
AS_OF_WARNING,
|
|
9
|
+
Company,
|
|
10
|
+
EdgarClient,
|
|
11
|
+
Fact,
|
|
12
|
+
FactSet,
|
|
13
|
+
Filing,
|
|
14
|
+
MissingUserAgentError,
|
|
15
|
+
RateLimiter,
|
|
16
|
+
as_of,
|
|
17
|
+
build_ticker_map,
|
|
18
|
+
cik_to_int,
|
|
19
|
+
facts_to_dataframe,
|
|
20
|
+
normalize_cik,
|
|
21
|
+
parse_company_concept,
|
|
22
|
+
parse_company_facts,
|
|
23
|
+
parse_fsds_zip,
|
|
24
|
+
parse_submissions,
|
|
25
|
+
resolve_user_agent,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"AS_OF_WARNING",
|
|
30
|
+
"Company",
|
|
31
|
+
"EdgarClient",
|
|
32
|
+
"Fact",
|
|
33
|
+
"FactSet",
|
|
34
|
+
"Filing",
|
|
35
|
+
"MissingUserAgentError",
|
|
36
|
+
"RateLimiter",
|
|
37
|
+
"as_of",
|
|
38
|
+
"build_ticker_map",
|
|
39
|
+
"cik_to_int",
|
|
40
|
+
"facts_to_dataframe",
|
|
41
|
+
"normalize_cik",
|
|
42
|
+
"parse_company_concept",
|
|
43
|
+
"parse_company_facts",
|
|
44
|
+
"parse_fsds_zip",
|
|
45
|
+
"parse_submissions",
|
|
46
|
+
"resolve_user_agent",
|
|
47
|
+
"__version__",
|
|
48
|
+
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Optional paid /v1 client for the Arkleon Data API.
|
|
2
|
+
|
|
3
|
+
Component (b) of the arkleon package (spec section 6). This subpackage is
|
|
4
|
+
always importable because its HTTP dependency (httpx) is a base dependency;
|
|
5
|
+
there is no import gate. The paid gate is a RUNTIME requirement instead: the
|
|
6
|
+
client refuses to construct without an ak_-prefixed key (contract section 6.2),
|
|
7
|
+
resolved from the constructor argument or the ARKLEON_API_KEY environment
|
|
8
|
+
variable.
|
|
9
|
+
|
|
10
|
+
Nothing here activates without a key. Installing the client does not activate
|
|
11
|
+
the paid path; a key must still be supplied at runtime.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from .client import DataClient
|
|
17
|
+
from .errors import (
|
|
18
|
+
ArkleonAPIError,
|
|
19
|
+
AuthError,
|
|
20
|
+
ConflictError,
|
|
21
|
+
NotFoundError,
|
|
22
|
+
NotServedError,
|
|
23
|
+
RateLimitError,
|
|
24
|
+
RequestError,
|
|
25
|
+
ScopeError,
|
|
26
|
+
ServerError,
|
|
27
|
+
UnprocessableError,
|
|
28
|
+
error_from_envelope,
|
|
29
|
+
)
|
|
30
|
+
from .errors import NotImplementedError # noqa: A004 (contract alias, spec section 6.5)
|
|
31
|
+
from .pagination import Page
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"DataClient",
|
|
35
|
+
"Page",
|
|
36
|
+
"error_from_envelope",
|
|
37
|
+
"ArkleonAPIError",
|
|
38
|
+
"AuthError",
|
|
39
|
+
"ScopeError",
|
|
40
|
+
"RequestError",
|
|
41
|
+
"NotFoundError",
|
|
42
|
+
"ConflictError",
|
|
43
|
+
"UnprocessableError",
|
|
44
|
+
"RateLimitError",
|
|
45
|
+
"NotServedError",
|
|
46
|
+
"ServerError",
|
|
47
|
+
# Alias exported per spec section 6.5; primary name is NotServedError.
|
|
48
|
+
"NotImplementedError",
|
|
49
|
+
]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""The paid gate: ak_ Bearer key resolution.
|
|
2
|
+
|
|
3
|
+
The key is read from an explicit argument or, failing that, the
|
|
4
|
+
``ARKLEON_API_KEY`` environment variable (contract section 6.2). The client
|
|
5
|
+
refuses to operate without an ak_-prefixed key, raising locally before any
|
|
6
|
+
network contact so an unauthenticated request is never sent. Keys are
|
|
7
|
+
presented, never stored or logged: nothing here writes the key to a file, a
|
|
8
|
+
log line, or an exception message.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
|
|
15
|
+
from .errors import AuthError
|
|
16
|
+
|
|
17
|
+
__all__ = ["resolve_api_key", "optional_api_key", "API_KEY_ENV_VAR", "API_KEY_PREFIX"]
|
|
18
|
+
|
|
19
|
+
API_KEY_ENV_VAR = "ARKLEON_API_KEY"
|
|
20
|
+
API_KEY_PREFIX = "ak_"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def resolve_api_key(explicit: str | None) -> str:
|
|
24
|
+
"""Return an ak_-prefixed key or raise AuthError locally.
|
|
25
|
+
|
|
26
|
+
Resolution order: the explicit argument, then ``ARKLEON_API_KEY``. The key
|
|
27
|
+
is required and validated for the ak_ prefix here, before any request, so
|
|
28
|
+
the client fails closed rather than issuing a silent unauthenticated call
|
|
29
|
+
(contract section 6.2). The raised message never contains the key value.
|
|
30
|
+
"""
|
|
31
|
+
key = explicit if explicit is not None else os.environ.get(API_KEY_ENV_VAR)
|
|
32
|
+
if not isinstance(key, str) or not key.strip():
|
|
33
|
+
raise AuthError(
|
|
34
|
+
"An Arkleon /v1 API key is required. Pass api_key=... or set the "
|
|
35
|
+
f"{API_KEY_ENV_VAR} environment variable to an {API_KEY_PREFIX}-prefixed key.",
|
|
36
|
+
error="unauthorized",
|
|
37
|
+
)
|
|
38
|
+
key = key.strip()
|
|
39
|
+
if not key.startswith(API_KEY_PREFIX):
|
|
40
|
+
raise AuthError(
|
|
41
|
+
f"Arkleon /v1 API keys must be {API_KEY_PREFIX}-prefixed. The supplied "
|
|
42
|
+
"value is not a valid key.",
|
|
43
|
+
error="unauthorized",
|
|
44
|
+
)
|
|
45
|
+
return key
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def optional_api_key(explicit: str | None) -> str | None:
|
|
49
|
+
"""Return a resolvable ak_ key, or None when none is available.
|
|
50
|
+
|
|
51
|
+
A soft companion to resolve_api_key for callers that must not raise when no
|
|
52
|
+
key is present, such as the MCP server deciding whether to register the paid
|
|
53
|
+
tools (spec section 7.1). It never raises and never logs the key.
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
return resolve_api_key(explicit)
|
|
57
|
+
except AuthError:
|
|
58
|
+
return None
|