Files
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

16 KiB
Raw Permalink Blame History

Python API 使用指南

连接管理

所有客户端支持 from_best_host() 自动选最低延迟服务器:

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 协议(推荐)

报价

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 线(支持复权)

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 条带指标的结果:

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)

分时

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点缩略采样

逐笔成交

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)

板块

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

资金流向

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

监控

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 种类型 (主力买卖/加速拉升/急速拉升/盘中强弱/竞价异动/涨跌停/大单盘口等)。类型码→名称 可用顶层常量映射:

from easy_tdx import UNUSUAL_TYPE_NAMES

df["type_name"] = df["unusual_type"].map(UNUSUAL_TYPE_NAMES)

扩展市场

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])。

统一客户端

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)

标准协议

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

检测停牌:

df = c.get_security_quotes([(Market.SH, "600000")])
is_suspended = df.iloc[0]["trading_status"] == 0x8020

公告检索(巨潮资讯网)

独立数据源(巨潮资讯网 cninfo),无需连接 TDX 行情服务器即可检索公司公告。 标准库 urllib 实现,零额外依赖。

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

# 下载最新一条公告的 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 实现,零额外依赖。

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

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 会把阻塞调用丢到线程池,不卡事件循环:

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() 里,否则协程不会被调度。