fd-open-data-mcp 0.2.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.
- fd_open_data_mcp-0.2.0/PKG-INFO +218 -0
- fd_open_data_mcp-0.2.0/README.md +188 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/__init__.py +9 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/__init__.py +74 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/agriculture.py +4 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/akshare.py +356 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/amac-fund.py +4 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/amac_fund.py +70 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/chemicals.py +54 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/cisa-industry.py +4 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/cisa_industry.py +74 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/cme_agricultural.py +61 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/dce_agricultural.py +57 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/electronics.py +53 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/fin_platforms.py +80 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/flowers_kifc.py +54 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/nbs-gdp.py +4 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/nbs_gdp.py +212 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/nonferrous.py +73 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/sac_securities.py +28 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/shfe_futures.py +74 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/adapters/steel-assoc.py +4 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/__init__.py +1 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/cnreport_rules.py +80 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/enrich.py +50 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/importer.py +172 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/providers.py +101 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/readers.py +290 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/register.py +164 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/seeds/__init__.py +1 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/seeds/cn_report.py +86 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/seeds/edgar.py +89 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/seeds/wbgapi.py +80 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/upstream.py +153 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/catalog/wbgapi_enumerate.py +111 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/cli.py +265 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/crawl/__init__.py +8 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/crawl/migrate.py +120 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/crawl/plan.py +67 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/crawl/planner.py +110 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/db.py +118 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/entities/__init__.py +1 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/entities/resolver.py +172 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/entities/taxonomy.py +69 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/errors.py +30 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/fetch/__init__.py +1 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/fetch/cache.py +76 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/fetch/dispatch.py +172 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/fetch/runner.py +210 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/migrate.py +32 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/models.py +307 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/ranking/__init__.py +1 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/ranking/scorer.py +122 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/refresh/__init__.py +1 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/refresh/runner.py +66 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/refresh/scheduler.py +48 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/semantic/__init__.py +1 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/semantic/bindings.py +121 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/semantic/concepts.py +65 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/semantic/mapper_llm.py +118 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp/server.py +314 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/PKG-INFO +218 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/SOURCES.txt +86 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/dependency_links.txt +1 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/entry_points.txt +2 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/requires.txt +20 -0
- fd_open_data_mcp-0.2.0/fd_open_data_mcp.egg-info/top_level.txt +1 -0
- fd_open_data_mcp-0.2.0/pyproject.toml +47 -0
- fd_open_data_mcp-0.2.0/setup.cfg +4 -0
- fd_open_data_mcp-0.2.0/tests/test_adapters.py +409 -0
- fd_open_data_mcp-0.2.0/tests/test_catalog.py +94 -0
- fd_open_data_mcp-0.2.0/tests/test_cli.py +74 -0
- fd_open_data_mcp-0.2.0/tests/test_cnreport.py +64 -0
- fd_open_data_mcp-0.2.0/tests/test_e2e.py +97 -0
- fd_open_data_mcp-0.2.0/tests/test_edgar.py +72 -0
- fd_open_data_mcp-0.2.0/tests/test_enrich_concept.py +89 -0
- fd_open_data_mcp-0.2.0/tests/test_entities_ranking.py +42 -0
- fd_open_data_mcp-0.2.0/tests/test_integration.py +44 -0
- fd_open_data_mcp-0.2.0/tests/test_planner.py +105 -0
- fd_open_data_mcp-0.2.0/tests/test_ranking_cache.py +74 -0
- fd_open_data_mcp-0.2.0/tests/test_readers_upstream.py +125 -0
- fd_open_data_mcp-0.2.0/tests/test_refresh.py +75 -0
- fd_open_data_mcp-0.2.0/tests/test_register.py +60 -0
- fd_open_data_mcp-0.2.0/tests/test_runners.py +22 -0
- fd_open_data_mcp-0.2.0/tests/test_server.py +26 -0
- fd_open_data_mcp-0.2.0/tests/test_wbgapi.py +60 -0
- fd_open_data_mcp-0.2.0/tests/test_wbgapi_enumerate.py +78 -0
- fd_open_data_mcp-0.2.0/tests/test_yfinance_reader.py +48 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fd-open-data-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Open-data ontology MCP: a semantic concept layer over multi-datasource financial/economic data
|
|
5
|
+
Author: FindDataOfficial
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: click>=8.0
|
|
13
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
14
|
+
Requires-Dist: pydantic>=2.0
|
|
15
|
+
Requires-Dist: fastmcp>=2.0
|
|
16
|
+
Requires-Dist: pandas>=2.0
|
|
17
|
+
Requires-Dist: fd-open-data-protocol
|
|
18
|
+
Requires-Dist: pyyaml>=6.0
|
|
19
|
+
Provides-Extra: data
|
|
20
|
+
Requires-Dist: akshare>=1.17; extra == "data"
|
|
21
|
+
Requires-Dist: yfinance>=0.2; extra == "data"
|
|
22
|
+
Requires-Dist: world_bank_data>=0.1; extra == "data"
|
|
23
|
+
Requires-Dist: edgartools>=2.0; extra == "data"
|
|
24
|
+
Requires-Dist: wbgapi>=1.0; extra == "data"
|
|
25
|
+
Requires-Dist: requests>=2.31; extra == "data"
|
|
26
|
+
Requires-Dist: beautifulsoup4>=4.12; extra == "data"
|
|
27
|
+
Requires-Dist: scrapling>=0.4.7; extra == "data"
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
30
|
+
|
|
31
|
+
# fd-open-data-mcp
|
|
32
|
+
|
|
33
|
+
An **open-data ontology MCP**: a semantic concept layer over multi-datasource
|
|
34
|
+
financial/economic data. You ask for data in **concepts + entities** (e.g.
|
|
35
|
+
"price.close for Moutai", "GDP for China"); the system resolves the concept to
|
|
36
|
+
physical columns across datasources, ranks candidate sources by quality +
|
|
37
|
+
accessibility, fetches from the best one (with failover), caches by concept,
|
|
38
|
+
and refreshes on a per-concept frequency.
|
|
39
|
+
|
|
40
|
+
It consumes the finddata `fd-*` datasource registries and
|
|
41
|
+
`fd-entities-indicators` **read-only** and adds the unifying layers on top.
|
|
42
|
+
|
|
43
|
+
## Architecture
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
CONSUMED (read-only) ADDED by fd-open-data-mcp
|
|
47
|
+
fd-akshare/yfinance/world/ concept_bindings (column -> concept)
|
|
48
|
+
cn-report/cn-gov registries entity_source_identifiers (per-source id)
|
|
49
|
+
fd-entities-indicators source_rankings (quality × access × freshness)
|
|
50
|
+
indicator_defs (926 concepts) semantic_observations (read-through cache)
|
|
51
|
+
countries/cities/symbols/sw_industries fetch_log / schedules / executions
|
|
52
|
+
│
|
|
53
|
+
TRANSFORMERS: import_catalog, consume_concepts, propose_bindings,
|
|
54
|
+
seed_entity_identifiers, generate_refresh_schedules
|
|
55
|
+
│
|
|
56
|
+
RUNTIME: read() -> cache hit? : dispatch (ranked, failover) -> cache -> log
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Six capabilities (see `openspec/changes/add-fd-open-data-mcp/specs/`):
|
|
60
|
+
`open-data-catalog`, `semantic-layer`, `entity-identity`, `source-ranking`,
|
|
61
|
+
`concept-fetch`, `scheduled-refresh`.
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
cd /Users/chengsishi/finddata/fd-open-data-mcp
|
|
67
|
+
uv sync # base install
|
|
68
|
+
uv sync --extra data # + akshare / yfinance / world_bank_data (for real fetches)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The DB path defaults to `fd_open_data_mcp/metadata/daas.db`; override with
|
|
72
|
+
`FD_OPEN_DATA_MCP_DATABASE_URL`. `FINDDATA_ROOT` (default: the parent
|
|
73
|
+
`finddata/` dir) locates the `fd-*` providers. `EDGAR_IDENTITY` (an email)
|
|
74
|
+
is required by the SEC before any edgar fetch - the runner refuses to call
|
|
75
|
+
anonymously if it is unset.
|
|
76
|
+
|
|
77
|
+
## Quickstart
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# 1. create the ontology tables
|
|
81
|
+
fd-open-data-mcp migrate
|
|
82
|
+
|
|
83
|
+
# 2. import the catalogs (akshare 673, yfinance 12, cn-gov 11, cn-report 44, edgar 6, ...)
|
|
84
|
+
fd-open-data-mcp import-catalog
|
|
85
|
+
# or one provider: fd-open-data-mcp import-catalog akshare
|
|
86
|
+
|
|
87
|
+
# 3. consume the 926 indicator_defs as concepts + propose column->concept bindings
|
|
88
|
+
fd-open-data-mcp consume-concepts
|
|
89
|
+
fd-open-data-mcp propose-bindings
|
|
90
|
+
|
|
91
|
+
# 4. seed per-source entity identifiers (akshare/yfinance for stocks, worldbank for countries)
|
|
92
|
+
fd-open-data-mcp seed-entities
|
|
93
|
+
|
|
94
|
+
# 5. generate per-concept refresh schedules from indicator_defs.frequency
|
|
95
|
+
fd-open-data-mcp generate-schedules
|
|
96
|
+
|
|
97
|
+
# 6. read data by concept + entity (read-through cache + ranked dispatch + failover)
|
|
98
|
+
fd-open-data-mcp read --concept-id 234 --entity-type stock --entity-id 1 --date 2024-07-26
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## MCP server
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
fd-open-data-mcp serve # FastMCP, stdio transport
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
16 tools: `import_catalog`, `consume_concepts`, `propose_bindings`,
|
|
108
|
+
`list_concepts`, `list_bindings`, `review_bindings`, `confirm_binding`,
|
|
109
|
+
`seed_entity_identifiers`, `resolve_entity`, `add_entity_identifier`,
|
|
110
|
+
`rank_sources`, `read`, `fetch`, `generate_refresh_schedules`,
|
|
111
|
+
`list_schedules`, `run_schedule`.
|
|
112
|
+
|
|
113
|
+
## Tests
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
uv run --with pytest pytest -q
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Design notes / v1 limitations
|
|
120
|
+
|
|
121
|
+
- **Propose-and-confirm**: column->concept bindings carry `confidence` +
|
|
122
|
+
`provenance`; below-threshold bindings are withheld from dispatch (review
|
|
123
|
+
queue). A real fetch promotes a binding to `sample-confirmed`.
|
|
124
|
+
- **Ranking** is per `(source × concept)`, self-tuning from `fetch_log`
|
|
125
|
+
(bounded so one failure can't remove a source).
|
|
126
|
+
- **Conflict policy**: one cached value per `(concept, entity, date)` with
|
|
127
|
+
`source_used` attached; values are never merged across sources.
|
|
128
|
+
- **LLM provider** for meaning-enrichment / cross-language concept mapping is
|
|
129
|
+
an open question (`design.md`); v1 uses a rule table + `semantic_type` hints.
|
|
130
|
+
- `_build_params` / `_extract_value` in the fetch runner are best-effort; a
|
|
131
|
+
production runner refines per-function date-format / payload-shape quirks.
|
|
132
|
+
|
|
133
|
+
See `openspec/changes/add-fd-open-data-mcp/` for the full spec.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### Usage Examples
|
|
138
|
+
|
|
139
|
+
#### Query NBS GDP Data
|
|
140
|
+
```python
|
|
141
|
+
from fd_open_data_mcp.fetch.runner import run_upstream
|
|
142
|
+
|
|
143
|
+
result = run_upstream(
|
|
144
|
+
source='nbs-gdp',
|
|
145
|
+
command='get_gdp_quarterly',
|
|
146
|
+
params={'start_year': 2020}
|
|
147
|
+
)
|
|
148
|
+
print(result.head())
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Query Steel Industry Production
|
|
152
|
+
```python
|
|
153
|
+
result = run_upstream(
|
|
154
|
+
source='cisa-industry',
|
|
155
|
+
command='get_steel_production',
|
|
156
|
+
params={}
|
|
157
|
+
)
|
|
158
|
+
print(result.head())
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### Query Metal Futures Pricing
|
|
162
|
+
```python
|
|
163
|
+
result = run_upstream(
|
|
164
|
+
source='shfe-metal-futures',
|
|
165
|
+
command='get_metal_pricing',
|
|
166
|
+
params={}
|
|
167
|
+
)
|
|
168
|
+
print(result.head())
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### CLI Usage
|
|
172
|
+
```bash
|
|
173
|
+
# List all data sources
|
|
174
|
+
fd-open-data-mcp list-sources
|
|
175
|
+
|
|
176
|
+
# Read specific data
|
|
177
|
+
fd-open-data-mcp read \
|
|
178
|
+
--source nbs-gdp \
|
|
179
|
+
--function get_gdp_quarterly \
|
|
180
|
+
--params '{"start_year": 2020}'
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### MCP Server Mode
|
|
184
|
+
```bash
|
|
185
|
+
uv run fd-open-data-mcp serve
|
|
186
|
+
# Then connect from Claude/Codex/etc.
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Rate Limits & Best Practices
|
|
193
|
+
|
|
194
|
+
### Recommended Refresh Intervals
|
|
195
|
+
|
|
196
|
+
| Data Source Type | Refresh Interval | Notes |
|
|
197
|
+
|------------------|------------------|-------|
|
|
198
|
+
| **GDP/Macro** | Weekly | Stable data, updates quarterly/monthly |
|
|
199
|
+
| **Industry Stats** | Daily | Can change frequently |
|
|
200
|
+
| **Futures Prices** | Hourly during market hours | Volatile pricing |
|
|
201
|
+
| **Fund Statistics** | Monthly | Updates monthly |
|
|
202
|
+
| **Market Indices** | Real-time | High volatility |
|
|
203
|
+
|
|
204
|
+
### API Rate Limiting
|
|
205
|
+
|
|
206
|
+
- **Government APIs**: Respect 10 requests/minute default limits
|
|
207
|
+
- **Exchange APIs**: Follow exchange-specific rate policies
|
|
208
|
+
- **Third-party Data**: Check individual terms of service
|
|
209
|
+
- **Recommendation**: Implement exponential backoff on 429 errors
|
|
210
|
+
|
|
211
|
+
### Caching Strategy
|
|
212
|
+
|
|
213
|
+
All fetch results are automatically cached based on data frequency:
|
|
214
|
+
- High-frequency data (futures): Cache for 1 hour
|
|
215
|
+
- Medium-frequency data (industry stats): Cache for 24 hours
|
|
216
|
+
- Low-frequency data (GDP, annual reports): Cache for 1 week
|
|
217
|
+
|
|
218
|
+
Use `fd-open-data-mcp read` to check cache status.
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# fd-open-data-mcp
|
|
2
|
+
|
|
3
|
+
An **open-data ontology MCP**: a semantic concept layer over multi-datasource
|
|
4
|
+
financial/economic data. You ask for data in **concepts + entities** (e.g.
|
|
5
|
+
"price.close for Moutai", "GDP for China"); the system resolves the concept to
|
|
6
|
+
physical columns across datasources, ranks candidate sources by quality +
|
|
7
|
+
accessibility, fetches from the best one (with failover), caches by concept,
|
|
8
|
+
and refreshes on a per-concept frequency.
|
|
9
|
+
|
|
10
|
+
It consumes the finddata `fd-*` datasource registries and
|
|
11
|
+
`fd-entities-indicators` **read-only** and adds the unifying layers on top.
|
|
12
|
+
|
|
13
|
+
## Architecture
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
CONSUMED (read-only) ADDED by fd-open-data-mcp
|
|
17
|
+
fd-akshare/yfinance/world/ concept_bindings (column -> concept)
|
|
18
|
+
cn-report/cn-gov registries entity_source_identifiers (per-source id)
|
|
19
|
+
fd-entities-indicators source_rankings (quality × access × freshness)
|
|
20
|
+
indicator_defs (926 concepts) semantic_observations (read-through cache)
|
|
21
|
+
countries/cities/symbols/sw_industries fetch_log / schedules / executions
|
|
22
|
+
│
|
|
23
|
+
TRANSFORMERS: import_catalog, consume_concepts, propose_bindings,
|
|
24
|
+
seed_entity_identifiers, generate_refresh_schedules
|
|
25
|
+
│
|
|
26
|
+
RUNTIME: read() -> cache hit? : dispatch (ranked, failover) -> cache -> log
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Six capabilities (see `openspec/changes/add-fd-open-data-mcp/specs/`):
|
|
30
|
+
`open-data-catalog`, `semantic-layer`, `entity-identity`, `source-ranking`,
|
|
31
|
+
`concept-fetch`, `scheduled-refresh`.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cd /Users/chengsishi/finddata/fd-open-data-mcp
|
|
37
|
+
uv sync # base install
|
|
38
|
+
uv sync --extra data # + akshare / yfinance / world_bank_data (for real fetches)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The DB path defaults to `fd_open_data_mcp/metadata/daas.db`; override with
|
|
42
|
+
`FD_OPEN_DATA_MCP_DATABASE_URL`. `FINDDATA_ROOT` (default: the parent
|
|
43
|
+
`finddata/` dir) locates the `fd-*` providers. `EDGAR_IDENTITY` (an email)
|
|
44
|
+
is required by the SEC before any edgar fetch - the runner refuses to call
|
|
45
|
+
anonymously if it is unset.
|
|
46
|
+
|
|
47
|
+
## Quickstart
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# 1. create the ontology tables
|
|
51
|
+
fd-open-data-mcp migrate
|
|
52
|
+
|
|
53
|
+
# 2. import the catalogs (akshare 673, yfinance 12, cn-gov 11, cn-report 44, edgar 6, ...)
|
|
54
|
+
fd-open-data-mcp import-catalog
|
|
55
|
+
# or one provider: fd-open-data-mcp import-catalog akshare
|
|
56
|
+
|
|
57
|
+
# 3. consume the 926 indicator_defs as concepts + propose column->concept bindings
|
|
58
|
+
fd-open-data-mcp consume-concepts
|
|
59
|
+
fd-open-data-mcp propose-bindings
|
|
60
|
+
|
|
61
|
+
# 4. seed per-source entity identifiers (akshare/yfinance for stocks, worldbank for countries)
|
|
62
|
+
fd-open-data-mcp seed-entities
|
|
63
|
+
|
|
64
|
+
# 5. generate per-concept refresh schedules from indicator_defs.frequency
|
|
65
|
+
fd-open-data-mcp generate-schedules
|
|
66
|
+
|
|
67
|
+
# 6. read data by concept + entity (read-through cache + ranked dispatch + failover)
|
|
68
|
+
fd-open-data-mcp read --concept-id 234 --entity-type stock --entity-id 1 --date 2024-07-26
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## MCP server
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
fd-open-data-mcp serve # FastMCP, stdio transport
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
16 tools: `import_catalog`, `consume_concepts`, `propose_bindings`,
|
|
78
|
+
`list_concepts`, `list_bindings`, `review_bindings`, `confirm_binding`,
|
|
79
|
+
`seed_entity_identifiers`, `resolve_entity`, `add_entity_identifier`,
|
|
80
|
+
`rank_sources`, `read`, `fetch`, `generate_refresh_schedules`,
|
|
81
|
+
`list_schedules`, `run_schedule`.
|
|
82
|
+
|
|
83
|
+
## Tests
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
uv run --with pytest pytest -q
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Design notes / v1 limitations
|
|
90
|
+
|
|
91
|
+
- **Propose-and-confirm**: column->concept bindings carry `confidence` +
|
|
92
|
+
`provenance`; below-threshold bindings are withheld from dispatch (review
|
|
93
|
+
queue). A real fetch promotes a binding to `sample-confirmed`.
|
|
94
|
+
- **Ranking** is per `(source × concept)`, self-tuning from `fetch_log`
|
|
95
|
+
(bounded so one failure can't remove a source).
|
|
96
|
+
- **Conflict policy**: one cached value per `(concept, entity, date)` with
|
|
97
|
+
`source_used` attached; values are never merged across sources.
|
|
98
|
+
- **LLM provider** for meaning-enrichment / cross-language concept mapping is
|
|
99
|
+
an open question (`design.md`); v1 uses a rule table + `semantic_type` hints.
|
|
100
|
+
- `_build_params` / `_extract_value` in the fetch runner are best-effort; a
|
|
101
|
+
production runner refines per-function date-format / payload-shape quirks.
|
|
102
|
+
|
|
103
|
+
See `openspec/changes/add-fd-open-data-mcp/` for the full spec.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### Usage Examples
|
|
108
|
+
|
|
109
|
+
#### Query NBS GDP Data
|
|
110
|
+
```python
|
|
111
|
+
from fd_open_data_mcp.fetch.runner import run_upstream
|
|
112
|
+
|
|
113
|
+
result = run_upstream(
|
|
114
|
+
source='nbs-gdp',
|
|
115
|
+
command='get_gdp_quarterly',
|
|
116
|
+
params={'start_year': 2020}
|
|
117
|
+
)
|
|
118
|
+
print(result.head())
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### Query Steel Industry Production
|
|
122
|
+
```python
|
|
123
|
+
result = run_upstream(
|
|
124
|
+
source='cisa-industry',
|
|
125
|
+
command='get_steel_production',
|
|
126
|
+
params={}
|
|
127
|
+
)
|
|
128
|
+
print(result.head())
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### Query Metal Futures Pricing
|
|
132
|
+
```python
|
|
133
|
+
result = run_upstream(
|
|
134
|
+
source='shfe-metal-futures',
|
|
135
|
+
command='get_metal_pricing',
|
|
136
|
+
params={}
|
|
137
|
+
)
|
|
138
|
+
print(result.head())
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### CLI Usage
|
|
142
|
+
```bash
|
|
143
|
+
# List all data sources
|
|
144
|
+
fd-open-data-mcp list-sources
|
|
145
|
+
|
|
146
|
+
# Read specific data
|
|
147
|
+
fd-open-data-mcp read \
|
|
148
|
+
--source nbs-gdp \
|
|
149
|
+
--function get_gdp_quarterly \
|
|
150
|
+
--params '{"start_year": 2020}'
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### MCP Server Mode
|
|
154
|
+
```bash
|
|
155
|
+
uv run fd-open-data-mcp serve
|
|
156
|
+
# Then connect from Claude/Codex/etc.
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Rate Limits & Best Practices
|
|
163
|
+
|
|
164
|
+
### Recommended Refresh Intervals
|
|
165
|
+
|
|
166
|
+
| Data Source Type | Refresh Interval | Notes |
|
|
167
|
+
|------------------|------------------|-------|
|
|
168
|
+
| **GDP/Macro** | Weekly | Stable data, updates quarterly/monthly |
|
|
169
|
+
| **Industry Stats** | Daily | Can change frequently |
|
|
170
|
+
| **Futures Prices** | Hourly during market hours | Volatile pricing |
|
|
171
|
+
| **Fund Statistics** | Monthly | Updates monthly |
|
|
172
|
+
| **Market Indices** | Real-time | High volatility |
|
|
173
|
+
|
|
174
|
+
### API Rate Limiting
|
|
175
|
+
|
|
176
|
+
- **Government APIs**: Respect 10 requests/minute default limits
|
|
177
|
+
- **Exchange APIs**: Follow exchange-specific rate policies
|
|
178
|
+
- **Third-party Data**: Check individual terms of service
|
|
179
|
+
- **Recommendation**: Implement exponential backoff on 429 errors
|
|
180
|
+
|
|
181
|
+
### Caching Strategy
|
|
182
|
+
|
|
183
|
+
All fetch results are automatically cached based on data frequency:
|
|
184
|
+
- High-frequency data (futures): Cache for 1 hour
|
|
185
|
+
- Medium-frequency data (industry stats): Cache for 24 hours
|
|
186
|
+
- Low-frequency data (GDP, annual reports): Cache for 1 week
|
|
187
|
+
|
|
188
|
+
Use `fd-open-data-mcp read` to check cache status.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""fd-open-data-mcp: open-data ontology MCP.
|
|
2
|
+
|
|
3
|
+
A semantic concept layer over multi-datasource financial/economic data.
|
|
4
|
+
Consumes the fd-* datasource registries and fd-entities-indicators read-only,
|
|
5
|
+
and adds: a unified catalog, concept<->column bindings, cross-source entity
|
|
6
|
+
identity, ranked dispatch, a read-through concept-keyed cache, and
|
|
7
|
+
frequency-driven auto-refresh.
|
|
8
|
+
"""
|
|
9
|
+
__version__ = "0.2.0"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Per-function adapter registry: concrete param-building + value-extraction per
|
|
2
|
+
``(source, command)``, shared by ``read()`` dispatch and the bulk crawl executor.
|
|
3
|
+
|
|
4
|
+
Replaces the best-effort ``_build_params`` / ``_extract_value`` in
|
|
5
|
+
``fetch/dispatch.py`` (design.md D4). Seeded by porting per-function logic from the
|
|
6
|
+
``scraw-*`` projects (akshare first - tasks 2.2/2.3). Where no adapter is registered,
|
|
7
|
+
callers fall back to the legacy best-effort mapping (coexistence during migration).
|
|
8
|
+
|
|
9
|
+
An adapter is registered for a ``(source, command)`` key and implements two methods:
|
|
10
|
+
|
|
11
|
+
``build_params(fn, identifier, date, binding) -> dict``
|
|
12
|
+
Build the concrete kwargs for the upstream callable. ``fn`` is the
|
|
13
|
+
``Function`` row (with ``.parameters``); ``identifier`` is the per-source
|
|
14
|
+
entity id; ``binding`` is the ``ConceptBinding`` (its ``.column.name`` is used
|
|
15
|
+
for indicator-style params, e.g. wbgapi ``indicator=column.name``).
|
|
16
|
+
|
|
17
|
+
``extract_value(result, column_name, date) -> value | None``
|
|
18
|
+
Pull the value for ``(date, column_name)`` from the upstream result.
|
|
19
|
+
|
|
20
|
+
An optional ``call(command, params)`` method wraps the upstream callable (e.g. with a
|
|
21
|
+
native timeout + retry); ``fetch/runner.py`` opts into it when present.
|
|
22
|
+
|
|
23
|
+
Dispatch checks ``adapter_for(source, command)``; if present it delegates, otherwise
|
|
24
|
+
it uses the legacy best-effort path. This keeps working reads from regressing while
|
|
25
|
+
adapters are ported one function at a time.
|
|
26
|
+
"""
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from typing import Any, Optional, Protocol
|
|
30
|
+
|
|
31
|
+
# Typed loosely to avoid an import cycle with fetch.dispatch / models.
|
|
32
|
+
FunctionLike = Any
|
|
33
|
+
BindingLike = Any
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Adapter(Protocol):
|
|
37
|
+
"""Per-function fetch mechanics: param mapping + value extraction."""
|
|
38
|
+
|
|
39
|
+
def build_params(
|
|
40
|
+
self, fn: FunctionLike, identifier: str, date: str, binding: BindingLike,
|
|
41
|
+
) -> dict: ...
|
|
42
|
+
|
|
43
|
+
def extract_value(
|
|
44
|
+
self, result: Any, column_name: str, date: str,
|
|
45
|
+
) -> Any: ...
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
_REGISTRY: dict[tuple[str, str], Adapter] = {}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def register(source: str, command: str, adapter: Adapter) -> Adapter:
|
|
52
|
+
"""Register an adapter for a ``(source, command)`` key (idempotent overwrite)."""
|
|
53
|
+
_REGISTRY[(source, command)] = adapter
|
|
54
|
+
return adapter
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def adapter_for(source: str, command: str) -> Optional[Adapter]:
|
|
58
|
+
"""Return the registered adapter for ``(source, command)``, or ``None``."""
|
|
59
|
+
return _REGISTRY.get((source, command))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def has_adapter(source: str, command: str) -> bool:
|
|
63
|
+
"""Whether a registered adapter exists for ``(source, command)``."""
|
|
64
|
+
return (source, command) in _REGISTRY
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def registered() -> list[tuple[str, str]]:
|
|
68
|
+
"""All registered ``(source, command)`` keys (for introspection/debugging)."""
|
|
69
|
+
return sorted(_REGISTRY.keys())
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# Load akshare adapters so they register at package import. Imported last so
|
|
73
|
+
# `register` is defined before akshare.py imports it (no cycle).
|
|
74
|
+
from fd_open_data_mcp.adapters import akshare as _akshare_adapters # noqa: E402,F401
|