Files
easy_tdx_max/docs/python-api.md
T
awayingsandClaude 36b586b015 docs: README Python API 章拆至 python-api.md(教程),枚举/API 列表归入参考文档
- python-api.md:连接/MAC/统一客户端/标准协议/离线/缠论/公告/财报/实时轮询教程
- MAC 协议枚举(Period/Adjust/Category/BoardType/SortType/ExMarket)并入 field_mapping.md
- MacClient/MacExClient 完整方法表并入 api_reference.md(TdxClient 表与正文重复,删除)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 23:21:35 +08:00

422 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Python API 使用指南
## 连接管理
所有客户端支持 `from_best_host()` 自动选最低延迟服务器:
```python
from easy_tdx import MacClient
with MacClient.from_best_host() as c:
df = c.get_stock_kline(...)
```
| 客户端 | 端口 | 覆盖范围 |
|--------|------|----------|
| `MacClient` / `AsyncMacClient` | 7709 | A 股行情(MAC 协议,推荐) |
| `MacExClient` / `AsyncMacExClient` | 7727 | 港股/美股/期货(MAC 协议) |
| `UnifiedTdxClient` / `AsyncUnifiedTdxClient` | 自动 | A 股 + 扩展市场统一入口 |
| `TdxClient` / `AsyncTdxClient` | 7709 | A 股行情(标准协议) |
## MAC 协议(推荐)
### 报价
```python
from easy_tdx import MacClient, Market, Category, SortType, SortOrder
with MacClient.from_best_host() as c:
# 批量报价(最多 80 只/次)
df = c.get_stock_quotes([(Market.SH, "600519"), (Market.SZ, "000858")])
# 市场分类排序报价
df = c.get_stock_quotes_list(
Category.A, count=20,
sort_type=SortType.CHANGE_PCT,
sort_order=SortOrder.DESC,
)
```
返回列:`market, code, name` + 动态字段(`pre_close, open, high, low, close, vol, amount, turnover, vol_ratio` 等)。
### K 线(支持复权)
```python
from easy_tdx import MacClient, Market, Period, Adjust
with MacClient.from_best_host() as c:
# 日K前复权
df = c.get_stock_kline(Market.SH, "600519", Period.DAILY, count=10, adjust=Adjust.QFQ)
# 5分钟线
df = c.get_stock_kline(Market.SZ, "000001", Period.MIN_5, count=100)
```
返回列:`datetime, open, close, high, low, vol, amount`
### 技术指标
自动获取 200+ 条历史数据预热 EMA,返回最后 `count` 条带指标的结果:
```python
from easy_tdx import MacClient, Market, Period, Adjust
from easy_tdx.indicator import compute_indicators, list_indicators
with MacClient.from_best_host() as c:
# 便捷方法:获取 K 线 + 计算指标一步完成(默认前复权)
df = c.get_stock_kline_with_indicators(
Market.SH, "600519",
indicators=["MACD", "KDJ", "RSI", "BOLL"],
count=30,
)
# df 包含: datetime, open, close, high, low, vol, amount
# + MACD_DIF, MACD_DEA, MACD_HIST, KDJ_K, KDJ_D, KDJ_J, RSI,
# BOLL_UPPER, BOLL_MID, BOLL_LOWER
# 自定义指标参数
df = c.get_stock_kline_with_indicators(
Market.SH, "600519",
indicators=["MACD"],
params={"MACD": {"SHORT": 10, "LONG": 22}},
)
# 独立使用:对已有 DataFrame 计算指标
raw = c.get_stock_kline(Market.SH, "600519", Period.DAILY, count=200, adjust=Adjust.QFQ)
result = compute_indicators(raw, ["ATR", "CCI", "WR"], tail=30)
# 查看所有可用指标
for info in list_indicators():
print(info["name"], info["description"], info["outputs"])
```
支持 34 个技术指标:
| 指标 | 输入 | 输出列 |
|------|------|--------|
| MACD | close | MACD_DIF, MACD_DEA, MACD_HIST |
| KDJ | close, high, low | KDJ_K, KDJ_D, KDJ_J |
| RSI | close | RSI |
| BOLL | close | BOLL_UPPER, BOLL_MID, BOLL_LOWER |
| DMI | close, high, low | DMI_PDI, DMI_MDI, DMI_ADX, DMI_ADXR |
| ATR | close, high, low | ATR |
| WR | close, high, low | WR1, WR2 |
| CCI | close, high, low | CCI |
| BIAS | close | BIAS1, BIAS2, BIAS3 |
| OBV | close, vol | OBV |
| VR | close, vol | VR |
| EMV | high, low, vol | EMV, EMV_MA |
| MFI | close, high, low, vol | MFI |
| BRAR | open, close, high, low | AR, BR |
| ASI | open, close, high, low | ASI, ASI_MA |
| TRIX | close | TRIX, TRIX_MA |
| DPO | close | DPO, DPO_MA |
| MTM | close | MTM, MTM_MA |
| ROC | close | ROC, ROC_MA |
| EXPMA | close | EXPMA_12, EXPMA_50 |
| BBI | close | BBI |
| PSY | close | PSY, PSY_MA |
| DFMA | close | DFMA_DIF, DFMA_DMA |
| CR | close, high, low | CR |
| KTN | close, high, low | KTN_UPPER, KTN_MID, KTN_LOWER |
| XSII | close, high, low | XSII_TD1, XSII_TD2, XSII_TD3, XSII_TD4 |
| MASS | high, low | MASS, MASS_MA |
| TAQ | high, low | TAQ_UP, TAQ_MID, TAQ_DOWN |
| ZHUOYAO | close | ZY_LONG, ZY_MID, ZY_SHORT, ZY_TREND |
| BIAS_SIGNAL | close | BS_X, BS_SMA, BS_LMA |
| SAR | high, low | SAR(抛物线转向/动态止损位) |
| VWAP | close, high, low, vol | VWAPN日滚动成交量加权均价) |
| AROON | high, low | AROON_UP, AROON_DOWN, AROON_OSC |
| FK | close | FKEMA(2) 突破斜率外推 EMA(42) |
### 分时
```python
with MacClient.from_best_host() as c:
df = c.get_tick_chart(Market.SH, "600519") # 单日分时
df = c.get_tick_charts(Market.SH, "600519", days=3) # 多日分时(最多5天)
df = c.get_chart_sampling(Market.SH, "600519") # 240点缩略采样
```
### 逐笔成交
```python
with MacClient.from_best_host() as c:
df = c.get_transactions(Market.SH, "600519", count=100)
df = c.get_transactions(Market.SH, "600519", count=100, date=20250115)
```
### 板块
```python
from easy_tdx import BoardSortColumn, BoardType
with MacClient.from_best_host() as c:
df = c.get_board_list(BoardType.GN) # 概念板块
df = c.get_board_list(sort_column=BoardSortColumn.SPEED) # 按涨速排序取涨速%
df = c.get_board_members("881001", sort_type=SortType.CHANGE_PCT)
df = c.get_belong_board(Market.SZ, "000001") # 个股所属板块
# 板块汇总:成交额、主力净流入、涨跌家数
summary = c.get_board_summary("881001")
# summary = {
# "member_count": 82,
# "amount": 5823456000.0, # 板块总成交额(元)
# "vol": 412356789, # 板块总成交量(股)
# "main_net_amount": -123456.0, # 当日主力净流入
# "main_net_3d": -567890.0, # 近3日主力净流入
# "main_net_5d": -234567.0, # 近5日主力净流入
# "up_count": 45,
# "down_count": 37,
# "members": DataFrame(...), # 成分股明细
# }
# 板块涨跌幅排行榜
df = c.get_board_ranking(BoardType.HY, top_n=10, sort_by="change_pct")
df = c.get_board_ranking(BoardType.GN, top_n=20, sort_by="main_net_amount")
# 返回列:code, name, change_pct, amount, vol, main_net_amount, up_count, down_count, member_count
# 板块 N 日涨跌幅排行(支持指定截止日期,默认全部)
df = c.get_board_change_ranking(BoardType.HY, days=20)
df = c.get_board_change_ranking(BoardType.GN, target_date=20250530, days=10, top_n=15)
# 返回列:code, name, close_end, close_start, change_pct
```
### 资金流向
```python
with MacClient.from_best_host() as c:
df = c.get_capital_flow(Market.SH, "600519")
```
返回列:`date, main_in, main_out, main_net, small_in/out/net, mid_in/out/net, large_in/out/net`
### 监控
```python
with MacClient.from_best_host() as c:
df = c.get_auction(Market.SH, "600519") # 集合竞价
df = c.get_unusual(Market.SH) # 市场异动
df = c.get_symbol_info(Market.SZ, "000001") # 个股特征快照
df = c.get_server_info() # 服务器交易时段
```
`get_unusual` 返回列含 `unusual_type`(类型码)与 `desc`(中文描述),共 19 种类型
(主力买卖/加速拉升/急速拉升/盘中强弱/竞价异动/涨跌停/大单盘口等)。类型码→名称
可用顶层常量映射:
```python
from easy_tdx import UNUSUAL_TYPE_NAMES
df["type_name"] = df["unusual_type"].map(UNUSUAL_TYPE_NAMES)
```
## 扩展市场
```python
from datetime import date
from easy_tdx import MacExClient, ExMarket, Period
with MacExClient.from_best_host() as c:
count = c.goods_count(ExMarket.HK_MAIN_BOARD)
df = c.goods_list(ExMarket.HK_MAIN_BOARD, start=0, count=50)
df = c.goods_kline(ExMarket.US_STOCK, "AAPL", Period.DAILY, count=10)
df = c.goods_quotes([(ExMarket.HK_MAIN_BOARD, "00700")])
df = c.goods_tick_chart(ExMarket.HK_MAIN_BOARD, "00700")
df = c.goods_transaction(ExMarket.HK_MAIN_BOARD, "00700", count=100)
df = c.goods_transaction_all(ExMarket.HK_MAIN_BOARD, "00700", date(2026, 7, 3)) # 港股当日全部逐笔
```
> **逐笔成交排序**:通达信协议为**倒序**——`start=0` 指向最新一笔(收盘方向),`count=2000` 默认只取最近 2000 笔。港股单日成交常达数万笔(如 02715 约 1.3 万笔/日),若需当日全部成交,用 `goods_transaction_all`(仅港股股票类市场,自动按 1800/页翻页取全天,安全上限 9 万条;返回协议原生倒序,需正序展示自行 `df.iloc[::-1]`)。
## 统一客户端
```python
from easy_tdx import UnifiedTdxClient, ExMarket, Market, Period
with UnifiedTdxClient() as client:
# A 股 -- 自动路由到 MacClient
df = client.get_stock_kline(Market.SH, "600519", Period.DAILY, count=5)
df = client.get_stock_quotes([(Market.SH, "600519")])
df = client.get_board_list()
# 扩展市场 -- 自动路由到 MacExClient
df = client.goods_kline(ExMarket.HK_MAIN_BOARD, "00700", Period.DAILY, count=5)
```
## 标准协议
```python
from easy_tdx import TdxClient, Market, KlineCategory
with TdxClient.from_best_host() as c:
count = c.get_security_count(Market.SH)
stocks = c.get_security_list(Market.SH, start=0)
quotes = c.get_security_quotes([(Market.SH, "600000"), (Market.SZ, "000001")])
bars = c.get_security_bars(Market.SZ, "002176", KlineCategory.DAY, 0, 100)
minute = c.get_minute_time_data(Market.SH, "600000")
trades = c.get_transaction_data(Market.SH, "600000", 0, 20)
flow = c.get_fund_flow(Market.SH, "600519")
blocks = c.get_block_info("block_gn.dat")
xdxr = c.get_xdxr_info(Market.SH, "600519")
stat = c.get_market_stat()
```
> **资金流口径注意**Issue #55):`get_fund_flow` / `get_history_fund_flow` 按 0x0fb5 逐笔接口的"单笔成交额"分档,而该接口返回的记录是交易所真实逐笔**聚合**后的(实测 000001.SZ 单日约 17:1),分档看的也不是挂单额。结果:高价股小单档可不足成交额 1%、主力档常占 95%+`main_net_inflow` 实质更接近"当日主动买卖总失衡"。东财/同花顺的"主力净流入"基于 L2 逐笔委托、按挂单额分档——两套口径**不可比**(实证同规则选股信号重合度仅约 14%),勿混用于同一张表或同一个因子。
`AsyncTdxClient` 提供对应的 `async def` 方法,接口一一对应。
## SecurityQuote 字段说明
`get_security_quotes()` 返回的 DataFrame 包含以下特殊字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `trading_status` | int | 交易状态标志。`0x8020`(32800) = 停牌,其余值表示正常交易或集合竞价 |
| `open_amount` | float | 集合竞价成交金额(元)。仅个股有效,指数该字段无意义 |
| `server_time` | str | 服务器时间,格式 `HH:MM:SS.mmm` |
| `unknown_2` | int | 指数: 集合竞价成交金额/100;个股: 舍入残差≈0 |
| `unknown_3` | int | 个股: 集合竞价成交金额/100;指数: 负值/无意义 |
| `unknown_5-8` | int | 保留字段,恒为 0 |
检测停牌:
```python
df = c.get_security_quotes([(Market.SH, "600000")])
is_suspended = df.iloc[0]["trading_status"] == 0x8020
```
## 公告检索(巨潮资讯网)
独立数据源(巨潮资讯网 cninfo),无需连接 TDX 行情服务器即可检索公司公告。
标准库 urllib 实现,零额外依赖。
```python
from easy_tdx.cninfo import CninfoClient
client = CninfoClient()
# 检索公告(默认 30 条,最新在前)
df = client.get_announcements("688017")
# → DataFrame[title, type, date, url, code, org_id, announcement_id, announcement_time, pdf_url]
# 翻页 + 自定义数量
df = client.get_announcements("601088", count=10, page=2)
# 返回示例(url 含 4 参数可直点打开,pdf_url 为 PDF 直链):
# title type date url pdf_url
# 0 关于召开2025年年度股东大会... 股东大会 2025-06-14 .../detail?stockCode=688017&announcementId=... http://static.cninfo.com.cn/.../xxx.PDF
# 1 2024年年度报告 PDF 2025-03-28 .../detail?stockCode=688017&announcementId=... http://static.cninfo.com.cn/.../yyy.PDF
```
> - ``type`` 优先取 cninfo 的 ``announcementTypeName``;该字段对很多公告为 null
> (数据源限制),此时回退到 ``adjunctType``(如 "PDF"),再为空给空字符串。
> - ``url`` 必须含 4 参数(``stockCode``/``announcementId``/``orgId``/``announcementTime``
> 才能打开,少参数会 404。
> - orgId 解析沿用 #19 修复:动态拉取官方映射表,查不到回退硬编码规则,
> 保证 601xxx 等非标 orgId 段也能正常查询。
### 下载公告 PDF
```python
# 下载最新一条公告的 PDF 到当前目录
df = client.get_announcements("601088", count=5)
path = client.download_pdf(df.iloc[0]) # 接受 Announcement 或 DataFrame 的一行
print(path) # /abs/path/20260605_1225351400.PDF
# 批量下载
for _, row in df.iterrows():
try:
path = client.download_pdf(row, dest_dir="./pdfs")
except Exception as e:
print(f"跳过(无附件或失败): {e}")
```
## 财报三表(新浪财经)
独立数据源(新浪财经),无需连接 TDX 行情服务器即可获取利润表/资产负债表/现金流量表。
标准库 urllib 实现,零额外依赖。
```python
from easy_tdx.sina import SinaClient
client = SinaClient()
# 利润表(默认 8 期,最新在前)
df = client.get_financial_report("600519", report_type="lrb")
# → DataFrame,每行一期,列 = [报告期, 营业总收入, 营业总收入_同比, ...]
# 资产负债表 / 现金流量表(report_type 也接受中文别名:利润表/资产负债表/现金流量表)
df = client.get_financial_report("600519", report_type="fzb", num=4)
df = client.get_financial_report("600519", report_type="llb", num=4)
# 返回示例(item_value 已转 float,可直接数值计算):
# 报告期 营业总收入 营业总收入_同比 营业收入 营业收入_同比
# 0 2026-03-31 54702912385.23 0.06336 53909252220.51 0.06538
# 1 2025-12-31 174000000000.00 0.10000 NaN NaN
```
> - ``item_value`` 是字符串(新浪原始格式),本实现转 float;空/非数值转 None
> - 有同比的科目附加 ``{科目}_同比`` 列(float 比例,如 0.06336 = +6.3%
> - 大类标题行(如 ``流动资产``,原 ``item_value=""``)保留为 None,反映报表结构
## 实时行情轮询(RealtimeDataFeed
> ⚠️ 通达信协议**没有服务端推送**,只有请求/响应。本模块的「实时」是 **轮询五档快照近似**
> (默认约 3 秒延迟),适合盘中信号提醒、轻量监控;**不适合高频 / 逐笔撮合**。
`EventBus` 自身是纯发布/订阅管道,不会产生数据。要让 `RealtimeStrategy` 跑起来,
需要配合 `RealtimeDataFeed`:它自动完成 `get_stock_quotes → MarketEvent → bus.publish`
```python
import asyncio
from easy_tdx.mac.client import AsyncMacClient
from easy_tdx.realtime import (
EventBus,
RealtimeStrategy,
MarketEvent,
RealtimeDataFeed,
)
class MyStrategy(RealtimeStrategy):
def on_tick(self, event: MarketEvent) -> None:
print(f"{event.market}{event.code} price={event.price} vol={event.volume}")
async def main():
bus = EventBus()
strategy = MyStrategy()
bus.subscribe("SZ000001", strategy.on_tick) # 注意:key 必须带市场前缀
feed = RealtimeDataFeed(
bus=bus,
symbols=[(0, "000001"), (1, "600519")], # [(Market.SZ, code), ...],单批 ≤80 只
interval=3.0, # 轮询间隔(秒),下限 0.1
dedup=True, # (price, volume) 未变的标的跳过发布
# sessions=(), # 传空 tuple 表示全天轮询;默认仅 9:15-11:30 / 13:00-15:00
)
async with AsyncMacClient.from_best_host() as client:
await feed.run_async(client) # Ctrl+C 或 feed.stop() 退出
asyncio.run(main())
```
同步客户端(`MacClient`)用 `run_sync`,feed 会把阻塞调用丢到线程池,不卡事件循环:
```python
from easy_tdx.mac.client import MacClient
with MacClient.from_best_host() as client:
feed.run_sync(client)
```
> **关键坑(issue #34**
> - 订阅 key 必须与 `publish` 内部拼的 `f"{market}{code}"` 一致,即 `"SZ000001"`
> 而不是 `"000001"`;否则事件分发匹配不到。
> - 传给 `subscribe` 的必须是**实例的绑定方法** `strategy.on_tick`
> 不是未绑定的类方法 `MyStrategy.on_tick`。
> - 整个流程跑在 `asyncio.run()` 里,否则协程不会被调度。