今天的核心工作是把项目从”本地能跑”推进到”自动化运行、线上可访问”。包括修复数据抓取逻辑、搭建 GitHub Actions 自动更新管道、清除代码中的密钥、推送到新仓库、以及 Streamlit Cloud 部署适配。
问题:daily_update.py 中 ETF 份额(fd_share)的抓取逻辑有设计缺陷。当昨天的 fd_share 拉不到时,代码会偷偷用更早日期的缓存值来替代,而不是像其他数据一样触发重试。
分析:我们用的是 T-1(昨天)的 fd_share 来计算今天的 ETF 换手率。到了 17:30 去拉昨天的数据,正常情况下肯定有。如果拉不到,说明接口出了问题,应该等一等再重试,而不是拿更旧的数据凑合。用旧数据算出来的换手率是不准的,会影响交易信号的判断。
修复内容:
if fd_share_val is None and status.get('last_fd_share_value'))raise ValueError,进入和其他数据相同的重试循环fd_share_date 变量和返回值背景:之前计划用 Windows Task Scheduler(update_daily.bat)每天 17:30 触发数据更新。但项目要上传到 GitHub 公开仓库,未来通过云端访问看板,本地定时任务就不合适了——电脑关机就跑不了。
方案对比:
| 方案 | 优点 | 缺点 |
|---|---|---|
| Windows Task Scheduler | 简单直接 | 依赖本地电脑开机 |
| Vercel Cron | 跟仓库绑定 | 超时上限太短(免费10秒/Pro 300秒),无持久文件系统,完全不适合我们有无限重试的脚本 |
| GitHub Actions | 免费(public 仓库无限制)、最长可跑 6 小时、可自动 commit push 回仓库 | 需要写 workflow 配置文件 |
最终选择 GitHub Actions,创建了 .github/workflows/daily_update.yml:
触发条件:
- 定时:每周一至周五 UTC 09:30(= 北京时间 17:30)
- 手动:在 GitHub 仓库 Actions 页面点击 Run workflow(可指定日期)
执行流程:
1. Checkout 仓库代码
2. 安装 Python 3.11 + requirements.txt 依赖
3. 运行 python daily_update.py
4. 如果有数据变更,自动 git commit & push 回仓库
重试上限:最初加了 RETRY_MAX_TOTAL=10 的环境变量来限制 CI 中的重试次数,后来确认 public 仓库 GitHub Actions 完全免费、无分钟数限制,就去掉了上限。现在 CI 和本地一样,无限重试直到成功,最多跑到 6 小时 job 超时。
测试:手动触发了一次 workflow,31 秒内全部通过。因为 20260224 的数据在之前本地测试时已经更新过,防重复逻辑生效,显示”已成功更新,跳过”。
项目要放到 public 仓库,代码里不能留 API 密钥。
改动:
daily_update.py:Token 改为 os.environ.get('TUSHARE_TOKEN'),取不到就报错退出build_strategy_data.py:同样改为从环境变量读取verify_adj.py:测试脚本,已加入 .gitignore 不上传密钥存储:
TUSHARE_TOKEN。这是 GitHub 内置的加密存储,只有 Actions 运行时能读取,不会出现在代码、日志或公开页面TUSHARE_TOKEN全面扫描确认:用 ripgrep 搜索所有 .py 文件,确认代码中零 token 残留。
新仓库:https://github.com/JerryZ8889/CSI500(public)
操作:
.gitignore 配置:排除了以下不该上传的文件:
__pycache__/、.claude/ — 缓存和工具配置daily_update.log、daily_update_console.log — 运行日志verify_adj.py、app_raw_temp.py、app_remote.py — 临时/测试脚本backtest_kline_*.png、backtest_result.png — 回测图片update_daily.bat — 已被 GitHub Actions 替代~$*.docx — Word 临时文件问题:dashboard.py 里 matplotlib 图表用 SimHei 字体显示中文。SimHei 只有 Windows 系统自带,Streamlit Cloud 的 Linux 服务器上没有这个字体,中文会全部显示为方块。
解决:
packages.txt,内容为 fonts-wqy-zenhei(Streamlit Cloud 会自动 apt-install 这个中文字体包)['SimHei', 'WenQuanYi Zen Hei', 'Microsoft YaHei'],找到第一个本机存在的字体就用import matplotlib.font_manager as _fm
_available = {f.name for f in _fm.fontManager.ttflist}
for _font in ['SimHei', 'WenQuanYi Zen Hei', 'Microsoft YaHei']:
if _font in _available:
plt.rcParams['font.sans-serif'] = [_font]
break
注意:最初尝试直接写字体回退列表 ['WenQuanYi Zen Hei', 'SimHei', ...],结果在 Windows 上因为第一个字体不存在导致全部中文变方块。matplotlib 的字体回退机制不太可靠,所以改用主动检测的方式。
原始配色:顶栏背景、分节标题侧边条、策略净值曲线都用了亮红色 #e11d48,视觉冲击太强,55 岁以上的合作伙伴看着不舒服。
调整为深藏青蓝:
linear-gradient(90deg, #1e3a5f, #2d5a87) — 沉稳的海军蓝渐变border-left: 4px solid #1e3a5fcolor='#1e3a5f'整体风格从”警报红”变成了”金融蓝”,更专业、更耐看。
创建了 requirements.txt:
tushare>=1.4.0
pandas>=2.0.0
numpy>=1.24.0
streamlit>=1.30.0
matplotlib>=3.7.0
mplfinance>=0.12.9b7
注意 mplfinance 没有正式版 0.12.10,PyPI 上全是 beta 版本号,最初写 >=0.12.10 导致 CI pip install 失败,改成了 >=0.12.9b7。
已在 Streamlit Cloud 上完成部署,链接为 https://csi500-mog4ccaigs8grc2sazknxq.streamlit.app/。
发现的问题:streamlit.app 域名在中国大陆无法访问,合作伙伴看不到。
待办:后续考虑用腾讯云轻量应用服务器(香港节点)部署,不需要 ICP 备案,国内访问也快。域名 sophiaz.cn 本身就在腾讯云管理,可以直接绑定子域名 csi500.sophiaz.cn。
CSI500/
├── .github/workflows/
│ └── daily_update.yml ← GitHub Actions 每日自动更新
├── stocks_data/ ← 1074 只成分股后复权日线数据
│ ├── 000006.SZ.csv
│ ├── ...
│ └── 689009.SH.csv
├── strategy_data.csv ← 策略总表(14列,1730行,20190102~20260224)
├── csi500_components_schedule.csv ← 成分股调度表
├── adj_factor_base.csv ← 全市场复权基准表(5485只股票,基准日20260213)
├── update_status.json ← 数据同步状态(GitHub Actions 每次更新后写入)
├── daily_update.py ← 每日增量更新脚本(GitHub Actions 调用)
├── build_strategy_data.py ← 全量构建 strategy_data.csv 的脚本
├── dashboard.py ← Streamlit 决策看板
├── backtest.py ← 回测引擎(本地运行,生成 K线图 + 净值曲线)
├── requirements.txt ← Python 依赖
├── packages.txt ← Streamlit Cloud 系统依赖(中文字体)
├── CLAUDE.md ← 项目说明(策略参数速查、待确认差异等)
└── .gitignore ← 排除日志/缓存/临时文件
csi500.sophiaz.cn,让国内合作伙伴能访问