feat(data): raw真实价数据源(task#79)—raw_dir+dir_key路由+新浪源重下

根因: daily_dir mixed-adjust(hfq bulk+akshare raw tail)致3-30 -94%假跌。
方案(Linus三问简化单raw, 除权留分期项#3):
- datareader read_parquet_daily/15min 加 dir_key 参数
- data_source iter_bars/fetch_day: adjust=raw→raw_dir(缺配置报错防混源), qfq→daily_dir
- engine PaperEngine 默认 adjust=raw
- config 加 raw_dir; scripts/raw_redownload.py 新浪源adjust='' 直连+单线程限速
- 验证: 浦发606行close 6.5/14.6 mean10.08 0跳变, 撮合成交价9.71-10.25真实
- 测试9/9+trader全量108/108通过
This commit is contained in:
2026-07-07 22:19:11 +08:00
parent fc39b549cf
commit 1ed7b72aca
10 changed files with 366 additions and 43 deletions
+51 -16
View File
@@ -1,7 +1,7 @@
"""data_source 测试(mock _read_fn,不依赖 vnpy 链,spec §5/§3.3)。
read_parquet_15min 的真测试依赖 vnpy BarData + NAS parquet,本机无完整依赖,
在容器内冒烟(spec §17);本文件测 iter_bars 调度逻辑。
read_parquet 的真测试依赖 vnpy BarData + NAS parquet,本机无完整依赖,
在容器内冒烟(spec §17);本文件测 iter_bars 调度逻辑adjust → dir_key 路由)
"""
import logging
from datetime import datetime
@@ -9,7 +9,7 @@ from types import SimpleNamespace
import pytest
from sanguo_trader.data_source import iter_bars
from sanguo_trader.data_source import iter_bars, fetch_day
def _mock_bar(sym: str, date_str: str, close: float):
@@ -20,25 +20,60 @@ def _mock_bar(sym: str, date_str: str, close: float):
)
def _cfg(paths):
return SimpleNamespace(data_paths=paths)
def test_iter_bars_cross_section_multi_symbol(monkeypatch):
def mock_read(sym, start, end, cfg):
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 "600000" in d and "000001" in d
assert d["600000"].close_price == 10.5
assert all(k == "daily_dir" for k in seen) # 默认 qfq → daily_dir
def test_iter_bars_raw_fallback_warning(monkeypatch, caplog):
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: [],
lambda iv: lambda s, st, e, c, dir_key: [],
)
with caplog.at_level(logging.WARNING):
list(iter_bars(["600000"], "2024-01-01", "2024-01-31", "d", adjust="raw"))
assert "raw" in caplog.text
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_unsupported(monkeypatch):
"""raw 仅日线;15min+raw 报错(raw 15min 待分期项)。"""
monkeypatch.setattr(
"sanguo_trader.data_source._read_fn",
lambda iv: lambda s, st, e, c, dir_key: [],
)
with pytest.raises(ValueError, match="日线"):
list(iter_bars(["600000"], "2024-01-01", "2024-01-31", "15m",
adjust="raw", cfg=_cfg({"raw_dir": "/x"})))
def test_unsupported_interval_rejected():
@@ -48,11 +83,11 @@ def test_unsupported_interval_rejected():
def test_fetch_day_returns_last_bar(monkeypatch):
from sanguo_trader.data_source import fetch_day
monkeypatch.setattr(
"sanguo_trader.data_source._read_fn",
lambda iv: lambda s, st, e, c: [_mock_bar(s, "2024-01-02", 10.0),
_mock_bar(s, "2024-01-02", 10.5)],
)
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 == ["daily_dir"]