feat: add examples (01-08), fix index bars parsing, add ruff hook

- Add example scripts for all API categories (connection, market info,
  kline, minute, transaction, finance, block, fund flow)
- Fix GetIndexBarsCmd: index bar records have 4 extra bytes (advance/
  decline counts) that were not consumed, causing pos drift and
  corrupted dates/volumes for all records after the first
- Fix price_limits.py example (SecurityQuote has no name attr)
- Fix finance_info.py display (scientific notation -> formatted numbers)
- Add PostToolUse ruff hook (scripts/ruff_hook.py)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
GitHub
2026-05-21 18:36:50 +08:00
co-authored by Claude Opus 4.7
parent ace1099ab0
commit 7fd6e610cf
27 changed files with 735 additions and 47 deletions
+24
View File
@@ -0,0 +1,24 @@
"""演示:异步客户端连接与基本用法。"""
import asyncio
from xmtdx import AsyncTdxClient, Market, KlineCategory
async def main():
# 手动指定服务器
async with AsyncTdxClient("180.153.18.170") as c:
count = await c.get_security_count(Market.SH)
print(f"沪市证券总数: {count}")
# 自动优选服务器
async with AsyncTdxClient.from_best_host() as c:
bars = await c.get_security_bars(
Market.SH, "600000", KlineCategory.DAY, 0, 5
)
for bar in bars:
print(f"{bar.year}-{bar.month:02d}-{bar.day:02d} "
f"开:{bar.open:.2f} 高:{bar.high:.2f} "
f"低:{bar.low:.2f} 收:{bar.close:.2f}")
asyncio.run(main())
@@ -0,0 +1,13 @@
"""演示:自动从候选服务器中选延迟最低的建立连接。"""
from xmtdx import TdxClient, Market
# 方式一:手动指定服务器
with TdxClient("180.153.18.170") as c:
print(f"已连接到 {c._host}:{c._port}")
# 方式二:自动优选最低延迟服务器
with TdxClient.from_best_host() as c:
print(f"已自动选择最优服务器: {c._host}:{c._port}")
count = c.get_security_count(Market.SH)
print(f"沪市证券总数: {count}")
+9
View File
@@ -0,0 +1,9 @@
"""演示:测量多台通达信服务器延迟并排序。"""
import pandas as pd
from xmtdx import TdxClient
results = TdxClient.ping_all()
df = pd.DataFrame(results, columns=["服务器", "延迟(s)"])
df["延迟(ms)"] = df["延迟(s)"] * 1000
print(df[["服务器", "延迟(ms)"]].to_string(index=False))
+19
View File
@@ -0,0 +1,19 @@
"""演示:获取全市场涨跌统计概况。"""
import pandas as pd
from xmtdx import TdxClient
with TdxClient.from_best_host() as c:
stat = c.get_market_stat()
df = pd.DataFrame([{
"上涨": stat.up_count,
"下跌": stat.down_count,
"平盘": stat.neutral_count,
"停牌(估算)": stat.suspended_count,
"总计": stat.total_count,
"成交额(亿)": round(stat.total_amount / 1e8, 2),
"总市值(万亿)": round(stat.total_market_cap / 1e12, 4),
"涨停": stat.limit_up_count,
"跌停": stat.limit_down_count,
}])
print(df.T.to_string(header=False))
@@ -0,0 +1,9 @@
"""演示:获取市场证券总数。"""
from xmtdx import TdxClient, Market
with TdxClient.from_best_host() as c:
sh_count = c.get_security_count(Market.SH)
sz_count = c.get_security_count(Market.SZ)
print(f"沪市证券总数: {sh_count}")
print(f"深市证券总数: {sz_count}")
+40
View File
@@ -0,0 +1,40 @@
"""演示:获取市场证券列表(分页)。
展示 SecurityInfo 全部字段的中文映射与表结构。
"""
import pandas as pd
from xmtdx import TdxClient, Market
with TdxClient.from_best_host() as c:
stocks = c.get_security_list(Market.SH, start=0)
# 表结构说明
print("=" * 70)
print("SecurityInfo 表结构(字段中英文对照)")
print("=" * 70)
schema = pd.DataFrame([
{"英文字段": "market", "中文含义": "市场", "类型": "Market", "说明": "SZ=深圳 SH=上海 BJ=北京"},
{"英文字段": "code", "中文含义": "证券代码", "类型": "str", "说明": "6位代码,如 600000"},
{"英文字段": "name", "中文含义": "证券名称", "类型": "str", "说明": "GBK 解码"},
{"英文字段": "volunit", "中文含义": "成交量单位", "类型": "int", "说明": "1手 = volunit 股"},
{"英文字段": "decimal_point", "中文含义": "价格小数位", "类型": "int", "说明": "通常为 2"},
{"英文字段": "pre_close", "中文含义": "昨收价", "类型": "float", "说明": "通达信自定义浮点"},
{"英文字段": "industry_tdx", "中文含义": "通达信行业", "类型": "str", "说明": "需 get_security_list_all()"},
{"英文字段": "industry_sw", "中文含义": "申万行业", "类型": "str", "说明": "需 get_security_list_all()"},
])
print(schema.to_string(index=False))
# 全字段中文 DataFrame
print(f"\n沪市第 1 页,共 {len(stocks)} 只:")
df = pd.DataFrame([{
"市场": s.market.name,
"代码": s.code,
"名称": s.name,
"成交量单位(股/手)": s.volunit,
"价格小数位": s.decimal_point,
"昨收价": s.pre_close,
"通达信行业": s.industry_tdx or "",
"申万行业": s.industry_sw or "",
} for s in stocks])
print(df.head(20).to_string(index=False))
@@ -0,0 +1,46 @@
"""演示:获取沪深 A 股完整列表(含行业映射)。
展示 SecurityInfo 全部字段(含扩展行业字段)的中文映射。
注意:此方法需要拉取 tdxhy.cfg 并遍历全部证券,耗时较长。
"""
import logging
import pandas as pd
from xmtdx import TdxClient
# 启用日志,查看分页进度
logging.basicConfig(level=logging.INFO, format="%(message)s")
# timeout 调大到 30 秒,避免全量拉取时分页请求超时
with TdxClient.from_best_host(timeout=30.0) as c:
all_stocks = c.get_security_list_all()
# 表结构说明
print("=" * 70)
print("SecurityInfo 表结构(字段中英文对照)")
print("=" * 70)
schema = pd.DataFrame([
{"英文字段": "market", "中文含义": "市场", "类型": "Market", "说明": "SZ=深圳 SH=上海 BJ=北京"},
{"英文字段": "code", "中文含义": "证券代码", "类型": "str", "说明": "6位代码,如 600000"},
{"英文字段": "name", "中文含义": "证券名称", "类型": "str", "说明": "GBK 解码"},
{"英文字段": "volunit", "中文含义": "成交量单位", "类型": "int", "说明": "1手 = volunit 股"},
{"英文字段": "decimal_point", "中文含义": "价格小数位", "类型": "int", "说明": "通常为 2"},
{"英文字段": "pre_close", "中文含义": "昨收价", "类型": "float", "说明": "通达信自定义浮点"},
{"英文字段": "industry_tdx", "中文含义": "通达信行业", "类型": "str", "说明": "如 T1001,来自 tdxhy.cfg"},
{"英文字段": "industry_sw", "中文含义": "申万行业", "类型": "str", "说明": "如 X500102,来自 tdxhy.cfg"},
])
print(schema.to_string(index=False))
# 全字段中文 DataFrame
print(f"\n沪深 A 股总数: {len(all_stocks)}")
df = pd.DataFrame([{
"市场": s.market.name,
"代码": s.code,
"名称": s.name,
"成交量单位(股/手)": s.volunit,
"价格小数位": s.decimal_point,
"昨收价": s.pre_close,
"通达信行业": s.industry_tdx or "",
"申万行业": s.industry_sw or "",
} for s in all_stocks])
print(df.head(20).to_string(index=False))
@@ -0,0 +1,25 @@
"""演示:批量获取实时五档行情。最多支持 80 只/次。"""
import pandas as pd
from xmtdx import TdxClient, Market
with TdxClient.from_best_host() as c:
stocks = [
(Market.SH, "600000"), # 浦发银行
(Market.SH, "600519"), # 贵州茅台
(Market.SZ, "000001"), # 平安银行
(Market.SZ, "000858"), # 五粮液
]
quotes = c.get_security_quotes(stocks)
df = pd.DataFrame([{
"代码": q.code,
"现价": q.price,
"涨跌幅%": (q.price - q.pre_close) / q.pre_close * 100,
"今开": q.open,
"最高": q.high,
"最低": q.low,
"昨收": q.pre_close,
"成交量(手)": q.vol,
"成交额": q.amount,
} for q in quotes])
print(df.to_string(index=False))
+25
View File
@@ -0,0 +1,25 @@
"""演示:获取指数 K 线数据。
常用指数代码:
上证指数: Market.SH, "000001"
深证成指: Market.SZ, "399001"
创业板指: Market.SZ, "399006"
"""
import pandas as pd
from xmtdx import TdxClient, Market, KlineCategory
with TdxClient.from_best_host() as c:
bars = c.get_index_bars(Market.SH, "999999", KlineCategory.DAY, 0, 10)
df = pd.DataFrame([{
"日期": f"{b.year}-{b.month:02d}-{b.day:02d}",
"开盘": b.open,
"最高": b.high,
"最低": b.low,
"收盘": b.close,
"成交量": b.vol,
"成交额": b.amount,
} for b in reversed(bars)])
print("上证指数 日K线:")
fmt = {"成交量": lambda x: f"{x:,.0f}", "成交额": lambda x: f"{x:,.0f}"}
print(df.to_string(index=False, formatters=fmt))
+23
View File
@@ -0,0 +1,23 @@
"""演示:获取个股 K 线数据。
K 线类别:
KlineCategory.MIN_1 / MIN_5 / MIN_15 / MIN_30 / MIN_60
KlineCategory.DAY / WEEK / MONTH / YEAR
"""
import pandas as pd
from xmtdx import TdxClient, Market, KlineCategory
with TdxClient.from_best_host() as c:
bars = c.get_security_bars(Market.SZ, "002176", KlineCategory.DAY, 0, 100)
df = pd.DataFrame([{
"日期": f"{b.year}-{b.month:02d}-{b.day:02d}",
"开盘": b.open,
"最高": b.high,
"最低": b.low,
"收盘": b.close,
"成交量": b.vol,
"成交额": b.amount,
} for b in reversed(bars)])
print("上证指数 日K线:")
print(df.to_string(index=False))
+15
View File
@@ -0,0 +1,15 @@
"""演示:获取历史某日分时数据。date 参数为 YYYYMMDD 格式的整数。"""
import pandas as pd
from xmtdx import TdxClient, Market
with TdxClient.from_best_host() as c:
date = 20250110
bars = c.get_history_minute_time_data(Market.SH, "600000", date)
df = pd.DataFrame([{
"序号": i + 1,
"价格": bar.price,
"成交量": bar.vol,
} for i, bar in enumerate(bars)])
print(f"浦发银行 {date} 分时数据,共 {len(df)} 条:")
print(df.to_string(index=False))
+14
View File
@@ -0,0 +1,14 @@
"""演示:获取今日分时数据(240 条)。"""
import pandas as pd
from xmtdx import TdxClient, Market
with TdxClient.from_best_host() as c:
bars = c.get_minute_time_data(Market.SH, "600000")
df = pd.DataFrame([{
"序号": i + 1,
"价格": bar.price,
"成交量": bar.vol,
} for i, bar in enumerate(bars)])
print(f"浦发银行今日分时,共 {len(df)} 条:")
print(df.to_string(index=False))
@@ -0,0 +1,16 @@
"""演示:获取历史逐笔成交数据。date 参数为 YYYYMMDD 格式的整数。"""
import pandas as pd
from xmtdx import TdxClient, Market
with TdxClient.from_best_host() as c:
date = 20250110
records = c.get_history_transaction_data(Market.SH, "600000", date, 0, 20)
df = pd.DataFrame([{
"时间": f"{r.hour:02d}:{r.minute:02d}",
"成交价": r.price,
"成交量": r.vol,
"方向": "" if r.buyorsell == 0 else "",
} for r in records])
print(f"浦发银行 {date} 最近 {len(df)} 笔成交:")
print(df.to_string(index=False))
@@ -0,0 +1,15 @@
"""演示:获取当日逐笔成交数据。"""
import pandas as pd
from xmtdx import TdxClient, Market
with TdxClient.from_best_host() as c:
records = c.get_transaction_data(Market.SH, "600000", 0, 20)
df = pd.DataFrame([{
"时间": f"{r.hour:02d}:{r.minute:02d}",
"成交价": r.price,
"成交量": r.vol,
"方向": "" if r.buyorsell == 0 else "",
} for r in records])
print(f"浦发银行最近 {len(df)} 笔成交:")
print(df.to_string(index=False))
+78
View File
@@ -0,0 +1,78 @@
"""演示:获取公司信息目录与各个分类的详细内容。"""
import pandas as pd
from xmtdx import TdxClient, Market
CODE = "600519"
NAME = "贵州茅台"
MARKET = Market.SH
# 要展示的分类,按需注释/取消注释
SHOW_CATEGORIES = [
"最新提示",
"公司概况",
"财务分析",
"股本结构",
"股东研究",
"机构持股",
"分红融资",
"高管治理",
"资金动向",
"资本运作",
"热点题材",
"公司公告",
"公司报道",
"经营分析",
"行业分析",
"研报评级",
]
def show_categories(categories):
"""显示公司信息目录。"""
df = pd.DataFrame([{
"目录名": cat.name,
"文件名": cat.filename,
"起始偏移": cat.start,
"内容长度": cat.length,
} for cat in categories])
print(f"{NAME} 公司信息目录:")
print(df.to_string(index=False))
def show_category_content(client, categories, category_name, max_chars=500):
"""获取并展示指定分类的内容。"""
cat = next((c for c in categories if c.name == category_name), None)
if not cat:
print(f" 未找到分类: {category_name}")
return
content = client.get_company_info_content(
MARKET, CODE, cat.filename, cat.start, cat.length
)
text = content.strip()
if len(text) > max_chars:
text = text[:max_chars] + f"\n... (共 {len(content.strip())} 字,仅显示前 {max_chars} 字)"
print(f"\n{'='*60}")
print(f"{cat.name}】 (共 {cat.length} 字节)")
print(f"{'='*60}")
print(text)
def show_all_categories(client, categories):
"""依次展示所有 SHOW_CATEGORIES 中列出的分类内容。"""
for name in SHOW_CATEGORIES:
show_category_content(client, categories, name)
with TdxClient.from_best_host() as c:
categories = c.get_company_info_category(MARKET, CODE)
# 1. 显示目录
show_categories(categories)
# 2. 显示所有分类内容(每个分类默认只显示前500字)
show_all_categories(c, categories)
# 3. 也可以单独获取某个分类的完整内容,例如:
# show_category_content(c, categories, "公司概况", max_chars=99999)
+21
View File
@@ -0,0 +1,21 @@
"""演示:获取最新财务数据。"""
import pandas as pd
from xmtdx import TdxClient, Market
with TdxClient.from_best_host() as c:
info = c.get_finance_info(Market.SH, "600519")
df = pd.DataFrame([
{"项目": "总股本(万股)", "数值": info.zong_guben},
{"项目": "流通股本(万股)", "数值": info.liutong_guben},
{"项目": "每股净资产", "数值": info.meigujing_zichan},
{"项目": "净利润(元)", "数值": info.jing_lirun},
{"项目": "主营收入(元)", "数值": info.zhuying_shouru},
{"项目": "主营利润(元)", "数值": info.zhuying_lirun},
{"项目": "净资产(元)", "数值": info.jing_zichan},
{"项目": "总资产(元)", "数值": info.zong_zichan},
{"项目": "股东人数", "数值": info.gudong_renshu},
{"项目": "上市日期", "数值": info.ipo_date},
])
print("贵州茅台 最新财务数据:")
print(df.to_string(index=False, formatters={"数值": lambda x: f"{x:,.0f}"}))
+23
View File
@@ -0,0 +1,23 @@
"""演示:计算个股涨跌停价格。"""
import pandas as pd
from xmtdx import TdxClient, Market
CODE = "600519"
NAME = "贵州茅台"
with TdxClient.from_best_host() as c:
quotes = c.get_security_quotes([(Market.SH, CODE)])
if quotes:
q = quotes[0]
limit_up, limit_down = c.get_price_limits(
Market.SH, CODE, NAME, q.pre_close
)
df = pd.DataFrame([{
"代码": CODE,
"名称": NAME,
"昨收": q.pre_close,
"涨停价": limit_up,
"跌停价": limit_down,
}])
print(df.to_string(index=False))
+17
View File
@@ -0,0 +1,17 @@
"""演示:获取除权除息历史记录。"""
import pandas as pd
from xmtdx import TdxClient, Market, XDXR_CATEGORY_NAMES
with TdxClient.from_best_host() as c:
records = c.get_xdxr_info(Market.SH, "600519")
df = pd.DataFrame([{
"日期": f"{r.year}-{r.month:02d}-{r.day:02d}",
"类型": XDXR_CATEGORY_NAMES.get(r.category, f"未知({r.category})"),
"每股分红(元)": r.fenhong,
"送转股比例": r.songzhuangu,
"配股价": r.peigujia,
"配股比例": r.peigu,
} for r in records])
print(f"贵州茅台 除权除息记录,共 {len(df)} 条:")
print(df.tail(10).to_string(index=False))
+21
View File
@@ -0,0 +1,21 @@
"""演示:获取板块信息(行业、概念、风格)。
常用板块文件:
'block_zs.dat' - 行业/指数板块
'block_gn.dat' - 概念板块
'block_fg.dat' - 风格板块
"""
import pandas as pd
from xmtdx import TdxClient
with TdxClient.from_best_host() as c:
blocks = c.get_block_info("block_gn.dat")
df = pd.DataFrame([{
"板块名称": b.name,
"分类": b.category,
"成分股数": b.count,
"代码(前5)": ", ".join(b.codes[:5]),
} for b in blocks])
print(f"概念板块,共 {len(df)} 个:")
print(df.head(20).to_string(index=False))
+19
View File
@@ -0,0 +1,19 @@
"""演示:获取个股当日资金流向(基于 L1 逐笔数据统计)。
资金分为四级: 超大(>100万)、大(20-100万)、中(4-20万)、小(<4万)。
"""
import pandas as pd
from xmtdx import TdxClient, Market
with TdxClient.from_best_host() as c:
flow = c.get_fund_flow(Market.SH, "600519")
df = pd.DataFrame([
{"级别": "超大单", "流入(亿)": flow.super_in / 1e8, "流出(亿)": flow.super_out / 1e8},
{"级别": "大单", "流入(亿)": flow.large_in / 1e8, "流出(亿)": flow.large_out / 1e8},
{"级别": "中单", "流入(亿)": flow.medium_in / 1e8, "流出(亿)": flow.medium_out / 1e8},
{"级别": "小单", "流入(亿)": flow.small_in / 1e8, "流出(亿)": flow.small_out / 1e8},
])
df["净流入(亿)"] = df["流入(亿)"] - df["流出(亿)"]
print("贵州茅台 当日资金流向:")
print(df.to_string(index=False))
@@ -0,0 +1,15 @@
"""演示:获取个股历史日线资金流向序列。"""
import pandas as pd
from xmtdx import TdxClient, Market
with TdxClient.from_best_host() as c:
flows = c.get_history_fund_flow(Market.SH, "600519", 0, 10)
df = pd.DataFrame([{
"日期": f"{f.year}-{f.month:02d}-{f.day:02d}",
"超大单净流入(亿)": (f.super_in - f.super_out) / 1e8,
"大单净流入(亿)": (f.large_in - f.large_out) / 1e8,
"主力净流入(亿)": f.main_net_inflow / 1e8,
} for f in flows])
print(f"贵州茅台 历史资金流向,共 {len(df)} 天:")
print(df.to_string(index=False))