一个面向 A 股的个人量化项目:提供 行情数据入库(日/周 K)、通达信(TDX)公式选股、以及 FastAPI 后端接口供前端展示(K 线 + 指标 + 选股结果流式加载)。
本文档面向“可运行、可迭代、可上线”的工程化使用方式,尽量覆盖:
- 项目结构与职责边界
- 本地开发与部署方式
- 数据入库与选股流程
- 配置项(环境变量)
- 常见问题排查
horacequant/
backend/ # 后端与运维脚本(Python)
app/ # FastAPI 应用代码
ops/scripts/ # 运维脚本:入库、选股
rules/ # 选股策略(TDX 公式文件)
logs/ # 运行日志(默认)
requirements.txt # Python 依赖
run.py # 启动 FastAPI
worker.py # 启动定时任务 Worker(APScheduler)
frontend/ # 前端(Vue3 + TypeScript + Vite + ECharts)
src/ # 源代码
api/ # API 服务层
components/ # 可复用组件(K线图等)
views/ # 页面组件(列表、详情)
router/ # 路由配置
types/ # TypeScript 类型定义
package.json # 依赖配置
vite.config.ts # Vite 构建配置
README.md # 前端文档
README.md # 项目总览(本文件)
FRONTEND_COMPLETE.md # 前端完成报告
FRONTEND_USAGE.md # 前端使用指南
- Python 版本建议:3.10+(推荐 3.11+)
- PostgreSQL:12+(建议 14+)
- 依赖安装(推荐在虚拟环境/conda env 里执行):
pip install -r backend/requirements.txt备注:仓库依赖包含
psycopg2-binary(用于 ops 脚本)与asyncpg(用于 FastAPI)。
- Node.js 建议:18+(Vue 3 + Vite 5 + TypeScript)
- 技术栈:Vue 3 + Vue Router + ECharts 5 + Axios + Day.js
- 安装依赖:
cd frontend
npm installpython backend/run.py默认监听:0.0.0.0:8000(可通过环境变量配置)。
前端默认端口:5173,并已在 frontend/vite.config.ts 配置了代理:
/api -> http://127.0.0.1:8000,便于本地联调。
cd frontend
npm run dev浏览器访问:http://localhost:5173
功能特性:
- 📊 类富途 App 的 K线图展示(日K/周K切换)
- 📈 技术指标:成交量、MACD、KDJ、ZX Short/Long 趋势线
- 📱 响应式布局:PC端 1x2 网格,移动端 1x1 单列
- 🚀 流式加载:支持 NDJSON 流式接口,边拉边画
- 🎨 深色主题:专业交易界面风格
Worker 负责每日定时:入库 + 选股(可配置策略列表)。
python backend/worker.pyGET /api/v1/hello:连通性测试GET /api/v1/healthz:健康检查(含 DB)
-
GET /api/v1/picks/{rule_name}/{trade_date}- 返回 JSON(带统一
ApiResponse包裹) - 支持
cursor分页(每页limit <= 50) - 参数示例:
trade_date支持YYYY-MM-DD或YYYYMMDD
- 返回 JSON(带统一
-
GET /api/v1/picks/{rule_name}/{trade_date}/stream- 返回 NDJSON(流式)
- 第一条为
meta,后续为item - 适合前端“下拉加载/边拉边画”
说明:当某日结果表不存在时(例如还没跑出选股结果),接口会返回空列表而不是 404,方便前端做“等待数据生成”的体验。
脚本:backend/ops/scripts/a_share_daily_to_postgres.py
- 数据源:AkShare
stock_zh_a_hist(period="daily") - 目标表:
stock_basic、stock_daily
示例:
export PG_HOST=127.0.0.1
export PG_PORT=5432
export PG_USER=你的用户名
export PG_PASSWORD=你的密码 # 可为空(本机免密)
export PG_DB=horace_quant
python backend/ops/scripts/a_share_daily_to_postgres.py --adjust qfq脚本:backend/ops/scripts/a_share_weekly_to_postgres.py
- 数据源:AkShare
stock_zh_a_hist(period="weekly") - 目标表:
stock_weekly - 特别处理:同一周内重复写入时,会删除旧的“本周旧数据”,避免周线重复/不一致
python backend/ops/scripts/a_share_weekly_to_postgres.py --adjust qfq脚本:backend/ops/scripts/stock_picker_tdx.py
- 输入:
backend/rules/*.tdx - 输出:按交易日分表
stock_pick_results_YYYYMMDD
python backend/ops/scripts/stock_picker_tdx.py --rule backend/rules/b1.tdx --rule-name b1 --trade-date 2025-12-29重要:本项目的 TDX 解析器实现了覆盖
b1.tdx所需的子集(LLV/HHV/SMA/EMA/MA/REF/INBLOCK/NAMELIKE等)。
为了避免前端每次拉取都实时计算大量指标,后端采用“按需计算 + DB 回填”的方式:
- 指标缓存表:
stock_daily_indicatorsstock_weekly_indicators
- 市值缓存表:
stock_market_cap_latest
当 API 发现某个交易日指标缺失时,会:
- 拉取 K 线(含历史窗口)
- 计算指标(MACD / KDJ / ZX Short / ZX Long 等)
- 只回填缺失行(逐行缺失判断)
Worker(backend/worker.py)内部使用 APScheduler,默认在 Asia/Shanghai 18:00 触发:
- 当日日 K 入库
- 周 K 更新(含“同周覆盖”)
- 遍历策略列表执行选股(目前默认
b1)
并使用 PostgreSQL advisory lock 防止多实例重复执行。
后端使用 HQ_ 前缀(见 backend/app/core/config.py),常用项:
-
运行环境
HQ_ENV=dev|staging|prodHQ_HOST=0.0.0.0HQ_PORT=8000HQ_LOG_LEVEL=INFOHQ_LOG_DIR=backend/logs
-
PostgreSQL
HQ_PG_HOST=127.0.0.1HQ_PG_PORT=5432HQ_PG_USER=...HQ_PG_PASSWORD=...HQ_PG_DB=horace_quant
-
CORS(前端联调)
HQ_CORS_ENABLED=trueHQ_CORS_ALLOW_ORIGINS=http://127.0.0.1:5173,http://localhost:5173
-
定时任务
HQ_SCHEDULER_ENABLED=true|falseHQ_SCHEDULER_TIMEZONE=Asia/ShanghaiHQ_SCHEDULER_HOUR=18HQ_SCHEDULER_MINUTE=0HQ_SCHEDULER_LOCK_KEY=42424242HQ_STRATEGIES=b1,b2(策略列表,逗号分隔或 JSON 数组)
建议:生产部署中 API 进程与 Worker 进程分开运行。通常做法是:
- API:
HQ_SCHEDULER_ENABLED=false- Worker:
HQ_SCHEDULER_ENABLED=true
运维脚本使用 PG_ 前缀:
PG_HOST / PG_PORT / PG_USER / PG_PASSWORD / PG_DB
默认输出到 backend/logs/,并按类型拆分(按天滚动、自动清理):
app.log:业务日志jobs.log:定时任务日志(含 worker)access.log:HTTP accesserror.log:ERROR 汇总
可能原因:
- 当天选股结果表尚未生成(还没跑选股脚本/worker)
建议:
- 检查
backend/logs/jobs.log - 手动执行一次:
python backend/ops/scripts/stock_picker_tdx.py --rule backend/rules/b1.tdx --rule-name b1 --trade-date YYYY-MM-DD
通达信 SMA 是递推型指标,若初始值为 NaN 容易污染整条链路。
项目已在 stock_picker_tdx.py 中按 TDX 语义处理 NaN(启动值与断档回填),若你新增策略时仍遇到类似问题,建议优先检查:
- 分母为 0 导致
inf/-inf - 数据窗口长度不足
- 某些列缺失或类型不是数值
检查网络与 AkShare 数据源可用性,并查看脚本/worker 日志中的报错堆栈。
个人项目,默认不提供任何商业保证。如需开源协议可再补充。