fix: route index files to get_index_bars() in sync-daily/sync-all

Root cause: _fetch_all_daily_bars used get_security_bars() for all files,
but index server responses have 4 extra bytes per record. Wrong parser
produced garbage dates like '12897-50-77' for sh000001, sh000300, etc.

Fix: add _is_index_code() to detect index codes by prefix (sh: 00/88/99,
sz: 39) and route to get_index_bars() accordingly.

Bumps version to 1.6.1.
This commit is contained in:
Justin Gu
2026-06-07 21:55:26 +08:00
parent d01b11fa74
commit 4cab9aa325
3 changed files with 36 additions and 4 deletions
+8
View File
@@ -785,6 +785,14 @@ ruff format --check src/ tests/ # format check
## Changelog
### 1.6.1 (2026-06-07)
**Bug 修复** — 修复 sync-all/sync-daily 对指数文件误用股票解析器导致垃圾日期的问题。
- 修复 `_fetch_all_daily_bars` 对指数文件(sh00/sh88/sh99, sz39)错误调用 `get_security_bars()` 的问题
- 指数文件现在正确使用 `get_index_bars()`(服务端响应每条记录多 4 字节上涨/下跌家数)
- 新增 `_is_index_code()` 辅助函数,根据市场和代码前缀判断证券类型
### 1.6.0 (2026-06-07)
**离线数据写入同步** — 从服务端获取最新日线数据并写入本地通达信 .day 文件,替代通达信内置下载功能。
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "easy-tdx"
version = "1.6.0"
version = "1.6.1"
description = "通达信 TCP 协议行情数据客户端,支持在线行情、离线数据读取与写入同步"
readme = "README.md"
requires-python = ">=3.10"
+27 -3
View File
@@ -459,8 +459,26 @@ def _df_to_bars(df: pd.DataFrame) -> list[SecurityBar]:
return bars
def _is_index_code(exchange: str, code: str) -> bool:
"""根据市场和代码前缀判断是否为指数。
指数需要调用 get_index_bars()(服务端响应每条记录多 4 字节),
而非 get_security_bars()。
"""
head = code[:2]
if exchange == "sh":
return head in ("00", "88", "99")
if exchange == "sz":
return head == "39"
return False
def _fetch_all_daily_bars(
client: TdxClient, market: int, code: str, need_full: bool = False
client: TdxClient,
market: int,
code: str,
need_full: bool = False,
is_index: bool = False,
) -> list[SecurityBar]:
"""从服务端分页获取全部日线数据。
@@ -470,19 +488,22 @@ def _fetch_all_daily_bars(
code: 6 位股票代码。
need_full: True 表示拉取全量历史(空文件场景),
False 表示只拉最近一页(增量更新)。
is_index: True 表示指数,使用 get_index_bars()。
Returns:
SecurityBar 列表(按日期升序)。
"""
from ..models.enums import KlineCategory
fetch_fn = client.get_index_bars if is_index else client.get_security_bars
all_bars: list[SecurityBar] = []
start = 0
page_size = 800
max_pages = 50 if need_full else 1 # 50 页 = 40000 条,足够覆盖 A 股全部历史
for _ in range(max_pages):
df = client.get_security_bars(market, code, KlineCategory.DAY, start, page_size)
df = fetch_fn(market, code, KlineCategory.DAY, start, page_size)
if df.empty:
break
all_bars.extend(_df_to_bars(df))
@@ -517,8 +538,11 @@ def _sync_one_daily(client: TdxClient, filepath: Path) -> tuple[int, str]:
last_date = get_last_bar_date(filepath)
need_full = last_date is None
# 判断是否为指数(指数需要 get_index_bars,响应格式不同)
is_index = _is_index_code(exchange, code)
# 从服务端分页获取日线
bars = _fetch_all_daily_bars(client, market, code, need_full=need_full)
bars = _fetch_all_daily_bars(client, market, code, need_full=need_full, is_index=is_index)
if not bars:
return 0, "服务端无数据"