Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
期货行情分析系统 — 使用说明
基于 Docker + Python(tushare) + PostgreSQL 的中国期货行情分析系统。当前阶段已实现数据采集、三层加权打分模型与 Web 报表浏览端。运行方式支持两种模式:① 宿主机定时器触发 docker-compose run 执行 CLI;② 通过 FastAPI HTTP API 服务触发。
环境准备
- Docker >= 20.10
- Docker Compose >= 2.0
- (可选) psql 或任意 PostgreSQL 客户端用于本地查库
快速开始
1. 启动全栈服务
docker-compose -f docker-compose.trade.yml up -d
这会同时启动 PostgreSQL、tushare API 服务(端口 8000)与 Web 浏览端(端口 8080)。
3. 通过 CLI 跑当月主力
docker-compose -f docker-compose.trade.yml run --rm tushare python -m src.main
不传参时,按 tushare/src/contracts.py 的 ROLLOVER_RULES 自动选 FG 玻璃当月主力(例如 2026-05 -> FG2609.ZCE),启动后会先打印 [AUTO] FG 当月主力 -> ...,然后:
- 从 tushare 拉取合约日线数据
- 写入 PostgreSQL
futures数据库 - 运行三层打分模型
- 保存打分结果并输出到 stdout
4. 通过 API 触发流水线
# 触发 FG 打分
curl -X POST http://localhost:4001/api/v1/run -H "Content-Type: application/json" \
-d '{"symbol":"FG"}'
# 批量触发所有固定品种今日打分
curl -X POST http://localhost:4001/api/v1/run/batch
# 查询最新打分
curl "http://localhost:4001/api/v1/scores?limit=5"
# 查询合约列表
curl "http://localhost:4001/api/v1/contracts"
# 查询 K 线数据
curl "http://localhost:4001/api/v1/candles?ts_code=FG2609.ZCE"
# 清空所有行情数据(谨慎操作)
curl -X POST http://localhost:4001/api/v1/admin/reset-data
5. 跑其他合约或品种
# 显式指定合约
docker-compose -f docker-compose.trade.yml run --rm tushare python -m src.main RB2510.SHF
docker-compose -f docker-compose.trade.yml run --rm tushare python -m src.main I2601.DCE
# 按品种代号自动选当月主力(目前只配置了 FG)
docker-compose -f docker-compose.trade.yml run --rm tushare python -m src.main --symbol FG
6. 玻璃 FG 主力轮换规则
| 当前自然月 | 主力合约 |
|---|---|
| 1、2、3 月 | 当年 05 |
| 4、5、6、7 月 | 当年 09 |
| 8、9、10、11 月 | 次年 01 |
| 12 月 | 次年 05 |
三层打分模型
综合分数公式
综合分数 = (短期动力 × 0.4 + 中期趋势 × 0.35 + 长期结构 × 0.25) × 波动率惩罚系数
1. 短期动力(7 日窗口,权重 0.4)
逐日打分后取均值。每日评分 = (象限基础分 + 幅度加成) × 量能确认,产出 0-100 连续值。
象限基础分(持仓与价格方向):
| 象限 | 持仓变化 | 价格方向 | 基础分 |
|---|---|---|---|
| accumulation(增仓上涨) | 增仓 | 上涨 | 75 |
| distribution(增仓下跌) | 增仓 | 下跌 | 25 |
| covering(减仓上涨) | 减仓 | 上涨 | 65 |
| liquidation(减仓下跌) | 减仓 | 下跌 | 20 |
| flat(持平) | |变化|<1% | 上涨 | 60 |
| flat(持平) | |变化|<1% | 下跌 | 40 |
幅度加成(根据 OI 变化率和涨跌幅放大有利方向得分):
- OI 变化率封顶 5%,价格涨跌幅封顶 3%
- 增仓上涨 / 减仓下跌(有利方向):加成 = (OI 幅度 + 价格幅度) / 2 × 20
- 持仓持平:加成 = 价格幅度 × 10
- 增仓下跌 / 减仓上涨(不利方向):无加成
量能确认:量比 = 当日成交量 / 7 日均量,系数范围 [0.9, 1.2],量比 1.5 以上封顶
2. 中期趋势(15 日窗口,权重 0.35)
价格收益率 = (今收 - 15日前收) / 15日前收
价格信号分 = clamp(50 + 收益率 × 500, 0, 100)
资金意愿 = 50 + (增仓上涨天数 - 增仓下跌天数) / 15 × 50 (连续值 0-100)
模块得分 = 价格信号 × 0.6 + 资金意愿 × 0.4
3. 长期结构(30 日窗口,权重 0.25)
OI 趋势分 = clamp(50 + OI变化幅度 × 250, 0, 100) (权重 60%)
价格趋势分 = clamp(50 + 30日价格收益率 × 200, 0, 100) (权重 40%)
模块得分 = OI 趋势分 × 0.6 + 价格趋势分 × 0.4
4. 波动率调整
基于近 30 日日收益率标准差和 ATR%(平均真实波幅/均价):
日波动率 ≤ 1.5% → 惩罚系数 = 1.0(无惩罚)
日波动率 > 1.5% → 惩罚系数 = max(0.85, 1.0 - (日波动率 - 1.5%) × 10)
高波动品种的综合分会被适当打折,最低打 85 折。
信号解读
| 综合分数 | 信号 |
|---|---|
| 80-100 | 强烈看多 — 价格与资金共振 |
| 50-80 | 偏多/震荡偏强 |
| 40-50 | 偏空/震荡偏弱 |
| 0-40 | 强烈看空 — 资金主动打压 |
数据查询
业务数据存储在 PostgreSQL 中,可通过以下方式查询:
# 查看最新打分
docker-compose -f docker-compose.trade.yml exec postgres psql -U trade -d futures -c \
"SELECT ts_code, trade_date, composite, signal FROM scores ORDER BY trade_date DESC LIMIT 5;"
# 查看合约日线
docker-compose -f docker-compose.trade.yml exec postgres psql -U trade -d futures -c \
"SELECT trade_date, open, high, low, close, vol, oi FROM candles WHERE ts_code='FG2609.ZCE' ORDER BY trade_date DESC LIMIT 10;"
# 或通过 API 查询
curl "http://localhost:4001/api/v1/scores?ts_code=FG2609.ZCE&limit=10"
curl "http://localhost:4001/api/v1/candles?ts_code=FG2609.ZCE"
项目结构
trade/
├── docker-compose.trade.yml # Docker Compose 编排(postgres + tushare + web)
├── README.md # 本文件
├── CLAUDE.md # Claude Code 项目指引
├── data/ # 数据目录(gitignored)
│ └── (运行时生成)
├── .gitignore # Git 忽略配置
├── tushare/ # Python 数据服务
│ ├── Dockerfile
│ ├── requirements.txt
│ └── src/ # 数据采集 + 打分 + FastAPI
│ ├── api.py # FastAPI 服务入口
│ ├── models.py
│ ├── fetcher.py
│ ├── scorer.py
│ ├── storage.py # PostgreSQL 读写
│ ├── contracts.py
│ └── main.py # CLI 入口
└── web/ # Web 浏览端
├── .dockerignore
├── backend/ # Go 1.25 后端 (chi + lib/pq)
│ ├── Dockerfile # 多阶段:node 构 UI → go 构二进制 → alpine 运行
│ ├── go.mod
│ ├── main.go
│ ├── embed.go # //go:embed all:dist
│ ├── go.sum
│ ├── dist/ # 占位,Docker 构建期被 vite 输出覆盖
│ └── internal/
│ ├── config/ # 环境变量加载
│ ├── store/ # PostgreSQL 业务查询 + 用户管理
│ ├── auth/ # bcrypt + 首启 admin 引导
│ ├── middleware/ # RequireUser / RequireAdmin / 日志
│ ├── handlers/ # 登录 / 打分 / K线 / 用户管理
│ └── router/ # chi 路由装配
└── frontend/ # Vue 3 + Vite + Element Plus + ECharts
├── package.json
├── vite.config.ts
├── tsconfig.json
├── index.html
└── src/
├── main.ts / App.vue
├── router/ # 守卫(未登录/管理员路由)
├── stores/ # Pinia: auth.ts(持久化 token) + theme.ts(暗/浅色模式)
├── api/ # axios 封装 + 各端点
├── views/ # 登录 / 打分列表 / 图表 / 用户管理
└── components/ # 抽屉 + ECharts K 线
技术栈
- Python 3.13 (alpine) + tushare + pandas + FastAPI + psycopg3 — 数据采集、打分与 API 服务
- Go 1.25.8 (alpine 3.23) + chi + lib/pq — Web 后端
- Vue 3 + Vite + Element Plus + ECharts — Web 前端
- PostgreSQL 18.3 (alpine 3.23) — 业务数据存储
- PostgreSQL — 业务数据与用户鉴权数据统一存储
- Docker / Docker Compose — 容器化部署
常见问题
Q: 为什么某些日期返回空数据?
A: tushare 数据更新有延迟,且不同接口对 token 积分等级有要求。若 fut_daily 返回空但 trade_cal 正常,通常是该日期实际行情数据尚未入库。
Q: 合约代码格式?
A: 郑商所用 .ZCE 后缀(如 FG2609.ZCE),上期所用 .SHF,大商所用 .DCE。注意不是 .CZC。
Q: 如何定时自动跑?
A: 通过宿主机 cron / launchd 等定时器调用 docker-compose -f docker-compose.trade.yml run --rm tushare ...。也可直接调用 API: curl -X POST http://localhost:4001/api/v1/run ... 或批量接口 curl -X POST http://localhost:4001/api/v1/run/batch。
Web 报表(浏览端)
./web/ 提供一个图形化的浏览端,展示 tushare 流水线写入 PostgreSQL 的打分与行情数据。后端 Go(golang:1.25.8-alpine3.23)读取数据库,前端 Vue 3 + Element Plus + ECharts,通过 docker-compose 一起部署。
1. 启动
# 构建并启动 web 服务,不影响现有 tushare
docker-compose -f docker-compose.trade.yml up -d --build web
# 查看启动日志:首启会出现 [bootstrap] admin created
docker-compose -f docker-compose.trade.yml logs -f web
浏览器访问 http://localhost:4000。首次启动时系统会自动创建默认管理员账号 admin / admin,首次登录后系统会强制要求修改密码。
3. 页面说明
- 打分列表
/scores:按合约、日期、条数筛选,展示综合分/信号/三层得分;点击「明细」弹抽屉,显示短期 7 日逐日打分(涨跌幅/OI变化%/量比/象限)、中期(15d)价格收益与资金意愿、长期(30d)OI/价格趋势分与波动率调整。 - K 线 / 持仓
/chart:选合约 + 日期区间,主图蜡烛(开高低收),副图持仓量曲线;鼠标拖选缩放。 - 同步数据(侧边栏):点击调用批量打分接口,对所有固定品种执行当日打分,完成后自动跳转并刷新打分列表。
- 手动打分
/run:选品种 + 日期,对单个合约执行数据拉取与打分。 - 数据重置(侧边栏,仅管理员):输入确认文字后清空所有行情数据(candles + scores),用户表不受影响。
- 用户管理
/admin/users:仅管理员可见。可创建子账号(user默认,亦可建admin)、重置密码、禁用/启用、删除;不允许对自己执行禁用或删除。
普通用户登录后 /admin/* 路径会被前端守卫拦截并跳回 /scores,后端也会以 403 拒绝。
前端支持暗/浅色模式切换,点击顶部导航栏的「暗/亮」开关即可切换。侧边导航在暗色模式使用深色背景,浅色模式使用浅色背景。
4. 子账号维护流程
- 用 admin 登录 → 进入
/admin/users→ 「新建账号」,填写用户名 / 密码(≥6 位) / 角色。 - 把账号发给同事即可登录;无注册入口。
- 离职 / 风险事件:用「禁用」临时停用(被禁用的账号将无法登录),或「删除」彻底清除。
5. 数据流向与数据库
tushare(写) → PostgreSQL futures 数据库 ←(读写)── web 后端
业务数据(candles + scores)与用户鉴权数据(users)统一存储在 PostgreSQL futures 数据库中。users 表结构:
users(id SERIAL PRIMARY KEY, username TEXT UNIQUE, password_hash TEXT,
role TEXT CHECK(role IN ('admin','user')), disabled BOOLEAN,
force_password_change BOOLEAN, created_at TEXT, updated_at TEXT)
6. 常见问题
Q: 忘记管理员密码怎么办?
docker-compose -f docker-compose.trade.yml stop web
docker-compose -f docker-compose.trade.yml exec postgres psql -U trade -d futures -c \
"DELETE FROM users WHERE role='admin';"
docker-compose -f docker-compose.trade.yml up -d web
启动时会重新触发 bootstrap 写入新的默认管理员 admin / admin。
Q: 改了 Go / Vue 代码但页面没变?
源码不挂载,镜像内是 COPY 进去的。重建:docker-compose -f docker-compose.trade.yml build web && docker-compose -f docker-compose.trade.yml up -d web。
Q: 为什么 tushare 容器启动后没有立即退出?
因为默认命令改为 uvicorn src.api:app 常驻 API 服务。如需执行单次 CLI,用 docker-compose -f docker-compose.trade.yml run --rm tushare python -m src.main ...。