114a69e997
根因: dbbardata UNIQUE(symbol,exchange,interval,datetime) 按字符串字面比较, 多写入路径混用 'YYYY-MM-DD' 与 'YYYY-MM-DD 00:00:00' -> 同一交易日双行, INSERT OR REPLACE 不去重 -> 回测交易日翻倍/pivot duplicate/信号异常。 方案A (统一纯日期, 详见 Main Agent 诊断): - 新增 scripts/data_platform/dbbardata_utils.py: normalize_daily_dt(s) 取前 10 字符, None/短串安全 - 4 个日线写入脚本写入前调 helper: - bs_eod.py (sanguo-bs-eod 个股日线 baostock) - migrate_daily_baostock.py (历史迁移) - xt_eod.py (sanguo-xt-eod ETF/基金 xtata) - import_vnpy_daily_fast.py (NAS 日线 parquet 导入, 加防御) - TDD: tests/data_platform/test_dbbardata_utils.py 9 cases 全过 - 回归: tests/data_platform + tests/portfolio 199 passed 12 skipped peewee DateTimeField formats 含 '%Y-%m-%d' (阶段0 VPS 实测确认), 读纯日期不崩, 方案A 前提成立。 15min 干净, 不动 (分钟必须带时分)。只改日线 interval='d'。 数据层根治, 不在 provider 适配兜底 (用户铁律)。
47 lines
1.2 KiB
Python
47 lines
1.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""TDD for dbbardata daily datetime normalizer (dbbardata 双行根治方案A)."""
|
|
import pytest
|
|
|
|
from scripts.data_platform.dbbardata_utils import normalize_daily_dt
|
|
|
|
|
|
@pytest.mark.parametrize("s,expected", [
|
|
("2026-07-20", "2026-07-20"),
|
|
("2010-01-04", "2010-01-04"),
|
|
])
|
|
def test_pure_date_noop(s, expected):
|
|
# 纯日期应原样返回
|
|
assert normalize_daily_dt(s) == expected
|
|
|
|
|
|
@pytest.mark.parametrize("s", [
|
|
"2026-07-20 00:00:00",
|
|
"2026-07-20 00:00:00.000000",
|
|
])
|
|
def test_truncate_midnight(s):
|
|
# 带时间(00:00:00 历史迁移路径) 截断为纯日期
|
|
assert normalize_daily_dt(s) == "2026-07-20"
|
|
|
|
|
|
@pytest.mark.parametrize("s", [
|
|
"2026-07-20 15:00:00",
|
|
"2026-07-20 09:30:01",
|
|
])
|
|
def test_truncate_real_time(s):
|
|
# 带真实时分(异常情况) 也截断为纯日期 — 日线无时分语义
|
|
assert normalize_daily_dt(s) == "2026-07-20"
|
|
|
|
|
|
def test_none_safe():
|
|
# None 不应崩溃 — 返回 None 保留语义
|
|
assert normalize_daily_dt(None) is None
|
|
|
|
|
|
def test_empty_string_safe():
|
|
assert normalize_daily_dt("") == ""
|
|
|
|
|
|
def test_short_string_safe():
|
|
# 短于 10 字符的异常输入不崩溃,返回本身(取前 10)
|
|
assert normalize_daily_dt("2026") == "2026"
|