tradingSystem/REAL_TRADING_DEPLOY_PLAN.md

123 lines
8.6 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.

# 正式盘部署方案(模拟 / 正式双系统)
2026-08-28 定稿,同日随拍板收敛到最终口径:正式盘部署在 **192.168.16.188**,正式 QMT
**192.168.16.98**ws 端点 ws://192.168.16.98:9443/pms**两套系统共用 my_quant_db
一个库,全部靠表名区分**——正式 PMS 的 19 张自有表加表名前缀PMS_TABLE_PREFIX=real_
→ real_pms_*),下游表由正式 QMT 写另一套表名PMS 侧 PMS_DS_TABLE_* 指过去)。
不建新库、不动代理、不用跨库授权。现有这套 PMS 保留为**模拟仓**原地不动。
**一份代码,两份 .env** —— 不开分支、不复制仓库:两份代码必然分叉,修一个账务 bug 要
修两遍测两遍本仓库的设计本来就是「settings 只是初值,实例身份由 .env 与各自库里的
参数表决定」。
## 〇、先后次序(为什么先搭台架再测协议)
正式 QMT 侧从没成交过一笔真实买卖,所以第一优先级是**把 ws 协议在正式侧跑通并用一笔
小额真实买卖判收**,然后才谈全量部署与影子期。但协议测试本身需要的台架——出口表
pms_qmt_order、入站表 pms_qmt_inbox、ws 常驻进程、签名链路——就是这套系统的最小部署:
ws_smoke 不是独立客户端,它往出口表塞单、由 pms-ws 进程签名发出。所以次序是:
阶段A 最小台架(只起 页面+ws 通道,**不起调度器**)→ 协议实测(握手/快照/一买一卖)
阶段B 协议判收后,全量部署(加调度)→ 影子期 ≥3 个交易日 → 切 ws、放自动档
阶段A 里 beat/worker 根本不存在,系统不可能自主做任何动作;下发模式默认 shadow
ws_smoke 发的联调单绕开这个开关、且带 SMOKE 前缀——成交回报会被联调单闸挡在账本外。
**绝不要**拿模拟机那套临时把端点切到 98 去试:模拟的账本会吃进正式账户的回报,两边
密钥也不同,历史上 SMOKE 假成交污染账本的教训就是这么来的。
## 一、两套系统各自用什么、共用什么
| 资源 | 模拟(现状) | 正式(新) | 说明 |
|---|---|---|---|
| 代码 | git 仓库 | 同一仓库 | 两边各自 `git pull`,永远同版本 |
| PMS 自有表19 张) | my_quant_db 的 pms_* | **同库、加前缀 real_pms_***PMS_TABLE_PREFIX | 表名映射在 SQL 唯一执行入口统一做 |
| 下游表 trading_* / strategy_daily_results | 原库、原表名 | **原库、新表名**.env 的 PMS_DS_TABLE_* 指过去) | 正式 QMT 写一套新表名 |
| Celery 总线 | 150 redis **db8** | 150 redis **db9** | 共用一个 db 两套 worker 互相抢任务 |
| 决策系统信号流 | 208 db2/db3只读共享 | 同左 | 流共享,**消费组必须不同名** |
| 行情/大盘/mtf | 208 db13 / 199 / 214 | 同左 | 纯只读,直接共用 |
| QMT ws 通道 | ws://192.168.18.182:9443/pms + 模拟密钥 | **ws://192.168.16.98:9443/pms + 新密钥对** | 密钥绝不复用 |
| 管理页面 | tlai4090:38100 | 192.168.16.188:38100 | 不同机器,端口可同名 |
消费组为什么必须不同名Redis Stream 的一个消费组内,一条消息只投递给一个消费者。
两套系统若同组,风控卖出信号会被随机一家抢走,另一家永远看不见。组名在 .env 里设
`PMS_SIGNAL_GROUP`settings 已声明该键,参数表为空时 .env 值直接生效。
## 二、部署前的准备件
1. **库与表的接线(同库表名区分,什么都不用建)**。PROXY_DB_URL 与模拟**完全相同**
(照抄,还是 153 代理的 my_quant_db。正式 PMS 的自有表靠前缀区分:.env 设
PMS_TABLE_PREFIX=real_db.session 在 SQL 执行唯一入口把 pms_* 统一映射成
real_pms_*;首次 `make deploy` 建表自动带前缀建出 real_pms_* 这一套init_db 开头
会打印「表名前缀 real_ 生效」;个别 DDL 若被代理拒绝,脚本会打印**带前缀的**完整
语句,直接拿去物理库执行即可,与模拟当年建表同一套兜底)。下游表由正式 QMT 在同库
写一套**新表名**,向 QMT 侧要到四张表名填进 PMS_DS_TABLE_*。模拟与正式的数据从
表名上就分开,不会混。
2. **正式密钥对**:容器里 `python scripts/gen_keys.py` 生成seed 填正式 .env 的
PMS_QMT_SIGN_SEED_HEX公钥带外交给 QMT 侧,拿回对方公钥填 PMS_QMT_PEER_PUBKEY_B64。
3. **真实账户规模与纪律参数**PMS_TOTAL_SCALE 按真实资金;上线初期 shadow +
propose_only + 新建仓 off。
## 三、正式服务器 .env 与模拟的差异清单
照抄模拟的 .env改动以下键其余全部照抄行情/信号这些共享源**不要改**
# 必须不同 (注意: PROXY_DB_URL **不在这里** —— 照抄模拟的, 同一个 my_quant_db)
PMS_TABLE_PREFIX=real_
PMS_REDIS_URL=redis://:<pass>@192.168.16.150:6379/9
PMS_SIGNAL_GROUP=pms_signal_consumer_real
PMS_SIGNAL_CONSUMER=pms_real_1
PMS_QMT_WS_URL=ws://192.168.16.98:9443/pms
PMS_QMT_SIGN_SEED_HEX=<gen_keys 新生成>
PMS_QMT_PEER_PUBKEY_B64=<QMT 正式侧公钥>
PMS_SESSION_SECRET=<新生成, 别抄模拟的>
# 下游表名: 按正式 QMT 实际写的表名填 (同库, 直接写表名)
PMS_DS_TABLE_POSITION=<正式持仓表名>
PMS_DS_TABLE_ORDER=<正式委托成交表名>
PMS_DS_TABLE_BUY_PLAN=<正式买入计划表名>
PMS_DS_TABLE_DAILY=<正式定性结论表名>
# 阶段A 先关, 密钥交换完再开
PMS_QMT_WS_ENABLED=false
# 可选 (单独服务器用默认即可; 将来若与模拟合并到一台机器才需要)
# PMS_STACK_PREFIX=real-
# PMS_IMAGE=pms-real:latest
# PMS_WEB_PORT_HOST=38200
# COMPOSE_PROJECT_NAME=pms-real
## 四、阶段A最小台架 + ws 协议实测(在 188 上)
1. 装 docker 与 docker compose`git clone` 仓库;按第三节写好 .env密钥先留空
2. 表名口径:.env 设 PMS_TABLE_PREFIX=real_向 QMT 侧要到四张正式下游表的表名,
填进 PMS_DS_TABLE_*。不用建库、不用授权、不动代理。
3. 网络预检188 要能通 153:3307、150:6379、208:6379、199:3306、214:36379、98:9443。
4. 建表前自证(最后一道闸):`docker compose build` 后跑
`docker compose run --rm --no-deps pms-web python -c "from config.settings import settings; print(repr(settings.PMS_TABLE_PREFIX))"`
——必须打印 'real_';打印空串绝不往下走(会建出与模拟同名的表)。
5. 最小台架(只起 页面+ws不起调度`PROFILES="--profile ws" ./scripts/deploy.sh --no-pull`
—— 建镜像、起 pms-web/pms-ws、幂等建表。输出必须出现
「[双实例] 表名前缀 'real_' 生效」COUNT 验证列出的全是 real_pms_*;没看到立即中止。
6. `make check`(库/参数/通道逐项)与 `make test`ALL SUITES PASS + 指纹一致)。
7. 生成密钥、与 QMT 侧交换公钥;.env 里填好两把钥匙并把 PMS_QMT_WS_ENABLED=true
重跑 `PROFILES="--profile ws" ./scripts/deploy.sh --no-pull` 让新 env 生效。
8. 通道判收(不动钱):`make ws-status` 看 连接 ONLINE、心跳在走、能读到持仓快照
`ws_smoke.py inbox` 里有 snapshot/心跳类上行。
9. S2 式发单(挂不上才是预期):盘中发一张限价远离市价的买单(--ttl 5 到点对端自动撤),
watch 里状态走 QUEUED→SENT→受理inbox 里有 ack 与回报。
10. **一笔真实买卖**判收核心建议选一只当日可回转T+0的跨境或货币 ETF一手
一两百块,当天买进当天卖出;用普通股票也行,但 A 股 T+1卖出要等次日。判收三条
两张单全成、inbox 有 trade 回报、**账本零变化**SMOKE 联调单闸把成交挡在账外)。
11. 清场:测试买的货**必须卖光**再进阶段B——否则影子期对账会把它当真实持仓收编进账本。
## 五、阶段B全量部署与影子期协议判收后再做
`make deploy`(加上调度)→ 页面参数中心设 PMS_TOTAL_SCALE / PMS_AUTONOMY=propose_only /
PMS_OPEN_AUTONOMY=off → 真实账户已有持仓则等盘前对账以 ws 快照建账 → 影子期 ≥3 个
交易日逐日判收(日报对得上、回放游标推进、无孤儿成交)→ 页面把 PMS_DISPATCH_MODE 改
ws管理员→ 按表现逐层放开自动档。逐条命令等阶段A 判收后再展开。
## 六、日常纪律
两台各自 `git pull` + `make deploy`**收盘后部署**;发版前后两边都 `make test`
DEVLOG 单仓共用,节点里写清动的是模拟侧、正式侧还是两侧。模拟侧本次**零改动**。