2026-08-06 12:34:57 +08:00
|
|
|
|
# 开发节点记录(三系统量化交易)
|
|
|
|
|
|
|
|
|
|
|
|
> 这份文件的用途只有一个:**让下一次交接不必重新查一遍**。
|
|
|
|
|
|
> 每个重要节点写一条,写完就补,不攒到最后。一条节点包含五样东西:
|
|
|
|
|
|
> 做了什么、动了哪些文件、部署方式、真机判收结果、还欠着什么。
|
|
|
|
|
|
>
|
|
|
|
|
|
> 三个系统的部署方式(改错等于白改):
|
|
|
|
|
|
> - **tradingSystem(PMS)** 跑在 `factor@factorevaluation`,源码打进镜像,
|
|
|
|
|
|
> 改码必须 `make deploy`,跑完 `make test` 见 ALL SUITES PASS,盯盘 `make watch`。
|
|
|
|
|
|
> 容器时钟是北京时间。
|
|
|
|
|
|
> - **bionic_trader(决策系统)** 跑在 `192.168.16.188:38000`,挂载卷,
|
|
|
|
|
|
> `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。
|
|
|
|
|
|
>
|
|
|
|
|
|
> 记录纪律:判收状态只写「真机见过」或「未判收」两种,不写「应该没问题」。
|
|
|
|
|
|
> 时间一律写北京时间,引用桥或决策系统的日志时间时先加八小时并标注已换算。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 2026-08-06 · 新建仓动作:方案已出,待拍板
|
|
|
|
|
|
|
|
|
|
|
|
**做了什么**
|
|
|
|
|
|
读完 PMS 的动作引擎主链代码,出了《给动作引擎加「新建仓」动作》的方案,等待拍板。
|
|
|
|
|
|
一行代码都还没改。
|
|
|
|
|
|
|
|
|
|
|
|
**读代码得到的、与既有文档不一致的事实(以代码为准)**
|
|
|
|
|
|
|
|
|
|
|
|
1. `OPEN` 这个动作名在 PMS 里**已经存在**,而且从方案到下单的整条管道是通的:
|
|
|
|
|
|
`planner.py:31` 定义 `A_OPEN`、`executor.py:41` 的 `BUY_ACTIONS` 已含 `OPEN`、
|
|
|
|
|
|
`command_service.py:647` 已经在用 `action="OPEN"` 写评审账本。
|
|
|
|
|
|
缺的只有自主提议这一段:`action_engine.py:27` 的动作词表没有 OPEN,
|
|
|
|
|
|
`action_engine.scan()` 的输入是持仓列表,结构上不可能引入新标的。
|
|
|
|
|
|
2. `caps_ctx(view, ts_code=...)` 对**从未持仓过的票**不会带出行业名,
|
|
|
|
|
|
不显式传 `sector` 的话,行业集中度那道硬拦截会整段静默跳过。
|
|
|
|
|
|
3. 从未持仓过的票在 `pms_position` 里没有行,`update_position` 会影响 0 行。
|
|
|
|
|
|
建行要显式调 `ensure_position`,它只写 `ts_code / status='PLANNED' / updated_at`。
|
|
|
|
|
|
4. `command_spec.py` 里那个 `"planner": "..."` 字段**全代码库没有任何一处读它**,
|
|
|
|
|
|
真正的分派是 `command_service._dispatch_planner` 里一串硬编码的 `if cmd_type ==`。
|
|
|
|
|
|
5. 参考位取数(`ledger_service` 盘前那一跳)只对 `only_open=True` 的持仓取,
|
|
|
|
|
|
候选票没有支撑压力。这对择时实现 A 无影响(它用决策系统自己库里的结论),
|
|
|
|
|
|
但实现 B 的「回踩带」判据对新票是空的。
|
|
|
|
|
|
|
|
|
|
|
|
**动了哪些文件**
|
|
|
|
|
|
无。方案文件 `NEW_POSITION_ACTION_PLAN.md` 已放进 tradingSystem 项目目录。
|
|
|
|
|
|
|
|
|
|
|
|
**部署方式**
|
|
|
|
|
|
不涉及。
|
|
|
|
|
|
|
|
|
|
|
|
**真机判收**
|
|
|
|
|
|
未判收(尚未动码)。
|
|
|
|
|
|
|
|
|
|
|
|
**还欠着什么**
|
|
|
|
|
|
方案第十节的四件待拍板:新建仓的档位怎么给、每天最多新开几只、研判闸这一轮做不做、
|
|
|
|
|
|
盘中参考位漂移要不要加防护。拍板之后才动手。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 2026-08-06 · 新建仓动作:四条取舍已拍板,决策系统侧评估完毕,方案定稿
|
|
|
|
|
|
|
|
|
|
|
|
**做了什么**
|
|
|
|
|
|
四条取舍拍板:新建仓单独一个档位参数且默认开启;不设每日开仓上限,由既有上限、资金、
|
|
|
|
|
|
候选池与择时自己收敛;研判闸这一轮双侧一并加上;参考位漂移做首答锁定加偏离即停。
|
|
|
|
|
|
按第三条读了决策系统的相关代码,方案更新为定稿版 V2。代码仍然一行未改。
|
|
|
|
|
|
|
|
|
|
|
|
**读决策系统代码得到的三条事实(以代码为准)**
|
|
|
|
|
|
|
|
|
|
|
|
1. **择时那一侧一个字都不用改。** `app/services/pms_advisor.py` 的 `_advise`
|
|
|
|
|
|
(163 到 226 行)从头到尾没有读过 `action` 字段,只用 `ts_code`、`side`、`day.price`。
|
|
|
|
|
|
买入区间由昨夜支撑压力推出,与这笔买单是建新仓还是加老仓无关。
|
|
|
|
|
|
2. **研判那一侧没有任何 action 白名单。** `app/api/main.py:323` 的路由是裸 dict,
|
|
|
|
|
|
只校验总开关与 `ts_code` 非空;worker 侧 `tasks_intraday.py:512` 是
|
|
|
|
|
|
`_task_by_action.get(_act, 兜底文案)`。所以今天发 `action="OPEN"` 就能跑通,
|
|
|
|
|
|
但拿到的是给加仓写的兜底文案,后面还跟着一句「仓位纪律不归你管」。
|
|
|
|
|
|
要改的是三处,都在 `workers/tasks_intraday.py`:加一条 OPEN 判据、
|
|
|
|
|
|
OPEN 且现价缺失时显式回 UNAVAILABLE、边界声明按动作分岔。
|
|
|
|
|
|
3. **新票的现价链路最脆弱。** `tasks_intraday.py` 449 到 456 行,新票没有持仓快照,
|
|
|
|
|
|
`fetch_realtime_close` 读不到分钟线时返回 `0.0` 被 `or None` 变成 None,
|
|
|
|
|
|
提示词渲染成「当前现价 未知 元」。加仓类还有摊薄成本可依,建仓判断没有现价锚不成立。
|
|
|
|
|
|
|
|
|
|
|
|
**新撞出来的一条硬约束(工程事实,不是取舍)**
|
|
|
|
|
|
`app/scheduler.py:45` 给所有调度任务设了 `task_soft_time_limit=240, task_time_limit=300`,
|
|
|
|
|
|
而 `judge.request` 是同步阻塞、单次超时上限 90 秒。**三只票送研判就是 270 秒,已经超过
|
|
|
|
|
|
软超时。** 今天没出事是因为送研判的只有三只持仓票且多数轮次被去重挡掉。新建仓不节流、
|
|
|
|
|
|
候选池默认取前 30 只,第一跳就会捅穿。
|
|
|
|
|
|
处理办法是给单轮扫描一个研判时间预算 `PMS_JUDGE_TICK_BUDGET_SEC`(默认 150 秒),
|
|
|
|
|
|
用尽就把剩下的候选留到下一分钟的心跳,并写进 skipped 留痕。
|
|
|
|
|
|
**这不是节流:一条候选都没丢,也没有按天计的上限**,只是让一次心跳做得完。
|
|
|
|
|
|
|
|
|
|
|
|
**动了哪些文件**
|
|
|
|
|
|
无代码改动。`NEW_POSITION_ACTION_PLAN.md` 更新为定稿版 V2,已放进项目目录。
|
|
|
|
|
|
|
|
|
|
|
|
**部署方式**
|
|
|
|
|
|
不涉及。定稿版里写明:决策系统这次只改 worker 代码、不动 `.env`,
|
|
|
|
|
|
所以 `git pull` 后 `docker compose restart backend-api worker-brain` 即可;
|
|
|
|
|
|
哪天真要往 `.env` 写值(比如切研判独立队列)才必须 `up -d --force-recreate`。
|
|
|
|
|
|
PMS 侧因为新建仓档位默认就是自动执行,**建议收盘后 `make deploy`**,
|
|
|
|
|
|
让第一次真实建仓发生在次日开盘、人在场的时候。
|
|
|
|
|
|
|
|
|
|
|
|
**真机判收**
|
|
|
|
|
|
未判收(尚未动码)。
|
|
|
|
|
|
|
|
|
|
|
|
**还欠着什么**
|
|
|
|
|
|
等点头后开工。开工顺序:决策系统侧先上并用 `pms_smoke.py` 手工发一条
|
|
|
|
|
|
`action=OPEN` 的研判请求验证,再回来做 PMS 侧。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-06 13:55:27 +08:00
|
|
|
|
## 2026-08-06 · 新立一条规矩:动 tradingSystem 以外的系统之前先答三题
|
|
|
|
|
|
|
|
|
|
|
|
**做了什么**
|
|
|
|
|
|
用户指出决策系统本来就不管仓位(它是聚合大量信号的判断系统),往里面塞仓位信息
|
|
|
|
|
|
会把别的在跑的逻辑带偏。据此立了一条通用规矩,并按它把方案重过一遍,改成 V3。
|
|
|
|
|
|
|
|
|
|
|
|
**规矩(改 tradingSystem 以外的系统,动手前先答,答不上就别动)**
|
|
|
|
|
|
|
|
|
|
|
|
1. 这段代码是不是只有我这条路径会走到?共用的提示词模板、裁决解析、上下文组装、
|
|
|
|
|
|
配置项,都不能在里面加只对我有意义的分支。
|
|
|
|
|
|
2. 我加进去的字段,会不会被别的逻辑读到并当真?尤其是写共享表、共享缓存、
|
|
|
|
|
|
往共享 context 里塞键。
|
|
|
|
|
|
3. 我的东西全删掉,原系统能不能一字不差地回到今天?答不上「能」就不是加法。
|
|
|
|
|
|
|
|
|
|
|
|
**按规矩核过的结论**
|
|
|
|
|
|
决策系统这三处改动全部落在两段 `if direction == "PMS_JUDGE":` 里
|
|
|
|
|
|
(`workers/tasks_intraday.py` 439 到 523 行、632 到 656 行),不碰共用的提示词模板
|
|
|
|
|
|
(557 到 576 行)、共用的裁决解析(579 到 590 行)、资金与板块注入(530 到 555 行);
|
|
|
|
|
|
不写 `decision_ledger`;不加 settings 字段;留痕仍只有 PMS_PASS / PMS_REJECT /
|
|
|
|
|
|
PMS_UNAVAILABLE 三个值,而 `pms_advisor._today_exit_verdict` 第 102 行明确跳过
|
|
|
|
|
|
`PMS_` 开头的行,所以写进去的东西不会反过来影响择时。第三题答案是能,`git revert` 一次。
|
|
|
|
|
|
|
|
|
|
|
|
**额外补的一条:仓位数字不进研判请求**
|
|
|
|
|
|
`judge.request` 现在把 `hard_numbers` 整个塞进请求体,决策系统会逐键渲染进提示词。
|
|
|
|
|
|
新建仓的 hard_numbers 里有名额、可投金额、批次比例——等于当面请一个不该管仓位的系统
|
|
|
|
|
|
去看仓位。所以在 PMS 侧加键白名单,只对 OPEN 生效:送现价、分数、主题、档位、
|
|
|
|
|
|
预期空间、热度、名次;不送名额、金额、批次、组合占比。账本照旧存全量。
|
|
|
|
|
|
|
|
|
|
|
|
**重过一遍还改掉与补上的(详见方案 V3 第三节)**
|
|
|
|
|
|
改掉两处:去掉 `PMS_OPEN_TARGET_PCT` 与建持仓行(改由首次成交时既有的
|
|
|
|
|
|
`ensure_position` 自然建行,省掉僵尸空行的清理);去掉 `PMS_OPEN_MIN_SCORE`
|
|
|
|
|
|
(与候选池的 `PMS_PLAN_MIN_SCORE` 重复)。
|
|
|
|
|
|
补上六处,其中三条是真问题:
|
|
|
|
|
|
- **候选之间要滚动扣减**。`_route_one` 每条各自用本轮开始时的旧快照算上限,
|
|
|
|
|
|
一跳内五条各自不超、加起来超,规则闸拦不住。命令那条路有 `_ctx_after` 滚动,
|
|
|
|
|
|
自主这条没有。滚动放进 `scan_open`,复用 `planner.check_all_caps`。
|
|
|
|
|
|
- **研判驳回要做当日去重(只对 OPEN)**。`_rejected_today_keys` 只读 arbiter='rule',
|
|
|
|
|
|
研判驳回不在里面,`_inflight_keys` 也认不出,于是被驳回的候选每分钟往账本写一行
|
|
|
|
|
|
一模一样的记录——正是 07-29 那个把判分锚淹掉的教训。
|
|
|
|
|
|
- **可投金额要用真实可用资金封顶**,并加 `PMS_OPEN_REQUIRE_WS_CASH`:
|
|
|
|
|
|
拿不到资金快照就不自动新建仓。规则闸那条「拿不到资金只告警不拦」是为已排好的命令
|
|
|
|
|
|
设计的,不适合无人值守地从零建仓。
|
|
|
|
|
|
另外三条:行业名一次批量取本轮复用;研判预算用尽的候选整条跳过不入人工队列;
|
|
|
|
|
|
新建仓退实现B 时择时判据只剩均价一条(已知降级,要在理由里写明)。
|
|
|
|
|
|
|
|
|
|
|
|
**一条顺带发现、但这次不夹带的**
|
|
|
|
|
|
`industry.get_many` 走 `gp_stock_category` 时是逐只查库、没有缓存,
|
|
|
|
|
|
可以照 `_hybk_many` 加按日缓存。收益是每跳少几十次查询,但它改的是既有函数的时序行为,
|
|
|
|
|
|
按上面第 1 题的规矩不夹带,要做单独一条、单独拍板。
|
|
|
|
|
|
|
|
|
|
|
|
**动了哪些文件**
|
|
|
|
|
|
无代码改动。`NEW_POSITION_ACTION_PLAN.md` 更新为 V3。
|
|
|
|
|
|
|
|
|
|
|
|
**部署方式**
|
|
|
|
|
|
不涉及。
|
|
|
|
|
|
|
|
|
|
|
|
**真机判收**
|
|
|
|
|
|
未判收(尚未动码)。
|
|
|
|
|
|
|
|
|
|
|
|
**还欠着什么**
|
|
|
|
|
|
同上,等点头开工。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 2026-08-06 · 新建仓动作:两侧代码写完,容器内全量单测通过,待实机部署
|
|
|
|
|
|
|
|
|
|
|
|
**做了什么**
|
|
|
|
|
|
按 V3 方案把两侧代码写完了。容器里拼了一份可运行的副本跑全量单测:**ALL SUITES PASS,
|
|
|
|
|
|
451 例**(新增的第十二批 26 例 + 既有 425 例,含装配自检 58 例)。**尚未在实机部署。**
|
|
|
|
|
|
|
|
|
|
|
|
**动了哪些文件**
|
|
|
|
|
|
|
|
|
|
|
|
决策系统(`bionic_trader`)一个文件,48 增 4 删,三处全在 `if direction == "PMS_JUDGE":` 块内:
|
|
|
|
|
|
- `workers/tasks_intraday.py`:`_task_by_action` 加 OPEN 建仓判据(含必答「把这只票挑出来的
|
|
|
|
|
|
驱动到今天还成不成立」);OPEN 且现价缺失时块内早退回 `UNAVAILABLE`(加仓类有摊薄成本可依,
|
|
|
|
|
|
建仓没有现价锚就是盲判);边界声明按动作分岔(原句里的「配额」对新建仓不成立)。
|
|
|
|
|
|
|
|
|
|
|
|
持仓系统(`tradingSystem`)九个文件:
|
|
|
|
|
|
- `app/core/action_engine.py`:加 `A_OPEN` 并进 `JUDGE_ACTIONS`;新增 `eval_open` 与
|
|
|
|
|
|
`scan_open`。**既有 `scan()` 一个字未动。**
|
|
|
|
|
|
- `app/services/proposal_service.py`:新增 `_scan_open`(候选池取数、批量取行业、
|
|
|
|
|
|
真实资金封顶);`_route_one` 加新建仓特判与档位分支;研判时间预算。
|
|
|
|
|
|
- `app/services/judge.py`:`OPEN_JUDGE_KEYS` 键白名单,新建仓只送定性材料。
|
|
|
|
|
|
- `app/services/exec_advisor.py`:存 `observed` 的支撑压力区间;`_check_ref_drift` 首答锁定、
|
|
|
|
|
|
偏离即停。
|
|
|
|
|
|
- `app/services/executor.py`:等待分支看到 `ref_drift` 落一条账本 WARN,**一天一条**。
|
|
|
|
|
|
- `app/repo/pms_repo.py`:新增只读 `judge_rejected_today` 与 `opened_names_today`。
|
|
|
|
|
|
- `app/services/param_store.py`:四个新参数的说明、校验、`FAIL_CLOSED`。
|
|
|
|
|
|
- `config/settings.py`:四个新参数;`PMS_JUDGE_ACTIONS` 默认值加 `OPEN`。
|
|
|
|
|
|
- `scripts/test_batch12_units.py`(新,26 例)与 `scripts/run_tests.py`(登记新批次)。
|
|
|
|
|
|
|
|
|
|
|
|
**写代码过程中改掉的一条设计(单测逼出来的)**
|
|
|
|
|
|
原方案的取数口径抄的是命令驱动那条路:`want = min(单股目标, 剩余额度)`。单测发现它会在
|
|
|
|
|
|
可投金额只剩一万时开出一只 0.5% 的零头仓位——**拿一个持仓名额换一个永远补不到目标的半截仓,
|
|
|
|
|
|
还挡住了后面真正建得起来的票**。改成「钱不够一整只就不开」,并把与命令那条路的差别写在注释里:
|
|
|
|
|
|
命令是用户明确下了「投这么多」,最后一只缩水是命令的收尾;自主建仓没有这层意思。
|
|
|
|
|
|
|
|
|
|
|
|
**部署方式**
|
|
|
|
|
|
- 决策系统:不加配置、不动 `.env` → `git pull` 后 `docker compose restart backend-api worker-brain`。
|
|
|
|
|
|
- 持仓系统:源码打进镜像 → `make deploy`,跑完 `make test` 要见 ALL SUITES PASS。
|
|
|
|
|
|
**新建仓档位默认就是 full(自动执行),`make deploy` 一跑完下一个整分钟的心跳就可能真建仓,
|
|
|
|
|
|
所以务必收盘后部署**,让第一次真实运行发生在次日开盘、人在场的时候。
|
|
|
|
|
|
|
|
|
|
|
|
**真机判收**
|
|
|
|
|
|
未判收。判收步骤见 `NEW_POSITION_ACTION_PLAN.md` 第十节,顺序是决策系统先上并手工发一条
|
|
|
|
|
|
`action=OPEN` 的研判请求,确认理由是在谈从零建仓而不是加仓、且请求体里没有仓位数字。
|
|
|
|
|
|
|
|
|
|
|
|
**还欠着什么**
|
|
|
|
|
|
1. 实机部署与八步判收。
|
|
|
|
|
|
2. 第一周要盯的四个数:每天有几只候选落在买入区间内、研判驳回率、`intraday_exec` 一跳的
|
|
|
|
|
|
耗时分布、评审账本每天的行数。
|
|
|
|
|
|
3. 未夹带的一条:给 `industry.get_many` 的 `gp_stock_category` 分支加按日缓存
|
|
|
|
|
|
(每跳省几十次库查询,但改的是既有函数的时序行为,单独提、单独拍板)。
|
|
|
|
|
|
4. 文档补账仍欠着:`WS_INTEGRATION_STATUS.md` 停在 07-30;`BIONIC_PMS_INTERFACE.md`
|
|
|
|
|
|
要补新建仓这一路(研判闸第五类动作、硬数字裁剪、择时侧零改动)。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-06 12:34:57 +08:00
|
|
|
|
<!--
|
|
|
|
|
|
下一条节点从这里往下写,格式照抄上面:
|
|
|
|
|
|
## YYYY-MM-DD · 一句话标题
|
|
|
|
|
|
**做了什么** / **动了哪些文件** / **部署方式** / **真机判收** / **还欠着什么**
|
|
|
|
|
|
-->
|