Metadata-Version: 2.4
Name: pywestockdata
Version: 0.2.0
Summary: Python SDK & CLI for Tencent Finance public APIs (A股/港股/美股)
Requires-Python: >=3.10
Requires-Dist: click>=8.0
Requires-Dist: primp>=0.10
Requires-Dist: rich>=13.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# pywestockdata

Python SDK & CLI for Tencent / East Money public finance APIs.

A 股（含北交所）实时行情、K 线、分时、搜索、财务、资金流、股东、分红、融资融券等数据一站获取。港股 / 美股仅行情与搜索类接口支持（详见下文「代码规则」）。

---

## 安装

```bash
# 可编辑安装（含 CLI 命令 westock）
pip install -e .

# 开发模式（含测试依赖）
pip install -e ".[dev]"
```

安装后即可直接使用 CLI：

```bash
westock quote sh600000 sz000001
```

---

## 性能

> 以下为设计目标 / 参考量级，实际耗时取决于网络环境与并发数，请以本地实测为准。

| 功能 | 并发策略 | 目标耗时 |
|------|---------|---------|
| 全量行情（沪 + 深 + 北，约 5000+ 股） | asyncio + primp.AsyncClient, 4 workers | <500ms |
| 全市场代码发现 | asyncio.gather 并发请求 sh/sz/bj | <200ms |
| 分时 K 线批量 | asyncio + primp.AsyncClient, bounded concurrency | <500ms |
| 财务三表 | asyncio.gather 并发请求 income/balance/cashflow | <300ms |
| 单次请求（K 线、搜索、资金流等） | primp.AsyncClient | <200ms |

所有公开函数均为 **async coroutine**，使用 `await` 调用。

### 本地缓存

`fetch_all_stock_codes()` 和 `fetch_all_stock_info()` 支持本地文件缓存，避免重复请求：

| 函数 | 缓存路径 | 默认 TTL |
|------|---------|---------|
| `fetch_all_stock_codes(market)` | `~/.cache/pywestockdata/stock_codes_{market}.json` | 4 小时 |
| `fetch_all_stock_info()` | `~/.cache/pywestockdata/stock_info.json` | 5 分钟 |

```python
# 默认使用缓存（TTL 内命中则直接返回）
codes = await ws.fetch_all_stock_codes()

# 强制刷新，忽略缓存
codes = await ws.fetch_all_stock_codes(force_refresh=True)

# 自定义 TTL（秒），0 = 禁用缓存
codes = await ws.fetch_all_stock_codes(cache_ttl=86400)   # 缓存 1 天
info  = await ws.fetch_all_stock_info(cache_ttl=0)         # 每次都拉最新
```

---

## Python API

所有函数均为 **async coroutine**，使用 `await` 调用，返回 `dict` / `list[dict]` / `list[list]`，不抛网络异常（失败返回 `[]` 或 `{}` 并记录 `warning` 日志）。

```python
import pywestockdata as ws
import asyncio

async def main():
    # —— 实时行情 ——
    quotes = await ws.fetch_quotes_batch(["sh600000", "sz000001"], workers=4)
    all_quotes = await ws.fetch_quotes_batch(await ws.fetch_all_stock_codes())

    # —— K 线（day/week/month/5min/15min/30min/60min）——
    day_bars = await ws.fetch_kline("sh600000", period="day", count=30, adjust="qfq")
    min_bars = await ws.fetch_kline("sh600000", period="5min", count=48)

    # —— 分时 ——
    minute = await ws.fetch_minute_data("sh600519")
    raw = await ws.fetch_minute_batch(["sh600000", "sz000001"])

    # —— 搜索 / 热门 ——
    results = await ws.search_stock("中国平安")
    hot = await ws.fetch_hot_search()

    # —— 资金流 / 板块 ——
    flow = await ws.fetch_fund_flow("sh600519")
    boards = await ws.fetch_board_list()
    board_stocks = await ws.fetch_board_stocks("BK0493", limit=20)

    # —— 公司基本面（A 股）——
    profile = await ws.fetch_profile("sh600519")
    finance = await ws.fetch_finance("sh600519")
    holders = await ws.fetch_shareholder("sh600519")
    dividends = await ws.fetch_dividend("sh600519")

    # —— 交易类（A 股）——
    blocks = await ws.fetch_block_trades("sh600519", limit=10)
    margin = await ws.fetch_margin("sh600519")
    buyback = await ws.fetch_buyback("sh600519", limit=10)
    suspended = await ws.fetch_suspensions()

    # —— 全市场列表 ——
    all_codes = await ws.fetch_all_stock_codes()
    sh_codes = await ws.fetch_all_stock_codes("sh")
    all_info = await ws.fetch_all_stock_info()

asyncio.run(main())
```

### 函数一览

所有函数均为 `async`，使用 `await` 调用。

| 函数 | 说明 | 支持市场 |
|------|------|---------|
| `await fetch_quotes_batch(codes, workers=4)` | 批量实时行情（含五档盘口） | 沪 / 深 / 北 / 港 / 美 |
| `await fetch_kline(code, period, count, adjust)` | 日 / 周 / 月 / 分钟 K 线 | 行情接口全部；分钟仅 A 股 |
| `await fetch_minute_batch(codes, workers)` | 批量分时原始数据 | A 股 |
| `await fetch_minute_data(code)` | 单股分时（已解析） | A 股 |
| `await search_stock(keyword)` | 股票搜索 | 全部 |
| `await fetch_hot_search()` | 热门股票榜 | A 股 |
| `await fetch_fund_flow(code)` | 资金流向 | A 股 |
| `await fetch_board_list()` | 行业板块列表 | A 股 |
| `await fetch_board_stocks(board_code, limit)` | 板块成分股 | A 股 |
| `await fetch_finance(code)` | 利润表 / 资产负债表 / 现金流量表 | A 股 |
| `await fetch_profile(code)` | 公司概况（市值 / PE / PB 等） | A 股 |
| `await fetch_shareholder(code)` | 十大股东 | A 股 |
| `await fetch_dividend(code)` | 分红历史 | A 股 |
| `await fetch_block_trades(code, limit)` | 大宗交易 | A 股 |
| `await fetch_margin(code)` | 融资融券 | A 股 |
| `await fetch_buyback(code, limit)` | 回购（† 暂不可用） | A 股 |
| `await fetch_suspensions()` | 停牌列表（† 暂不可用） | A 股 |
| `await fetch_all_stock_codes(market, force_refresh, cache_ttl)` | 全市场代码列表（支持缓存） | 沪 / 深 / 北 |
| `await fetch_all_stock_info(force_refresh, cache_ttl)` | 全市场基础信息（支持缓存） | 沪 / 深（East Money） |

> † 回购 / 停牌因东方财富上游「特色数据」接口报表名变更暂不可用，见下「已知问题」。

> 港 / 美股代码虽可被 `_normalize_code` 识别，但财务、资金流、股东、分红、融资融券等基于东方财富的接口**仅支持沪深 A 股**，传入其余市场会返回空结果并记录警告日志。

---

## CLI 命令

共 22 个命令，所有命令均支持 `--json` 输出 JSON。

```bash
# ── 实时行情 ──
westock quote sh600000 sz000001 sh600519          # 多代码实时行情
westock quote-all                                 # 全量行情（沪+深+北）
westock quote-all -w 5                            # 指定并发线程数

# ── K 线 / 分时 ──
westock kline sh600000 -f day -c 30               # 日 K 线（-f: day/week/month/5/15/30/60）
westock kline sh600000 -f 5                       # 5 分钟 K 线
westock kline sh600000 -a hfq                     # 后复权（默认 qfq 前复权）
westock minute sh600000                           # 当日分时
westock intraday sh600000                         # minute 的别名

# ── 搜索 / 热门 ──
westock search 贵州茅台                           # 股票搜索
westock hot                                       # 热门股票榜

# ── 资金流 / 板块 ──
westock fund-flow sh600519                        # 资金流向
westock money-flow sh600519                       # fund-flow 的别名
westock board                                     # 行业板块列表
westock board-stocks BK0493 -l 30                 # 板块成分股（-l/--limit 数量）

# ── 公司基本面（A 股）──
westock finance sh600519                          # 三大财务报表
westock profile sh600519                          # 公司概况
westock shareholder sh600519                      # 十大股东
westock dividend sh600519                         # 分红历史
westock exdiv sh600519                            # dividend 的别名

# ── 交易类（A 股）──
westock block-trade sh600519                      # 大宗交易（-l/--limit 数量）
westock block-trade                               # 全市场大宗交易
westock margin sh600519                           # 融资融券（-l 数量）
westock buyback sh600519                          # 回购（-l 数量）
westock buyback                                   # 全市场回购

# ── 其他 ──
westock suspension                                # 停牌列表
westock reserve sh600519                          # 最早财报披露日期
westock market-overview                           # 主要指数概览（上证/深成/创业板/沪深300等）
```

### 命令速查表

| 命令 | 参数 | 关键选项 |
|------|------|---------|
| `quote` | `CODES...` | `--json` |
| `quote-all` | — | `-w/--workers`, `--json` |
| `kline` | `CODE` | `-f/--freq`, `-c/--count`, `-a/--adjust`, `--json` |
| `minute` / `intraday` | `CODE` | `--json` |
| `search` | `KEYWORD` | `--json` |
| `hot` | — | `--json` |
| `fund-flow` / `money-flow` | `CODE` | `--json` |
| `board` | — | `-l/--limit`, `--json` |
| `board-stocks` | `BOARD_CODE` | `-l/--limit`, `--json` |
| `finance` | `CODE` | `--json` |
| `profile` | `CODE` | `--json` |
| `shareholder` | `CODE` | `--json` |
| `dividend` / `exdiv` | `CODE` | `--json` |
| `block-trade` | `[CODE]` | `-l/--limit`, `--json` |
| `margin` | `CODE` | `-l/--limit`, `--json` |
| `buyback` | `[CODE]` | `-l/--limit`, `--json` |
| `suspension` | — | `--json` |
| `reserve` | `CODE` | `--json` |
| `market-overview` | — | `--json` |

---

## 代码规则

- 上海: `sh600000` (6 开头)
- 深圳: `sz000001` (0/3 开头)
- 北京: `bj830799` (8 开头)
- 港股: `hk00700`（仅行情 / 搜索类接口）
- 美股: `usAAPL`（仅行情 / 搜索类接口）

> 实时行情、K 线、搜索基于腾讯公开接口；财务 / 资金流 / 股东 / 分红 / 融资融券等基于东方财富，仅支持沪深 A 股，对港股 / 美股 / 北交所会返回空结果。

---

## 数据源

| 数据类别 | 接口 |
|---------|------|
| 实时行情 | Tencent `qt.gtimg.cn` |
| 日 / 周 / 月 K 线 | Tencent `proxy.finance.qq.com` |
| 分钟 K 线 | Tencent `web.ifzq.gtimg.cn/appstock/app/kline/mkline` |
| 分时数据 | Tencent `web.ifzq.gtimg.cn/appstock/app/minute/query` |
| 搜索 | Tencent `smartbox.gtimg.cn` |
| 热门 / 资金流 / 板块 / 全量列表 | East Money `push2.eastmoney.com` |
| 财务 / 股东 / 分红 / 大宗 / 融资融券 | East Money `datacenter-web.eastmoney.com` |
| 回购 / 停牌 | ⚠️ 暂不可用（东财「特色数据」报表名已变更，待修复，见下） |
| 公司概况 | East Money `push2.eastmoney.com/api/qt/stock/get` |

所有接口均为公开 HTTP GET，无需鉴权。

> **已知问题（已核对，暂搁置）**
> - `fetch_buyback` / `westock buyback`（股票回购）与 `fetch_suspensions` / `westock suspension`（停牌）：所依赖的东方财富「特色数据」`datacenter-web` 报表名（`RPT_SHARE_BUYBACK_DET`、`RPT_DATA_SUSPENSION` 等）现已返回 `9501 报表配置不存在`（经实测核对，财务类报表 `RPT_DMSK_FN_*` 仍正常）。这两个命令已保留兜底（返回空或提示），不会崩溃，待后续定位到有效接口后修复。
> - 实时行情 `amount`（成交额）腾讯原始单位为**万元**，已 `×1e4`；`total_market_cap` / `circ_market_cap` 原始单位为**亿元**，已 `×1e8`。`pe_ratio`、`turnover_rate`、`change_pct` 等字段为原始数值，无需换算。

---

## 开发

```bash
# 运行测试（已 mock 网络请求，无需联网）
pytest tests/ -q

# 代码规范
ruff check src tests
```

项目结构：

```
pywestockdata/
├── src/pywestockdata/
│   ├── api.py      # 全部数据获取函数（纯 async）
│   ├── cli.py      # click 命令行（22 个命令）
│   └── __init__.py # 公共导出
├── tests/          # pytest 测试（mock 网络层）
└── scripts/
```

---

## License

MIT
