mirror of
https://ghfast.top/https://github.com/aeroxw/easy-tdx.git
synced 2026-09-12 16:54:17 +08:00
Fix security list pre-close decoding
This commit is contained in:
@@ -226,7 +226,7 @@ main_net_inflow
|
||||
|---|------|------|------|
|
||||
| 1 | `xdxr_info` | 循环内始终读 `body[:7]`,所有记录字段相同 | 改为从当前 `pos` 读取,pos 正确推进 |
|
||||
| 2 | `security_list` | GBK 解码截断时 crash | `decode('gbk', errors='replace')` |
|
||||
| 3 | `security_list` | `pre_close` 错用成交量解码函数 | 改用价格解码 |
|
||||
| 3 | `security_list` | `pre_close` 误当作整数价格 `/100` | 恢复为通达信自定义浮点解码 |
|
||||
| 4 | `transaction` | 最后一个字段被 `_` 丢弃 | 保留为 `unknown_last` |
|
||||
| 5 | `minute_time` | `reversed1` 字段被丢弃 | 保留为 `unknown_1` |
|
||||
| 6 | `xdxr_info` | 股本字段用 `float(uint32)` 直解,差约 374 倍 | 改用 `_decode_volume`(通达信自定义浮点),单位万股,与 `FinanceInfo` 完全吻合 |
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
"""获取证券列表命令(每页最多1000条,按 start 分页)。
|
||||
|
||||
修复 pytdx Bug #2:GBK 解码使用 errors='replace',截断多字节序列不再崩溃。
|
||||
修复 pytdx Bug #3:pre_close 使用 get_price 解码,而非 get_volume。
|
||||
修复 pytdx Bug #3:pre_close 保持使用通达信自定义浮点解码。
|
||||
"""
|
||||
|
||||
import struct
|
||||
|
||||
from .._binary import slice_bytes, unpack_from
|
||||
from ..codec.volume import _decode_volume
|
||||
from ..models.enums import Market
|
||||
from ..models.security import SecurityInfo
|
||||
from .base import BaseCommand
|
||||
@@ -48,10 +49,8 @@ class GetSecurityListCmd(BaseCommand[list[SecurityInfo]]):
|
||||
# Bug #2 修复:errors='replace' 避免截断 GBK 多字节序列时崩溃
|
||||
name = name_bytes.decode("gbk", errors="replace").rstrip("\x00")
|
||||
|
||||
# Bug #3 修复:pre_close 不用 get_volume(成交量解码),
|
||||
# 而是直接将 uint32 当作价格整数(/ 100)
|
||||
# 实际服务器返回的 pre_close_raw 是 price * 100 的整数
|
||||
pre_close = pre_close_raw / 100.0
|
||||
# pre_close_raw 与协议里的成交量/股本字段一样,使用通达信自定义浮点编码。
|
||||
pre_close = _decode_volume(pre_close_raw)
|
||||
|
||||
results.append(
|
||||
SecurityInfo(
|
||||
|
||||
@@ -14,7 +14,7 @@ class SecurityInfo:
|
||||
name: str # 股票名称(GBK 解码,截断字节用 replacement char 替代)
|
||||
volunit: int # 成交量单位(手 = volunit 股)
|
||||
decimal_point: int # 价格小数位数
|
||||
pre_close: float # 昨收价(已修复 pytdx Bug #3:改用正确价格解码)
|
||||
pre_close: float # 昨收价(通达信自定义浮点解码)
|
||||
|
||||
# 扩展字段(通过 get_security_list_all 关联 tdxhy.cfg 获得)
|
||||
industry_tdx: str = "" # 通达信行业代码 (如 T1001)
|
||||
|
||||
Vendored
+1
-1
@@ -3,6 +3,6 @@
|
||||
"first": {
|
||||
"code": "999999",
|
||||
"name": "上证指数",
|
||||
"pre_close": 11654847.33
|
||||
"pre_close": 3966.171142578125
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ fixtures/ 目录下每个 .hex 文件是一次真实服务器响应的 body(
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import struct
|
||||
|
||||
FIXTURES = pathlib.Path(__file__).parent.parent / "fixtures"
|
||||
|
||||
@@ -50,13 +51,37 @@ def test_security_list_parse():
|
||||
r0 = records[0]
|
||||
assert r0.code == "999999"
|
||||
assert r0.name == "上证指数"
|
||||
# pre_close is a float; the index level is stored ×100 and has many decimal places
|
||||
assert abs(r0.pre_close - 11654847.33) < 1.0
|
||||
assert abs(r0.pre_close - 3966.171142578125) < 0.01
|
||||
|
||||
# _raw present and non-empty for every record
|
||||
assert all(len(r._raw) > 0 for r in records)
|
||||
|
||||
|
||||
def test_security_list_pre_close_uses_tdx_float_for_a_share():
|
||||
from xmtdx.commands.security_list import GetSecurityListCmd
|
||||
from xmtdx.models.enums import Market
|
||||
|
||||
body = (
|
||||
struct.pack("<H", 1)
|
||||
+ struct.pack(
|
||||
"<6sH8s4sBI4s",
|
||||
b"600000",
|
||||
100,
|
||||
"\u6d66\u53d1\u94f6\u884c".encode("gbk"),
|
||||
b"\x00\x00\x00\x00",
|
||||
2,
|
||||
0x411B851F,
|
||||
b"\x00\x00\x00\x00",
|
||||
)
|
||||
)
|
||||
|
||||
record = GetSecurityListCmd(Market.SH, 24000).parse_response(body)[0]
|
||||
|
||||
assert record.code == "600000"
|
||||
assert record.name == "浦发银行"
|
||||
assert abs(record.pre_close - 9.72) < 0.01
|
||||
|
||||
|
||||
def test_security_list_gbk_no_crash():
|
||||
"""Bug #2 修复验证:GBK 解码不崩溃,所有记录均有 code。"""
|
||||
from xmtdx.commands.security_list import GetSecurityListCmd
|
||||
|
||||
Reference in New Issue
Block a user