0810259911
- _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双源分期项落地.
99 lines
3.9 KiB
Python
99 lines
3.9 KiB
Python
"""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 qfq(task #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_dir(baostock 双源,分期项已落地)。"""
|
||
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(双源)
|