perf(data): 四热路径日期区间SARGable化——substr(datetime,1,10)对索引列套函数打不进复合索引datetime列,每股扫全量日线史取短窗——08-25晨9:30生产实锤:momentum选股1/2(RPS池)174s未达<60s验收,阶段日志精确定罪(过滤段含双seek仅7.8s);病根=PanelFetcher的30天窗SQL每只股扫~5000行日线史取~22行,3226只≈1600万行=174s量级吻合。修=裸列datetime>=start AND datetime<end+1天排他上界(与按日期前10位比较在d裸日期/15m·5m时间戳两格式下语义严格等价,含end当日全部行排除次日),区间打进复合索引第4列每股只扫窗口行。四处同病同修:PanelFetcher宽表/PriceFetcher逐只get_price/limit-status近2根90天窗(原substr版90天下界同样打不进索引)/datareader CTA回测K线。+3测试:防回潮扫描(三文件钉死禁substr谓词)+双格式边界行为(d裸日期与15m时间戳end当日含次日排)+get_price同语义;portfolio+data_platform 644绿;待VPS探针终验计时 [vps]
CI/CD / test (push) Successful in 3s
CI/CD / nas-deploy (push) Successful in 21s
CI/CD / nas-verify (push) Successful in 8s

This commit is contained in:
2026-08-25 20:54:40 +08:00
parent 841ea1536e
commit 3304ff46b2
4 changed files with 101 additions and 13 deletions
+59
View File
@@ -242,3 +242,62 @@ class TestOptionalContract:
fields=["close", "acc_net_value"],
)
assert df["acc_net_value"].isna().all()
# ======================== SARGable 日期区间(2026-08-25 P0) ========================
class TestSargableDateRange:
"""substr(datetime,1,10) 对索引列套函数 → 日期区间打不进复合索引 datetime 列,
每股扫全量日线史取短窗(momentum RPS 池 3226 只 30 天窗 VPS 实测 174s;provider
/datareader 四热路径同病)。裸列 ``datetime>=start AND datetime<end+1天`` 与
substr 语义严格等价(d 裸日期/带时间戳两格式, 含 end 当日全部行、排除次日)。"""
def test_no_substr_datetime_regression(self):
"""钉死四个热路径文件不再回潮非 SARGable 谓词。"""
import pathlib
root = pathlib.Path(__file__).resolve().parents[2]
for rel in (
"sanguo_portfolio/providers/fetchers/price.py",
"sanguo_portfolio/providers/local_unified_provider.py",
"sanguo_data/datareader.py",
):
src = (root / rel).read_text(encoding="utf-8")
assert "substr(datetime" not in src, f"{rel} 回潮非 SARGable 谓词"
def test_panel_end_date_inclusive_bare_and_timestamp(self, unified_provider):
"""end 当日全部行含(d 裸日期 + 15m 时间戳两格式), 次日排除。"""
conn = sqlite3.connect(unified_provider.db_path)
conn.executemany("INSERT INTO dbbardata VALUES(?,?,?,?,?,?,?,?,?,?,?)", [
# d 裸日期: end 当日含 / 次日排
("600600", "SSE", "2024-06-19", "d", 100, 1e3, 0, 1.0, 1.0, 1.0, 10.0),
("600600", "SSE", "2024-06-21", "d", 100, 1e3, 0, 1.0, 1.0, 1.0, 12.0),
# 15m 时间戳: end 当日含 / 次日排
("600600", "SSE", "2024-06-20 14:35:00", "15m", 100, 1e3, 0, 1.0, 1.0, 1.0, 11.0),
("600600", "SSE", "2024-06-21 09:35:00", "15m", 100, 1e3, 0, 1.0, 1.0, 1.0, 13.0),
])
conn.commit()
conn.close()
# d 面: 窗口 06-18~06-20 → 只有裸日期 06-19 行
panel_d = unified_provider.get_closes_panel(
["600600.XSHG"], "2024-06-18", "2024-06-20", fq="raw")
assert [str(d)[:10] for d in panel_d.index] == ["2024-06-19"]
assert panel_d.iloc[0, 0] == 10.0
# 15m 面: 同窗 → 只有 06-20 14:35 行(end 当日带时间戳不被右界误伤)
panel_15 = unified_provider.get_closes_panel(
["600600.XSHG"], "2024-06-18", "2024-06-20", interval="15m", fq="raw")
assert [str(d)[:16] for d in panel_15.index] == ["2024-06-20 14:35"]
assert panel_15.iloc[0, 0] == 11.0
def test_get_price_end_date_inclusive_bare_date(self, unified_provider):
"""PriceFetcher 逐只路径同语义: 裸日期 end 当日含、次日排。"""
conn = sqlite3.connect(unified_provider.db_path)
conn.executemany("INSERT INTO dbbardata VALUES(?,?,?,?,?,?,?,?,?,?,?)", [
("600600", "SSE", "2024-06-19", "d", 100, 1e3, 0, 1.0, 1.0, 1.0, 10.0),
("600600", "SSE", "2024-06-21", "d", 100, 1e3, 0, 1.0, 1.0, 1.0, 12.0),
])
conn.commit()
conn.close()
df = unified_provider.get_price(
"600600.XSHG", start_date="2024-06-18", end_date="2024-06-20")
assert len(df) == 1
assert df.iloc[-1]["close"] == 10.0