mirror of
https://ghfast.top/https://github.com/aeroxw/easy_tdx_max.git
synced 2026-09-12 21:34:21 +08:00
三个根因(全部实测核实): 1. Category 22 直连接口为虚构协议——52 台已知服务器中 46 台可达的 全部仅回 2 字节空包,从未成功过;移除死代码与臆造解析格式。 2. 历史逐笔接口当日数据要收盘清算后才有,日 K 盘中已含当日 bar, 故 start=0 的最新一行恒为全 0——当日 bar 改走当日实时逐笔接口。 3. main_net_inflow 此前仅为 dataclass property,asdict 静默丢弃, 返回 DataFrame 无主力净额列——新增 _fund_flow_df_with_net 物化 (history 紧随 date 列、当日快照放首列)。 sync + async 双客户端同步修改;更新示例与三份文档;重写/新增回归 测试(当日实时逐笔路径、主力净额列断言)。
131 lines
4.5 KiB
Python
131 lines
4.5 KiB
Python
"""协议底层修复验证(针对 2026-04-15 审查结论)。"""
|
|
|
|
import struct
|
|
|
|
from easy_tdx.codec.price_rules import compute_price_limits
|
|
from easy_tdx.commands.security_bars import GetSecurityBarsCmd
|
|
from easy_tdx.commands.security_list import GetSecurityListCmd
|
|
from easy_tdx.commands.security_quotes import GetSecurityQuotesCmd
|
|
from easy_tdx.models.enums import KlineCategory, Market
|
|
|
|
|
|
def test_security_bars_exact_layout():
|
|
"""验证 K 线请求包布局与旧版 working bytes 完全一致。"""
|
|
cmd = GetSecurityBarsCmd(Market.SH, "600000", KlineCategory.DAY, 0, 10)
|
|
req = cmd.build_request()
|
|
# Header: 0x010C, 0x01016408, 0x1C, 0x1C
|
|
# Payload: 0x052D, 1 (Market.SH), "600000", 4 (KlineCategory.DAY), 1, 0 (start), 10, 0, 0, 0
|
|
expected = struct.pack(
|
|
"<HIHHHH6sHHHHIIH",
|
|
0x010C,
|
|
0x01016408,
|
|
0x001C,
|
|
0x001C,
|
|
0x052D,
|
|
1,
|
|
b"600000",
|
|
4,
|
|
1,
|
|
0,
|
|
10,
|
|
0,
|
|
0,
|
|
0,
|
|
)
|
|
assert req == expected
|
|
assert len(req) == 38
|
|
|
|
|
|
def test_security_list_request_length():
|
|
"""验证证券列表请求包载荷长度为 6 字节。"""
|
|
cmd = GetSecurityListCmd(Market.SH, 0)
|
|
req = cmd.build_request()
|
|
# Header 12 + Payload 6 = 18
|
|
assert len(req) == 18
|
|
payload_len = struct.unpack("<H", req[6:8])[0]
|
|
assert payload_len == 6
|
|
|
|
|
|
def test_security_quotes_limit_mapping():
|
|
"""验证涨跌停价现在返回 None,且 pre_close 正确。"""
|
|
from easy_tdx.codec.price import put_price
|
|
|
|
cmd = GetSecurityQuotesCmd([(Market.SH, "600000")])
|
|
|
|
# 构造响应报文
|
|
body = bytearray(b"\x00\x00")
|
|
body.extend(struct.pack("<H", 1))
|
|
|
|
# Record: Market(B), Code(6s), Active1(H) + ...
|
|
body.extend(struct.pack("<B6sH", 1, b"600000", 0))
|
|
|
|
body.extend(put_price(1010)) # price_raw
|
|
body.extend(put_price(-5)) # last_close_diff
|
|
body.extend(put_price(0))
|
|
body.extend(put_price(0))
|
|
body.extend(put_price(0))
|
|
body.extend(put_price(12345))
|
|
body.extend(put_price(-1010))
|
|
body.extend(put_price(100))
|
|
body.extend(put_price(10))
|
|
body.extend(struct.pack("<I", 10000))
|
|
body.extend(put_price(50))
|
|
body.extend(put_price(50))
|
|
body.extend(put_price(2))
|
|
body.extend(put_price(3))
|
|
for _ in range(20):
|
|
body.extend(put_price(0))
|
|
body.extend(struct.pack("<H", 0))
|
|
body.extend(put_price(96))
|
|
body.extend(put_price(-106))
|
|
body.extend(put_price(0))
|
|
body.extend(put_price(0))
|
|
body.extend(struct.pack("<hH", 0, 0))
|
|
|
|
quotes = cmd.parse_response(bytes(body))
|
|
q = quotes[0]
|
|
assert q.limit_up is None
|
|
assert q.limit_down is None
|
|
assert q.pre_close == 10.05
|
|
|
|
|
|
def test_security_quotes_server_time_format():
|
|
"""服务器时间应按“小时 + 百万分之一小时”统一解码。"""
|
|
from easy_tdx.commands.security_quotes import _format_server_time
|
|
|
|
assert _format_server_time(9500000) == "09:30:00.000"
|
|
assert _format_server_time(14999212) == "14:59:57.163"
|
|
|
|
|
|
def test_compute_price_limits_for_stocks():
|
|
"""普通股票 / ST / 创业板 / 科创板 / 北交所规则应可正确计算。"""
|
|
assert compute_price_limits(Market.SH, "600000", "浦发银行", 10.05) == (11.06, 9.05)
|
|
assert compute_price_limits(Market.SH, "603939", "ST益丰", 22.53) == (23.66, 21.4)
|
|
assert compute_price_limits(Market.SZ, "301269", "华大九天", 86.36) == (103.63, 69.09)
|
|
assert compute_price_limits(Market.SH, "688981", "中芯国际", 101.52) == (121.82, 81.22)
|
|
assert compute_price_limits(Market.BJ, "920002", "万达轴承", 84.36) == (109.67, 59.05)
|
|
|
|
|
|
def test_compute_price_limits_for_indices():
|
|
"""指数与板块类代码不应计算涨跌停。"""
|
|
assert compute_price_limits(Market.SH, "999999", "上证指数", 4026.63) == (None, None)
|
|
assert compute_price_limits(Market.SH, "880005", "涨跌家数", 1841.0) == (None, None)
|
|
assert compute_price_limits(Market.SZ, "399001", "深证成指", 10412.63) == (None, None)
|
|
|
|
|
|
def test_compute_price_limits_for_newly_listed_stocks():
|
|
"""上市初期限价窗口应返回 None。"""
|
|
assert compute_price_limits(Market.SH, "600001", "主板新股", 10.0, listed_days=5) == (
|
|
None,
|
|
None,
|
|
)
|
|
assert compute_price_limits(Market.SH, "600001", "主板新股", 10.0, listed_days=6) == (11.0, 9.0)
|
|
assert compute_price_limits(Market.BJ, "920002", "北交所新股", 84.36, listed_days=1) == (
|
|
None,
|
|
None,
|
|
)
|
|
assert compute_price_limits(Market.BJ, "920002", "北交所新股", 84.36, listed_days=2) == (
|
|
109.67,
|
|
59.05,
|
|
)
|