11 KiB
交接:三系统量化交易 · 2026-08-06 收盘前
这份东西是写给下一个对话开头的。可以整段贴过去,也可以留在仓库里当阶段存档。
我们在推进三个相互衔接的量化交易系统。请先读项目记忆里的 tradingsystem-bionic-bridge、 clock-and-timezones、tradingsystem-silent-failure 三篇(都在 08-06 更新过), 背景需要时再读 tradingsystem-ws-channel 和 quant-factor-synthesis,工作方式看 feedback-plan-first 和 writing-style-readability。读完再看下文,然后等我给任务,不要自行开工。
一、三系统与部署方式(改错部署方式等于白改)
tradingSystem(PMS,持仓管理) 跑在 factor@factorevaluation。源码是打进镜像的,
所以改码必须 make deploy,跑完再 make test 看到 ALL SUITES PASS;盯盘用 make watch。
容器时钟是北京时间。
bionic_trader(决策系统) 跑在 192.168.16.188:38000(tlaipro 机)。挂载卷,
git pull 之后 docker compose restart backend-api worker-brain 即可生效;
但新增 env 必须 docker compose up -d --force-recreate,restart 不重读 env。
容器时钟是 UTC。
akg-factor-bridge(因子桥) 在桥机 factorevaluation,容器名 akg_factor_bridge,
API 在 8300。常驻 API 改码要 compose restart。容器时钟是 UTC。
线上运行中的系统只做加法。
二、规矩(不变)
先出方案再动手;中文可读性第一,写完整自然句、不自造缩写;只做加法;宁可空池; 关键路径禁止丢弃返回值;全程 Docker,不给宿主机直跑 pip/python/psql;每条命令必须标注 运行在哪台机器,一块代码里只放一台机器;判收的标准是真机各见一次;写回 Mac 的文件以 written 回执加 md5 为准;看桥和 bionic 的任何时间戳,先加八小时再和对话里的时间对齐。
测试脚本由我在实机上运行,跑完把结果发给你,你不用跑复杂测试。里程碑的代码和文档 请提示我及时创建或上传到项目目录。
三、下一个任务:给动作引擎加「新建仓」动作
要解决的问题:不能每天靠人手工走一遍下命令、排方案那一串。正常应该是上游选股计划 出现之后,PMS 按规则自己执行。
已经查清的现状,不用重查:整条链除了「下命令」这一步,其余全是自动的。调度表里
08:40 拉候选池,08:50 盘前准备,每分钟跑命令轮询(把已下达的命令排成方案)、盘中执行
(方案转指令 + 自主提议扫描 + 择时出手)、成交入账加轻对账、消化决策系统的盘中信号,
14:50 T 仓平回,15:10 日终结算加窗口收口,15:30 日报。make t-* 只是这些调度位的手动
等价物,是排查用的,不是日常流程。
唯一要人的就是下命令,而且是刻意的:计划回答「能买哪些」,命令回答「今天打算投多少」。
三条路,性质完全不同:
一是把下命令挂成定时,每天自动下一条升仓命令。改动最小,加一个调度位;代价是仓位单向 增长到撞上组合上限,而且不看市场状态。
二是把自主档位调到 full。这条解决不了问题——它只让已有持仓的增持类提议不用人确认, 而动作引擎产出的只有补足底仓、回踩补仓、盈利加仓、减持这四类,根本没有「新建仓」 这个动作。
三是给动作引擎加新建仓动作,让自主提议除了管已有持仓,也能从候选池里挑新票,走同样的 规则闸、研判闸、档位分流。这才对得上要解决的问题,是新逻辑不是新调度位。
我选第三条。先出方案,我拍板之后再动手。
开工前要知道的约束:择时读的那张 strategy_daily_results 表,在盘中跑 push-pool
触发补扫时会被就地改写(详见第六节)。开发期靠「盘中不跑 push-pool」这条纪律规避,
没有改代码。设计新建仓动作时要把这件事放进考虑,别让无人值守的建仓建在会漂移的输入上。
四、当前系统状态(2026-08-06 盘中,约 11:15)
账户总资产 2,000,288.86,可用 1,881,764.86,组合仓位 5.92%。参数:总规模 200 万, 自主档位 propose_only,下发模式 ws,择时档位 A,组合上限 70%,单股上限 8%,最多 20 只, 执行窗口默认 3 个交易日。
持仓三只:600150.SH 一千七百股、可卖一千二、摊薄成本 34.625、安全垫 +0.16%,参考位 支撑 34 压力 37 止损 34(来源 bionic);002335.SZ 一千九百股、可卖零、摊薄成本 30.984、 安全垫 +0.41%;000035.SZ 一百股、可卖零、摊薄成本 5.028、安全垫 -1.55%,决策系统对它 的定性是 SELL。
在途四条:000035 的减持一百股(来源自主提议,11:13 我在页面人工放行);002335 的清仓 一千九百股(来源决策系统风控信号,置信度 95%);002518 的买入九百股(被新装的定性闸 拦着,窗口今天到期);000063 的买入一千七百股(现价高于买入区间上沿,不追,窗口今天到期)。 前两条都要等明天 T+1 才能出手。
五、08-05 到 08-06 做完的事
买入侧补上了一道闸,两侧都已部署,真机判收成立。 起因是发现候选层的结论对执行层
完全不可见:桥用 strategy_daily_results.signal_type 落在 SELL、AVOID、DROPPED 就把票请出
候选池,而决策系统的择时应答读同一个字段、放进 observed.y_signal 原样回传却从不用它判断,
PMS 又只解析 verdict、limit_price、reason。同一条结论一边当判决一边当摆设。
600841 就差一根 3.3% 的阴线会被买进来。
改动:决策系统 app/services/pms_advisor.py 加 BAD_Y_SIGNALS 常量和买入分支的闸,
config/settings.py 加开关 PMS_EXEC_BLOCK_BAD_Y_SIGNAL;PMS 侧 app/core/rule_gate.py
加 BAD_SIGNAL 检查项并把定性无条件写进 hard_numbers,app/services/exec_advisor.py
把 observed.y_signal 存进 advice 并挂到决策上,app/services/executor.py 传进规则闸 flags,
scripts/test_batch3_units.py 加一条用例。08-06 真机拦下第一笔:002518 停在 PROPOSED、
成交 0、理由是新写的那句。 PMS 那道 BAD_SIGNAL 至今没真的触发过(决策系统先回 WAIT
就走不到规则闸),符合当初「第二道闸平时空转、只为留痕」的预期。
其余:600841 的在途买入指令已撤(回执是 cancelled:0 加「仅本地置撤销」,PROPOSED 从没
下发过子单时走的就是这个分支);freeze 的 manifest.json 判收通过,08-03 和 08-04 两天
都在;XXL 例行调度的根因查实——从来没有自动拉起过,日志里只有手动触发的行,配置已重新设计。
六、悬而未决(按优先级)
- 主线:动作引擎加新建仓动作,见第三节。
- 昨夜结论会被盘中补扫就地改写。push-pool 跑完触发增量补扫,补扫用
trade_date=昨日就地覆盖已有那行,而fetch_yesterday_strategy是取最新一行、不按日期过滤。实证: 000035 在 09:53 成交时定性是 WATCH、压力 5.2,补扫之后变 SELL、压力 5.15;002335 的支撑 从 31.27 变成 29.00,差 7.3%。开发期靠「盘中不跑 push-pool」规避,代码未改。 真要修,最小加法是给fetch_yesterday_strategy加「只取今天之前」的过滤,但那样盘中 补扫的产出就完全用不上了,是不是想要的要先拍板。 - 卖出侧的镜像缺口。桥对「持仓且形态恶化」只警示、明确不出池,而 PMS 没有任何机制 会因为决策系统的定性去减仓——研判闸只管买入侧的补足、加仓、补仓,规则闸的冻结和黑名单 只挡增持。买入侧补上了,卖出侧还是空的。000035 现在就是这个状态。
- 总资产与持仓市值的口径。总资产来自 QMT 的 ws 资金快照,被组合刹车当权益基准用 (高水位与回撤,15:10 跑);持仓市值是 PMS 拿 Redis 分钟线自算的。08-05 两边差 564 元, 08-06 盘中只差 51 元。倾向是取数时点差不是口径差,收盘后价格静止时再对一次才能定案。
- 时区显示错位八小时。
_today_exit_verdict把 created_at 直接截成「01:35」拼进理由, 传到 PMS 页面和北京时间戳并排显示。给 bionic 容器设TZ=Asia/Shanghai(新增 env, 必须 force-recreate)或者格式化时显式转。低优先。 - 600150 最后五百股买在区间上沿。区间连续四个交易日都是 [33.66, 34.9] 没变过, 今天现价正好等于上沿,闭区间判定放行,成交在 34.89,摊薄成本被从 34.514 抬到 34.625、 安全垫从峰值 +1.99% 压到 +0.01%。这是分批建仓遇上区间长期不变的固有副作用。 要不要改(现价等于上沿时不买,或者区间连续 N 天不变时降档)是策略取舍,未拍板。
- XXL 例行调度已重新配置,验证点是看桥的
data/xxl_build.log有没有在北京时间 07:10 自动长出新行(日志是 UTC,会写成前一天的 23:10)。 trade_no格式核验(关系 S3 收尾函第一节的阻断项):docker compose run --rm --no-deps pms-web python scripts/ws_smoke.py inbox --type trade --width 600。 是SHADOW-xxxx#1这种确定式就闭环,还是 T-SHADOW 随机串就继续追。这条一直没做。- 命令目录里
FREEZE_STOCK的说明写着「在途买入撤销」,与实现不符(它是参数类命令、 没有 planner,只让规则闸的冻结项拦),文案要补账。 - 文档补账:
WS_INTEGRATION_STATUS.md停在 07-30;BIONIC_PMS_INTERFACE.md第三节要 加一句新闸;README 待办 9 和 10 标判收。 - 老悬项不变:分歧票候选层拦截未拍板;AKG_PLAN 并入 TECH_POOL 的共管规矩未拍板; 07-29 那 19 条 SIG_INVALID 未查明。
七、这轮我犯过的三个误判,别重犯
一是看见巧合就当线索。 600150 的现价 34.900 恰好等于两笔委托的限价,我据此断定取价 有问题,还推出「安全垫其实是负的」。实际行情源是活的、分钟线连续,34.900 就是当时的 真实价。先验证数据源是否在更新,再怀疑取值。
二是没换算时区。 审计行写着 01:35,我当成北京时间,于是把一个正常的四分钟赛跑 (09:31 咨询干净、09:32 成交、09:35:51 结论落库、09:36 信号到达)判成了「老闸漏读」的 bug, 差点去改代码。桥和 bionic 的时间戳一律先加八小时。
三是推理没落地就当结论。 我推定 audit_date 会因为容器在 UTC 而错位一天,实际写入侧
用的是北京日期,洞根本不存在。时间相关的猜测必须先查实际数据再下判断。