第 8 章 · 01 双模式设计:日常增量与历史回填


文档摘要

第 8 章 · 01 双模式设计:日常增量与历史回填 本节摘要:本节讲 Sequoia-X 主程序入口的第一个关键设计——双模式。日常模式( )和回填模式( )走完全不同的代码路径:日常模式追求「快」(23 分钟跑完全市场),回填模式追求「稳」(12 分钟一次性灌全市场)。它们共用配置层、数据引擎、策略库,但入口分流、行为差异。本节会先讲「为什么需要两种模式」(不同的时间预算与容错要求),再讲 argparse 模式分发(一个标志位控制两条路径),然后讲两条路径的工作流对比(回填走单进程重试、日常走多进程并行),最后讲「crontab 自动运行」——Sequoia-X 的日常模式可以挂到系统定时任务,收盘后自动跑。 内容来源:原项目主程序入口精读。

第 8 章 · 01 双模式设计:日常增量与历史回填

本节摘要:本节讲 Sequoia-X 主程序入口的第一个关键设计——双模式。日常模式(python <主程序入口>)和回填模式(python <主程序入口> --backfill)走完全不同的代码路径:日常模式追求「」(2~3 分钟跑完全市场),回填模式追求「」(12 分钟一次性灌全市场)。它们共用配置层、数据引擎、策略库,但入口分流、行为差异。本节会先讲「为什么需要两种模式」(不同的时间预算与容错要求),再讲 argparse 模式分发(一个标志位控制两条路径),然后讲两条路径的工作流对比(回填走单进程重试、日常走多进程并行),最后讲「crontab 自动运行」——Sequoia-X 的日常模式可以挂到系统定时任务,收盘后自动跑

内容来源:原项目主程序入口精读。

💡 核心心法:好的「主流程」设计——一条入口、两种模式、各自优化。把「互斥的场景」分到不同模式,不试图用一个模式做所有事

学习目标

阅读完本节,你应当能够:

  1. 讲清「双模式」的工程理由场景适配
  2. 读懂 argparse 的「一个标志位控制两条路径」的分发模式。
  3. 理解日常模式与回填模式的代码路径差异
  4. 写一段可运行的「主程序入口 + argparse 分发」教学示例。

一、为什么需要两种模式

回看第 2 章讲的「冷启动三步」:装环境 → 配配置 → 灌数据。灌数据和「每天用」其实是两件时间预算与容错要求完全不同的事:

维度 日常模式 回填模式
触发时机 每个交易日收盘后 首次部署 / 数据修复
数据规模 每只股票 1~2 根 K 线 每只股票 数百根 K 线
时间预算 2~3 分钟内必须结束 十几分钟
容错要求 快速完成,单股失败可丢 每只都不能漏必须重试
进程模型 8 进程并行(追求速度) 单进程 + 失败重试(追求稳)
中断处理 重跑从「最后日期」续 重跑从「未完成股票」续

为什么不能合并成一种模式

合并的代价 后果
用回填模式跑日常 12 分钟才出结果——没人能接受
用日常模式跑首次 5000 只股票要 5000 次单股查询——几小时
用同一进程模型 日常需要的并行加速与回填需要的失败重试逻辑相互干扰

双模式」的本质是「风险特征不同 → 工程取舍不同」——Sequoia-X 用「两条代码路径」让两种场景各自优化

二、argparse:一条标志位控制两条路径

Sequoia-X 用 Python 标准库 argparse 实现命令行参数解析:

import argparse def main() -> None: parser = argparse.ArgumentParser(description="Sequoia-X V2 选股系统") parser.add_argument( "--backfill", action="store_true", help="回填模式:通过 baostock 拉取全市场历史 K 线(约 12 分钟)", ) args = parser.parse_args() if args.backfill: # 回填模式 ... return # 日常模式 ...

2.1 action="store_true" 的精妙

parser.add_argument("--backfill", action="store_true", help="...")
  • 不传 --backfillargs.backfill = False → 走日常模式
  • --backfillargs.backfill = True → 走回填模式

action="store_true" 是 Python argparse 的「布尔开关」惯用法——不传为 False,传了为 True——一个标志位控制两条路径

2.2 if ... return 早返模式

if args.backfill: # 回填逻辑 return # ← 关键:早返 # 日常逻辑(继续往下走)

早返」让两种模式互不干扰——回填模式 return 后,日常模式的代码不会被执行——避免大量 if/else 嵌套

三、两种模式的工作流对比

3.1 日常模式工作流

日常模式:python <主程序入口> ───────────────────────────────────── 1. 初始化配置 + 日志 2. 初始化数据引擎(如果 SQLite 不存在则报错) 3. 拉增量 K 线(8 进程并行,约 2 分钟) 4. 实例化 7 个策略 5. 遍历策略 → 选股结果 6. 每个策略结果推送到对应飞书机器人 7. 结束

关键:从「拉数据」到「推送」是一条完整闭环——跑完即结束

3.2 回填模式工作流

回填模式:python <主程序入口> --backfill ───────────────────────────────────── 1. 初始化配置 + 日志 2. 初始化数据引擎 3. 拉全市场股票列表 4. 单进程遍历每只股票 ├─ 查 SQLite 该股最后日期 ├─ 已是最新 → 跳过(断点续传) ├─ 需要补数据 → 拉历史 K 线 ├─ 失败重试 3 次(2s/4s/8s) └─ 每 200 只重连 baostock 5. 结束

关键:回填模式不跑策略、不推送飞书——只灌数据

3.3 模式差异速查表

维度 日常模式 回填模式
拉数据范围 增量(最后日期+1 → 今天) 全市场(起始日期 → 今天)
进程数 8 1
失败处理 仅记日志 + 跳过 重试 3 次 + 指数退避
是否跑策略
是否推飞书
典型耗时 2~3 分钟 约 12 分钟
典型场景 每个交易日收盘后 首次部署 / 数据修复

是否跑策略」「是否推飞书」这两个问题,让两种模式几乎没有代码复用——它们是两套独立的逻辑,仅共享配置和数据库。

四、典型使用场景

场景 1:首次部署

# 1. 装环境 uv sync # 2. 配配置 cp <环境变量模板> <本地配置> # 填入飞书 Webhook # 编辑本地配置,填入 FEISHU_WEBHOOK_URL # 3. 回填一次(首次灌入全市场历史) python <主程序入口> --backfill # 约 12 分钟 # 4. 日常运行(每天收盘后跑一次) python <主程序入口> # 约 2~3 分钟

场景 2:日常使用(推荐挂 crontab)

# 每天 19:00 跑(A股收盘 15:00,缓冲 4 小时) 0 19 * * 1-5 cd <项目根目录> && <虚拟环境>/python <主程序入口> >> <日志文件> 2>&1

4 小时缓冲是为了让 baostock 完成数据汇总——--backfill 模式更适合非交易时段跑(避开数据未就绪)。

场景 3:数据修复

# 假设某天拉数据失败,需要重拉 python <主程序入口> --backfill # 12 分钟,会跳过已入库的

backfill 模式天然支持断点续传——只补缺失部分。

五、可运行教学代码:argparse 模式分发

下面这段代码完整演示 Sequoia-X 的「argparse 模式分发」教学版。它不实际拉数据,只演示「一个标志位控制两条路径」:

# mode_dispatch_demo.py # 可运行:argparse 模式分发演示 import argparse import time def run_daily_mode() -> str: """日常模式:拉增量 → 跑策略 → 推飞书(教学模拟)。""" print("=" * 50) print("【日常模式】") print("=" * 50) print(" 1. 拉增量 K 线(8 进程并行,模拟 30s)") time.sleep(0.3) print(" 2. 实例化 7 个策略") print(" 3. 遍历策略 → 选股结果") print(" - 海龟突破 → 3 只") print(" - 均线金叉 → 5 只") print(" - RPS → 8 只") print(" - ...") print(" 4. 推送飞书(共 7 个机器人)") return "daily_done" def run_backfill_mode() -> str: """回填模式:单进程拉全市场历史(教学模拟)。""" print("=" * 50) print("【回填模式】") print("=" * 50) print(" 1. 拉全市场股票列表(5200 只)") print(" 2. 单进程遍历每只股票") print(" - 已是最新 → 跳过(断点续传)") print(" - 需要补数据 → 拉历史 K 线") print(" - 失败重试 3 次") print(" 3. 不跑策略、不推飞书") return "backfill_done" def main(): parser = argparse.ArgumentParser(description="Sequoia-X V2 选股系统(教学版)") parser.add_argument( "--backfill", action="store_true", help="回填模式:拉全市场历史 K 线(约 12 分钟)", ) args = parser.parse_args() # 早返模式分叉 if args.backfill: print(run_backfill_mode()) return # 日常模式 print(run_daily_mode()) if __name__ == "__main__": main()

测试方式:

# 不传参数 → 日常模式 python mode_dispatch_demo.py # 传 --backfill → 回填模式 python mode_dispatch_demo.py --backfill # 看 help python mode_dispatch_demo.py --help

预期输出(日常模式):

================================================== 【日常模式】 ================================================== 1. 拉增量 K 线(8 进程并行,模拟 30s) 2. 实例化 7 个策略 3. 遍历策略 → 选股结果 - 海龟突破 → 3 只 - 均线金叉 → 5 只 - RPS → 8 只 - ... 4. 推送飞书(共 7 个机器人) daily_done

预期输出(回填模式):

================================================== 【回填模式】 ================================================== 1. 拉全市场股票列表(5200 只) 2. 单进程遍历每只股票 - 已是最新 → 跳过(断点续传) - 需要补数据 → 拉历史 K 线 - 失败重试 3 次 3. 不跑策略、不推飞书 backfill_done

这段代码清晰展示「一个 --backfill 标志位控制两条完全不同的代码路径」的精妙结构。

六、Sequoia-X 的「主程序入口」核心代码

Sequoia-X 真实的主程序入口核心结构(简化):

def main() -> None: parser = argparse.ArgumentParser(description="Sequoia-X V2 选股系统") parser.add_argument("--backfill", action="store_true", help="...") args = parser.parse_args() try: # 1. 初始化 settings = get_settings() logger = get_logger(__name__) engine = DataEngine(settings) # 2. 模式分叉 if args.backfill: # 回填模式 all_symbols = engine.get_all_symbols() engine.backfill(all_symbols) return # 3. 日常模式 engine.sync_today_bulk() # 拉增量 # 4. 注册策略 strategies = [ MaVolumeStrategy(engine, settings), TurtleTradeStrategy(engine, settings), # ... 共 7 个 ] # 5. 推送器 notifier = FeishuNotifier(settings) # 6. 遍历执行 + 推送 for strategy in strategies: try: selected = strategy.run() if selected: notifier.send(selected, ..., webhook_key=strategy.webhook_key) except Exception: logger.exception(...) # 异常隔离 except Exception: # 主流程兜底 logger.exception("主流程异常") sys.exit(1) # 非零退出码

结构非常清晰

  • 初始化(配置 + 日志 + 引擎)→ 模式分叉(日常 / 回填)→ 执行(策略 / 回填)→ 推送异常兜底

整个主程序入口不到 100 行——这是「好主流程」的范本:职责清晰、不嵌套、易读

七、为什么「主程序入口」要这么短

新手常犯的错是把「业务逻辑」写在主程序里——结果主程序入口 500+ 行,难以阅读、难以测试、难以维护

Sequoia-X 的「短小主程序」哲学:

长主程序 短主程序
业务逻辑混在主流程 业务逻辑在子模块
一个文件改崩全盘 各自模块独立
测试要起整个主程序 子模块可独立测试
多人改同一文件 多人改不同文件

主程序入口应该只做「调度——把每个动作叫一下就够了

八、本节在全景图的位置

双模式设计 ✅ ← 本节 │ ▼ 下一节(第 02 节)策略执行与推送编排:日常模式的完整流程

本节要点回顾

  1. 双模式理由:日常追求「」+ 回填追求「」——时间预算与容错要求不同
  2. argparse 模式分发action="store_true" 标志位 + 早返模式——一个参数控制两条路径
  3. 代码路径差异:日常走「拉数据 + 跑策略 + 推飞书」,回填只走「拉数据」——几乎没有代码复用
  4. 典型场景:首次部署用回填、日常运行挂 crontab、数据修复重跑回填。
  5. if ... return 早返:避免 if/else 嵌套——让两条路径各自独立
  6. 短小主程序:主程序入口只做调度——业务逻辑下沉到子模块
  7. 可运行示例:argparse 分发的两种模式,亲眼看代码路径分叉

配套教学脚本:images/mode_dispatch_demo.py——演示 argparse 模式分发。

下一节,把日常模式的「策略执行 + 推送编排」讲透——这是 Sequoia-X 「选股 + 触达」闭环的最后一环。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U