Files
sanguo_vnpy_v2/sanguo_trader/data_source.py
T
claude_dev 1ed7b72aca 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通过
2026-07-07 22:19:11 +08:00

81 lines
3.0 KiB
Python
Raw 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.
"""模拟盘行情统一接口(raw 真实价 / qfq 前复权,spec §3.3 / §5)。
raw:撮合/涨跌停/成交价用真实价(adjustflag=3 / akshare adjust=""),
从 cfg.data_paths['raw_dir'] 读 parquetraw 重下脚本生成,单一 adjust 不混源)。
qfq:策略信号用前复权(无除权缺口),从 cfg.data_paths['daily_dir'] 读。
_read_fn 内 lazy import datareader,避免模块级依赖 vnpy 链(tzlocal 等),
本机无 vnpy 完整依赖时仍可 import + 单测(mock _read_fn)。
"""
import logging
from collections.abc import Iterator
logger = logging.getLogger(__name__)
def _read_fn(interval: str):
"""按 interval 返回读取函数(lazy import)。"""
if interval == "d":
from sanguo_data.datareader import read_parquet_daily
return read_parquet_daily
if interval == "15m":
from sanguo_data.datareader import read_parquet_15min
return read_parquet_15min
raise ValueError(f"不支持的 interval: {interval}(首版仅 d / 15m")
def _resolve_dir_key(adjust: str, interval: str) -> str:
"""adjust → cfg.data_paths 的目录 key。
raw 仅支持日线(raw 15min 待分红除权分期项);qfq/默认按 interval 选日线/15min。
"""
if adjust == "raw":
if interval != "d":
raise ValueError(
f"raw 模式暂仅支持日线(interval='d'),got '{interval}'"
"raw 15min 待分期项)"
)
return "raw_dir"
return "daily_dir" if interval == "d" else "minute_15_dir"
def _check_raw_cfg(adjust: str, cfg) -> None:
"""raw 模式需 raw_dir 配置,缺失明确报错(不静默 fallback 到 qfq,避免混源)。"""
if adjust == "raw" and (not cfg or "raw_dir" not in getattr(cfg, "data_paths", {})):
raise ValueError(
"raw 模式需 cfg.data_paths['raw_dir'](未配置;"
"先用 scripts/data_platform/raw_redownload.py 生成 raw parquet"
)
def iter_bars(
symbols: list[str],
start: str,
end: str,
interval: str,
adjust: str = "qfq",
cfg=None,
) -> Iterator[tuple]:
"""按日期 cross-section yield (date, {symbol: BarData})。"""
dir_key = _resolve_dir_key(adjust, interval)
_check_raw_cfg(adjust, cfg)
read_fn = _read_fn(interval)
by_date: dict = {}
for sym in symbols:
for bar in read_fn(sym, start, end, cfg, dir_key):
dt = bar.datetime
key = dt.date() if hasattr(dt, "date") else dt
by_date.setdefault(key, {})[sym] = bar
for date in sorted(by_date.keys()):
yield date, by_date[date]
def fetch_day(symbol: str, date: str, interval: str,
adjust: str = "qfq", cfg=None):
"""实走模式拉当日 bar(C-S3 用)。"""
dir_key = _resolve_dir_key(adjust, interval)
_check_raw_cfg(adjust, cfg)
read_fn = _read_fn(interval)
bars = read_fn(symbol, date, date, cfg, dir_key)
return bars[-1] if bars else None