Files
claude_dev 0810259911 feat(data): 15min双源集成(data_source路由 + baostock格式适配)
- _resolve_dir_key: 15min支持raw/qfq双源(移除raw15min抛错)
- _check_adjust_cfg: 按interval查dir(minute_15_raw_dir/qfq_dir)
- read_parquet_15min: datetime列优先(baostock时分,旧date兼容)
- config: 加 minute_15_qfq_dir/minute_15_raw_dir
- test: 更新raw15min路由断言(6 passed)

实测容器: 600000 15min qfq 336bars close6.1977 / raw 336bars close6.6200,
datetime时分正确(09:45:00). 15min双源分期项落地.
2026-07-09 19:34:43 +08:00

99 lines
3.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""data_source 测试(mock _read_fn,不依赖 vnpy 链,spec §5/§3.3)。
read_parquet 的真测试依赖 vnpy BarData + NAS parquet,本机无完整依赖,
在容器内冒烟(spec §17);本文件测 iter_bars 调度逻辑(adjust → dir_key 路由)。
"""
import logging
from datetime import datetime
from types import SimpleNamespace
import pytest
from sanguo_trader.data_source import iter_bars, fetch_day
def _mock_bar(sym: str, date_str: str, close: float):
return SimpleNamespace(
symbol=sym,
datetime=datetime.strptime(date_str, "%Y-%m-%d"),
close_price=close,
)
def _cfg(paths):
return SimpleNamespace(data_paths=paths)
def test_iter_bars_cross_section_multi_symbol(monkeypatch):
seen = []
def mock_read(sym, start, end, cfg, dir_key):
seen.append(dir_key)
return [_mock_bar(sym, "2024-01-02", 10.5 if sym == "600000" else 15.5)]
monkeypatch.setattr("sanguo_trader.data_source._read_fn", lambda iv: mock_read)
sections = list(iter_bars(["600000", "000001"], "2024-01-01", "2024-01-31", "d"))
assert len(sections) == 1
_date, d = sections[0]
assert d["600000"].close_price == 10.5
assert all(k == "qfq_dir" for k in seen) # 默认 qfq → qfq_dir(双源)
def test_iter_bars_raw_uses_raw_dir(monkeypatch, caplog):
"""raw 模式路由到 raw_dir,不再 fallback qfqtask #79)。"""
seen = []
def mock_read(sym, start, end, cfg, dir_key):
seen.append(dir_key)
return [_mock_bar(sym, "2024-01-02", 10.01)]
monkeypatch.setattr("sanguo_trader.data_source._read_fn", lambda iv: mock_read)
with caplog.at_level(logging.WARNING):
sections = list(iter_bars(
["600000"], "2024-01-01", "2024-01-31", "d",
adjust="raw", cfg=_cfg({"raw_dir": "/x/raw"}),
))
assert seen == ["raw_dir"] # raw → raw_dir
assert "fallback" not in caplog.text.lower()
assert sections[0][1]["600000"].close_price == 10.01
def test_iter_bars_raw_missing_dir_raises(monkeypatch):
"""raw 缺 raw_dir 配置 → 明确报错(不静默 fallback,防混源)。"""
monkeypatch.setattr(
"sanguo_trader.data_source._read_fn",
lambda iv: lambda s, st, e, c, dir_key: [],
)
with pytest.raises(ValueError, match="raw_dir"):
list(iter_bars(["600000"], "2024-01-01", "2024-01-31", "d",
adjust="raw", cfg=_cfg({})))
def test_iter_bars_raw_15min_routes_dedicated_dir(monkeypatch):
"""raw 15min 路由 minute_15_raw_dirbaostock 双源,分期项已落地)。"""
seen = []
monkeypatch.setattr(
"sanguo_trader.data_source._read_fn",
lambda iv: lambda s, st, e, c, dir_key: seen.append(dir_key) or [],
)
list(iter_bars(["600000"], "2024-01-01", "2024-01-31", "15m",
adjust="raw", cfg=_cfg({"minute_15_raw_dir": "/x"})))
assert seen == ["minute_15_raw_dir"]
# 缺 minute_15_raw_dir 配置 → 明确报错(不 fallback,防混源)
with pytest.raises(ValueError, match="minute_15_raw_dir"):
list(iter_bars(["600000"], "2024-01-01", "2024-01-31", "15m",
adjust="raw", cfg=_cfg({})))
def test_unsupported_interval_rejected():
with pytest.raises(ValueError):
# _read_fn 在生成器首次 next 时才执行,用 list 触发
list(iter_bars(["600000"], "2024-01-01", "2024-01-31", "5m"))
def test_fetch_day_returns_last_bar(monkeypatch):
seen = []
def mock_read(sym, start, end, cfg, dir_key):
seen.append(dir_key)
return [_mock_bar(sym, "2024-01-02", 10.0), _mock_bar(sym, "2024-01-02", 10.5)]
monkeypatch.setattr("sanguo_trader.data_source._read_fn", lambda iv: mock_read)
bar = fetch_day("600000", "2024-01-02", "d")
assert bar.close_price == 10.5 # 取最后一个
assert seen == ["qfq_dir"] # 默认 qfq → qfq_dir(双源)