mirror of
https://ghfast.top/https://github.com/aeroxw/easy_tdx_max.git
synced 2026-09-12 14:34:18 +08:00
- 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>
422 lines
16 KiB
Markdown
422 lines
16 KiB
Markdown
# 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 | VWAP(N日滚动成交量加权均价) |
|
||
| AROON | high, low | AROON_UP, AROON_DOWN, AROON_OSC |
|
||
| FK | close | FK(EMA(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()` 里,否则协程不会被调度。
|
||
|