Metadata-Version: 2.4
Name: kdata-quant
Version: 2.2.5
Summary: K线数据下载与处理模块
Author-email: your name <you@example.com>
License: MIT
Requires-Python: <3.14,>=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pandas>=3.0.0
Requires-Dist: numpy>=1.22
Requires-Dist: baostock>=0.8.9
Requires-Dist: akshare>=1.18
Requires-Dist: efinance>=0.5.5
Requires-Dist: aiohttp>=3.13.2
Requires-Dist: requests>=2.32.5
Requires-Dist: lxml>=6.0.2
Requires-Dist: beautifulsoup4>=4.14.2
Requires-Dist: python-dateutil>=2.9.0
Requires-Dist: pytz>=2025.2
Requires-Dist: matplotlib>=3.10.0
Requires-Dist: mplfinance>=0.12.10b0
Requires-Dist: TA-Lib>=0.4.0
Requires-Dist: fear-and-greed>=0.4
Requires-Dist: build>=1.3.0
Requires-Dist: setuptools>=80.9.0
Requires-Dist: wheel>=0.45.1
Requires-Dist: pyyaml>=6.0
Requires-Dist: mootdx2>=1.7.0
Requires-Dist: ruamel-yaml>=0.18.17
Requires-Dist: py-mini-racer>=0.6.0
Requires-Dist: longport>=3.0.0
Requires-Dist: python-dotenv>=1.0.1
Requires-Dist: yfinance>=0.2.0
Requires-Dist: pandas-market-calendars>=5.4.0
Requires-Dist: pyarrow>=14.0.0
Provides-Extra: dev
Requires-Dist: pytest>=9.0.2; extra == "dev"
Dynamic: license-file

# kdata 模块核心 API 接口文档 (独立对外版)

`kdata` 是一个专注于量化行情数据获取和分析的 Python 本地开发包。它高度整合了多种行情数据源（如 Efinance, Akshare, Mootdx 等），为量化交易与数据分析提供一站式、高性能、自动本地缓存的极简接口。

本规范文档完全独立自包含，无需依赖其他说明文件即可独立查阅使用。

---

## 📑 目录 (Table of Contents)

- [📖 快速开始与环境要求](#-快速开始与环境要求)
- [⚠️ 异常体系与最佳实践](#️-异常体系与最佳实践-exception-handling)
- [📌 数据规范与格式说明](#-数据规范与数据格式说明-data-specifications)
- [🏷️ 标的代码命名规范](#️-标的代码命名规范-symbol-formats)
- [📊 第一部分：Python SDK 核心 API](#-第一部分python-sdk-核心-api)
  - [1. 行情与市场数据 (Market Data & Indices)](#1-行情与市场数据-market-data--indices)
    - [1.1 `get_any_ohlc` - 自适应统一获取 K 线数据（核心推荐入口）](#11-get_any_ohlc---自适应统一获取-k-线数据核心推荐入口)
    - [1.2 `generate_weekly_kdata` - 日K转换为周K数据](#12-generate_weekly_kdata---日k转换为周k数据)
    - [1.3 `get_market_breadth` - 市场广度指标](#13-get_market_breadth---市场广度指标)
    - [1.4 `get_sector_ranking` - 行业板块排名](#14-get_sector_ranking---行业板块排名)
    - [1.5 `get_market_overview` - 市场宏观全貌快照](#15-get_market_overview---市场宏观全貌快照)
    - [1.6 `get_index_history` - 核心指数近期多日行情](#16-get_index_history---核心指数近期多日行情)
    - [1.7 `get_cn_indices` / `get_hk_indices` / `get_global_indices` - 大盘行情快照](#17-get_cn_indices--get_hk_indices--get_global_indices---大盘行情快照)
  - [2. ETF 资产规模与筛选 (ETF Scale & Universe)](#2-etf-资产规模与筛选-etf-scale--universe)
    - [2.1 `get_etf_scale` - ETF 资产规模查询](#21-get_etf_scale---etf-资产规模查询)
    - [2.2 `get_filtered_etfs` - ETF 条件筛选与指标过滤](#22-get_filtered_etfs---etf-条件筛选与指标过滤)
  - [3. ETF/LOF 折溢价套利组件 (Arbitrage & Premium)](#3-etflof-折溢价套利组件-arbitrage--premium)
    - [3.1 `etf_premium` - ETF 实时折溢价率一键计算（推荐主接口）](#31-etf_premium---etf-实时折溢价率一键计算推荐主接口)
    - [3.2 `Scanner` - ETF/LOF 溢价监控扫描器](#32-scanner---etflof-溢价监控扫描器)
    - [3.3 `get_etf_premium_data` - ETF 估算溢价率数据序列](#33-get_etf_premium_data---etf-估算溢价率数据序列)
    - [3.4 `get_latest_etf_premium` - 单只 ETF 最新溢价快照](#34-get_latest_etf_premium---单只-etf-最新溢价快照)
  - [4. 标的行情与市值综合查询 (Quote & Market Cap)](#4-标的行情与市值综合查询-quote--market-cap)
    - [4.1 `get_quote` - 标的行情、成交额、成交量与市值综合查询](#41-get_quote---标的行情成交额成交量与市值综合查询)
  - [5. 交易日历与节假日服务 (Calendar Service)](#5-交易日历与节假日服务-calendar-service)
    - [5.1 `is_trade_date` - 交易日判定](#51-is_trade_date---交易日判定)
    - [5.2 `get_previous_trading_date` - 历史交易日回溯](#52-get_previous_trading_date---历史交易日回溯)
    - [5.3 `get_latest_settled_trading_date` - 最新已结算交易日](#53-get_latest_settled_trading_date---最新已结算交易日)
  - [6. 专业财务与公告检索服务 (Fundamentals & Announcements)](#6-专业财务与公告检索服务-fundamentals--announcements)
    - [6.1 `get_financial_report` - 解析专业财务数据包](#61-get_financial_report---解析专业财务数据包)
    - [6.2 `get_stock_announcements` - 巨潮资讯权威公告检索](#62-get_stock_announcements---巨潮资讯权威公告检索)
  - [7. Central Hub 远程配置中心接口 (Central Hub Config)](#7-central-hub-远程配置中心接口-central-hub-config)
    - [7.1 `list_etf_configs` - 查询可用 ETF 配置文件列表](#71-list_etf_configs---查询可用-etf-配置文件列表)
    - [7.2 `fetch_etf_config` - 下载指定分类的 ETF YAML 配置文本](#72-fetch_etf_config---下载指定分类的-etf-yaml-配置文本)
  - [8. KV 键值存储与时效性校验 (KV Store & Freshness)](#8-kv-键值存储与时效性校验-kv-store--freshness)
    - [8.1 `KVStore` - 面向对象的命名空间客户端（推荐）](#81-kvstore---面向对象的命名空间客户端推荐)
    - [8.2 全局快捷函数 (`kv_set` / `kv_get` / `kv_head` / `kv_list` / `kv_delete`)](#82-全局快捷函数-kv_set--kv_get--kv_head--kv_list--kv_delete)
  - [9. 基础工具与辅助配置 (Utils & Env)](#9-基础工具与辅助配置-utils--env)
    - [9.1 `init_env` / `get_data_dir` - 缓存环境管理](#91-init_env--get_data_dir---缓存环境管理)
    - [9.2 `get_name_from_code` - 标的中文名反查](#92-get_name_from_code---标的中文名反查)
  - [10. 枚举与常量定义 (Constants & Enums)](#10-枚举与常量定义-constants--enums)
    - [10.1 `Period` - K 线周期枚举](#101-period---k-线周期枚举)
    - [10.2 `DownloadProvider` - 行情下载数据源枚举](#102-downloadprovider---行情下载数据源枚举)
    - [10.3 `MarketIndex` - 核心大盘指数枚举](#103-marketindex---核心大盘指数枚举)
    - [10.4 `EtfConfigCategory` - ETF 资产池分类枚举](#104-etfconfigcategory---etf-资产池分类枚举)
- [💻 第二部分：命令行终端工具体系 (CLI Commands)](#-第二部分命令行终端工具体系-cli-commands)
  - [1. `kdata-download` - K 线数据下载与 TDX 离线直通入库](#1-kdata-download---k-线数据下载与-tdx-离线直通入库)
  - [2. `kdata-quote` - 标的行情、成交额、成交量与市值综合查询](#2-kdata-quote---标的行情成交额成交量与市值综合查询)
  - [3. `kdata-etf` - ETF 筛选、流动性过滤与配置导出](#3-kdata-etf---etf-筛选流动性过滤与配置导出)
  - [4. `kdata-premium` - ETF 折溢价率与 IOPV 分析](#4-kdata-premium---etf-折溢价率与-iopv-分析)
  - [5. `kdata-scan` - ETF/LOF 实时折溢价套利机会扫描器](#5-kdata-scan---etflof-实时折溢价套利机会扫描器)
  - [6. `kdata-market` - 大盘市场概览与多市场指数快照](#6-kdata-market---大盘市场概览与多市场指数快照)
  - [7. `kdata-kv` - KV 存储运维管理工具](#7-kdata-kv---kv-存储运维管理工具)
  - [8. `kdata-serve` - HTTP 数据中心与配置中继服务](#8-kdata-serve---http-数据中心与配置中继服务)
  - [9. 常用极简命令行工作流（以 .day 为主、极少网络）](#9-常用极简命令行工作流以-day-为主极少网络)
- [💡 第三部分：设计原理与最佳实践 (Architecture & Best Practices)](#-第三部分设计原理与最佳实践-architecture--best-practices)

---

## 📖 快速开始与环境要求

* **Python 版本**: Python >= 3.11
* **安装与引用**:
  ```bash
  pip install kdata
  ```
  ```python
  import kdata
  from kdata import get_any_ohlc, KDataError, KDataParamError, KDataFetchError
  ```

---

## ⚠️ 异常体系与最佳实践 (Exception Handling)

`kdata` 提供了结构化、分工明确的自定义异常体系（定义于 `kdata.exceptions`，并通过顶层统一导出），便于调用方精准区分**参数校验与路由问题**与**网络与数据拉取问题**。

为了确保 100% 向后兼容已有业务代码，核心异常均采用**多重继承**设计：

| 异常类 | 继承关系 | 触发场景说明 |
| :--- | :--- | :--- |
| `KDataError` | `Exception` | 所有 `kdata` 业务异常的根基类。 |
| `KDataParamError` | `KDataError`, `ValueError` | 参数非法、周期不支持、代码格式错误等。 |
| `KDataFetchError` | `KDataError`, `RuntimeError` | 网络超时、数据源接口拒绝/限流、标的无数据或所有备选数据源抓取均失败。 |
| `KDataNotFoundError` | `KDataError`, `FileNotFoundError` | 本地缓存文件不存在或指定标的数据无法定位。 |

### 最佳捕获与调用模式：

```python
import kdata
from kdata import get_any_ohlc, KDataParamError, KDataFetchError

# 精准区分参数错误与网络抓取失败
try:
    df = get_any_ohlc("sh.600519", start_date="2024-01-01")
except KDataParamError as e:
    # 处理参数格式或周期不支持等输入错误
    print(f"输入参数错误: {e}")
except KDataFetchError as e:
    # 处理上游网络波动或数据拉取失败
    print(f"数据获取失败: {e}")
```

---

## 📌 数据规范与数据格式说明 (Data Specifications)

本项目统一使用 `pandas.DataFrame` 作为 K 线数据的手持结构，标准列名为 `open`, `high`, `low`, `close`, `volume`，索引 (Index) 为交易日期 `Date` (`DatetimeIndex`)。

### 1. 字段定义表

| 字段名 | 类型 | 说明 |
| :--- | :--- | :--- |
| `Date` | `DatetimeIndex` | 交易日期，级别通常为日 (Daily) 或周 (Weekly)，统一归一化为北京时间 (UTC+8)。 |
| `open` | `float` | 开盘价：该周期内的第一笔成交价格。 |
| `high` | `float` | 最高价：该周期内的最高成交价格。 |
| `low` | `float` | 最低价：该周期内的最低成交价格。 |
| `close` | `float` | 收盘价：该周期的最后一笔成交价格。 |
| `volume` | `float` | 成交活跃度指标（**物理含义随标的资产类型自动适配**，详见下文）。 |

### 2. 关于 `volume` 列物理含义的统一说明

为了保持接口一致性，本库统一使用 `volume` 列名，但其物理含义根据标的自动切换：
1. **个股 (Stocks) & ETF**: `volume` 代表 **成交量 (Trading Volume)**，即成交的股数 (Shares) 或手数 (Lots)。
2. **大盘指数 (Indices)**: `volume` 代表 **成交金额 (Trading Amount / Turnover)**。因为大盘指数无实体发行股数，成交金额更能真实反映全市场的流动性与活跃度。

### 3. 复权规则与分红数据机制 (Adjustment & Dividends)

* **复权规则**: 
  - 个股与 ETF 默认直接返回前复权 (`'qfq'`) 数据，大盘指数使用不复权数据。
  - **Fail-Fast 原则**: 若在复权计算时发生分红除权数据拉取失败或因子对齐异常，系统严禁静默降级为未复权行情并禁止污染本地缓存，将显式抛出 `DividendFetchError` 或 `AdjustmentCalculationError` 专用异常。
* **分红数据机制**:
  - ETF/LOF 前复权依赖 `fund_fh_em_cache.parquet` 分红除息表。
  - 系统内置 7 天分红缓存 TTL，并在检测到除权事件时自动自愈历史 QFQ 缓存，确保历史序列无除权价格断层。
* **本地缓存**: 历史行情数据自动落地 Parquet（兼容历史 CSV）本地缓存。缓存命中时不发起重复网络请求，避免受网络波动或频控影响。

---

## 🏷️ 标的代码命名规范 (Symbol Formats)

| 资产类别 | 代码格式示例 | 说明 |
| :--- | :--- | :--- |
| **A股 股票/指数** | `sh.600519`, `sz.000001`, `sh.000300` | `sh.` / `sz.` + 6 位代码 |
| **港股 股票/指数** | `hk.00700`, `hk.HSI` | `hk.` + 5 位代码或指数缩写 |
| **美股 股票/指数** | `us.AAPL`, `us.GSPC` | `us.` + 股票代码或指数缩写 |
| **场内 ETF / LOF** | `510300`, `513100`, `161129` | 6 位基金代码 |

---

## 📊 第一部分：Python SDK 核心 API

### 1. 行情与市场数据 (Market Data & Indices)

#### 1.1 `get_any_ohlc` - 自适应统一获取 K 线数据（核心推荐入口）

**描述**: `kdata` 推荐的核心统一入口。根据传入的标的代码（个股、ETF、大盘指数）或 `MarketIndex` 枚举，系统在底层自动识别标的类型并安全派发：
- **个股与 ETF 标的**：自动路由至个股存储引擎（`data/{market}/stocks/` 或 `etfs/`），默认进行前复权 (`qfq`) 处理；
- **大盘指数标的**：自动路由至指数独立存储引擎（`data/indices/`），不复权并保持点位真实历史轨迹。

> [!WARNING]
> **关于 `volume` 列物理含义的统一说明（关键）**：
> - **个股 (Stocks) & ETF**：`volume` 列代表**成交量 (Trading Volume)**，即成交的股数 (Shares) 或份数/手数 (Lots)。
> - **大盘指数 (Indices)**：`volume` 列代表**全市场成交金额 (Trading Amount / Turnover)**（单位：元/各市场货币）。因为大盘点位本身无实体股数，成交金额更能客观衡量全市场的流动性与活跃度。

**代码示例**:
```python
import kdata
from kdata import get_any_ohlc, MarketIndex, Period

# 1. 获取个股日 K 线（自动识别为个股，前复权）
df_stock = get_any_ohlc("sh.600519", start_date="2024-01-01", period=Period.DAILY)
print(df_stock.head())

# 2. 获取场内 ETF 日 K 线（自动识别为 ETF）
df_etf = get_any_ohlc("510300", start_date="2024-01-01")

# 3. 获取大盘指数日 K 线（自动识别为指数，隔离存储并保留真实点位）
df_index = get_any_ohlc("sh.000300", start_date="2024-01-01")

# 4. 使用 MarketIndex 枚举获取核心指数（支持 IDE 补全与防错）
df_sh   = get_any_ohlc(MarketIndex.SH, start_date="2024-01-01")    # 上证指数
df_hsi  = get_any_ohlc(MarketIndex.HSI, start_date="2024-01-01")   # 恒生指数
df_spx  = get_any_ohlc(MarketIndex.SP500, start_date="2024-01-01") # 标普500
```

**参数说明**:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|------|------|
| `symbol` | str / MarketIndex | **是** | - | 标的代码（如 `'sh.600519'`, `'510300'`, `'sh.000300'`, `'hk.HSI'`, `'us.AAPL'`）或 `MarketIndex` 枚举 |
| `start_date` | str / datetime | 否 | `None` | 开始日期 (YYYY-MM-DD)，默认自动推导为 2023-01-01 或环境配置值 |
| `end_date` | str / datetime | 否 | `None` | 结束日期 (YYYY-MM-DD)，未指定则按所属市场取最新逻辑结算日 |
| `period` | Period / str | 否 | `Period.DAILY` | K线周期，支持日K (`Period.DAILY` / `'d'`) 或周K (`Period.WEEKLY` / `'w'`) |
| `**kwargs` | Any | 否 | - | 可选关键字参数（如 `skip_central_http=True` 跳过数据中心远程拉取） |

**返回值说明**: `pandas.DataFrame`
- **索引 (`Index`)**: `Date` (`DatetimeIndex`，标准化为北京时间 UTC+8)
- **列 (`Columns`)**: `open`, `high`, `low`, `close`, `volume`
- **底层存储**: 个股/ETF 存入 `K_DATA_CENTER/{start}_{end}/`，指数存入 `K_DATA_CENTER/indices/`

---

#### 1.2 `generate_weekly_kdata` - 日K转换为周K数据

**描述**: 将日 K 线 DataFrame 自动重采样并合成为符合标准的周 K 线 DataFrame。

**代码示例**:
```python
from kdata import get_any_ohlc, generate_weekly_kdata, Period

daily_df = get_any_ohlc('sh.600519', start_date='2023-01-01', period=Period.DAILY)
weekly_df = generate_weekly_kdata(daily_df)
```

**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `df` | pandas.DataFrame | **是** | 含有 `open`, `high`, `low`, `close`, `volume` 列及 `Date` 索引的日 K 线数据 |

**返回值说明**: `pandas.DataFrame`（周K线结构，按每周最后一个交易日聚合并重采样）

---

#### 1.3 `get_market_breadth` - 市场广度指标

**描述**: 获取 A 股全市场多空家数与涨跌停历史统计数据。

**代码示例**:
```python
from kdata import get_market_breadth

df = get_market_breadth(days=5)
print(df)
```

**参数说明**:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|------|------|
| `days` | int | 否 | `5` | 回溯的历史交易天数 |

**返回值说明**: `pandas.DataFrame`
- **列 (`Columns`)**: `日期`, `上涨家数`, `下跌家数`, `涨停家数`, `跌停家数`

---

#### 1.4 `get_sector_ranking` - 行业板块排名

**描述**: 获取全市场行业板块领涨/领跌排名数据。

**代码示例**:
```python
from kdata import get_sector_ranking

# 返回元组: (领涨 TOP N 板块 DataFrame, 领跌 TOP N 板块 DataFrame)
top_sectors, bottom_sectors = get_sector_ranking(top_n=10)
```

**参数说明**:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|------|------|
| `top_n` | int | 否 | `10` | 提取的前 N 个领涨/领跌板块数量 |

**返回值说明**: 包含两个 `pandas.DataFrame` 的元组 `(top_sectors, bottom_sectors)`
- **列 (`Columns`)**: `板块名称`, `涨跌幅%`, `上涨家数`, `下跌家数`, `领涨股票`, `领涨股票涨幅%`

---

#### 1.5 `get_market_overview` - 市场宏观全貌快照

**描述**: 一站式拉取并汇总包含核心指数、行业板块排名、涨跌停、两融余额、市场情绪、全球主要指数的完整宏观快照字典，自动落盘存储于 `data/market/` 目录。

**代码示例**:
```python
from kdata import get_market_overview

overview = get_market_overview(include_global=True, include_hk=True)
print("包含宏观维度:", list(overview.keys()))
```

---

#### 1.6 `get_index_history` - 核心指数近期多日行情

**描述**: 获取主要指数近 N 个交易日的历史行情序列字典。

**代码示例**:
```python
from kdata import get_index_history

hist = get_index_history(days=10, include_global=True)
print(hist["上证指数"].tail())
```

---

#### 1.7 `get_cn_indices` / `get_hk_indices` / `get_global_indices` - 大盘行情快照

**描述**: 快速拉取国内、港股、全球主要大盘指数的最新行情快照 DataFrame。

```python
from kdata import get_cn_indices, get_hk_indices, get_global_indices

cn_df = get_cn_indices()        # 国内主要指数 DataFrame
hk_df = get_hk_indices()        # 港股核心指数 DataFrame
global_df = get_global_indices()# 全球核心指数 DataFrame
```

---

### 2. ETF 资产规模与筛选 (ETF Scale & Universe)

#### 2.1 `get_etf_scale` - ETF 资产规模查询

**描述**: 获取 ETF 资产管理规模数据（单位：亿元）。

**代码示例**:
```python
from kdata import get_etf_scale

# 1. 单个代码：返回 float
scale = get_etf_scale('513120')                 # 返回: 114.2 

# 2. 代码列表：返回 {code: scale_float} 字典
scales_dict = get_etf_scale(['513120', '513100'])
```

**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `symbol` | str / list / tuple / set | **是** | 单个 6 位 ETF 代码字符串，或 ETF 代码容器列表 |

**返回值说明**: 传入单个字符串返回 `float`（规模，亿元）；传入列表等容器返回 `Dict[str, float]`。

---

#### 2.2 `get_filtered_etfs` - ETF 条件筛选与指标过滤

**描述**: 基于本地 ETF 缓存快速根据规模、成交额、换手率、折溢价率等多维条件进行筛选与排序。

**代码示例**:
```python
from kdata.etf_cli import get_filtered_etfs

# 筛选规模 >= 50 亿且按成交额排序的前 20 支 ETF
top_etfs = get_filtered_etfs(min_scale=50, sort_by="amount", ascending=False)[:20]
for item in top_etfs:
    print(item["code"], item["name"], item["scale_yi"], item["turnover_rate"])
```

**参数说明**:
| 参数 | 类型 | 默认值 | 说明 |
|------|------|------|------|
| `input_path` | str / None | `None` | 输入 YAML 配置文件或标的池路径（未指定则基于全市场） |
| `sort_by` | str | `"scale"` | 排序字段：`"scale"` (规模), `"amount"` (成交额), `"turnover"` (换手率), `"premium"` (折溢价率) |
| `ascending` | bool | `False` | 是否升序（默认降序） |
| `min_scale` / `max_scale` | float / None | `5.0` / `None` | 资产规模区间（单位：亿元） |
| `min_vol` / `max_vol` | float / None | `0.5` / `None` | 单日成交额区间（单位：亿元） |
| `min_turnover` / `max_turnover` | float / None | `None` / `None` | 换手率区间 (%) |
| `min_premium` / `max_premium` | float / None | `None` / `None` | 折溢价率区间 (%) |

---

### 3. ETF/LOF 折溢价套利组件 (Arbitrage & Premium)

#### 概念解析
* **IOPV (实时参考净值)**: 盘中由交易所实时计算并发布的基金份额参考净值（每 15 秒更新）。
* **NAV (单位净值)**: 基金公司官方公布的每股净资产值。
* **折溢价率%**: 计算公式为 `(价格 - 净值) / 净值 * 100`。
* **LOF(S) 说明**: S 代表 Stale (过期/上日净值)，表示盘中无实时 IOPV 估值，使用前一日净值参考。

---

#### 3.1 `etf_premium` - ETF 实时折溢价率一键计算（推荐主接口）

**描述**: 单只与批量通用的 ETF / LOF 实时折溢价率核心接口。优先采用交易所每 15 秒更新的即时参考净值（IOPV）作为分母基准（彻底解决美股 QDII 跨时区滞后 1~2 天导致的溢价失真问题）。
- **查单只 ETF**：传入代码字符串，自动返回轻量级结构化字典；
- **批量查多只 ETF**：传入代码列表，底层自动以 60 只为一组打包发起单次聚合请求（避免全市场逐只查询被反爬），并按溢价率降序排列输出为标准 DataFrame。

**代码示例**:
```python
import kdata

# 1. 查单只 ETF（自适应返回结构化字典）
info = kdata.etf_premium("159513")
print(info)
# {'code': '159513', 'price': 1.821, 'iopv': 1.6579, 'nav': 1.63, 'basis': 'IOPV', 'premium_rate': 9.84}

# 2. 批量查多只 ETF（自适应返回降序宽表 DataFrame）
df = kdata.etf_premium(["513100", "159513", "510300", "159915"])
print(df)
#      code  price    iopv    nav basis  premium_rate
# 0  513100  2.269  1.9839  1.952  IOPV         14.37
# 1  159513  1.821  1.6579  1.630  IOPV          9.84
# 2  510300  4.582  4.5791  4.551  IOPV          0.06
# 3  159915  3.391  3.3909  3.331  IOPV          0.00
```

**参数说明**:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| :--- | :--- | :--- | :--- | :--- |
| `symbols` | `str` \| `list[str]` | **是** | - | 单只基金代码（如 `'159513'`）或基金代码列表（如 `['513100', '159513']`） |
| `client` | `Quotes` 实例 | 否 | `None` | 可选传入已建立连接的 Quotes 实例复用连接池；默认自动管理 |
| `basis` | `str` | 否 | `'IOPV'` | 优先基准：`'IOPV'`（默认，盘中参考净值）或 `'NAV'`（官方披露净值） |

**字段说明**:
- `price`: 二级市场最新撮合成交价
- `iopv`: 交易所实时参考净值（盘中每 15 秒更新）
- `nav`: 基金公司最新披露的官方每股净资产
- `basis`: 本次计算采纳的分母口径（优先为 `'IOPV'`，若无参考净值则平滑回退为 `'NAV'`）
- `premium_rate`: 实时折溢价率（%，如 `9.84` 表示 `+9.84%`）

---

#### 3.2 `Scanner` - ETF/LOF 溢价监控扫描器

**描述**: 批量抓取 ETF/LOF 实时价格与净值以计算折溢价率，支持一键筛选 Top 溢价/深折价标的及场内赎回费率提取。

**代码示例**:
```python
from kdata import Scanner  # 或 from kdata.scanner import Scanner

# 1. 初始化扫描器 (client 可选，默认自动创建 Quotes.factory(market="std"))
scanner = Scanner(f10_workers=12)

# 2. 加载资金池 (支持 YAML 配置文件解析)
etf_universe = Scanner.load_universe('data/etf/config_etf.yaml')

# 3. 执行实时折溢价扫描
scan_df = scanner.scan(etf_universe)

# 4. 批量补充场内赎回费率
scan_df = scanner.add_fees(scan_df)
print(scan_df.head())

# 5. 快捷获取 Top 10 溢价/折价标的 (自动异步补全 F10 标的/官方/赎回费)
all_df, top_premium, top_discount = scanner.scan_prospects(
    etf_universe, top_n=10, simple=False
)
```

**`Scanner` 构造函数与方法说明**:
| 方法/属性 | 参数类型 | 返回值 | 说明 |
| :--- | :--- | :--- | :--- |
| `Scanner(client=None, f10_workers=12)` | `client`: Quotes 实例<br>`f10_workers`: int | `Scanner` | 创建扫描器引擎。`f10_workers` 设置 F10 并行下载线程数（0 表示禁用预取）。 |
| `Scanner.load_universe(yaml_path)` | `yaml_path`: str | `list[dict]` | 从 YAML 文件解析加载 ETF/LOF 资金池。返回 `[{"code": "510300", "name": "300ETF"}, ...]` |
| `scanner.scan(etf_universe)` | `etf_universe`: list[dict] | `DataFrame` | 对资金池标的执行实时报价与净值扫描，计算溢价率。 |
| `scanner.add_fees(df)` | `df`: DataFrame | `DataFrame` | 从 F10 解析场内赎回费规则并补充 `'赎回费'` 列（就地修改并返回）。 |
| `scanner.scan_prospects(...)` | `top_n`: int<br>`simple`: bool<br>`min_discount_pct`: float | `tuple[DF, DF, DF]` | 一键返回 `(全量数据, Top溢价表, Top折价表)`，内部自动提取并补充 F10 信息。 |

**`scan` 返回的 DataFrame 列结构**:
| 列名 | 说明 |
| :--- | :--- |
| `代码` | 证券代码 (如 `'513100'`) |
| `名称` | 基金简称 (如 `'纳指100ETF'`) |
| `价格` | 盘口实时现价 |
| `净值` | IOPV 估算净值或上日官方净值 |
| `溢价率%` | 实时折溢价率 (%) |
| `标的` | 跟踪标的指数名称 |
| `官方` | 是否为官方/标准指数 (`'是'`, `'否'`, `'未知'`) |
| `时间` | 报价更新时间戳 (`'HH:MM:SS'`) |
| `类型` | `'ETF'`, `'LOF'`, `'LOF(S)'` |
| `T+0` | 是否支持 T+0 交易 (`'是'`, `'否'`) |
| `赎回费` | 场内赎回费率规则 (调用 `add_fees` 追加) |

---

#### 3.3 `get_etf_premium_data` - ETF 估算溢价率数据序列

**描述**: 获取指定 ETF 历史价格及最新估算净值的溢价率数据序列。支持本地优先（Local-First）与纯离线（Offline）模式。

**代码示例**:
```python
from kdata import get_etf_premium_data

# 1. 默认模式（优先本地 .day 文件与本地缓存，必要时在线补齐净值）
df = get_etf_premium_data(
    symbol='513100', 
    start_date='2026-07-01', 
    end_date='2026-07-24'
)
print(df.tail())

# 2. 纯离线模式（零网络请求：K 线直读本地 .day 二进制文件，净值走本地离线缓存）
df_offline = get_etf_premium_data(
    symbol='513100',
    start_date='2026-07-01',
    end_date='2026-07-24',
    allow_network=False,    # 开启纯离线
    prefer_local=True,
)
print(df_offline.tail())
```

**参数说明**:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|------|------|
| `symbol` | str | **是** | - | ETF / LOF 代码，如 `'513100'`, `'513120'` |
| `start_date` | str | **是** | - | 开始日期 `'YYYY-MM-DD'` |
| `end_date` | str | **是** | - | 结束日期 `'YYYY-MM-DD'` |
| `use_cache` | bool | 否 | `True` | 是否使用本地缓存，避免重复拉取 |
| `skip_central_http` | bool | 否 | `False` | 是否跳过中心数据服务（为 True 时仅使用本地） |
| `prefer_local` | bool \| None | 否 | `None` | 是否优先本地计算。为 None 时由环境变量 `KDATA_CN_PREFER_LOCAL` 决定（默认 True） |
| `allow_network` | bool | 否 | `True` | 是否允许发起外部网络请求。为 `False` 时进入**纯离线模式**，K 线直读本地通达信 `.day` 文件，净值自动从本地 `premium_cache` 或 `etf_cache.json` 回退，零网络请求 |

**返回值与列说明**: `pandas.DataFrame`
- **索引 (`Index`)**: `Date` (`DatetimeIndex`)
- **列 (`Columns`)**:
  - `close`: 每日收盘价 (`float`)
  - `NAV(IOPV_时间)`: 单位净值/IOPV（列名含时间戳后缀，如 `NAV(IOPV_15:30:00)`）
  - `premium`: 绝对折溢价差额 = `close - NAV` (`float`)
  - `premium_rate`: 折溢价率(%) = `(premium / NAV) * 100` (`float`)

---

#### 3.4 `get_latest_etf_premium` - 单只 ETF 最新溢价快照

**描述**: 获取单只 ETF 实时最新价格、净值和折溢价快照字典。支持纯离线快照查询。

**代码示例**:
```python
from kdata import get_latest_etf_premium

# 1. 默认查询
info = get_latest_etf_premium('513100')
print(info)

# 2. 纯离线查询（零网络请求）
info_offline = get_latest_etf_premium('513100', allow_network=False)
print(info_offline)
```

**参数说明**:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|------|------|
| `symbol` | str | **是** | - | ETF / LOF 代码，如 `'513100'`, `'513120'` |
| `use_cache` | bool | 否 | `True` | 是否使用当日已有的本地缓存 |
| `skip_central_http` | bool | 否 | `False` | 是否跳过数据中心远程拉取 |
| `prefer_local` | bool \| None | 否 | `None` | 是否优先本地行情，默认 True |
| `allow_network` | bool | 否 | `True` | 为 `False` 时进入纯离线模式，从本地离线数据中读取最新快照 |

**返回字典字段解析 (`dict`)**:
```python
{
    'code': '513100',              # 证券代码 (str)
    'name': '纳指100ETF',          # 基金简称 (str)
    'price': 2.104,                # 实时盘口现价 (float)
    'nav': 2.085,                  # 单位净值/IOPV (float 或 None)
    'premium_rate': 0.911,         # 实时折溢价率 % (float 或 None)
    'updated': '2026-07-24'        # 净值更新日期/时间戳 (str 或 None)
}
```

---

### 4. 标的行情与市值综合查询 (Quote & Market Cap)

#### 4.1 `get_quote` - 标的行情、成交额、成交量与市值综合查询

**描述**: 基于通达信生态（本地 `.day` 离线二进制文件与 `mootdx2` 在线通道），查询指定标的的最新行情、交易所真实撮合成交额、成交量、市值及换手率。

* **优先离线直读**: 默认优先检查本地通达信目录（`vipdoc`）中的 `.day` 文件，秒级直读最新已结算日线，零网络开销；
* **字段严格正交**:
  - **`amount`（成交额，元）**: 交易所真实撮合成交额，绝不走估算。大盘指数与个股均具备该指标。
  - **`volume`（成交量，股）**: 统一以“股”为基准单位（个股/ETF 统一由手折算为 $\times 100$ 股）。**大盘指数绝无“股”的属性，严格返回 `NaN`，绝不混淆**。
  - **`total_cap` / `float_cap`（总市值 / 流通市值，元）**: 基于通达信财务股本结合价格计算。大盘指数不适用返回 `NaN`。
  - **`turnover`（换手率，%）**: 基于成交股数 $\div$ 流通股数计算。

```python
import kdata

# 支持单个或批量标的查询（股票、ETF、指数）
df = kdata.get_quote(["510050", "sh.000001", "600519"], prefer_offline=True)
print(df[["code", "name", "price", "amount", "volume", "total_cap", "float_cap", "source"]])
```

**参数说明**:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| :--- | :--- | :--- | :--- | :--- |
| `symbols` | `str` / `list[str]` | **是** | - | 标的代码或列表（如 `'600519'`, `'510050'`, `'sh.000300'`） |
| `prefer_offline` | `bool` | 否 | `True` | 是否优先读取本地通达信 `.day` 离线文件。设为 `False` 时强制走 `mootdx2` 在线行情通道 |

**返回值说明**: `pd.DataFrame`，包含 `code`, `name`, `price`, `change_pct`, `amount`, `volume`, `total_cap`, `float_cap`, `turnover`, `date`, `source` 等字段。

---

### 5. 交易日历与节假日服务 (Calendar Service)

提供基于交易所真实开休市规则与法定节假日的交易日裁决与自动向前回溯能力。

#### 5.1 `is_trade_date` - 交易日判定

**描述**: 判定指定日期是否为指定市场的开盘交易日。自动过滤周末并精确识别国务院公布的元旦、春节、清明、劳动节、端午、中秋、国庆等法定休市安排。

```python
from kdata import is_trade_date

# 检查指定日期是否为 A 股交易日
is_open = is_trade_date("2024-10-01", market="cn")  # 返回: False (国庆节休市)
is_open_today = is_trade_date()                     # 默认检测当天是否开盘
```

#### 5.2 `get_previous_trading_date` - 历史交易日回溯

**描述**: 获取严格早于指定基准日期的最近一个法定交易日。自动跳过周末与连续法定休市长假。

```python
from kdata import get_previous_trading_date

# 获取国庆假期前最后一个交易日
prev_date = get_previous_trading_date("2024-10-08", market="cn")  # 返回: '2024-09-30'
```

#### 5.3 `get_latest_settled_trading_date` - 最新已结算交易日

**描述**: 获取逻辑上最新的已结算交易日。综合考量市场结算时间点（如 A 股 16:00 CST、港股 17:00 CST、美股 21:00 UTC）与休市日历。

```python
from kdata import get_latest_settled_trading_date

settled_date = get_latest_settled_trading_date("cn")
```

---

### 6. 专业财务与公告检索服务 (Fundamentals & Announcements)

#### 6.1 `get_financial_report` - 解析专业财务数据包

**描述**: 解析通达信本地专业财务 `.zip` 压缩包或解压后的 `.dat` 文件，覆盖 580+ 专业财务科目，原生支持中英文表头映射。

```python
from kdata import get_financial_report

# 解析本地财报并使用中文表头
df_fin = get_financial_report("data/financial/gpcw20240630.zip", header="zh")
if not df_fin.empty:
    print(df_fin[["基本每股收益", "每股净资产", "净资产收益率", "营业收入", "净利润"]])
```

#### 6.2 `get_stock_announcements` - 巨潮资讯权威公告检索

**描述**: 基于标准 HTTP 接口实时检索上市公司官方披露公告，支持全市场股票（含主板、科创板、创业板），提供标题、类型、日期及原始 PDF 附件直链。

```python
from kdata import get_stock_announcements

# 获取贵州茅台最近 10 条官方公告
df_ann = get_stock_announcements("600519", count=10, page=1)
if not df_ann.empty:
    for _, row in df_ann.iterrows():
        print(f"[{row['date']}] {row['title']} -> {row['pdf_url']}")
```

---

### 7. Central Hub 远程配置中心接口 (Central Hub Config)

#### 7.1 `list_etf_configs` - 查询可用 ETF 配置文件列表

**描述**: 从配置的 Central Hub 数据中心服务查询当前可用且已就绪的 ETF 资产池分类列表及元数据信息。

```python
from kdata import list_etf_configs

# 查询远程中心可用的分类列表
configs = list_etf_configs()
for item in configs:
    print(f"分类: {item.get('category')}, 文件名: {item.get('filename')}, 存在: {item.get('exists')}")
```

**参数说明**:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| :--- | :--- | :--- | :--- | :--- |
| `timeout` | float | 否 | `None` | HTTP 超时秒数，默认读取环境变量 `KDATA_CENTRAL_TIMEOUT` 或 30 秒 |

**返回值说明**: `list[dict[str, Any]]`，每个字典包含 `category`, `filename`, `exists`, `size_bytes`, `updated_at` 等元数据字段。

---

#### 7.2 `fetch_etf_config` - 下载指定分类的 ETF YAML 配置文本

**描述**: 从 Central Hub 远程拉取指定市场分类的 ETF 资产池 YAML 配置文件原始内容（字符串），可直接用于解析或写入本地配置文件。

```python
import yaml
from kdata import fetch_etf_config, EtfConfigCategory

# 1. 使用枚举或字符串下载境内 ETF 配置清单
yaml_text = fetch_etf_config(EtfConfigCategory.CN)  # 或 fetch_etf_config("cn")

# 2. 解析为 Python 数据结构
universe = yaml.safe_load(yaml_text)
print("标的列表:", universe.get("etf", [])[:5])

# 3. 也可按需拉取海外跨境与美股标的池
overseas_yaml = fetch_etf_config("overseas")
us_yaml = fetch_etf_config("us")
```

**参数说明**:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| :--- | :--- | :--- | :--- | :--- |
| `category` | EtfConfigCategory / str | **是** | - | 市场分类：`EtfConfigCategory.CN` (`"cn"`), `CN_LARGE` (`"cn_large"`), `OVERSEAS` (`"overseas"`), `US` (`"us"`) |
| `timeout` | float | 否 | `None` | HTTP 超时秒数，未指定时读取环境变量 `KDATA_CENTRAL_TIMEOUT` (默认 30s) |

**返回值说明**: `str`，YAML 文件的原始内容文本。若中心未配置或请求失败将抛出 `KDataFetchError`。

---

### 8. KV 键值存储与时效性校验 (KV Store & Freshness)

为外部量化投研系统、实盘交易策略及定时更新脚本提供中心化、高可靠、轻量级、具备**严格交易日时效校验**与**历史快照回溯**的 KV 数据持久化通道。

#### 核心设计与业务特性
* **两段式命名空间**: 存储路径为 `$K_DATA_CENTER/kv/{namespace}/`（默认 `./data/kv/{namespace}/`），实现不同策略、任务与标的池之间的物理隔离。
* **业务生效日期盖戳 (`data_date`)**: 专为量化场景中**每日变动数据**设计，写入数据时自动关联交易日（北京时间 `YYYY-MM-DD`）。
* **防读老数据 (Stale Defense)**:
  * 消费端可指定 `date="YYYY-MM-DD"` 强制匹配业务日期；
  * 可指定 `min_date="YYYY-MM-DD"` 设定最小日期容忍阈值；
  * 若上游数据流当日尚未就绪或早于要求，服务端与客户端均触发**强阻断（抛出 `DATA_NOT_FRESH` 异常 / 返回 HTTP 404）**，严防实盘系统误用昨天甚至更早的陈旧信号下单。
* **历史快照自动归档与最新指针自愈**:
  * 写入带日期的数据时，自动留存不可篡改的历史快照切片（`{key}@{date}.data`）；
  * 仅当新传入数据日期 $\ge$ 当前最新指针日期时才推进最新视图（`{key}.data`），补录过去历史绝不污染实盘正在读取的最新指针；
  * 若仅删除某天的快照，最新指针会自动定位次新快照回退自愈。
* **无锁并发原子写入**: 写操作先写入随机临时文件，刷盘并 `fsync` 后调用 `os.replace` 原子覆写，读取方零读锁，杜绝意外中断造成数据损坏。
* **白名单安全**: `namespace` 和 `key` 严格限制只允许字母、数字、下划线、中划线和单点（正则 `^[a-zA-Z0-9_\-\.]+$`），严禁连续点 `..` 与斜杠，单条 Payload 上限 10MB。

---

#### 8.1 `KVStore` - 面向对象的命名空间客户端（推荐）

```python
import kdata
from kdata.exceptions import KDataFetchError

# 初始化特定命名空间的存储客户端
store = kdata.KVStore("alpha_signals")

# 1. 写入今日调仓信号（dict 自动转 JSON 并设置 application/json，date 默认当天）
store.set("daily_weights", {"600519": 0.4, "000001": 0.6}, date="2026-09-18")

# 2. 实盘消费端：强校验必须是今天（2026-09-18）的数据
try:
    weights = store.get("daily_weights", date="2026-09-18")  # 自动反序列化为 dict
    print("今日最新权重:", weights)
except KDataFetchError as e:
    # 若今日信号尚未生成，抛出异常阻断，绝不使用昨天的老数据
    print("今日数据未就绪，阻断执行:", e)

# 3. 容忍度读取：只要数据不早于指定日期即可
weights = store.get("daily_weights", min_date="2026-09-15")

# 4. 回测复盘：调阅过去某天的不可变历史快照
history_weights = store.get("daily_weights", date="2026-09-10")

# 5. 存储任意类型载荷
store.set("strategy_doc", "这是策略说明文本", date="2026-09-18") # 纯文本
store.set("model_weights", b"\x00\x01\x02")                   # 二进制 Blob

# 6. 元数据探活与列表查询
meta = store.head("daily_weights")
print("最新生效日期:", meta["data_date"], "ETag:", meta["etag"])

keys_list = store.list()                     # 默认紧凑视图（仅列出各 Key 及其最新日期）
all_snaps = store.list(include_history=True) # 展开所有历史切片

# 7. 删除
store.delete("daily_weights", date="2026-09-10") # 仅删除某天历史快照（最新指针自动自愈）
store.delete("daily_weights")                    # 级联删除该 Key 及其全部历史
```

#### 8.2 全局快捷函数 (`kv_set` / `kv_get` / `kv_head` / `kv_list` / `kv_delete`)

```python
import kdata

# 快速存取
kdata.kv_set("global_config", "risk_limit", {"max_drawdown": 0.08})
config = kdata.kv_get("global_config", "risk_limit")

# 探活、列表与删除
meta = kdata.kv_head("global_config", "risk_limit")
items = kdata.kv_list("global_config", prefix="risk_")
kdata.kv_delete("global_config", "risk_limit")
```

> [!NOTE]
> **网络与本地透明降级**：
> - 若环境变量配置了 `KDATA_CENTRAL_URL`，SDK 自动向数据中心服务发起 HTTP 请求；
> - 若未配置 `KDATA_CENTRAL_URL`，SDK 自动无缝降级为本地直接读写（`$K_DATA_CENTER/kv/`），单机离线运行 100% 可用。

---

### 9. 基础工具与辅助配置 (Utils & Env)

#### 9.1 `init_env` / `get_data_dir` - 缓存环境管理

**描述**: 管理 `kdata` 本地缓存数据目录与环境初始化。

```python
from kdata import init_env, get_data_dir

data_dir = get_data_dir()  # 返回缓存目录路径，默认: ~/.kdata
init_env(force=True)       # 强制重置并重建本地缓存索引结构
```

---

#### 9.2 `get_name_from_code` - 标的中文名反查

**描述**: 通过代码反查股票、ETF 或大盘指数的官方中文名称。

```python
from kdata import get_name_from_code

name = get_name_from_code('sh.600519')  # 返回: '贵州茅台'
```

---

### 10. 枚举与常量定义 (Constants & Enums)

#### 10.1 `Period` - K 线周期枚举
```python
from kdata import Period

Period.DAILY   # 日线周期 ('d')
Period.WEEKLY  # 周线周期 ('w')
```

#### 10.2 `DownloadProvider` - 行情下载数据源枚举
```python
from kdata import DownloadProvider

DownloadProvider.AUTO  # 默认自动模式（系统自动调度并在多数据源间无感降级备份，外部调用推荐使用此项）
```

#### 10.3 `MarketIndex` - 核心大盘指数枚举
`MarketIndex` 枚举整合了跨市场的核心基准指数，传入 `get_any_ohlc` 时具备代码防错与 IDE 智能补全支持：

| 市场 | 枚举成员 | 中文名称 (枚举值) | 映射标准代码 | 资产代表说明 |
| :--- | :--- | :--- | :--- | :--- |
| **A股** | `MarketIndex.SH` | `"上证指数"` | `sh.000001` | 上证综合指数 |
| | `MarketIndex.SZ` | `"深证成指"` | `sz.399001` | 深证成份指数 |
| | `MarketIndex.CYB` | `"创业板指"` | `sz.399006` | 创业板指 |
| | `MarketIndex.KC50` | `"科创50"` | `sh.000688` | 上证科创板50成份指数 |
| | `MarketIndex.KCZZ` | `"科创综指"` | `sh.000680` | 上证科创板综合指数 |
| | `MarketIndex.HS300` | `"沪深300"` | `sh.000300` | 沪深300指数 |
| | `MarketIndex.ZZ500` | `"中证500"` | `sh.000905` | 中证500指数 |
| **港股** | `MarketIndex.HSI` | `"恒生指数"` | `hk.HSI` | 恒生指数 |
| | `MarketIndex.HSTECH` | `"恒生科技指数"` | `hk.HSTECH` | 恒生科技指数 |
| **美股** | `MarketIndex.SP500` | `"标普500"` | `us.SPY` | 标普500指数 |
| | `MarketIndex.NASDAQ` | `"纳斯达克"` | `us.QQQ` | 纳斯达克100指数 |
| | `MarketIndex.DJI` | `"道琼斯"` | `us.DIA` | 道琼斯工业平均指数 |

**调用示例**:
```python
from kdata import MarketIndex, get_any_ohlc

# 使用枚举直接获取指数历史 K 线
df_sh = get_any_ohlc(MarketIndex.SH, start_date="2024-01-01")
df_hsi = get_any_ohlc(MarketIndex.HSI, start_date="2024-01-01")
df_spx = get_any_ohlc(MarketIndex.SP500, start_date="2024-01-01")
```

#### 10.4 `EtfConfigCategory` - ETF 资产池分类枚举
```python
from kdata import EtfConfigCategory

EtfConfigCategory.CN        # "cn" (境内核心 ETF)
EtfConfigCategory.CN_LARGE  # "cn_large" (国内百亿大市值核心 ETF)
EtfConfigCategory.OVERSEAS  # "overseas" (跨境海外 ETF)
EtfConfigCategory.US        # "us" (美股核心 ETF)
```

---

## 💻 第二部分：命令行终端工具体系 (CLI Commands)

系统附带开箱即用的终端命令行工具，安装包后可直接在命令行调用，无需编写 Python 代码。

### 1. `kdata-download` - K 线数据下载与 TDX 离线直通入库

`kdata-download` 默认开启**通达信离线直通模式 (`--offline`)**，优先直接多进程并发读取本地通达信目录（如 `~/new_tdx/vipdoc`）的 `.day` 二进制行情文件，秒级解析并入库至本地 Parquet/CSV 缓存；若本地缺失 `.day` 文件，默认支持连网自动补齐下载生成 `.day` 文件。

**参数说明：**
- `-b`, `--base`: 目录或 YAML 配置文件路径（扫描并加载标的代码列表批量入库）
- `--offline`: 启用本地通达信离线直通模式（**默认已启用**），直读本地 `.day` 文件
- `--allow-network`: 离线模式下若本地缺失 `.day`，自动在线补齐下载 `.day` 文件（**默认已开启**）
- `--no-network`: 离线模式下严禁连网（本地缺失 `.day` 时直接报错，不尝试在线补齐）
- `--online`: 强制走在线网络接口下载模式（禁用默认的通达信离线直通模式）
- `--tdx-dir`: 本地通达信安装目录（不指定时优先读取环境变量 `MOOTDX2_TDX_DIR`，支持自动探测 `~/new_tdx`）
- `--workers`: 离线批量模式下的多进程并发 Worker 数量（默认为 4）

##### 实测效果：完全满足“以 .day 为主、极少网络”的需求

针对海外跨境 ETF 配置清单 `data/etf/overseas.yaml`：
```bash
uv run kdata-download -b data/etf/overseas.yaml
```

运行效果（零外部网络，4 进程并发直读本地 `/Users/hy/new_tdx` 的 `.day` 文件）：
```text
[kdata] 数据中心存储目录: /Users/hy/kdata_data
[kdata] 共 25 只标的，日期范围 2023-01-01 ~ 2026-09-17
[离线直通模式] 激活通达信目录: /Users/hy/new_tdx
[离线直通模式] 目标数据中心: /Users/hy/kdata_data
[离线直通模式] 并发 Worker 数量: 4, 待同步标的数: 25
DailyDataManager 启用地表最快【纯离线模式】，本地通达信目录: /Users/hy/new_tdx
  [1/25] 159502 成功: 成功写入 652 条 K 线至 etf.159502_*.parquet
  ...
  [25/25] 513880 成功: 成功写入 800 条 K 线至 etf.513880_*.parquet
[离线直通模式] 同步完毕: 成功 25 只, 失败 0 只
```
**整整 25 只跨境 ETF 全部从本地 `.day` 文件读取入库，全程仅耗时 1.5 秒！**

**常规使用示例：**
```bash
# 单只股票/ETF K线下载 (指定日期范围)
kdata-download sh.600519 2024-01-01 2024-12-31

# 单只指数下载
kdata-download sh.000300 2024-01-01 2024-12-31

# 根据 YAML 配置文件批量解析入库（默认秒级离线直读）
kdata-download -b data/etf/overseas.yaml
kdata-download -b data/etf/cn.yaml
kdata-download -b data/etf/10B_cn.yaml
```

---

### 2. `kdata-quote` - 标的行情、成交额、成交量与市值综合查询

```bash
# 1. 综合查询多支标的（自动优先读取本地通达信 .day 文件，秒级直读）
kdata-quote 510050 sh.000001 600519

# 2. 导出 JSON 结构化数据
kdata-quote 510050 --json

# 3. 强制走 mootdx2 在线实时行情通道（跳过本地 .day 离线文件）
kdata-quote 600519 --online
```

**终端输出预览效果：**
```text
代码    名称           最新价    涨跌幅  成交额      成交量(股)   总市值       流通市值     换手率  来源         
-----------------------------------------------------------------------------------------------------------------
510050  华夏上证50ETF       2.975  +0.57%    14.45 亿    4.85 亿股    225.84 亿    225.84 亿   6.39%offline_day  
000001  上证综合指数     3911.870  +0.52%  9941.69 亿            -            -            -       -offline_day  
600519  贵州茅台         1257.120  -0.78%    31.36 亿  248.90 万股  15715.03 亿  15715.03 亿   0.20%mootdx       
```

**字段口径与严格正交规则：**
- **成交额 (`amount`)**：交易所真实撮合成交额，单位为**元**（终端统一除以 $10^8$ 显示“xx 亿元”），绝不走估算。大盘指数与个股均具备该指标。
- **成交量 (`volume`)**：统一以**“股”**为基准单位（个股/ETF 统一换算为真实股数，展示为“xx 亿股 / 万股”）。**大盘指数绝无“股”的属性，严格展示为 `-`，严禁与金额或手混用**。
- **总市值 / 流通市值**：基于通达信财务股本结合价格计算，单位为**元**（终端显示为“xx 亿元”）。大盘指数不适用展示为 `-`。
- **换手率 (`turnover`)**：基于成交股数 $\div$ 流通股数计算。

---

### 3. `kdata-etf` - ETF 筛选、流动性过滤与配置导出

`kdata-etf` 是专门用于处理、过滤、更新 ETF 行情及规模数据的独立命令行工具。它支持全市场 ETF 元数据缓存、极速本地离线筛选、多维排序与对齐预览，以及一键导出量化策略标的池 YAML 配置文件。

```bash
kdata-etf [-h] {update,filter,export,pull-config} ...
```

#### (1) `update`：更新并建立本地缓存
拉取全市场 ETF 列表，获取最新财务信息（流通股本）和当日盘口行情（现价、成交量、成交额、IOPV、折溢价率等），计算出最新市值并缓存至本地（通常位于 `~/.kdata/etf_cache.json`）。
- `-i`, `--input`: 仅更新输入文件（YAML 或 TXT）中的 ETF 标的（大幅缩短耗时）
- `--offline`: 纯离线模式，直接从本地已有 TDX `.day` 或 K 线缓存快速重算成交额与换手率指标，不发起任何外部网络请求

```bash
# 联网全量更新（建议每天盘后运行一次，1700+ 支标的带进度条提示，耗时约 2-3 分钟）
kdata-etf update

# 仅更新指定标的池
kdata-etf update -i data/etf/10B_cn.yaml

# 纯离线指标重算与缓存刷新（无网络开销）
kdata-etf update --offline
```

#### (2) `filter`：极速条件筛选与排版预览
基于本地缓存极速过滤，通过 `unicodedata` 终端等宽排版输出最新价、IOPV、折溢价率、规模、成交额与换手率，适合人工复盘与量化选基。

**参数说明表：**
| 参数 | 类型 | 说明 |
| :--- | :--- | :--- |
| `-i`, `--input` | str | 输入文件路径（支持 YAML 格式如 `data/etf/10B_cn.yaml` 或纯文本 TXT 代码列表） |
| `--sort-by` | str | 排序维度：`scale`（规模，默认）、`amount`（成交额）、`turnover`（换手率）、`premium`（折溢价率） |
| `--asc` | flag | 升序排列（默认降序） |
| `--top` | int | 截取排序后的前 N 支标的 |
| `--min-scale` / `--max-scale` | float | 最小/最大资产规模，单位：**亿**（默认下限：5.0 亿） |
| `--min-vol` / `--max-vol` | float | 最小/最大单日成交额，单位：**亿**（默认下限：0.5 亿） |
| `--min-turnover` / `--max-turnover` | float | 最小/最大换手率（%） |
| `--min-premium` / `--max-premium` | float | 最小/最大折溢价率（%），支持负值（如 `--max-premium -1.0` 筛选折价 1% 以上） |
| `--top-vol` | int | （兼容参数）按单日成交额降序截取前 N 支 |
| `--top-turnover` | int | （兼容参数）按换手率降序截取前 N 支 |

**终端输出预览效果：**
```text
Updated at: 2026-09-16 18:32:37
Found 112 ETFs matching criteria.
---------------------------------------------------------------------------------------------------------
Code     Name                    Price     IOPV   Prem(%)   Scale(亿)  Amount(亿) Turnover(%)
---------------------------------------------------------------------------------------------------------
511360   海富通中证短融ETF            113.922        -    +0.00%     884.14     428.09       0.48%
511880   银华日利                  100.794        -    +0.00%    1147.12     269.03       0.23%
511100   华夏上证基准做市国债            110.717        -    +0.00%     122.76     147.13       1.20%
510300   华泰柏瑞沪深300ETF            3.985    3.984    +0.03%    3120.45      65.12       2.09%
---------------------------------------------------------------------------------------------------------
```

**使用示例：**
```bash
# 1. 针对 10B_cn.yaml 标的池，按换手率降序展示前 20 支
kdata-etf filter -i data/etf/10B_cn.yaml --sort-by turnover --top 20

# 2. 针对指定标的池按成交额降序排列并显示前 10 支
kdata-etf filter -i data/etf/10B_cn.yaml --sort-by amount --top 10

# 3. 筛选规模在 20亿~50亿 之间、且换手率高于 5% 的标的
kdata-etf filter --min-scale 20 --max-scale 50 --min-turnover 5

# 4. 筛选折价幅度超过 1.5% 的标的（溢价率 <= -1.5%）并按折价率排序
kdata-etf filter --max-premium -1.5 --sort-by premium --asc
```

#### (3) `export`：按需导出标准标的池配置文件
继承 `filter` 的所有过滤与排序参数，将满足条件的标的列表导出为标准的 YAML 格式文件，附带清晰的规模标签（大/中/小）及元数据注释。
- `-o`, `--output`: 输出 YAML 文件路径（默认为 `etfs_exported.yaml`）

```bash
# 从 10B_cn.yaml 中筛选成交额前 10 支并导出为 top10_amount.yaml
kdata-etf export -i data/etf/10B_cn.yaml --sort-by amount --top 10 -o data/etf/top10_amount.yaml

# 从全市场筛选规模大于 50 亿的 ETF 并导出
kdata-etf export --min-scale 50 -o target_etfs.yaml
```

**导出的 YAML 文件示例：**
```yaml
etf:
  - '510300'  # 华泰柏瑞沪深300ETF 市值:3120.45亿 (大)
  - '510050'  # 50ETF 市值:1123.51亿 (大)
  - '159001'  # 保证金 市值:15.20亿 (小)
```
*(规模标签规则：$\ge 100$ 亿标注为“大”、$20\sim 100$ 亿标注为“中”、$< 20$ 亿标注为“小”)*

#### (4) `pull-config`：从 Central Hub 拉取标的配置
直接从远程数据中心服务拉取标准分类的 ETF 资产池 YAML 配置文件：
```bash
# 列出远程可用的配置文件
kdata-etf pull-config --list

# 拉取境内核心 ETF 配置 (默认输出至 data/etf/)
kdata-etf pull-config -c cn

# 拉取国内百亿大市值核心 ETF 配置
kdata-etf pull-config -c cn_large

# 拉取全部配置 (cn, cn_large, overseas, us)
kdata-etf pull-config --all --out-dir data/etf
```

---

### 4. `kdata-premium` - ETF 折溢价率与 IOPV 分析

```bash
# 获取 513100 实时溢价快照
kdata-premium 513100 --snapshot

# 获取 513100 指定日期范围的历史溢价序列
kdata-premium 513100 2024-01-01 2024-12-31

# 纯离线计算历史溢价率（零网络请求：K 线直读本地 .day，净值走本地离线缓存）
kdata-premium 513100 --offline

# 纯离线查看最新溢价快照
kdata-premium 513100 --snapshot --offline
```

**参数说明：**
- `--offline`: 纯离线模式，零网络请求，直读本地 `.day` 与本地 `premium_cache`
- `--snapshot`: 仅查看最新一日的收盘/实时溢价快照
- `--skip-central`: 跳过远程数据中心中继服务
- `--prefer-local`: 优先使用本地数据源（默认开启）

---

### 5. `kdata-scan` - ETF/LOF 实时折溢价套利机会扫描器

```bash
# 扫描默认资金池
kdata-scan

# 简易加速模式 (跳过 F10 赎回费拉取)
kdata-scan --simple

# 筛选折价率 >= 3% 的标的
kdata-scan --min-discount-pct 3 --simple
```

---

### 6. `kdata-market` - 大盘市场概览与多市场指数快照

```bash
# A 股主要指数快照与概览
kdata-market --cn

# 港股主要指数概览
kdata-market --hk

# 美股主要指数概览
kdata-market --usa

# 全球全市场综合概览
kdata-market --all
```

---

### 7. `kdata-kv` - KV 存储运维管理工具

`kdata-kv` 是直接在 Shell / 脚本中操作与运维 KV 存储的 CLI 工具：

```bash
# 1. 上传 JSON 数据（指定生效日期为今天）
kdata-kv put alpha weights '{"600519": 0.5, "000001": 0.5}' --date 2026-09-18

# 2. 从本地文件上传载荷
kdata-kv put alpha model -f ./model.bin

# 3. 读取数据（带时效性强校验，未达到时报错阻断）
kdata-kv get alpha weights --date 2026-09-18

# 4. 读取数据并导出到本地文件
kdata-kv get alpha model -o ./restored_model.bin

# 5. 查看元数据与时效状态
kdata-kv head alpha weights

# 6. 列出命名空间下的所有键名与变动日期
kdata-kv list alpha
kdata-kv list alpha --history  # 展开查看全部历史切片

# 7. 删除单日快照或全删
kdata-kv del alpha weights --date 2026-09-10  # 仅删历史快照
kdata-kv del alpha weights                   # 级联全删
```

---

### 8. `kdata-serve` - HTTP 数据中心与配置中继服务

在指定端口启动 HTTP 数据中心服务，供各客户端作为统一数据源代理访问：
```bash
kdata-serve --host 0.0.0.0 --port 8765
```

#### 核心 HTTP 端点与标的隔离规范
- **`GET /ohlc`**: 个股与场内 ETF 历史 K 线（前复权）。**严格禁止传入大盘指数**，若误传大盘指数，服务端将拦截并返回 **HTTP 404**。
- **`GET /market/history`**: 大盘基准指数历史 K 线（不复权）。**严格禁止传入普通个股或 ETF**，若误传普通个股，服务端将拦截并返回 **HTTP 404**。
- **`GET /market/indices`**: 核心市场指数最新行情截面快照表。
- **`GET /market/overview`**: 宏观市场全貌快照字典（多空家数、两融、板块排名等）。
- **`GET /etf/config`**: 下载指定市场分类的 ETF 资产池标准 YAML 配置文件文本内容（参数 `category=cn|cn_large|overseas|us`）。
- **`GET /etf/configs`**: 查询服务端当前已就绪的 ETF 资产池 YAML 配置文件分类列表及文件元数据。

#### 中心服务 KV REST API
统一在请求头携带 Bearer Token 鉴权（`Authorization: Bearer <KDATA_CENTRAL_TOKEN>`）。

| HTTP 方法 | 路径与参数 | 说明 | 错误码对照 |
| :--- | :--- | :--- | :--- |
| `PUT` | `/api/kv/{namespace}/{key}?date=YYYY-MM-DD` | **上传/覆盖数据**。<br>Body 接受二进制或 JSON；从 Header 获取 `Content-Type`；自动生成历史快照并择机刷新最新指针。 | `INVALID_PARAMETER` (400)<br>`PAYLOAD_TOO_LARGE` (413)<br>`UNAUTHORIZED` (401) |
| `GET` | `/api/kv/{namespace}/{key}?date=...&min_date=...` | **下载数据**。<br>• 缺省读取最新指针；<br>• `date`: 精确匹配指定日期；<br>• `min_date`: 校验最小日期阈值。<br>响应头返回 `X-KData-Data-Date` 与 `ETag`。 | `KEY_NOT_FOUND` (404)<br>`DATA_NOT_FRESH` (404)<br>`UNAUTHORIZED` (401) |
| `HEAD` | `/api/kv/{namespace}/{key}?date=...&min_date=...` | **仅探活与检查元数据**。<br>返回与 GET 完全相同的时效校验结果及响应头（`X-KData-Data-Date`、`Content-Type`、`ETag` 等），不传输 Body。 | `KEY_NOT_FOUND` (404)<br>`DATA_NOT_FRESH` (404) |
| `DELETE`| `/api/kv/{namespace}/{key}?date=YYYY-MM-DD` | **删除数据**。<br>• 传 `date`: 仅删除该日期的历史快照，最新指针自愈回退；<br>• 不传 `date`: 级联清除该 Key 的最新视图与所有历史快照。 | `INVALID_PARAMETER` (400) |
| `GET` | `/api/kv/{namespace}?prefix=...&include_history=1` | **列表查询**。<br>• 默认返回去重后的各 Key 及其最新日期；<br>• `include_history=1` 展开显示历史快照切片；<br>• `prefix`: 按键名前缀过滤。 | `UNAUTHORIZED` (401) |

> [!NOTE]
> 当 Python 客户端（配置了 `KDATA_CENTRAL_URL`）请求服务端收到 404、429 或网络异常时，系统将自动无感降级回退至本地数据源链，不会中断业务运行。

---

### 9. 常用极简命令行工作流（以 .day 为主、极少网络）

#### (1) 批量用本地 `.day` 文件生成/刷新本地缓存（极速、免网络）
```bash
uv run kdata-download -b data/etf/overseas.yaml
uv run kdata-download -b data/etf/cn.yaml
uv run kdata-download -b data/etf/10B_cn.yaml
```

#### (2) 联动工作流 (Workflow: `kdata-etf` + `kdata-download`)
通过 `kdata-etf export` 导出筛选后的 YAML 配置文件后，可直接无缝衔接 `kdata-download` 进行批量拉取和本地持久化存储：
```bash
# 1. 批量下载导出的 ETF 历史日线数据（从 2023-01-01 至今）
kdata-download -b data/etf/top10_amount.yaml 2023-01-01

# 2. 结合大盘基准指数同步下载（自动识别并隔离存储至 data/indices/）
kdata-download sh.000300 2023-01-01
kdata-download sz.399001 2023-01-01
kdata-download hk.HSI 2023-01-01

# 3. 接入本地 Central Hub HTTP 数据中心加速（可选）
export KDATA_CENTRAL_URL="http://127.0.0.1:8765"
kdata-download -b data/etf/top10_amount.yaml 2023-01-01
```

#### (3) 纯离线计算并查看 ETF 溢价率
```bash
# 历史折溢价率序列（纯离线）
uv run kdata-premium 513100 --offline

# 最新折溢价快照（纯离线）
uv run kdata-premium 513100 --snapshot --offline
```

#### (4) Python 接口纯离线调用
```python
from kdata import get_etf_premium_data, get_latest_etf_premium

# 历史溢价率（纯离线，零网络请求：K 线直读本地 .day，净值走本地离线缓存）
df = get_etf_premium_data("513100", allow_network=False)

# 最新快照（纯离线，零网络请求）
snapshot = get_latest_etf_premium("513100", allow_network=False)
```

---

## 💡 第三部分：设计原理与最佳实践 (Architecture & Best Practices)

1. **自动多源降级与容灾**: `AUTO` 调度模式下，EFinance, Akshare, Mootdx, Baostock 互为备份。单个数据源遭遇网络波动或频控时，系统会自动无感切换备份数据源。
2. **高效增量缓存与通达信直通**: 所有下载的历史 K 线数据落地本地缓存（默认优先 Parquet，兼容历史 CSV）。二次查询时增量补全，极大减少网络 API 调用开销。配合本地通达信 `.day` 离线文件直通，可实现毫秒级批量解析入库。
3. **时区与日期防错**: 交易日期统一处理为北京时间 (CST) 00:00:00 对应交易日，避免跨时区或盘后交易日推导偏差。
4. **批量下载与并发控制 (Pacing)**:
   系统遵循 *Gentle on Providers* 原则以保障数据抓取的长期稳定性。若配置了 Central Hub (`KDATA_CENTRAL_URL`)，当服务端队列负载过高触发 429 限流保护时，内部已自动记录退避并平滑降级到本地行情源，**不会向外部抛出异常中断程序**。
   - **外部调用方最优做法**：
     如果外部是多线程/多进程批量下载任务（如 `ThreadPoolExecutor`）：
     - 将线程并发数调小（建议并发在 2 ~ 4 之间）。
     - 在批量循环中保留微小间隔（如 `time.sleep(0.05 ~ 0.1)`）。
5. **KV 存储防读老数据 (Stale Defense) 与原子写入**:
   - **Stale Defense**: 量化策略最怕使用过期或未更新的陈旧数据下单。通过 `data_date` 盖戳与 `min_date` 强校验，数据未达到最新交易日要求时直接阻断抛错，防范实盘故障。
   - **原子写入**: 写操作先写入临时文件，执行 `fsync` 刷盘后再通过 `os.replace` 原子覆写，读取方零加锁，彻底杜绝进程意外退出或并发读写造成的数据损坏。
