mirror of
https://ghfast.top/https://github.com/aeroxw/easy_tdx_max.git
synced 2026-09-12 20:24:19 +08:00
112 lines
4.1 KiB
Python
112 lines
4.1 KiB
Python
"""MAC 多日分时图(0x123E)解析测试。
|
||
|
||
包含 Issue #10 回归:当服务器返回的 minutes ≥ 1440(个别服务器累计或异常值)
|
||
时,parse_response 不应抛 ValueError,而应按 % 24 折算为日内时刻。
|
||
"""
|
||
|
||
import struct
|
||
|
||
from easy_tdx.mac.commands.tick_charts import TickChartsCmd
|
||
from easy_tdx.models.enums import Market
|
||
|
||
|
||
def _build_multi_tick_body(
|
||
days: int,
|
||
page_size: int,
|
||
minutes_grid: list[list[int]],
|
||
) -> bytes:
|
||
"""构造一个多日分时图响应 body。
|
||
|
||
Args:
|
||
days: 天数(写入 count)。
|
||
page_size: 每天分时点数。
|
||
minutes_grid: 长度 days 的列表,每个元素是长度 page_size 的 minutes 值列表。
|
||
"""
|
||
# 头部: market(2) + code(22)
|
||
body = bytearray(struct.pack("<H22s", int(Market.SH), b"600519" + b"\x00" * 16))
|
||
# 5 dates + 5 pre_closes(不足 5 天用 0 填充)at offset 24
|
||
dates = [20250115, 20250114, 20250113, 20250110, 20250109]
|
||
pre_closes = [1509.0, 1512.0, 1510.0, 1508.0, 1511.0]
|
||
body += struct.pack("<5I", *dates)
|
||
body += struct.pack("<5f", *pre_closes)
|
||
# count(2) + send_last(1) + page_size(2) + total(2) at offset 64
|
||
body += struct.pack("<HBHH", days, 1, page_size, days * page_size)
|
||
# tick records at offset 71, each 14 bytes: <HffHH
|
||
for day in minutes_grid:
|
||
assert len(day) == page_size
|
||
for m in day:
|
||
body += struct.pack("<HffHH", m, 1510.0, 1510.0, 100, 0)
|
||
# 尾部元数据: <44sBHf5x2I5ffIf12s2fI
|
||
body += struct.pack("<44s", "贵州茅台".encode("gbk") + b"\x00" * 32)
|
||
body += struct.pack("<BHf", 2, 0, 1.0) # decimal, category, vol_unit
|
||
body += b"\x00" * 5
|
||
body += struct.pack("<2I", 20250115, 0)
|
||
body += struct.pack("<5f", 1509.0, 1510.0, 1520.0, 1500.0, 1515.0)
|
||
body += struct.pack("<fI", 0.0, 100000) # momentum, vol
|
||
body += struct.pack("<f", 1000000.0) # amount
|
||
body += b"\x00" * 12
|
||
body += struct.pack("<2f", 0.5, 1512.0) # turnover, avg
|
||
body += struct.pack("<I", 0) # industry
|
||
return bytes(body)
|
||
|
||
|
||
def test_multi_tick_charts_normal_minutes():
|
||
"""常规数据:minutes 每天 570..689, 780..899,解析正常。"""
|
||
page_size = 4
|
||
grid = [
|
||
[570, 571, 572, 573],
|
||
[570, 600, 780, 899],
|
||
[570, 571, 572, 573],
|
||
]
|
||
body = _build_multi_tick_body(3, page_size, grid)
|
||
|
||
chart = TickChartsCmd(int(Market.SH), "600519", None, 3).parse_response(body)
|
||
|
||
assert len(chart.charts) == 3
|
||
# 第一天第一个点 09:30:00
|
||
assert str(chart.charts[0].ticks[0].time) == "09:30:00"
|
||
# 最后一个点 14:59:00
|
||
assert str(chart.charts[1].ticks[3].time) == "14:59:00"
|
||
|
||
|
||
def test_multi_tick_charts_oversized_minutes_no_crash():
|
||
"""Issue #10 回归:minutes ≥ 1440(如累计/异常值)不应抛 ValueError。
|
||
|
||
用户报告 hour must be in 0..23, not 1039(即 minutes // 60 == 1039)。
|
||
修复后应按 % 24 折算为日内时刻,保证不崩溃且结果合理。
|
||
"""
|
||
page_size = 4
|
||
# 第一天第 2 个点 minutes=62340(= 报错现场,//60=1039),其余正常
|
||
grid = [
|
||
[570, 62340, 572, 573],
|
||
[570, 600, 780, 899],
|
||
]
|
||
body = _build_multi_tick_body(2, page_size, grid)
|
||
|
||
chart = TickChartsCmd(int(Market.SH), "600519", None, 2).parse_response(body)
|
||
|
||
assert len(chart.charts) == 2
|
||
# 折算后 62340 -> 62340//60 % 24 = 1039 % 24 = 7 时, 62340 % 60 = 0 分
|
||
assert str(chart.charts[0].ticks[1].time) == "07:00:00"
|
||
# 正常点不受影响
|
||
assert str(chart.charts[0].ticks[0].time) == "09:30:00"
|
||
assert str(chart.charts[1].ticks[3].time) == "14:59:00"
|
||
|
||
|
||
def test_multi_tick_charts_request_layout():
|
||
"""验证请求包布局与文档一致:market + code(22) + start_ymd + days + 1。"""
|
||
from datetime import date as date_cls
|
||
|
||
cmd = TickChartsCmd(int(Market.SH), "600519", date_cls(2025, 1, 15), 5)
|
||
req = cmd.build_request()
|
||
# MAC 帧前缀由 build_mac_request 生成,body 在其后;校验 body 嵌入其中
|
||
expected_body = struct.pack(
|
||
"<H22sIHH6x",
|
||
int(Market.SH),
|
||
b"600519" + b"\x00" * 16,
|
||
20250115,
|
||
5,
|
||
1,
|
||
)
|
||
assert expected_body in req
|