mirror of
https://ghfast.top/https://github.com/aeroxw/tick-stock-panel.git
synced 2026-09-12 17:54:15 +08:00
fix(strategy): 实盘选股 lookback 窗口用自然日裁剪导致偏少 (#90)
策略 LOOKBACK_DAYS 语义是交易日(见 strategy-guide.md), 但实盘选股路径 用 timedelta(days=N) 按自然日裁剪历史窗口。N 个自然日 ≈ N×5/7 个交易日, 周末/节假日让窗口系统性偏少, 导致 filter_history 策略的滚动窗口/行号差 (_gap)漏算, 实盘与回测结果不一致。 - repository.py get_enriched_history: 从数据实际交易日序列取最后 N 个交易日 - screener.py _load_enriched_history: 同上(慢路径) - 不动 warmup 裁剪(×2 放宽, 多读无害)和 run_all cap=30(性能保护, 独立问题) - 回测路径本就正确(传完整 panel, 无裁剪), 不受影响 验证: lookback=10 旧逻辑返回 7 交易日 → 新逻辑返回 11 交易日(含target当天) 129 后端测试全过, 无回归
This commit is contained in:
@@ -428,9 +428,15 @@ class ScreenerService:
|
||||
if "name" not in df_full.columns:
|
||||
df_full = df_full.join(instruments.select(inst_cols), on="symbol", how="left")
|
||||
|
||||
# 裁剪掉 warmup 部分, 只保留 lookback 范围 (减少 group_by 开销)
|
||||
lookback_start = target_date - timedelta(days=lookback_days)
|
||||
# 裁剪掉 warmup 部分, 只保留 lookback 范围 (减少 group_by 开销)。
|
||||
# 按交易日计数: 从数据里实际存在的交易日序列取最后 lookback_days 个交易日,
|
||||
# 不能用 timedelta(days=N) (自然日), 否则周末/节假日会让窗口偏少, 与回测不一致。
|
||||
if "date" in df_full.columns:
|
||||
trading_dates = df_full["date"].unique().sort()
|
||||
if len(trading_dates) > lookback_days:
|
||||
lookback_start = trading_dates[-(lookback_days + 1)]
|
||||
else:
|
||||
lookback_start = trading_dates[0]
|
||||
df_full = df_full.filter(pl.col("date") >= lookback_start)
|
||||
|
||||
df_full = df_full.sort(["symbol", "date"])
|
||||
|
||||
@@ -942,12 +942,19 @@ class KlineRepository:
|
||||
cache_max = cache["date"].max()
|
||||
cache_min = cache["date"].min()
|
||||
from datetime import timedelta
|
||||
# 验证缓存覆盖完整范围 (含 warmup)
|
||||
# 验证缓存覆盖完整范围 (含 warmup)。lookback_days 是交易日语义, 用 ×2 日历日
|
||||
# 放宽确保覆盖 (节假日/周末), 与 warmup 60 一起留足余量。
|
||||
warmup_start = target_date - timedelta(days=(lookback_days + 60) * 2)
|
||||
if cache_min > warmup_start or cache_max < target_date:
|
||||
return None
|
||||
# 只返回 lookback 范围 (日历天数 ≈ 2/3 交易日, 足够覆盖)
|
||||
lookback_start = target_date - timedelta(days=lookback_days)
|
||||
# 按交易日计数裁剪: 从数据里实际存在的交易日序列取最后 lookback_days 个交易日。
|
||||
# 不能用 timedelta(days=N) (自然日), 否则周末/节假日会让窗口只有 ~N×5/7 个交易日,
|
||||
# 导致 filter_history 策略的滚动窗口/行号差(_gap)漏算, 与回测结果不一致。
|
||||
trading_dates = cache["date"].unique().sort()
|
||||
if len(trading_dates) > lookback_days:
|
||||
lookback_start = trading_dates[-(lookback_days + 1)]
|
||||
else:
|
||||
lookback_start = trading_dates[0]
|
||||
return cache.filter((pl.col("date") >= lookback_start) & (pl.col("date") <= target_date))
|
||||
|
||||
def get_enriched_range(
|
||||
|
||||
Reference in New Issue
Block a user