- Python 62.5%
- Vue 20.8%
- CSS 10.2%
- TypeScript 5.6%
- JavaScript 0.3%
- Other 0.5%
| backend | ||
| config/strategies | ||
| deploy | ||
| docs | ||
| frontend | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| A股策略轮动提示盘_系统规划与落地方案.md | ||
| docker-compose.https.yml | ||
| docker-compose.yml | ||
| LICENSE | ||
| README.md | ||
A 股策略轮动提示盘
这是一个面向个人使用的 A 股策略信号中心、数据健康监控盘和人工执行辅助系统。第一版实现 ETF 日频轮动与银行红利低波两套策略,采用 Vue 3 PWA、FastAPI、APScheduler、MySQL 8、Docker Compose 与 Nginx,并为后续缓存/队列扩展预置 Redis 服务。
本项目只提供策略研究和人工执行提示,不构成投资建议,也不会自动下单。
第一版范围
- ETF 收盘后排名、目标持仓和下一交易日换仓信号;
- 银行股实时 TTM 股息率、红利低波排名和调仓缓冲;
- 当前持仓、目标持仓、历史信号和信号确认;
- 历史日期试算与正式结果隔离,历史试算不会覆盖首页最新正式信号;
- 数据更新时间、任务状态、失败告警和手动刷新;
- 独立 API 与 APScheduler 进程,避免多进程重复调度;
- 响应式网页与 PWA,适配电脑、手机和平板;
- MySQL 持久化;Redis 作为后续缓存/队列扩展位;Nginx 统一反向代理、强制单用户认证并提供 HTTPS 编排。
规划原文见 A股策略轮动提示盘_系统规划与落地方案.md。
数据隔离边界
本项目的数据库名固定为:
a_share_rotation_dashboard_v1_20260720
docker-compose.yml 会把 MYSQL_DATABASE_OVERRIDE 强制设置为这个值。即使 config/.env.dev 中的 MYSQL_DSN 路径仍是旧的 trade_platform,应用也必须先替换数据库名再建立连接;不得在旧库中建表、迁移或写入业务数据。
外部 Redis 使用共享的 DB 0,但所有键都必须带以下前缀:
a_share_rotation_dashboard:v1:
禁止执行 FLUSHDB、FLUSHALL,也禁止扫描后删除不属于此前缀的键。MySQL 是第一版唯一的核心数据库;MongoDB 仅保留为未来存储原始接口 JSON、公告全文等非结构化数据的扩展位,当前启动和运行均不依赖 MongoDB。
服务结构
| 服务 | 作用 | 默认宿主机端口 |
|---|---|---|
nginx |
统一入口与反向代理 | 8080 |
frontend |
Vue 3 PWA 静态站点 | 仅容器网络 |
api |
FastAPI 接口 | 127.0.0.1:8000 |
scheduler |
独立 APScheduler 进程 | 无 |
mysql |
内置 MySQL 8(可不用) | 127.0.0.1:3307 |
redis |
内置 Redis(可不用) | 127.0.0.1:6380 |
快速启动:内置 MySQL 与 Redis
需要 Docker Desktop(Linux 容器)和 Docker Compose v2。Windows PowerShell 中执行:
Copy-Item .env.example .env
notepad .env
docker compose --env-file .env config
docker compose --env-file .env up -d --build mysql redis
docker compose --env-file .env run --rm api python -m app.cli init-database --migrate --backfill-history --history-days 300
docker compose --env-file .env up -d --build api scheduler frontend nginx
请先在 .env 中同时修改 MYSQL_PASSWORD 与 MYSQL_DSN 中的密码,并修改 MySQL root、Redis 密码及 DASHBOARD_AUTH_PASSWORD。入口密码至少 16 个字符;Nginx 会拒绝空值、短密码和模板占位值。MYSQL_DATABASE_OVERRIDE 和 REDIS_KEY_PREFIX 是隔离边界,不应改动。
浏览器打开 http://localhost:8080,使用 .env 中的单用户账号密码登录。入口默认只绑定 127.0.0.1,API 与页面均由 Basic Auth 保护,只有不含业务信息的 /healthz 公开。真实初始化会幂等回填至少 300 个交易日的策略池日线,并拉取最新估值因子和分红;推荐填写 TUSHARE_TOKEN。若主、备数据源返回不完整,系统会保持 PENDING_DATA,不会把残缺结果发布为正式信号。
只想本地预览界面时,可改用显式演示初始化;演示数据会被清楚标记,不能用于交易判断:
docker compose --env-file .env run --rm api python -m app.cli init-database --migrate --seed-demo
从演示环境切换到生产数据前,必须先清除所有带 synthetic 来源及其派生结果,再执行真实历史回填。清理命令只接受当前隔离的项目 MySQL 新库,并要求同时显式确认完整库名;它不会删除策略配置、标的基础定义或真实来源数据:
docker compose --env-file .env run --rm api python -m app.cli purge-demo-data --confirm-database a_share_rotation_dashboard_v1_20260720 --yes-i-understand
docker compose --env-file .env run --rm api python -m app.cli init-database --migrate --backfill-history --history-days 300
不要在演示数据仍存在时直接叠加生产数据。真实回填完成后,应在“数据状态”页确认日线、实时行情、估值因子、分红和交易日历均为真实来源且无缺失标的,再运行正式策略。生产银行策略建议配置 TUSHARE_TOKEN;未配置时,AKShare 可在日频估值刷新时从市场快照筛出策略池的市净率,并用分红事件与最新价计算 live 股息率。盘中实时行情只逐个请求当前策略池标的,不会每分钟下载全市场快照。任一主、备接口返回 0 行或缺少请求标的时,任务会保持 PENDING_DATA,不会误报为健康或发布残缺信号。每天 08:25 的基础信息任务会补全策略池新增代码的名称和上市日期。
正式信号的生效日必须由库中连续、完整的 SSE 交易日历证明;若上游日历只返回到当前日期、请求区间被截断,或中间缺少任一自然日,系统会保持 PENDING_DATA,不会用普通工作日猜测下一交易日。银行页的“重新生成今日信号”是带二次确认的人工 override,只跳过配置的调仓日限制,仍然执行数据完整性、正式数据源与交易日历门禁。
使用 config/.env.dev 的外部 MySQL 与 Redis
现有文件只负责提供服务器地址和凭据,不需要复制或改写。新的 MySQL 数据库应已由具备权限的账号创建;如需在另一套环境首次创建,可由 DBA 执行 deploy/mysql/create-database.sql。该脚本只执行幂等的 CREATE DATABASE IF NOT EXISTS,不会接触旧库。
外部模式不启动内置 mysql、redis 服务:
$env:DASHBOARD_AUTH_USERNAME = 'dashboard'
$env:DASHBOARD_AUTH_PASSWORD = '请替换为至少16位的随机强密码'
docker compose --env-file config/.env.dev build api frontend
docker compose --env-file config/.env.dev run --rm --no-deps api python -m app.cli init-database --migrate --backfill-history --history-days 300
docker compose --env-file config/.env.dev up -d --build --no-deps api scheduler frontend nginx
当前 config/.env.dev 没有数据源令牌。生产使用推荐在进程环境中额外提供 TUSHARE_TOKEN;没有令牌时仍会尝试 AKShare 的真实日线、基础信息、实时估值和分红接口,但其接口缺项或不可用时,安全门禁会保留 PENDING_DATA。如果当前只需验收整套系统,可把上述初始化命令的历史回填参数替换为 --seed-demo。
--no-deps 很重要:它让 Compose 跳过仅供自包含部署使用的本地 MySQL/Redis。可以用下面的静态检查确认隔离变量仍然生效:
docker compose --env-file config/.env.dev config | Select-String 'MYSQL_DATABASE_OVERRIDE|REDIS_KEY_PREFIX'
两项输出必须分别为 a_share_rotation_dashboard_v1_20260720 和 a_share_rotation_dashboard:v1:。Compose 展开结果中的原始 MYSQL_DSN 仍可能显示旧路径,这是预期现象;应用在连接前使用强制覆盖值替换它。
外部连接若使用 localhost,容器内应改用 host.docker.internal 或宿主机局域网地址。不要把包含真实密码的 Compose 完整展开结果、日志或截图发到公共渠道。
健康检查与运维
docker compose ps
Invoke-RestMethod http://localhost:8080/healthz
$credential = Get-Credential -UserName dashboard -Message 'Dashboard password'
Invoke-RestMethod -Credential $credential http://localhost:8080/api/health
docker compose logs --tail 100 api scheduler
/healthz检查 Nginx;/api/health检查 FastAPI;docker compose ps中mysql、redis、api、frontend、nginx应显示healthy(外部模式不会有本地 MySQL/Redis);- 数据源与任务明细可在页面“数据状态”中查看,也可调用
/api/data-status; scheduler必须只有一个实例,不要对其执行横向扩容。
常用命令:
docker compose restart api scheduler
docker compose logs -f --tail 200 scheduler
docker compose exec api alembic current
docker compose exec api alembic upgrade head
docker compose down
普通 docker compose down 会保留数据库卷。docker compose down -v 会永久删除内置 MySQL/Redis 数据,除非已确认备份且确实要重建,否则不要执行。
MySQL 备份与恢复
内置 MySQL 的一致性逻辑备份示例(data/ 不纳入版本控制):
New-Item -ItemType Directory -Force data\backups | Out-Null
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
docker compose exec mysql sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction --routines --triggers --events --set-gtid-purged=OFF "$MYSQL_DATABASE" > /tmp/dashboard.sql'
docker compose cp mysql:/tmp/dashboard.sql "data/backups/mysql-$stamp.sql"
docker compose exec mysql sh -c 'rm -f /tmp/dashboard.sql'
外部 MySQL 建议通过 MySQL Workbench、MySQL Shell 或服务器既有备份系统执行;无论使用哪种工具,都要显式选择 a_share_rotation_dashboard_v1_20260720,不能沿用 DSN 中的旧库名。推荐参数为单事务、包含 routines/triggers/events,并在每次 Alembic 升级前留一份备份。
恢复会覆盖同名表中的数据,应先停掉 api 与 scheduler,确认备份文件和目标库名,并在维护窗口操作:
docker compose stop api scheduler
docker compose cp .\data\backups\mysql-YYYYMMDD-HHMMSS.sql mysql:/tmp/restore.sql
docker compose exec mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < /tmp/restore.sql'
docker compose exec mysql sh -c 'rm -f /tmp/restore.sql'
docker compose start api scheduler
当前 V1 的任务状态和业务事实均保存在 MySQL;Redis 已预置给后续缓存、跨进程队列或 Celery 使用,不是当前运行的硬依赖。共享外部 Redis 不做整库备份或整库清理。
本地开发(不运行完整 Compose)
后端:
Set-Location backend
py -3 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
Get-Content ..\config\.env.dev | ForEach-Object {
if ($_ -match '^([^#=]+)=(.*)$') { [Environment]::SetEnvironmentVariable($matches[1], $matches[2], 'Process') }
}
Remove-Item Env:DATABASE_URL -ErrorAction SilentlyContinue
$env:MYSQL_DATABASE_OVERRIDE = 'a_share_rotation_dashboard_v1_20260720'
$env:REDIS_KEY_PREFIX = 'a_share_rotation_dashboard:v1:'
alembic upgrade head
uvicorn app.main:app --reload
首次使用真实数据时,在启动 API 前运行:
python -m app.cli init-database --migrate --backfill-history --history-days 300
本地后端使用 Python 3.11。另一个终端运行 python -m app.scheduler,且只运行一个调度器。修改银行盘中“行情刷新秒数”后需要重启该调度器进程,其他策略参数从下一次运行起生效。前端使用 Node.js 20:
Set-Location frontend
npm install
npm run dev
HTTPS
默认编排在 127.0.0.1:8080 提供带单用户认证的本机 HTTP。手机、平板或互联网访问必须使用 HTTPS:把证书链和私钥分别放到未纳入版本控制的 deploy/nginx/certs/fullchain.pem 与 deploy/nginx/certs/privkey.pem,确认 .env 中的强认证密码后运行:
如果 Linux 宿主机已经运行 Nginx 或承载其他站点,不要使用下面的 HTTPS overlay,也不要覆盖宿主机现有配置。请使用 Linux 部署、端口与网络规则:项目入口只绑定回环地址,宿主 Nginx 仅新增一个精确域名 vhost,并在 nginx -t 通过后平滑 reload。
docker compose --env-file .env -f docker-compose.yml -f docker-compose.https.yml config
docker compose --env-file .env -f docker-compose.yml -f docker-compose.https.yml up -d --build
HTTPS 编排会在 0.0.0.0:443 提供 TLS 1.2/1.3,并继续对页面和全部业务 API 强制 Basic Auth;受保护入口还按客户端 IP 限制为每秒 10 个请求、允许 20 个突发请求,以降低密码暴力尝试风险。HTTPS 绑定可由 NGINX_HTTPS_* 调整。若还需要从公网 80 端口自动跳转,请把 .env 中原本用于本机入口的 NGINX_BIND_ADDRESS、NGINX_PORT 分别改为 0.0.0.0、80;否则 HTTP 入口继续只在 127.0.0.1:8080 可见。/healthz 只返回固定的 ok,不泄露业务状态。可使用 Let's Encrypt 或现有证书,证书续期由宿主机的 Certbot/反向代理负责。非 localhost 的普通 HTTP 不是浏览器安全上下文,PWA Service Worker 不会启用,因此移动端“添加到桌面”只能使用 HTTPS。若已由外层网关终止 TLS,也应保留本项目认证或在外层实施等价的单用户访问控制。
目录
backend/ FastAPI、策略、数据适配器、任务与 Alembic
frontend/ Vue 3、Element Plus、ECharts 与 PWA
config/strategies/ 可版本化的策略初始配置
deploy/nginx/ 强制认证的 HTTP/HTTPS 反向代理
deploy/host-nginx/ 共享 Linux 宿主机 Nginx 的增量 vhost 样例
deploy/mysql/ 新库的幂等创建脚本
docs/LINUX_DEPLOYMENT.md Linux 部署、防火墙、网络规则与无侵入 Nginx 流程
docker-compose.yml 完整服务编排
docker-compose.https.yml 远程移动端 HTTPS 覆盖编排
.env.example 无密钥的部署变量模板