复盘周报固定动作入口:run.py plan-review 滚动窗口、调度步骤 plan-review、单测

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
zlt 2026-09-08 13:24:20 +08:00
parent 216aede701
commit 338558e03a
5 changed files with 71 additions and 4 deletions

View File

@ -12,7 +12,7 @@
| 系统 | 机器 | 代码 | 状态 | | 系统 | 机器 | 代码 | 状态 |
|---|---|---|---| |---|---|---|---|
| 数据基座 | 178 | 本段未改代码 | 日检 16 项通过;待抽取台账 1,086 份与队列对不上(运维核) | | 数据基座 | 178 | 本段未改代码 | 日检 16 项通过;待抽取台账 1,086 份与队列对不上(运维核) |
| 选股系统 | 155 | 1d76b90 | 逐票逻辑状态表在攒09-04、09-07 两天);催化事件与定价状态随明早构建生效;**接口容器要再重启一次**才把这两列送进实时计划 | | 选股系统 | 155 | 1d76b90 | 逐票逻辑状态表在攒09-04、09-07 两天);催化事件与定价状态随明早构建生效;接口容器 09-08 13:10 已重启实时计划已带这两列PMS 拉一次解析到 71 行定价状态、17 行事件 |
| PMS | 155 | 0fa4a5a镜像已重建 | 全套 680 例;四容器健康,通道在线,目标 192.168.18.182 模拟侧,表前缀空 | | PMS | 155 | 0fa4a5a镜像已重建 | 全套 680 例;四容器健康,通道在线,目标 192.168.18.182 模拟侧,表前缀空 |
| 择时决策系统 | 188 | 4778efd | PV_CHAIN_ENABLED 与 PV_VISION_ENABLED 已开,研判工作容器 09-08 12:40 重启 | | 择时决策系统 | 188 | 4778efd | PV_CHAIN_ENABLED 与 PV_VISION_ENABLED 已开,研判工作容器 09-08 12:40 重启 |

View File

@ -583,6 +583,17 @@ def ledger_table() -> pd.DataFrame:
for e in entries]) for e in entries])
def weekly_window(today=None, *, tail_days: int = 7, span_days: int = 21) -> tuple[str, str]:
"""每周五固定动作的复盘窗口2026-09-07 方案第五件之三)。
截止日取 7 个自然日前让最短的 5 日期限对窗口尾部的计划日也算得出收益
起点再往前 21 个自然日约十五个计划日返回 ISO 日期字符串 (since, until)"""
import datetime as dt
today = today or dt.date.today()
until = today - dt.timedelta(days=tail_days)
since = until - dt.timedelta(days=span_days)
return since.isoformat(), until.isoformat()
def main() -> int: def main() -> int:
ap = argparse.ArgumentParser(description="候选单复盘(只读,名单级观察收益,不是回测)") ap = argparse.ArgumentParser(description="候选单复盘(只读,名单级观察收益,不是回测)")
ap.add_argument("--since", default="2026-07-29") ap.add_argument("--since", default="2026-07-29")
@ -591,9 +602,12 @@ def main() -> int:
ap.add_argument("--start-price", choices=["next_close", "signal_close"], default="next_close", ap.add_argument("--start-price", choices=["next_close", "signal_close"], default="next_close",
help="next_close=次日收盘起算默认实盘口径signal_close=信号日收盘起算(对账方案 1.8 系列)") help="next_close=次日收盘起算默认实盘口径signal_close=信号日收盘起算(对账方案 1.8 系列)")
ap.add_argument("--out", default="data/review") ap.add_argument("--out", default="data/review")
ap.add_argument("--weekly", action="store_true",
help="按每周五固定动作的滚动窗口跑(截止 7 天前、往前 21 天),忽略 --since/--until")
a = ap.parse_args() a = ap.parse_args()
hs = tuple(int(x) for x in a.horizons.split(",") if x.strip()) hs = tuple(int(x) for x in a.horizons.split(",") if x.strip())
run(a.since, a.until, hs, a.start_price, a.out) since, until = (weekly_window() if a.weekly else (a.since, a.until))
run(since, until, hs, a.start_price, a.out)
return 0 return 0

17
run.py
View File

@ -187,6 +187,13 @@ def main():
ra.add_argument("--date", help="默认取 score 表最新日(与 plan 同口径)") ra.add_argument("--date", help="默认取 score 表最新日(与 plan 同口径)")
jg = sub.add_parser("judgement-snapshot") # 日频行业观点快照(只写不判,攒版本史) jg = sub.add_parser("judgement-snapshot") # 日频行业观点快照(只写不判,攒版本史)
jg.add_argument("--date", help="计划日,默认今天;补写过去的日期会让版本史失真,会有提示") jg.add_argument("--date", help="计划日,默认今天;补写过去的日期会让版本史失真,会有提示")
pr = sub.add_parser("plan-review") # 复盘周报(每周五固定动作;只读,名单级,不是回测)
pr.add_argument("--date", help="以哪天为今天算滚动窗口,默认今天;调度中心统一追加的就是它")
pr.add_argument("--since", help="给了就按给定窗口跑,不再滚动")
pr.add_argument("--until")
pr.add_argument("--horizons", default="5,10,20")
pr.add_argument("--start-price", choices=["next_close", "signal_close"], default="next_close")
pr.add_argument("--out", default="data/review")
f = sub.add_parser("freeze") f = sub.add_parser("freeze")
f.add_argument("--date", help="默认今天") f.add_argument("--date", help="默认今天")
b = sub.add_parser("build") b = sub.add_parser("build")
@ -227,6 +234,16 @@ def main():
elif a.cmd == "judgement-snapshot": elif a.cmd == "judgement-snapshot":
import judgement import judgement
judgement.snapshot(a.date) judgement.snapshot(a.date)
elif a.cmd == "plan-review":
import datetime as _dt
import plan_review
since, until = a.since, a.until
if not since and not until:
today = _dt.date.fromisoformat(a.date) if a.date else None
since, until = plan_review.weekly_window(today)
hs = tuple(int(x) for x in a.horizons.split(",") if x.strip())
print(f"[plan-review] 窗口 {since} ~ {until},期限 {hs},起算 {a.start_price}")
plan_review.run(since, until, hs, a.start_price, a.out)
elif a.cmd == "tracks": elif a.cmd == "tracks":
import tracks import tracks
tracks.coverage_report() tracks.coverage_report()

View File

@ -0,0 +1,32 @@
"""复盘周报的固定动作入口2026-09-07 方案第五件之三):滚动窗口与调度步骤白名单。"""
import datetime as dt
import plan_review
import xxl
def test_weekly_window_default_span():
# 周五 2026-09-11 触发:截止 7 天前的 09-04起点再往前 21 天的 08-14
since, until = plan_review.weekly_window(dt.date(2026, 9, 11))
assert (since, until) == ("2026-08-14", "2026-09-04")
def test_weekly_window_is_rolling_and_iso():
a = plan_review.weekly_window(dt.date(2026, 9, 11))
b = plan_review.weekly_window(dt.date(2026, 9, 18))
assert b != a
assert (dt.date.fromisoformat(b[0]) - dt.date.fromisoformat(a[0])).days == 7
assert dt.date.fromisoformat(a[1]) - dt.date.fromisoformat(a[0]) == dt.timedelta(days=21)
def test_weekly_window_today_default():
since, until = plan_review.weekly_window()
assert dt.date.fromisoformat(until) == dt.date.today() - dt.timedelta(days=7)
assert since < until
def test_plan_review_step_registered_but_not_default():
assert "plan-review" in xxl.STEP_ORDER
assert xxl.STEP_CMDS["plan-review"] == ["run.py", "plan-review"]
assert "plan-review" not in xxl.DEFAULT_STEPS # 不进默认三步,平台单建周五任务
assert xxl.STEP_ORDER.index("plan-review") > xxl.STEP_ORDER.index("push-pool")

8
xxl.py
View File

@ -24,9 +24,10 @@ tasks_periodic 的 notify_xxl对平台表现完全一致
build 因子构建run.py build all --mode daily build 因子构建run.py build all --mode daily
plan 生成当日选股计划文件 plan 生成当日选股计划文件
push-pool 计划写入股票池 + 触发决策系统增量补扫 push-pool 计划写入股票池 + 触发决策系统增量补扫
后面步不在默认三步里平台各建一个任务显式传 steps 才跑 后面步不在默认三步里平台各建一个任务显式传 steps 才跑
regime-append 把决策系统 08:40 预热的市场区制写进当天计划快照的环境段平台 08:45 触发 regime-append 把决策系统 08:40 预热的市场区制写进当天计划快照的环境段平台 08:45 触发
judgement-snapshot 把数据基座当前这一版产业研判与环节评析抄一行攒版本史只写不判 judgement-snapshot 把数据基座当前这一版产业研判与环节评析抄一行攒版本史只写不判
plan-review 候选单复盘周报滚动窗口截止 7 天前往前 21 只读平台每周五收盘后触发
""" """
import datetime as dt import datetime as dt
import os import os
@ -49,7 +50,7 @@ HERE = os.path.dirname(os.path.abspath(__file__))
LOG_PATH = os.path.join(HERE, "data", "xxl_build.log") LOG_PATH = os.path.join(HERE, "data", "xxl_build.log")
# 步骤 → 命令(顺序即执行顺序;--date 由触发参数统一追加) # 步骤 → 命令(顺序即执行顺序;--date 由触发参数统一追加)
STEP_ORDER = ("build", "plan", "push-pool", "regime-append", "judgement-snapshot") STEP_ORDER = ("build", "plan", "push-pool", "regime-append", "judgement-snapshot", "plan-review")
STEP_CMDS = { STEP_CMDS = {
"build": ["run.py", "build", "all", "--mode", "daily"], "build": ["run.py", "build", "all", "--mode", "daily"],
"plan": ["run.py", "plan"], "plan": ["run.py", "plan"],
@ -62,6 +63,9 @@ STEP_CMDS = {
# 产业研判与环节评析抄进选股系统自己的表,攒版本史,只写不判。它与出计划互不依赖,同样 # 产业研判与环节评析抄进选股系统自己的表,攒版本史,只写不判。它与出计划互不依赖,同样
# 不在默认三步里,必须显式 steps=judgement-snapshot 才跑。 # 不在默认三步里,必须显式 steps=judgement-snapshot 才跑。
"judgement-snapshot": ["run.py", "judgement-snapshot"], "judgement-snapshot": ["run.py", "judgement-snapshot"],
# 复盘周报2026-09-07 方案第五件之三):每周五收盘后平台单建一个任务只跑这一步,写 data/review。
# 只读,名单级观察收益,不是回测;--date 由平台追加时当作「今天」算滚动窗口。
"plan-review": ["run.py", "plan-review"],
} }
DEFAULT_STEPS = ("build", "plan", "push-pool") DEFAULT_STEPS = ("build", "plan", "push-pool")
STEP_TIMEOUT_SEC = 3600 # 单步上限一小时,防呆死(正常盘前链全程分钟级) STEP_TIMEOUT_SEC = 3600 # 单步上限一小时,防呆死(正常盘前链全程分钟级)