Metadata-Version: 2.4
Name: n-ashare-market
Version: 0.5.10
Summary: Agent-ready A-share market and research data SDK with DuckDB, Tushare, iFinD, optional MinIO, REST and MCP.
Project-URL: Documentation, https://gitlab.thoughtyard.com.cn/aion/ashare-market-sdk
Project-URL: Repository, https://gitlab.thoughtyard.com.cn/aion/ashare-market-sdk.git
Project-URL: Issues, https://gitlab.thoughtyard.com.cn/aion/ashare-market-sdk/-/issues
Keywords: a-share,market-data,duckdb,tushare,minio,mcp,fastapi
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: certifi>=2025.1.31
Requires-Dist: duckdb<2,>=1.1
Requires-Dist: fastapi<1,>=0.115
Requires-Dist: httpx<1,>=0.28
Requires-Dist: iFinDAPI==0.0.8
Requires-Dist: mcp[cli]<2,>=1.27
Requires-Dist: minio<8,>=7.2
Requires-Dist: pandas<3,>=2.2
Requires-Dist: pyarrow<23,>=18
Requires-Dist: pydantic<3,>=2.10
Requires-Dist: pydantic-settings<3,>=2.7
Requires-Dist: tushare<2,>=1.4
Requires-Dist: urllib3<3,>=2.2
Requires-Dist: uvicorn[standard]<1,>=0.34
Provides-Extra: dev
Requires-Dist: ruff<1,>=0.11; extra == "dev"
Provides-Extra: release
Requires-Dist: build<2,>=1.2; extra == "release"
Requires-Dist: twine<7,>=6; extra == "release"

# n-ashare-market

面向程序与 Agent 的 A 股市场数据 SDK。当前版本提供：

- Tushare 历史日线：`none / qfq / hfq`；
- iFinD `THS_RQ` 实时行情快照；
- Insight 历史 tick 与 `1/5/15/30/60min` K 线；
- Insight 实时 tick/1min 单会话订阅与 SSE；
- 完整覆盖旧 `ashare-history-system` 的 41 个数据集与 8 个引用表；
- 在旧范围之上额外提供财务报表、Insight 1min/tick 和实时能力；
- DuckDB 本地高速查询；MinIO raw/curated Parquet、覆盖清单、实时归档和带鉴权的外部下载可选；
- MinIO 实时小对象安全 compact；
- 本地/远程 Python SDK；
- FastAPI REST/OpenAPI；
- MCP tools/resources；
- 支持 NDJSON 分批消费，并提供来源、新鲜度和覆盖度元数据。

Insight 使用独立 subprocess bridge，可继续运行在供应商要求的 Python 3.7 环境，主 SDK 保持 Python 3.11+。

## 1. 架构

```text
Tushare history ─┐
iFinD snapshot ──┼─ MarketDataService ─ normalization ─ DuckDB query store
Insight bridge ──┘                           └────────── optional MinIO replica/artifacts
                           │
                           ├─ Python SDK
                           ├─ REST / OpenAPI
                           ├─ CLI
                           └─ MCP Server
```

DuckDB 是查询优先的本地存储；MinIO 是可选的共享持久化和分发层，不承担实时消息总线职责。实时请求先由 iFinD 返回给调用方，再按配置归档到 DuckDB/MinIO。

详细设计见 [docs/architecture.md](docs/architecture.md)，数据目录和全量重建见
[docs/research.md](docs/research.md)，旧系统逐项覆盖见
[docs/legacy-coverage.md](docs/legacy-coverage.md)。

## 2. 安装

Python 需要 3.11 或更高版本。

```bash
cd ashare-market-sdk
python -m venv .venv
. .venv/bin/activate
pip install -e .
cp .env.example .env
```

从 PyPI 安装发布版本：

```bash
pip install n-ashare-market
```

### 从 Git 仓库安装 Python SDK

仓库发布后，客户端不需要复制源码。推荐打 tag 后固定版本安装：

```bash
pip install "n-ashare-market @ git+https://gitlab.thoughtyard.com.cn/aion/ashare-market-sdk.git@v0.5.10"
```

私有仓库建议使用已经配置好的 SSH key，避免把访问 token 写入命令或日志：

```bash
pip install "n-ashare-market @ git+ssh://git@gitlab.thoughtyard.com.cn/aion/ashare-market-sdk.git@v0.5.10"
```

如果以后把它放在一个 monorepo 的 `ashare-market-sdk/` 子目录，则使用：

```bash
pip install "n-ashare-market @ git+https://gitlab.example.com/YOUR_ORG/YOUR_REPO.git@v0.5.10#subdirectory=ashare-market-sdk"
```

安装后验证命令是否在 PATH（这一步由部署人员执行）：

```bash
ashare-market --help
```

iFinD 的官方 `iFinDAPI==0.0.8` 已作为运行依赖安装，并提供 `iFinDPy` 模块。运行 REST/MCP 的 Python 环境必须能够执行：

```python
import iFinDPy
```

Linux 版还依赖原生动态库。Docker 镜像会安装 `libidn.so.11`、`libgcc`、`libstdc++`、`zlib`、`ncurses`，
并把 SDK `bin64` 加入动态库搜索路径；构建最后会对 `libShellExport.so`、`libFTDataInterface.so` 和
`libhcrypt-3.1.so` 执行 `ldd` 检查，缺失依赖会直接终止构建。`/health` 与 `/v1/sync/status` 的
`providers.ifind.sdk` 会返回 `loadable`、`missing_libraries`、`missing_files`、Python 位数和机器架构。

非 Docker 环境必须按照同花顺 Linux 部署文档补齐 `ldd` 中的 `not found` 项，并确保 SDK `bin64` 位于
`LD_LIBRARY_PATH`。如果登录返回 `-340`，需要开通官方接口域名/IP 白名单或配置
`bin64/Tool/etc/system_setting.ini` 代理。

Docker 服务镜像会从 PyPI 下载并安装官方 `insight-sdk==4.0.7`。由于该版本发布包中的
`python_requires` 元数据格式错误，不能作为普通 Python 依赖安装；项目安装命令会校验官方源码哈希，
只修正该元数据后再交给 pip 安装。直接从 PyPI 安装本项目且需要 Insight 分钟线或实时订阅时，使用：

```bash
pip install n-ashare-market
ashare-market-install-insight
```

默认使用主服务当前 Python 运行 Insight bridge，不需要配置解释器路径。如果供应商 SDK 只能安装在
独立 Python 环境，该环境至少需要 Insight SDK、pandas 和可写 Parquet 的 pyarrow，再覆盖：

```dotenv
ASHARE_MARKET_INSIGHT_USERNAME=...
ASHARE_MARKET_INSIGHT_PASSWORD=...
ASHARE_MARKET_INSIGHT_PYTHON=/absolute/path/to/separate/.venv/bin/python
```

桥接部署细节见 [docs/insight.md](docs/insight.md)。

然后在 `.env` 中配置：

```dotenv
ASHARE_MARKET_TUSHARE_TOKEN=...
ASHARE_MARKET_IFIND_USERNAME=...
ASHARE_MARKET_IFIND_PASSWORD=...
```

不要把真实 token、用户名或密码提交到仓库。

## 3. 启动 MinIO

```bash
docker compose -f deploy/docker-compose.minio.yml up -d
ashare-market ensure-buckets
```

默认地址：

- S3 API：`http://127.0.0.1:9000`
- Console：`http://127.0.0.1:9001`

`ensure-buckets` 会创建并启用版本控制：

- `ashare-curated`
- `ashare-raw`
- `ashare-realtime`
- `ashare-artifacts`
- `ashare-catalog`

### 一键启动完整服务

配置好 `.env` 后，一条命令启动 REST API、Streamable HTTP MCP、历史同步和实时监督器。`deploy/start.sh` 会根据
`ASHARE_MARKET_STORAGE_BACKEND` 自动选择是否启动 MinIO：只有 `minio` 或 `hybrid` 才会启动 MinIO，默认 `duckdb` 只启动主服务：

```bash
./deploy/start.sh
```

停止服务：

```bash
./deploy/stop.sh
```

不使用 Docker 时可直接运行（`minio`/`hybrid` 模式才需要先启动 MinIO）：

```bash
ashare-market serve --host 0.0.0.0 --port 8020
```

启动完成后，REST 地址为 `http://<服务器>:8020`，MCP 地址为
`http://<服务器>:8020/mcp`。MCP 作为 ASGI 子应用挂载到 REST 服务，共用同一个 `MarketDataService`、
DuckDB/MinIO 存储客户端、查询缓存、Provider 限流器和运行时监督器。

默认自动同步注册表中的全部历史/引用数据集，并额外纳入 `daily_raw`、`weekly` 和分钟数据集。有历史起点的数据集优先从默认起点断点补齐到当前目标日，不会在全量 coverage 闭合前强制刷新最近5天；不传同步日期就是全量同步。监督器目标、THS 游标和回补任务持久化在 DuckDB，重启从第一个未覆盖 chunk 恢复。同一目标日期已经完成后不会重复同步。查询和回测导出不会改变同步范围，只按照各自请求的 `start_date/end_date/lookback_bars` 读取本地数据。必须携带代码的 Provider 接口会分别从 `stock_basic`、`index_basic` 自动推导股票和指数全集；ETF 使用 `fund_daily` 按交易日批量提取全市场。跟踪库只用于可选的范围缩小。之后服务按照官方数据发布时间和交易日历持续刷新。实时订阅只有在
`ASHARE_MARKET_RUNTIME_REALTIME_SYMBOLS` 明确配置股票列表时才启用。运行状态可通过
`GET /v1/runtime` 查看；同步任务统一后的 `progress.scanned/progress.total` 扫描进度和
`coverage_percent` 覆盖率通过 `GET /v1/sync/status` 查看；容器同步日志可通过
`docker compose --project-directory . -f deploy/docker-compose.yml logs -f ashare-market` 跟踪。
状态响应包含 `service_version` 和 `status_schema_version=3.2`；`queue` 会显示同步 worker 数、Provider 每分钟请求上限、当前分钟已用/剩余额度、窗口起止时间和限流模式。`dataset_groups` 同时列出重要、已完成、未开始、未完成和跳过的数据集，主列表默认显示全部数据集并按核心行情优先级排列。每个数据集分别返回 `rows_current_operation`、`rows_current_slice` 和跨切片累计的 `rows_fetched_total`，历史空窗口仍会推进 coverage，但不会伪造数据行数。传 `?include_completed=false` 可只看未完成项，追加 `?verbose=true` 才返回原始 supervisor/operation 诊断状态。

`trade_cal` 每次默认同步当前自然年的 `01-01..12-31`，包含尚未到来的交易日安排。股票日线的有效全量起点为
`1990-12-19`，`stk_limit` 按 Tushare 的可用历史从 `2007-01-01` 开始。状态中的 `phase/sync_scope` 明确区分
`recent_refresh` 与 `full_backfill`；`refresh_start_date/refresh_end_date` 仅用于没有默认全量范围的数据集，
`full_history_start_date/full_history_end_date` 才是历史回补范围，`current_start_date/current_end_date` 只在当前 chunk 执行期间有值，完成后会清空。全量 coverage 未闭合时状态为 `partial/failed`，不会再用 `rows=0` 冒充同步完成。
无日期的 `ths_member` 快照会按 `ASHARE_MARKET_RUNTIME_REFERENCE_BATCH_SIZE` 个概念代码分批刷新，避免单次全量 Provider 请求阻塞其他数据集。

需要代码的自动同步默认从基础代码表推导全集；独立 SQLite 跟踪库用于按需缩小范围，默认位于 `ASHARE_MARKET_TRACKING_DB_PATH`。记录以
`dataset + symbol` 为主键，可以为同一股票选择不同的数据集和不同历史起点：

```bash
ashare-market track-add income 002436.SZ --start 2018-01-01
ashare-market track-add balancesheet 002436.SZ --start 2018-01-01
ashare-market track-list --dataset income
ashare-market track-update income 002436.SZ --enabled false
ashare-market track-delete income 002436.SZ
```

也可通过 REST、Python SDK 或 MCP 工具维护；启用的记录会在后续自动同步周期按所属 dataset 拉取，不会影响其他数据集的股票范围。

## 4. Python SDK

### 本地模式

本地模式在当前 Python 进程中直接调用 Tushare、iFinD 和存储后端。将
`ASHARE_MARKET_STORAGE_BACKEND=duckdb` 后，查询直接读本机 `ASHARE_MARKET_DUCKDB_PATH`，不经过 HTTP，也不连接 MinIO；
`hybrid` 会在 DuckDB 未覆盖时从 MinIO 回源并自动写入 DuckDB。

生产推荐先使用 `hybrid` 预热已有 MinIO 数据，确认本地库覆盖完整后再切换为 `duckdb`：

```dotenv
ASHARE_MARKET_STORAGE_BACKEND=hybrid
ASHARE_MARKET_DUCKDB_PATH=/data/ashare/ashare-market.duckdb
ASHARE_MARKET_DUCKDB_THREADS=32
ASHARE_MARKET_DUCKDB_MEMORY_LIMIT=32GB
```

服务端 HTTP 仍返回 JSON，数据扫描由 DuckDB 在进程内完成；主服务同机的 `ashare-market ...` CLI 直接复用同一 DuckDB 文件。

```python
from ashare_market import AshareMarketClient

with AshareMarketClient() as client:
    bars = client.history.daily(
        ["000001.SZ", "600000.SH"],
        "2025-01-01",
        "2025-03-31",
        adjustment="qfq",
        cache_policy="cache_first",
    )
    print(bars.dataframe)
    print(bars.meta)

    # 大结果直接逐批进入策略，不创建或下载文件。
    for chunk in client.history.stream_daily(
        ["000001.SZ", "600000.SH"],
        "2020-01-01",
        "2025-01-01",
        chunk_rows=2000,
    ):
        strategy.consume(chunk)

    # 不传 symbols 时使用本地 stock_basic 股票池读取当日截面。
    daily_snapshot = client.history.snapshot("2026-07-17")
    factor_snapshot = client.research.snapshot("daily_basic", "2026-07-17")

    # 标准化前的 Tushare pro_bar 原始字段。
    raw_bars = client.history.raw_daily(
        "000001.SZ",
        "2025-01-01",
        "2025-01-31",
    )

    quotes = client.realtime.quotes(["000001.SZ", "600000.SH"])
    print(quotes.dataframe)

    artifact = client.history.export_daily(
        "000001.SZ",
        "2020-01-01",
        "2025-01-01",
        file_format="parquet",
    )
    print(artifact["download_url"])
    client.download_artifact(artifact, "daily.parquet")

    minute = client.history.minutes(
        "000001.SZ",
        "2026-07-16 09:30:00+08:00",
        "2026-07-16 15:00:00+08:00",
        frequency="5min",
        adjustment="qfq",
    )

    ticks = client.history.ticks(
        "000001.SZ",
        "2026-07-16 09:30:00+08:00",
        "2026-07-16 10:00:00+08:00",
    )

    indicators = client.research.query(
        "fina_indicator",
        symbols="000001.SZ",
        start_date="2024-01-01",
        end_date="2026-07-17",
    )

    # reference 数据集不需要日期，使用 TTL 缓存。
    stocks = client.research.query("stock_basic")
```

### 远程模式

```python
from ashare_market import AshareMarketClient

with AshareMarketClient(
    base_url="http://127.0.0.1:8020",
    api_key="your-api-key",
) as client:
    bars = client.history.daily("000001.SZ", "2025-01-01", "2025-01-31")
    index_bars = client.research.query(
        "index_daily",
        symbols="000300.SH",
        start_date="2025-01-01",
        end_date="2025-01-31",
    )

    # 一次生成回测窗口需要的三张完整 Parquet；不再把全市场拆成上千次 JSON 请求。
    bundle = client.backtests.export(
        ["000001.SZ", "600000.SH"],
        "2026-07-01",
        "2026-07-17",
        lookback_bars=120,
    )
    for dataset, artifact in bundle["datasets"].items():
        client.download_artifact(artifact, f"{dataset}.parquet")
```

远程 `history.daily()`、`history.intraday()` 和 `research.query()` 统一通过 NDJSON 流取得完整数据并组装成
`MarketFrame`，不会自动创建或下载 artifact。策略需要控制内存时直接使用 `stream_daily()`、
`stream_intraday()` 或 `research.stream()`，每次消费一个 `DataFrame` 批次。只有显式调用 `export_*`
或 `backtests.export()` 时才创建文件。

### SDK 主服务应该在哪里运行

生产环境推荐在一台常驻 Linux 数据服务器上运行单实例 REST 服务和 MCP 服务。Tushare、iFinD、
Insight 与 MinIO 凭证只放在服务器；研究终端、策略程序和 Agent 只安装 Python 包并使用上面的
`base_url` 远程模式。这样缓存、额度控制、Insight 会话和审计口径只有一份。

- REST 主服务使用单 Uvicorn worker；当前 Insight session 状态在进程内，多 worker 会产生多个互不共享的会话。
- MCP stdio 可运行在 Agent 所在机器并通过远程 Python SDK/REST 访问主服务；受信任环境也可直接在数据服务器运行 MCP。
- MinIO 开发时可以同机，生产建议独立磁盘或独立节点，并备份 catalog bucket。
- 只有单机研究或开发场景才建议本地模式；它会让每个客户端分别持有 Provider 登录和频次。

systemd 示例见 [deploy/ashare-market.service.example](deploy/ashare-market.service.example)，完整运维说明见
[docs/operations.md](docs/operations.md)。

### 缓存策略

| 策略 | 行为 |
|---|---|
| `cache_only` | 只读 MinIO，不调用 Provider；允许返回部分覆盖 |
| `cache_first` | coverage commit 完整时读 MinIO，否则从 Tushare 刷新并写 MinIO |
| `refresh` | 强制从 Tushare 获取，并发布新的不可变对象和 commit |
| `direct` | 直接从 Tushare 获取，不读写 MinIO |

## 5. REST / OpenAPI

```bash
ashare-market api --host 127.0.0.1 --port 8020
```

- OpenAPI：`https://ashare-data.thoughtyard.com.cn/docs`
- `GET /health`：只显示配置能力，不登录 Provider；
- `GET /ready`：连接 MinIO 并确保 bucket 存在；
- `GET /v1/datasets`；
- `GET /v1/datasets/{name}/schema`；
- `GET /v1/compatibility/ashare-history-system`；
- `GET /v1/runtime`：同步、紧凑化和实时状态；
- `GET /v1/sync/status`：worker/队列摘要、数据集分组、完整进度与错误列表；`?include_completed=false` 只显示未完成项，`?verbose=true` 返回原始诊断状态；
- `GET/POST/PATCH/DELETE /v1/tracking/symbols`：维护按数据集隔离的自动同步代码；
- `GET /v1/artifacts/{object_name}`：经 REST 同源鉴权下载真实对象；
- `POST /v1/history/daily`；
- `POST /v1/history/daily/stream`：按批流式返回完整日线；
- `POST /v1/history/daily-raw`；
- `POST /v1/history/daily-raw/stream`：按批流式返回原始日线；
- `POST /v1/history/daily/export`：提交异步 daily Parquet 导出任务，立即返回 job id；
- `POST /v1/backtests/export`：提交异步 `daily/daily_basic/stk_limit` 完整 Parquet bundle；
- `GET /v1/exports/jobs/{job_id}`：查询任务进度并在完成后取得 artifact；
- `POST /v1/realtime/quotes`；
- `POST /v1/coverage/daily`。
- `POST /v1/coverage/daily-raw`；
- `POST /v1/history/intraday`；
- `POST /v1/history/intraday/stream`：按批流式返回分钟或 tick 历史；
- `POST /v1/coverage/intraday`；
- `POST /v1/realtime/sessions`；
- `GET /v1/realtime/sessions/{id}/events`；
- `GET /v1/realtime/sessions/{id}/stream`；
- `POST /v1/realtime/compact`。
- `GET /v1/research/datasets`；
- `POST /v1/research/query`；
- `POST /v1/research/query/stream`：按批流式返回完整研究数据；
- `POST /v1/research/coverage`；
- `POST /v1/research/sync`。
- `POST /v1/datasets/sync`（通用别名）。

服务升级后的首次启动会在同步 worker 启动前使用 DuckDB 现有 OHLCV 修复缺失的
`ma5/ma10/ma20/ma30/ma60/ma250` 与成交量均线。修复不调用 Provider，结果可在
`/v1/sync/status` 的 `runtime.daily_ma_repair` 查看；完成后写入版本标记，后续重启不会重复全库计算。

日线示例：

```bash
curl -X POST https://ashare-data.thoughtyard.com.cn/v1/history/daily \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: your-api-key' \
  -d '{
    "symbols":["000001.SZ"],
    "start_date":"2025-01-01",
    "end_date":"2025-01-31",
    "adjustment":"qfq",
    "cache_policy":"cache_first"
  }'
```

普通 JSON 接口仍受 `ASHARE_MARKET_API_MAX_ROWS` 限制，但不会隐式创建 artifact；reference 数据集（例如
`trade_cal`、`stock_basic`）的内联阈值由 `ASHARE_MARKET_API_INLINE_REFERENCE_MAX_ROWS` 控制。
Python SDK 使用对应 `/stream` 接口取得完整结果，流中先返回 meta，再按 `chunk_rows` 返回数据批次。
只有显式导出时才会返回文件链接。
`ASHARE_MARKET_PUBLIC_BASE_URL` 配置后，`meta.artifact.download_url` 是外部可达的 REST URL；未配置时返回
`/v1/artifacts/...` 相对路径，不再暴露容器内的 `minio:9000`。

### 为什么数据目录里只有 `xl.meta`

`deploy/data` 是 MinIO 的私有 XL 后端目录，不是普通文件导出目录。一个逻辑上的 `.parquet` S3 对象在磁盘上会表现为
同名目录和 `xl.meta`，小对象还可能直接内联在元数据中；因此不能对该目录直接运行 `parquet-tools`。应从 MinIO Console、
S3/`mc` 或上述 REST artifact 接口下载对象后再检查，例如：

```bash
parquet-tools inspect daily.parquet
```

新写 EOD 数据按“请求批次 + 交易日”合并成 `symbol_bucket=all`；后台还会从最近月份向历史月份逐月生成活跃紧凑快照。
旧 hash 小对象暂不物理删除，读取会在紧凑快照更新后自动跳过它们，便于回滚和后续按生命周期安全清理。

## 6. MCP / Agent

stdio 模式：

```bash
ashare-market mcp --transport stdio
```

Streamable HTTP 随 REST 服务一起启动：

```bash
ashare-market serve --host 0.0.0.0 --port 8020
```

客户端连接 `http://<服务器>:8020/mcp`。配置 `ASHARE_MARKET_API_KEY` 后，REST 与 MCP 都要求相同的
`X-API-Key`；对外暴露时仍应通过反向代理配置 TLS。

可用工具：

- `market_list_datasets`
- `market_get_schema`
- `market_get_legacy_coverage`
- `market_get_realtime_quotes`
- `market_get_daily_bars`
- `market_get_raw_daily_bars`
- `market_get_raw_daily_coverage`
- `market_get_daily_coverage`
- `market_create_daily_export`
- `market_create_backtest_export`
- `market_get_export_job`
- `market_get_minute_bars`
- `market_get_tick_history`
- `market_get_intraday_coverage`
- `market_create_intraday_export`
- `market_start_insight_session`
- `market_get_insight_session`
- `market_get_insight_events`
- `market_stop_insight_session`
- `market_compact_realtime`
- `market_list_research_datasets`
- `market_query_research`
- `market_get_research_coverage`
- `market_create_research_export`
- `market_sync_research_dataset`
- `market_sync_dataset`

可用 resources：

- `ashare://datasets/catalog`
- `ashare://datasets/{dataset}/schema`
- `ashare://research/catalog`
- `ashare://compatibility/ashare-history-system`

通用 stdio MCP 配置示例：

```json
{
  "mcpServers": {
    "ashare-market": {
      "command": "/absolute/path/to/ashare-market-sdk/.venv/bin/ashare-market-mcp",
      "env": {
        "ASHARE_MARKET_MINIO_ENDPOINT": "127.0.0.1:9000"
      }
    }
  }
}
```

凭证应配置在服务器的 `.env` 或密钥系统中，不要复制到 Agent prompt 或 MCP tool 参数里。

## 7. CLI

```bash
ashare-market datasets
ashare-market legacy-coverage
ashare-market daily --symbols 000001.SZ --start 2025-01-01 --end 2025-01-31
ashare-market quotes --symbols 000001.SZ,600000.SH
ashare-market coverage --symbols 000001.SZ --start 2025-01-01 --end 2025-01-31
ashare-market daily --symbols 000001.SZ --start 2020-01-01 --end 2025-01-01 --export parquet
ashare-market intraday --symbols 000001.SZ \
  --start '2026-07-16 09:30:00+08:00' --end '2026-07-16 15:00:00+08:00' \
  --kind 5min --adjustment qfq

# 实时 session 由常驻 API 服务持有；CLI 通过 REST 操作它。
ashare-market --base-url http://127.0.0.1:8020 stream-start \
  --symbols 000001.SZ,600000.SH --tick --kline
ashare-market --base-url http://127.0.0.1:8020 stream-sessions
ashare-market --base-url http://127.0.0.1:8020 stream-events SESSION_ID --after 0
ashare-market --base-url http://127.0.0.1:8020 stream-stop SESSION_ID

ashare-market compact-realtime --dataset tick --trade-date 2026-07-17

ashare-market research-list
ashare-market research fina_indicator --symbols 000001.SZ \
  --start 2024-01-01 --end 2026-07-17

# 默认按 coverage 断点续跑；不传日期时使用数据集默认全量起点。
ashare-market research-sync daily_basic
ashare-market research-sync income --start 2010-01-01 --end 2026-07-17
# `sync` 是面向全部注册数据集的等价短命令。
ashare-market sync daily
ashare-market sync weekly
ashare-market sync etf_daily --symbols 510300.SH,159915.SZ
ashare-market sync index_daily --symbols 000300.SH
ashare-market sync kline_5min --symbols 000001.SZ --start 2026-01-01 --end 2026-07-17
```

## 8. 当前边界

- 股票、ETF、指数和概念/行业代码型行情要求明确代码列表；全市场日频与事件表支持按日期分块同步。
- MinIO catalog 采用不可变 JSON commit，并有 TTL 索引缓存和有界并发读取；更大规模部署仍可引入数据库或 Iceberg catalog。
- iFinD 实时快照为请求式 `THS_RQ`；推送式 tick/1min 由 Insight 提供。
- 当前实时 manager 状态保存在 API 进程内，API 重启后需重新创建 session；已归档数据不受影响。
- EOD 旧小对象由后台按月紧凑，无需从 Provider 重抓；物理清理旧版本仍由运维生命周期策略控制。
- 已覆盖旧系统全部 41+8 数据；基金范围目前仅包含旧系统已有的场内 ETF 日线，不扩展债券、期货、宏观和海外市场。
- MinIO 内数据的共享范围必须符合 Tushare、iFinD 和 Insight 的账户授权与再分发条款。
