功能:Web 前端与报告格式化(工作区中此前未提交的工作)

说明:本提交**不是本轮会话所做**,而是工作区里此前遗留的未提交改动。
为把历史分开,先单独提交它,再提交本轮会话的修改。

包含:
- Web 前端:web/index.html、web/app.js(统一 SPA,含回测/画像/Walk-forward 页面)
- 后端接口:web/server.py 路由、web/analysis.py(新增个股分析)
- 报告层:report/format.py(新增统一数字格式化 NumFmt)、
  report/{backtest,profile,sensitivity,universe,walkforward}_report.py 接入 NumFmt、
  report/renderer.py
- 股息率口径:factor/dividend_yield.py(毛刺消除 smooth_spikes)
- 筛选:universe/selector.py、universe/filters/dividend.py
- 绩效/敏感性:analysis/performance.py、analysis/sensitivity.py
- 部署:deploy/install-service.sh
- 测试:tests/test_format.py、tests/test_dividend_smoothing.py(新增)、
  tests/test_web.py、tests/test_universe.py

提交时全量测试 403 项通过。
This commit is contained in:
2026-10-04 12:47:10 +08:00
parent fce725e13c
commit fb6608193b
21 changed files with 2579 additions and 125 deletions
+16 -1
View File
@@ -7,10 +7,16 @@
# ./deploy/install-service.sh uninstall 停止并移除
# ./deploy/install-service.sh status 查看状态与连通性
# ./deploy/install-service.sh reinstall 重新渲染 plist 并重启
# ./deploy/install-service.sh restart 只重启进程(改完 src/ 或 config/ 后执行)
#
# 为什么需要:nginx 由 brew services 托管、开机自启。若后端只是 nohup 进程,
# 机器重启后它就不在了 —— 页面能打开但会显示「API 不可用」。
#
# 为什么 restart 是刚需:Python 进程把 hdiv 模块与 config/*.yml 读进了内存
# (配置经 lru_cache 缓存),改完源码或配置**不会**自动生效。不重启就会出现
# 「一边读新配置、一边用旧模型校验」的假故障,例如给 datasource.yml 加了
# 新字段却报 ``Extra inputs are not permitted``。
#
# 若你不用 launchd,也可用 ./deploy/serve.sh start 手动启动(重启后需再次执行)。
# ============================================================
set -euo pipefail
@@ -85,11 +91,20 @@ cmd_status() {
tail -3 "${PROJECT_ROOT}/logs/web.err.log" 2>/dev/null | sed 's/^/ /' || true
}
cmd_restart() {
is_loaded || die "服务未加载,先执行:./deploy/install-service.sh install"
# kickstart -k 先杀旧进程再拉新进程;PID 会变,所以顺手打印状态
launchctl kickstart -k "gui/$(id -u)/${LABEL}"
sleep 3
cmd_status
}
case "${1:-install}" in
install) cmd_install ;;
reinstall) render; launchctl unload -w "${TARGET}" 2>/dev/null || true
launchctl load -w "${TARGET}"; sleep 3; cmd_status ;;
restart) cmd_restart ;;
uninstall) cmd_uninstall ;;
status) cmd_status ;;
*) die "未知动作:$1(可用:install|reinstall|uninstall|status)" ;;
*) die "未知动作:$1(可用:install|reinstall|restart|uninstall|status)" ;;
esac