Metadata-Version: 2.4
Name: qdata-quote
Version: 0.5.0
Summary: 实时行情采集服务，支持新浪/腾讯/腾讯完整三数据源，统一 70 列输出，跨市场 A/港/美
License-Expression: MIT
Requires-Python: >=3.10
Requires-Dist: aiohttp>=3.9
Requires-Dist: pandas>=2.0
Requires-Dist: requests>=2.28
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# qdata-quote

实时 A 股行情采集服务，支持新浪和腾讯两个数据源。性能超越 easyquotation 约 8-10%。

## 安装

```bash
pip install qdata-quote
```

## 快速开始

```python
from qdata_quote import QuoteService

# 默认加载全市场股票列表（初始化时自动从网络刷新）
service = QuoteService()

# 同步获取指定股票行情
df = service.get_real_sync(["000001", "600000"])
print(df)

# 同步获取全市场行情快照
df_all = service.get_all_sync()

# 异步获取（高性能路径）
import asyncio
df = asyncio.run(service.get_real(["000001", "600000"]))
df_all = asyncio.run(service.get_all())
```

## 自定义股票列表

```python
from qdata_quote import QuoteService

# 传入自定义股票列表（不会自动刷新）
service = QuoteService(codes=["000001", "600000", "300750"])

# 随时替换股票列表（切换为自定义模式，不再接受 refresh）
service.set_stock_codes(["000001", "600000"])

# 刷新全市场股票列表（仅在使用默认列表时生效）
service.refresh_stock_codes()
```

## 获取股票代码

```python
from qdata_quote import get_stock_codes

# 读取缓存（如果没有缓存则自动获取）
codes = get_stock_codes()

# 强制从网络刷新
codes = get_stock_codes(refresh=True)
```

缓存文件位置：`~/.qdata_quote/stock_codes.json`

## 数据源

支持三个数据源，通过 `source` 参数指定：

```python
# 新浪源（默认）
df = service.get_real_sync(["000001"], source="sina")

# 腾讯源
df = service.get_real_sync(["000001"], source="tencent")

# 腾讯完整源（跨市场 + 扩展字段最全）
df = service.get_real_sync(["000001"], source="tencent_full")
```

### 统一输出结构

**三个数据源输出结构完全一致**：均为 70 列 `DataFrame`（44 基础列 + 26 扩展列），缺失字段统一填 `NaN`。调用方无需针对不同源写不同处理逻辑——切换 `source` 只改变「哪些列有值」，不改变列结构。

| 数据源 | 每批数量 | 市场支持 | 可填充字段 | 特点 |
|--------|---------|---------|-----------|------|
| sina | 800 只/批 | 仅 A 股 | 28 列（基础行情+买卖盘） | 速度快，但缺少涨跌/估值/市值等 |
| tencent | 60 只/批 | 仅 A 股 | 42 列（基础+涨跌+估值+市值+盘口） | 额外提供涨跌、市盈率、市值、量比等 |
| **tencent_full** | **800 只/批** | **A 股 + 港股 + 美股 + ETF + 北交所** | **70 列（全字段）** | **字段最全 + 跨市场支持** |

> 三源返回的列结构都是完整的 70 列；上表「可填充字段」指该源实际能提供值的列数，其余列恒为 `NaN`。

### 扩展字段（26 列）

下列扩展列仅 `tencent_full` 能填值，`sina` / `tencent` 恒为 `NaN`（按类别分组）：

| 类别 | 字段 | 单位/类型 |
|------|------|----------|
| 标识/类型/市场 | `secu_code` `stock_type_cn` `market_tags_cn` `market_mark` `currency` `is_suspend` | str / Int64 |
| 成交明细 | `turnover_rate` `turnover_full` `outer_volume` `inner_volume` | % / 元 / 股(Int64) |
| 盘口 | `committee` `capital_ratio` | 股(Int64) / % |
| 估值 | `pe_ttm` | 倍 |
| 涨速 | `speed_5min` `speed_5min_real` | %/分钟 |
| 区间涨跌 | `change_ytd` `change_5d` `change_10d` `change_20d` `change_60d` `change_6m` `change_1y` | % |
| 52 周高低 | `high_52w` `low_52w` | 元 |
| 股本 | `float_shares` `total_shares` | 股(Int64) |

### 跨市场支持

tencent_full 支持 A 股 / 港股 / 美股 / ETF / 北交所：

```python
# 跨市场查询（港美股字段较少，错位字段自动置 NaN）
df = service.get_real_sync(
    ["600519.SH", "000001.SZ", "00700.HK", "AAPL.US"],
    source="tencent_full"
)
```

## 返回格式

返回统一的 `pandas.DataFrame`，**三源均为 70 列**，index 为带后缀的股票代码（如 `600000.SH`、`000001.SZ`、`830789.BJ`）。

### 股票代码格式

- **输出统一后缀格式**：`<代码>.<交易所>`，交易所后缀大写 —— `.SH`（上交所）、`.SZ`（深交所）、`.BJ`（北交所）、`.HK`（港股）、`.US`（美股）。
- **输入兼容多种写法**（tencent_full 独有跨市场支持）：
  - 纯代码：`000001`、`600519`、`00700`、`AAPL`
  - 带后缀：`000001.SZ`、`00700.HK`、`AAPL.US`（大小写不敏感）
  - 带前缀：`sh600519`、`hk00700`、`usAAPL`
- `get_stock_codes()` 返回的默认列表同样为后缀格式（仅 A 股）。
- **跨市场查询仅 tencent_full 支持**（sina/tencent 仅 A 股）。

### 单位与类型规范

| 类别 | 字段 | 单位 | 类型 |
|------|------|------|------|
| 价格 | `open` `close` `now` `high` `low` `buy` `sell` `bid1~5` `ask1~5` `limit_up` `limit_down` `avg_price` `change` `high_52w` `low_52w` | 元 | float |
| 成交量 | `volume` `bid*_volume` `ask*_volume` | 股 | float（NaN 时为 NaN） |
| 成交额 | `turnover` `turnover_full` | 元 | float |
| 市值 | `total_market_cap` `circulating_market_cap` | 元 | float |
| 比率 | `change_pct` `amplitude` `turnover_rate` `volume_ratio` `bid_ask_ratio` `capital_ratio` `speed_5min` `speed_5min_real` `change_ytd/5d/10d/20d/60d/6m/1y` | **百分点数值**（2.34 表示 2.34%） | float |
| 估值 | `pe_dynamic` `pe_static` `pe_ttm` `pb` | 倍 | float |
| 可空整数量 | `outer_volume` `inner_volume` `committee` `float_shares` `total_shares` `market_mark` | 股 / 计数 | **Int64（可空）** |
| 文本 | `name` `datetime` `secu_code` `currency` `stock_type_cn` `market_tags_cn` `is_suspend` | — | str |

### 字段列表（70 列）

三源均输出下表全部 70 列；✅ 表示该源能提供数据，- 表示恒为 NaN。

#### 基础列（44 列）

| 字段 | 类型 | 新浪 | 腾讯 | 腾讯完整 | 说明 |
|------|------|:----:|:----:|:--------:|------|
| name | str | ✅ | ✅ | ✅ | 股票名称 |
| open | float | ✅ | ✅ | ✅ | 开盘价（元） |
| close | float | ✅ | ✅ | ✅ | 昨收价（元） |
| now | float | ✅ | ✅ | ✅ | 当前价（元） |
| high | float | ✅ | ✅ | ✅ | 最高价（元） |
| low | float | ✅ | ✅ | ✅ | 最低价（元） |
| buy | float | ✅ | - | - | 买一价（元） |
| sell | float | ✅ | - | - | 卖一价（元） |
| volume | float | ✅ | ✅ | ✅ | 成交量（股） |
| turnover | float | ✅ | ✅ | ✅ | 成交额（元） |
| bid1 ~ bid5 | float | ✅ | ✅ | ✅ | 买一到买五价（元） |
| bid1_volume ~ bid5_volume | float | ✅ | ✅ | ✅ | 买一到买五量（股） |
| ask1 ~ ask5 | float | ✅ | ✅ | ✅ | 卖一到卖五价（元） |
| ask1_volume ~ ask5_volume | float | ✅ | ✅ | ✅ | 卖一到卖五量（股） |
| datetime | str | ✅ | ✅ | ✅ | 行情时间 |
| change | float | - | ✅ | ✅ | 涨跌额（元） |
| change_pct | float | - | ✅ | ✅ | 涨跌幅（百分点） |
| amplitude | float | - | ✅ | ✅ | 振幅（百分点） |
| pe_dynamic | float | - | ✅ | ✅ | 动态市盈率（倍） |
| pe_static | float | - | ✅ | ✅ | 静态市盈率（倍） |
| pb | float | - | ✅ | ✅ | 市净率（倍） |
| total_market_cap | float | - | ✅ | ✅ | 总市值（元） |
| circulating_market_cap | float | - | ✅ | ✅ | 流通市值（元） |
| volume_ratio | float | - | ✅ | ✅ | 量比 |
| bid_ask_ratio | float | - | ✅ | ✅ | 委比（百分点） |
| avg_price | float | - | ✅ | ✅ | 均价（元） |
| limit_up | float | - | ✅ | ✅ | 涨停价（元） |
| limit_down | float | - | ✅ | ✅ | 跌停价（元） |

#### 扩展列（26 列，仅 tencent_full 可填值）

| 字段 | 类型 | 新浪 | 腾讯 | 腾讯完整 | 说明 |
|------|------|:----:|:----:|:--------:|------|
| secu_code | str | - | - | ✅ | 交易所代码（如 600519） |
| stock_type_cn | str | - | - | ✅ | 股票类型（中文：沪深主板/科创板/创业板/北交所/基金/债券） |
| market_tags_cn | str | - | - | ✅ | 市场标签（中文） |
| market_mark | Int64 | - | - | ✅ | 市场标识 |
| currency | str | - | - | ✅ | 币种（CNY/HKD/USD） |
| is_suspend | str | - | - | ✅ | 停牌状态 |
| turnover_rate | float | - | - | ✅ | 换手率（百分点） |
| turnover_full | float | - | - | ✅ | 成交额完整精度（元） |
| outer_volume | Int64 | - | - | ✅ | 外盘/主动买盘（股） |
| inner_volume | Int64 | - | - | ✅ | 内盘/主动卖盘（股） |
| committee | Int64 | - | - | ✅ | 委差（股） |
| capital_ratio | float | - | - | ✅ | 流通占比（百分点） |
| pe_ttm | float | - | - | ✅ | TTM 滚动市盈率（倍） |
| speed_5min | float | - | - | ✅ | 5 分钟涨速（百分点/分钟） |
| speed_5min_real | float | - | - | ✅ | 5 分钟真实涨速（百分点/分钟） |
| change_ytd | float | - | - | ✅ | 年初至今涨跌幅（百分点） |
| change_5d | float | - | - | ✅ | 近 5 日涨跌幅（百分点） |
| change_10d | float | - | - | ✅ | 近 10 日涨跌幅（百分点） |
| change_20d | float | - | - | ✅ | 近 20 日涨跌幅（百分点） |
| change_60d | float | - | - | ✅ | 近 60 日涨跌幅（百分点） |
| change_6m | float | - | - | ✅ | 近 6 月涨跌幅（百分点） |
| change_1y | float | - | - | ✅ | 近 1 年涨跌幅（百分点） |
| high_52w | float | - | - | ✅ | 52 周最高价（元） |
| low_52w | float | - | - | ✅ | 52 周最低价（元） |
| float_shares | Int64 | - | - | ✅ | 流通股本（股） |
| total_shares | Int64 | - | - | ✅ | 总股本（股） |

> 港美股行因 qt 端点字段错位，估值/市值/比率/区间涨跌/52周/限价/换手等 A 股专属列会自动置 `NaN`。

## 会话管理

建议使用上下文管理器复用连接，在频繁轮询场景下性能更佳：

```python
# 同步
with QuoteService() as service:
    df = service.get_all_sync()

# 异步
async with QuoteService() as service:
    df = await service.get_all()
```

## 性能对比

与 easyquotation 对比（5500+ 只股票）：

| 数据源 | easyquotation | qdata_quote sync | qdata_quote async |
|--------|--------------|-------------------|-------------------|
| 新浪 | ~710ms | **~640ms** | **~640ms** |
| 腾讯 | ~1830ms | **~1720ms** | **~1680ms** |

三个 source 实测对比（全 A 股 5527 只）：

| source | 耗时 | 速率 | 可填充字段 | 市场支持 |
|--------|------|------|-----------|---------|
| sina | 0.12s | 44304 只/秒 | 28 列 | 仅 A 股 |
| tencent | 0.28s | 19680 只/秒 | 42 列 | 仅 A 股 |
| **tencent_full** | **0.48s** | 11479 只/秒 | **70 列（全字段）** | **A 股 + 港股 + 美股 + ETF + 北交所** |

> 三源输出结构统一为 70 列；「可填充字段」指该源实际能提供值的列数，其余列恒为 NaN。

- 同步引擎：`requests` + `ThreadPoolExecutor` 并发请求
- 异步引擎：`aiohttp` + `asyncio.gather` 并发请求
- 解析优化：文本合并后一次性正则匹配，元组直接构建 DataFrame
- tencent_full：qt 端点纯文本解析（GBK `~` 分隔），无 JSON 开销

## 运行基准测试

```bash
python -m qdata_quote.bench
```

## 依赖

- Python >= 3.10
- requests >= 2.28
- aiohttp >= 3.9
- pandas >= 2.0
