1ed7b72aca
根因: 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通过
104 lines
3.8 KiB
Python
104 lines
3.8 KiB
Python
import sys
|
||
import os
|
||
from pathlib import Path
|
||
|
||
# Add real vnpy source code to sys.path
|
||
_VNPY_SRC = os.path.join(os.path.dirname(__file__), "..", "vnpy_v4.4.0")
|
||
_VNPY_SRC = os.path.abspath(_VNPY_SRC)
|
||
if _VNPY_SRC not in sys.path:
|
||
sys.path.insert(0, _VNPY_SRC)
|
||
|
||
import pandas as pd
|
||
from datetime import datetime
|
||
from vnpy.trader.object import BarData
|
||
from vnpy.trader.constant import Exchange, Interval
|
||
from vnpy.trader.setting import SETTINGS
|
||
|
||
def read_parquet_daily(symbol: str, start: str, end: str, cfg, dir_key: str = "daily_dir") -> list[BarData]:
|
||
daily_dir = Path(cfg.data_paths[dir_key])
|
||
start_dt = datetime.strptime(start, "%Y-%m-%d")
|
||
end_dt = datetime.strptime(end, "%Y-%m-%d")
|
||
bars: list[BarData] = []
|
||
prefix = "sh" if guess_exchange(symbol) == Exchange.SSE else "sz"
|
||
for year in range(start_dt.year, end_dt.year + 1):
|
||
f = daily_dir / str(year) / f"{prefix}{symbol}_daily.parquet"
|
||
if not f.exists():
|
||
continue
|
||
df = pd.read_parquet(f)
|
||
for _, row in df.iterrows():
|
||
d = pd.to_datetime(row["date"])
|
||
if start_dt <= d <= end_dt:
|
||
bars.append(_row_to_bar(symbol, row, Interval.DAILY))
|
||
return bars
|
||
|
||
def _row_to_bar(symbol: str, row, interval: Interval) -> BarData:
|
||
return BarData(
|
||
symbol=symbol,
|
||
exchange=guess_exchange(symbol), # Task 4 已改为 guess_exchange
|
||
datetime=pd.to_datetime(row["date"]).to_pydatetime(),
|
||
interval=interval,
|
||
open_price=float(row["open"]),
|
||
high_price=float(row["high"]),
|
||
low_price=float(row["low"]),
|
||
close_price=float(row["close"]),
|
||
volume=float(row["volume"]),
|
||
gateway_name="DATA",
|
||
)
|
||
|
||
|
||
def guess_exchange(symbol: str) -> Exchange:
|
||
"""按代码前缀判断交易所:6/68/5x→SSE,0/3/15x→SZSE"""
|
||
if symbol.startswith(("60", "68", "51", "56", "58")):
|
||
return Exchange.SSE
|
||
if symbol.startswith(("00", "30", "15")):
|
||
return Exchange.SZSE
|
||
return Exchange.SSE
|
||
|
||
|
||
def read_db_daily(symbol: str, start: str, end: str, cfg) -> list[BarData]:
|
||
from vnpy.trader.database import get_database # lazy:避免模块 import 依赖数据库驱动
|
||
# Configure vnpy database SETTINGS before calling get_database()
|
||
SETTINGS["database.name"] = "sqlite"
|
||
SETTINGS["database.database"] = cfg.data_paths["vnpy_db"]
|
||
|
||
db = get_database()
|
||
start_dt = datetime.strptime(start, "%Y-%m-%d")
|
||
end_dt = datetime.strptime(end, "%Y-%m-%d")
|
||
return db.load_bar_data(
|
||
symbol=symbol,
|
||
exchange=guess_exchange(symbol),
|
||
interval=Interval.DAILY,
|
||
start=start_dt,
|
||
end=end_dt,
|
||
)
|
||
|
||
|
||
def read_parquet_15min(symbol: str, start: str, end: str, cfg, dir_key: str = "minute_15_dir") -> list[BarData]:
|
||
"""读 15min parquet(NAS /volume1/stock/minute_kline/15min/sh{symbol}_15min.parquet)。"""
|
||
minute_dir = Path(cfg.data_paths[dir_key])
|
||
start_dt = datetime.strptime(start, "%Y-%m-%d")
|
||
end_dt = datetime.strptime(end, "%Y-%m-%d")
|
||
prefix = "sh" if guess_exchange(symbol) == Exchange.SSE else "sz"
|
||
f = minute_dir / f"{prefix}{symbol}_15min.parquet"
|
||
if not f.exists():
|
||
return []
|
||
df = pd.read_parquet(f)
|
||
time_col = "date" if "date" in df.columns else df.columns[0]
|
||
bars: list[BarData] = []
|
||
for _, row in df.iterrows():
|
||
d = pd.to_datetime(row[time_col])
|
||
if start_dt <= d <= end_dt:
|
||
bars.append(BarData(
|
||
symbol=symbol,
|
||
exchange=guess_exchange(symbol),
|
||
datetime=d.to_pydatetime(),
|
||
interval=Interval.MINUTE,
|
||
open_price=float(row["open"]),
|
||
high_price=float(row["high"]),
|
||
low_price=float(row["low"]),
|
||
close_price=float(row["close"]),
|
||
volume=float(row["volume"]),
|
||
gateway_name="DATA",
|
||
))
|
||
return bars
|