Files
trade/README.md
2026-05-03 22:31:19 +08:00

288 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 期货行情分析系统 — 使用说明
基于 Docker + Python(tushare) + PostgreSQL 的中国期货行情分析系统。当前阶段已实现数据采集、三层加权打分模型与 Web 报表浏览端。运行方式支持两种模式:① 宿主机定时器触发 `docker-compose run` 执行 CLI;② 通过 FastAPI HTTP API 服务触发。
## 环境准备
- Docker >= 20.10
- Docker Compose >= 2.0
- (可选) psql 或任意 PostgreSQL 客户端用于本地查库
## 快速开始
### 1. 启动全栈服务
```bash
docker-compose -f docker-compose.trade.yml up -d
```
这会同时启动 PostgreSQL、tushare API 服务(端口 8000)与 Web 浏览端(端口 8080)。
### 3. 通过 CLI 跑当月主力
```bash
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 当月主力 -> ...`,然后:
1. 从 tushare 拉取合约日线数据
2. 写入 PostgreSQL `futures` 数据库
3. 运行三层打分模型
4. 保存打分结果并输出到 stdout
### 4. 通过 API 触发流水线
```bash
# 触发 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"
```
### 5. 跑其他合约或品种
```bash
# 显式指定合约
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
逐日打分后取均值:
| 持仓变化 | 价格方向 | 得分 |
|---------|---------|------|
| 增仓 | 上涨 | 100多头主动进攻 |
| 增仓 | 下跌 | 0空头主动进攻 |
| 减仓 | 上涨 | 70空头撤退 |
| 减仓 | 下跌 | 30多头撤退 |
| 持平(\|变化\|<1% | 上涨 | 60 |
| 持平(\|变化\|<1% | 下跌 | 40 |
### 2. 中期趋势15 日窗口,权重 0.35
```
价格信号 = (今收 - 15日前收) / 15日前收
价格信号得分 = clamp(50 + 收益率×500, 0, 100)
资金意愿:
增仓上涨天数 > 增仓下跌天数 → 80
两者相当 → 50
增仓下跌天数 > 增仓上涨天数 → 20
模块得分 = 价格信号 × 0.6 + 资金意愿 × 0.4
```
### 3. 长期结构30 日窗口,权重 0.25
```
持仓变化幅度 = (30日日均持仓 - 30日前持仓) / 30日前持仓
> 10% → 90显著增仓
5%~10% → 70温和增仓
-5%~5% → 50基本持平
-10%~-5% → 30温和减仓
< -10% → 10显著减仓
```
### 信号解读
| 综合分数 | 信号 |
|---------|------|
| 80-100 | 强烈看多 — 价格与资金共振 |
| 50-80 | 偏多/震荡偏强 |
| 40-50 | 偏空/震荡偏弱 |
| 0-40 | 强烈看空 — 资金主动打压 |
## 数据查询
业务数据存储在 PostgreSQL 中,可通过以下方式查询:
```bash
# 查看最新打分
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. 启动
```bash
# 构建并启动 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 日逐日打分、中期(15d)价格收益与资金意愿、长期(30d)持仓变化。
- **K 线 / 持仓** `/chart`:选合约 + 日期区间,主图蜡烛(开高低收),副图持仓量曲线;鼠标拖选缩放。
- **用户管理** `/admin/users`:仅管理员可见。可创建子账号(`user` 默认,亦可建 `admin`)、重置密码、禁用/启用、删除;不允许对自己执行禁用或删除。
普通用户登录后 `/admin/*` 路径会被前端守卫拦截并跳回 `/scores`,后端也会以 403 拒绝。
前端支持暗/浅色模式切换,点击顶部导航栏的「暗/亮」开关即可切换。侧边导航在暗色模式使用深色背景,浅色模式使用浅色背景。
### 4. 子账号维护流程
1. 用 admin 登录 → 进入 `/admin/users` → 「新建账号」,填写用户名 / 密码(≥6 位) / 角色。
2. 把账号发给同事即可登录;无注册入口。
3. 离职 / 风险事件:用「禁用」临时停用(被禁用的账号将无法登录),或「删除」彻底清除。
### 5. 数据流向与数据库
```
tushare(写) → PostgreSQL futures 数据库 ←(读写)── web 后端
```
业务数据(`candles` + `scores`)与用户鉴权数据(`users`)统一存储在 PostgreSQL `futures` 数据库中。`users` 表结构:
```sql
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: 忘记管理员密码怎么办?**
```bash
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 ...`