Merge commit 'e3e8dd4'

# Conflicts:
#	CHANGELOG.md
#	pyproject.toml
#	src/easy_tdx/commands/security_bars.py
This commit is contained in:
Justin Gu
2026-07-07 01:54:20 +08:00
4 changed files with 113 additions and 23 deletions
+34 -15
View File
@@ -1,15 +1,19 @@
"""获取 K 线数据命令(支持全部周期)。"""
import logging
import struct
from .._binary import unpack_from
from ..codec.datetime_ import get_datetime
from ..codec.price import get_price
from ..codec.volume import get_volume
from ..exceptions import TdxDecodeError
from ..models.bar import SecurityBar
from ..models.enums import KlineCategory, Market
from .base import BaseCommand
_log = logging.getLogger(__name__)
class GetSecurityBarsCmd(BaseCommand[list[SecurityBar]]):
"""获取指定股票的 K 线数据。
@@ -63,12 +67,7 @@ class GetSecurityBarsCmd(BaseCommand[list[SecurityBar]]):
pre_diff_base = 0
cat = int(self.category)
# 服务器偶发返回的 ret_count 与 body 实际长度不匹配(pytdx/mootdx 均
# 有类似报告):ret_count 撒谎或网络帧粘包/截断,循环到中途 pos 已
# 读到底("剩余 0 字节")。改用"取 min(ret_count, body 可解析条数)"
# 策略——TdxDecodeError 视为记录边界,提前结束循环并丢弃残缺尾记录,
# 而不是让整批数据 500。调用方拿到的是完整记录(少几根 K 线比全崩好)。
for _ in range(ret_count):
for i in range(ret_count):
record_start = pos
try:
year, month, day, hour, minute, pos = get_datetime(cat, body, pos)
@@ -80,10 +79,21 @@ class GetSecurityBarsCmd(BaseCommand[list[SecurityBar]]):
vol, pos = get_volume(body, pos)
amount, pos = get_volume(body, pos)
except Exception:
# 残缺尾记录:body 已读到底或字段不完整,丢弃本条并停止。
# 不重新抛出—— degrade gracefully,返回已解析的完整记录。
break
except TdxDecodeError as e:
# TDX 服务端偶发截断:响应头声称有 N 条,但 body 末尾若干条
# 被切掉(停牌/退市/分页边界常见)。丢弃残缺的末条,保留已
# 成功解析的前若干条,避免一条坏数据让整页 500。
if bars:
_log.warning(
"K线响应在第 %d/%d 条处被截断(%s),已丢弃末尾残缺记录,"
"返回前 %d",
i + 1,
ret_count,
e,
len(bars),
)
return bars
raise
# 差分还原(与 pytdx 完全一致)
open_abs = open_diff + pre_diff_base
@@ -126,9 +136,7 @@ class GetIndexBarsCmd(GetSecurityBarsCmd):
pre_diff_base = 0
cat = int(self.category)
# 同 GetSecurityBarsCmdret_count 与 body 实际长度偶发不匹配,
# 残缺尾记录提前 break,详见父类同名注释。
for _ in range(ret_count):
for i in range(ret_count):
record_start = pos
try:
year, month, day, hour, minute, pos = get_datetime(cat, body, pos)
@@ -143,9 +151,20 @@ class GetIndexBarsCmd(GetSecurityBarsCmd):
# 指数记录额外 4 字节:上涨家数 + 下跌家数(各 uint16 LE
pos += 4
except Exception:
break
except TdxDecodeError as e:
if bars:
_log.warning(
"指数K线响应在第 %d/%d 条处被截断(%s),已丢弃末尾残缺记录,"
"返回前 %d",
i + 1,
ret_count,
e,
len(bars),
)
return bars
raise
# 差分还原(与 pytdx 完全一致)
open_abs = open_diff + pre_diff_base
close_abs = open_abs + close_diff
high_abs = open_abs + high_diff