fix(data): bs_eod 15min datetime 格式 bug 根治 + --no-daily 选项

bug 根因(2026-07-25): baostock 15min 实测 date='2026-07-21'(带 -),
time='20260721094500000'(17 位 YYYYMMDDHHMMSSmmm)。原代码假设 date 纯数字 +
time[:6] 取年月, 产乱 datetime 致 dbbardata 15min 全市场停 2026-07-17(8 天
没正确累积), 日线正常(7-24)。

修复:
- 提纯函数 _build_15m_dt(date_series, time_series): date 直连 + 从 17 位 time
  第 8-12 位提取 HHMM, 产出 'YYYY-MM-DD HH:MM:00' (符合 GLOB 清理模式)
- upsert_15m 调用 _build_15m_dt 替代内联拼接
- 新增 --no-daily 选项(只跑 15min, 用于重灌快), 与 --no-15m 对称
- TDD: 8 case 覆盖(09:45/14:30/15:00/13:00/跨日/多行混合/GLOB 格式)
- conftest.py 修补 sibling import 在 pytest 下可用
This commit is contained in:
2026-07-25 21:14:12 +08:00
parent e807bed09c
commit b06af336c3
3 changed files with 120 additions and 4 deletions
+21 -4
View File
@@ -154,6 +154,20 @@ def upsert_daily(conn, code, prefix, rows):
return len(db)
def _build_15m_dt(date_series, time_series):
"""baostock 15min datetime 拼接: date="2026-07-21"(带 -) + time="20260721094500000"(17 位)。
从 17 位 time 第 8-12 位提取 HHMM, date 直连(带 -)。
产出 'YYYY-MM-DD HH:MM:00' (符合 dbbardata GLOB 模式, 不被清理误删)。
bug 根因(2026-07-25): 原代码假设 date 纯数字 + time[:6] 取年月,
但 baostock 实测 date 带 -, time[:6]=YYYYMM, 产乱 datetime 致 15min 全市场停 7-17。
"""
_t = time_series.astype(str)
return (date_series.astype(str) + " "
+ _t.str.slice(8, 10) + ":" + _t.str.slice(10, 12) + ":00")
def upsert_15m(conn, code, prefix, rows):
if not rows:
return 0
@@ -161,8 +175,7 @@ def upsert_15m(conn, code, prefix, rows):
for c in ["open", "high", "low", "close", "volume", "amount"]:
df[c] = pd.to_numeric(df[c], errors="coerce")
exc = EXC_MAP[prefix]
dt_col = (df["date"].astype(str) + " " + df["time"].astype(str).str.slice(0, 6)
).apply(lambda s: f"{s[0:4]}-{s[4:6]}-{s[6:8]} {s[8:10]}:{s[10:12]}:00")
dt_col = _build_15m_dt(df["date"], df["time"])
db = pd.DataFrame({
"symbol": code, "exchange": exc, "datetime": dt_col,
"interval": "15m", "volume": df["volume"], "turnover": df["amount"],
@@ -183,6 +196,8 @@ def main():
ap = argparse.ArgumentParser()
ap.add_argument("--limit", type=int, default=0)
ap.add_argument("--no-15m", action="store_true")
ap.add_argument("--no-daily", action="store_true",
help="跳日线, 只跑 15min(用于 15min 重灌快)")
args = ap.parse_args()
today = dt.date.today()
@@ -221,8 +236,10 @@ def main():
break
bs_code = f"{prefix}.{code}"
try:
d_rows = fetch_k(bs_code, DAILY_FIELDS, "d", start, end)
n1 = upsert_daily(conn, code, prefix, d_rows)
n1 = 0
if not args.no_daily:
d_rows = fetch_k(bs_code, DAILY_FIELDS, "d", start, end)
n1 = upsert_daily(conn, code, prefix, d_rows)
n2 = 0
if not args.no_15m:
m_rows = fetch_k(bs_code, M15_FIELDS, "15", start, end)