Skip to main content

crawldata-engine

By FindData — powers the 柏讯 (Wire) product line. MIT licensed.

English | 中文

crawldata-engine is a data-stream acquisition and processing engine: a crawl-control plane, an open-data ontology, and an analysis surface in one deployable service.

You declare what you want by concept + entity ("price.close of Moutai", "GDP of China"); the engine resolves concepts to physical columns in each source, ranks candidate sources by quality and reachability, and fetches from the best one (with failover). Around that core it runs the full crawl lifecycle:

  • Crawl control plane — declarative policies (crawl_policies) → compiled plans (CrawlPlan) → scheduled k8s Jobs or a lease-queue worker fleet; per-source rate limiting, proxy pools, ban rules and circuit breakers built in.
  • Storage & governance — a governed observation store (semantic_observations) with date-shape checks, month-bucket (obs_month) indexing, and O(Δ) incremental rollups feeding a storage watermark.
  • Event stream — an at-least-once Redis Streams plane (envelopes with monotonic-ULID idempotency keys) landing realtime pushes with idempotent natural-key writes; cron stays as gapfill reconciliation.
  • Analysis plane — pluggable AnalysisBackend (default: rollup-backed aggregates + time series) behind capped, fail-loud API endpoints.
  • Operations panel — a bilingual (EN/中文) /panel console for coverage, scheduling, login stations and stream health, with permission tiers and a ships-dark gate.

Quick start

Zero-setup (Docker) — Postgres + Redis + engine + operations panel:

docker compose -f docker-compose.dev.yaml up --build
# panel: http://localhost:8300/panel   MCP: http://localhost:8300/mcp

CLI (your own Postgres or the default SQLite):

pip install "crawldata-engine"        # +[data] for akshare/yfinance SDKs

crawldata-engine migrate              # create the ontology tables
crawldata-engine import-catalog       # load the in-code example catalog
                                      # (World Bank wbgapi is key-free)
crawldata-engine serve                # MCP server (stdio)

The in-code seed catalog works with no workspace and no keys. Providers that need API keys (POLYGON_API_KEY, EDGAR_IDENTITY, …) or a local datasource workspace are skipped with a stated reason, never a crash. See CONFIG.md for the full environment reference and docs/ for datasource integration guides.

Architecture

 Datasources (pluggable)              Engine layer (this repo)
 akshare / yfinance / polygon /       concepts + concept_bindings  (column -> concept)
 EDGAR / World Bank / cn-gov /        entities + per-source identifiers
 datacommons / your own provider      source_rankings (quality x reach x freshness)
        │                             semantic_observations (governed store)
   Catalog loaders: import-catalog,   rollups + storage watermark
   consume-concepts, propose-         crawl: policies -> plans -> k8s Jobs
     bindings, seed-entities,               or lease-queue worker fleet
   generate-schedules                 stream: Redis Streams + idempotent landing
        │                             analysis: pluggable backends (aggregates,
   Runtime: read() -> cache hit? :          time series)
     dispatch (ranked + failover)     panel: /panel operations console
   Search: semantic_search + graph_search + ai_search

Eight capabilities (see openspec/changes/add-crawldata-engine/specs/): open-data-catalog, semantic-layer, entity-identity, source-ranking, concept-fetch, scheduled-refresh, entity-graph, vector-search.

Install

cd crawldata-engine
uv sync                  # base install

# full datasource support (akshare, yfinance, edgar, world bank, ...)
uv sync --extra data

The database path defaults to fd_open_data_mcp/metadata/daas.db (SQLite), overridable via FD_OPEN_DATA_MCP_DATABASE_URL (Postgres recommended for production; the Docker quickstart provisions one). FINDDATA_ROOT (optional) points at a local datasource workspace for providers that need it — the in-code seed catalog works without it.

Note: before using SEC EDGAR data, set EDGAR_IDENTITY="your_email@example.com" in the environment.

Quick start

Zero-setup option — bring up Postgres + Redis + the engine (MCP server and the /panel console on http://localhost:8300/panel) with one command. The init service runs migrate + import-catalog automatically, loading the in-code example catalog (World Bank wbgapi is key-free, so fetch works out of the box; providers that need a local workspace or API keys are skipped with a reason, never a crash):

docker compose -f docker-compose.dev.yaml up --build
# teardown + reset data:  docker compose -f docker-compose.dev.yaml down -v

Note: the panel ships dark (no auth) in this dev stack — never expose it publicly without setting PANEL_TOKEN or LOGTO_* (see SECURITY.md).

Or run the CLI directly against your own Postgres:

# 1. Create the ontology tables
crawldata-engine migrate

# 2. Import catalogs (akshare 673, yfinance 12, cn-gov 11, cn-report 44, edgar 6, ...)
crawldata-engine import-catalog
# or a single provider: crawldata-engine import-catalog akshare

# 3. Consume the 926 indicator_defs as concepts and propose column->concept bindings
crawldata-engine consume-concepts
crawldata-engine propose-bindings

# 4. Seed per-source entity identifiers (stocks for akshare/yfinance, countries for worldbank)
crawldata-engine seed-entities

# 5. Generate refresh schedules from indicator_defs.frequency
crawldata-engine generate-schedules

# 6. Read by concept + entity (read-through cache + ranked dispatch + failover)
crawldata-engine read --concept-id 234 --entity-type stock --entity-id 1 --date 2024-07-26

MCP server

crawldata-engine serve          # FastMCP, stdio transport

71 tools, registered across six files (49 @mcp.tool in server.py + 22 from the five register_* modules; count verified 2026-10-06 against the live instance's tools/list = 71):

Group Tools
Catalog/bindings (server.py) import_catalog, register_datasource, update_entity, update_concept, update_binding, consume_concepts, propose_bindings, list_concepts, list_concept_families, list_registry_entries, record_concept_mapping, list_concept_mappings, import_crosswalks, list_bindings, review_bindings, confirm_binding
Entity identity (server.py) seed_entity_identifiers, resolve_entity, add_entity_identifier, list_entities, get_entity, add_entity
Entity graph (server.py) list_relationships, add_relationship, graph_search
Vector/semantic search (server.py) semantic_search, semantic_search_entities, semantic_search_unified, re_embed_concept, ai_search
Ranking/read (server.py) rank_sources, read, read_series, fetch
Search scopes (server.py) scope_create, scope_list, scope_update, scope_delete, scope_bind_caller, scope_unbind_caller, scope_list_bindings, scope_stats
Refresh schedules/indexes (server.py) plan_crawl, generate_refresh_schedules, list_schedules, run_schedule, list_cnreport_rules, enumerate_wbgapi_indicators, register_discovered
Crawl policy control plane (policy_tools.py) policy_create, policy_list, policy_get, policy_update, policy_enable, policy_disable, policy_delete, policy_trigger_now, policy_runs, policy_estimate, data_stats
Crawl platform control (platform_tools.py) platform_sources, platform_runs, platform_trigger, platform_cancel_run, platform_cancel_pending
Crawl visibility (visibility_tools.py) crawl_status
Coverage gaps (coverage_tools.py) coverage_report
Authenticated-crawling identity pool (auth_tools.py) auth_status, auth_events, auth_request_login, auth_launch_login

Catalog expansion and the read boundary

The list_concepts family (MCP tools and the list-concepts CLI) also returns entries from the unified indicator registry (registry_entries table) that passed the naming-quality gate (verified), covering world_bank / gta_panel / china_city_panel / the fd_open_data mirror; yearbook's numeric-placeholder entries stay unverified and out of the catalog.

  • Merge semantics: local concepts (concepts table) first, registry entries after (ordered by domain, semantic_code); entries whose semantic_code duplicates a local concept code are deduped (local row wins). Registry rows carry native_code and source_db identity fields, so retrieval works with either the native or the semantic code (the query parameter substring-matches code/name). Without that table in the local DB, the tool falls back to the pure local catalog — behavior unchanged.
  • Read boundary (important): this server does not read observation data for those external-registry indicators — forwarding reads through business-mcp is a later change; in this version read/fetch on registry indicators does not return external-source data. This expansion affects the indicator dictionary only, not any data-reading capability.

Full-registry enumeration (list_registry_entries, v0.5.38)

The parallel tiered-browsing channel (ADR-0002): pages through ALL registered entries — verified and not — ordered by (source_db, native_code), each row carrying the authoritative verified flag, with an optional status filter (verified | registered; default all). The catalog/search/ read verified gate is untouched: list_concepts, ai_search and the read paths still exclude unverified entries. Consumers: the Wire public catalog mirror re-keyed its ids to source_db:native_code from this feed. An optional scope filters rows by the indicator-scope rules (source_db / domain / semantic_code dimensions); unscoped calls return a plain list (the Wire sync contract).

Data sources

The dispatcher run_upstream() routes by source name to adapters. The table below is ordered by verified reachability, not self-reported status.

Verified online ✅

Source Notes
akshare A-share stocks/funds/financials
yfinance Yahoo Finance global equities
cn-report Chinese financial reports (44 tools, see fd-cn-report)
edgar SEC EDGAR filings (needs EDGAR_IDENTITY)
wbgapi World Bank data API
cnstats National Bureau of Statistics data
ckan CKAN catalog data
nbs-gdp NBS GDP data
datacommons Google Data Commons (needs DC_API_KEY)
polygon Polygon.io US equities (external package fd-polygon)
edinet Japan EDINET filings
dartlab Korea DARTLab disclosures

The polygon and datacommons runners live in external fd-* packages, lazily loaded via the manifest's fetch.module — crawldata-engine does not depend on polygon-api-client / requests unless a fetch actually happens.

Stub adapters ⚠️ (sample data only, no network)

Source Status
cisa-industry stub — static sample rows
amac-fund stub
shfe-metal-futures stub
agriculture stub (DCE)
cme-agricultural-futures stub (CME)
chemicals stub
electronics stub
nonferrous stub
flowers-kifc stub
fin_platforms stub (Wind)
sac-securities stub

These adapters expose run_<source>() entries and manifest registrations, so list-sources self-reports "✅ Full support" — but they return synthetic sample rows, not real exchange or association data. Read the adapter file before depending on any of them.

Read-only catalogs

Source Status
cn-gov manifest registration only (11 ministry catalogs)
world CKAN + Chinese NBS statistical catalogs

Crawl control center (panel + reconciler)

A policy describes what to crawl: concept × entity scope × date range × frequency × mode. CrawlPolicy objects are created on the panel, compiled into CrawlPlans by the reconciler, and executed by scraw-fd-open-data-mcp writing into semantic_observations.

# Start the control panel (default http://0.0.0.0:8000)
FD_OPEN_DATA_MCP_DATABASE_URL=<db url> crawldata-engine panel

# Run the reconciler once (due policies -> launch; close stale runs)
python -m fd_open_data_mcp.refresh.reconciler

Environment variables:

  • PANEL_TOKEN — when set, /panel/* requires it (header X-Panel-Token, ?token=, or cookie).
  • POLICY_MAX_FETCHES (default 50000) — plan-size guardrail; a due policy whose fetch estimate exceeds it is rejected (recorded as a failed run) unless the policy sets force.
  • RECONCILER_LAUNCHER — scrapyd (default) or k8s (K8sJobLauncher).
  • SCRAPYD_URL / SCRAW_PLAN_DIR (scrapyd launcher); SCRAW_K8S_NAMESPACE / SCRAW_K8s_IMAGE / SCRAW_K8S_DATABASE_URL / SCRAW_K8S_REDIS_URL (k8s launcher).
  • FD_PROXY_FORWARDER — empty for local dev (the injection layer returns a direct sentinel → direct egress); cluster crawling uses the standalone fd-proxy-service forwarder for proxy selection. The legacy vars FD_PROXY_POOL/FD_EGRESS_MODE are no longer read.

Tests

uv run --with pytest pytest -q

Design notes / v1 limitations

  • Propose-confirm flow: column→concept bindings carry confidence + provenance; below-threshold bindings stay out of dispatch (into the review queue). One real fetch promotes a binding to sample-confirmed.
  • Ranking is per (source × concept), self-tuning from fetch_log (bounded — one failure never removes a source).
  • Conflict policy: one cached value per (concept, entity, date) with its source_used; cross-source values are never merged.
  • LLM providers power semantic enrichment / cross-language concept mapping. v1 uses rule tables + semantic_type hints.

Full specifications: openspec/changes/add-crawldata-engine/.

Proxy pool & circuit breaker

The fetch stack rotates IPs through a proxy pool to avoid source bans, with per-(source, real_source×proxy_ip) circuit breakers and ban rules. Pool state is a plain table (proxies), so any deployment that keeps the table fresh works; FD_PROXY_POOL / FD_PROXY_FORWARDER select the pool implementation.

Key files: fd_open_data_mcp/proxy/ (selector, breaker, ban rules, injection); real-source failover in fd_open_data_mcp/fetch/dispatch.py.

Standard real-source names

  • eastmoney — Eastmoney (A-share primary, via akshare)
  • tencent — Tencent Finance (A-share failover)
  • sina — Sina Finance (A-share alternative)
  • yahoo_finance — Yahoo Finance (global markets, via yfinance)

Libraries (akshare, yfinance) call several real sources underneath; the breaker tracks health per real source, not per library, enabling smart failover.

LLM configuration (PDF report extraction)

fd-cn-report uses an LLM to extract financial indicators from annual-report PDFs. The current provider is DeepSeek on Ark (see the fd-cn-report README):

# crawldata-engine/.env
LLM_BASE_URL=…/api/plan/v1     # Ark endpoint
LLM_API_KEY=…                  # Ark key
LLM_MODEL=deepseek-v4-flash

LLM_API_KEY and OPENAI_API_KEY (legacy) are both accepted; LLM_API_KEY wins when both are set.

Images

docker build -t crawldata-engine . produces the runtime image (the compose quickstart builds it locally). CI (.github/workflows/image.yml) builds and pushes per-commit sha-<short> + rolling main tags to the registry configured in repository secrets; pin the sha tag in deployment manifests for immutable rollouts.

License

MIT

Metadata

Release files for crawldata-engine 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for crawldata-engine 1.0.0
File Size Uploaded Provenance Actions
crawldata_engine-1.0.0.tar.gz 1.1 MB With provenance Details for crawldata_engine-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for crawldata-engine 1.0.0
File Interpreter ABI Platform Actions
crawldata_engine-1.0.0-py3-none-any.whl Python 3 none any Details for crawldata_engine-1.0.0-py3-none-any.whl

Total release size: 1.8 MB

Release files / crawldata_engine-1.0.0.tar.gz

Download URL crawldata_engine-1.0.0.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
9414c5675295a9042396e61f8ab3283c6ede01d312cb3223c117a57d482154e3
BLAKE2b-256 checksum
How to use checksums
f5c099744a91054b117435c0220d0fe5b3f317e891d9396649f1b454ea6a97ad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

Release files / crawldata_engine-1.0.0-py3-none-any.whl

Download URL crawldata_engine-1.0.0-py3-none-any.whl
Size 779.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0ba775e9bbd394f71b848d84a6ae3fe087bdd11bb5bfdada53fbfbb5ba490e6b
BLAKE2b-256 checksum
How to use checksums
005b1cdc4cb6db2423074554e00f1c3b3773467c34b24e71e226f4fd82b0729c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

Security for crawldata-engine 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for crawldata_engine-1.0.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page