2026-08-06 13:05:40 +08:00
|
|
|
|
# 方案(定稿):给动作引擎加「新建仓」动作
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
> V2,2026-08-06。四条取舍已拍板,决策系统侧已评估完毕。**待你点头即可动手。**
|
|
|
|
|
|
> 全部依据来自实际代码,不引用设计文档的结论。
|
|
|
|
|
|
> 部署:PMS 源码打进镜像,改码必须 `make deploy`,跑完 `make test` 见 ALL SUITES PASS;
|
|
|
|
|
|
> 决策系统挂载卷,`git pull` 后 `docker compose restart backend-api worker-brain` 即可。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 〇、拍板结果(V1 的四问)
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
| # | 问题 | 决定 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| 1 | 档位 | **单独一个开关,默认开启**。新建仓不跟随全局档位,自己一个参数,默认就是自动执行 |
|
|
|
|
|
|
| 2 | 每日节流 | **不做**。逻辑进逻辑出,不设人为上限,由既有的上限、资金、候选池与择时自己收敛 |
|
|
|
|
|
|
| 3 | 研判闸 | **这一轮一并加上**,双侧改到位 |
|
|
|
|
|
|
| 4 | 漂移防护 | **做**,首答锁定加偏离即停 |
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
---
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 一、现状:管道已经铺好,缺的只有一段
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
`OPEN` 这个动作名在 PMS 里已经存在,从方案到下单的整条管道是通的:
|
|
|
|
|
|
`planner.py:31` 定义 `A_OPEN`、`executor.py:41` 的 `BUY_ACTIONS` 已含 `OPEN`、
|
|
|
|
|
|
`command_service.py:647` 已经在用 `action="OPEN"` 写评审账本。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
缺的只有两处:`action_engine.py:27` 的自主动作词表里没有 `OPEN`;
|
|
|
|
|
|
`action_engine.scan()` 第 197 行是 `for p in positions or []`,输入是持仓列表,
|
|
|
|
|
|
**结构上就不可能引入新标的**。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
所以这次做的事是:补一个以候选池为输入的求值器,产出 `OPEN` 候选,并进现有的
|
|
|
|
|
|
「规则闸 → 研判闸 → 档位分流」那条路。下游一行不用改。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 二、决策系统侧的评估结论(第 3 条拍板的前置)
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
读了 `workers/tasks_intraday.py`、`app/api/main.py`、`app/services/pms_advisor.py`、
|
|
|
|
|
|
`config/settings.py`。三条结论:
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
### 1. 择时那一侧,一个字都不用改
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
`pms_advisor._advise`(第 163 到 226 行)从头到尾**没有读过 `action` 字段**,
|
|
|
|
|
|
它只用 `ts_code`、`side`、`day.price` 三样。买入区间由昨夜结论的支撑压力推出来,
|
|
|
|
|
|
跟这笔买单是建新仓还是加老仓无关。所以新建仓的择时天然就能用,零改动。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
### 2. 研判那一侧,没有任何白名单,但兜底文案是给加仓写的
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
从 HTTP 入口到大模型提示词,全链路**没有一处校验 `action` 的取值**:
|
|
|
|
|
|
`app/api/main.py:323` 的路由是裸 `dict`,只查总开关和 `ts_code` 非空;
|
|
|
|
|
|
worker 侧 `tasks_intraday.py:512` 是
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
```python
|
|
|
|
|
|
core_task = _task_by_action.get(_act, f"这是 PMS 的【{_act}】提议, 请按提议理由与硬数字做定性仲裁。")
|
|
|
|
|
|
```
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
也就是说,**今天就把 `action="OPEN"` 发过去,它不会报错、不会回不可用,会照常打大模型
|
|
|
|
|
|
并按 PASS/REJECT 收口**——但拿到的是一段没有针对性判据的兜底文案,
|
|
|
|
|
|
后面还无条件跟着一句「仓位纪律 PMS 的规则闸已经查过,不归你管」,那是给加仓写的语境。
|
|
|
|
|
|
让它就这么跑,等于用一个含糊的问题去问一个昂贵的模型,答出来的东西没法信。
|
|
|
|
|
|
|
|
|
|
|
|
所以「一并加上」要改的是三处,都在 `workers/tasks_intraday.py`:
|
|
|
|
|
|
|
|
|
|
|
|
- **加一条 OPEN 判据**(第 495 到 511 行的 `_task_by_action` 字典里加一项)。
|
|
|
|
|
|
核心问题写成:这是一只从零开始建仓的新票,当初把它选进候选池的驱动,
|
|
|
|
|
|
到今天是否仍然成立、有没有被证伪;证据不足以支持从零建仓时选驳回。
|
|
|
|
|
|
- **现价缺失时显式回不可用**。第 449 到 456 行的现价链路:新票没有持仓快照,
|
|
|
|
|
|
`_pos.get("price")` 拿不到,退到 `fetch_realtime_close`,而那个函数读不到分钟线时
|
|
|
|
|
|
返回 `0.0`,被 `or None` 变成 `None`,提示词最终渲染成「当前现价 未知 元」。
|
|
|
|
|
|
加仓类还有摊薄成本可依,**建仓判断没有现价锚基本不成立**,
|
|
|
|
|
|
所以要给 OPEN 加一条:现价拿不到直接回 `UNAVAILABLE`,不让模型盲判。
|
|
|
|
|
|
- **边界声明按动作分岔**。那句「仓位纪律不归你管」对 OPEN 仍然成立
|
|
|
|
|
|
(名额、行业集中度、资金确实是 PMS 规则闸查的),但措辞要改成建仓语境,
|
|
|
|
|
|
免得模型把它读成「这是一笔加仓」。
|
|
|
|
|
|
|
|
|
|
|
|
另外两件不用改、但要知道的事:提示词里会出现「(无持仓快照)」「(无近期评审流水)」
|
|
|
|
|
|
这两个空占位(新票本来就没有),是噪声但不误导;
|
|
|
|
|
|
`get_board_qrs(ts_code)` 那里没做代码格式转换,我核过 PMS 侧
|
|
|
|
|
|
`command_spec.normalize_code`(第 296 到 313 行)产出的是点式,
|
|
|
|
|
|
`get_board_qrs` 对点式解析正常,**这条路上不是 bug**。
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 部署方式:只要不动 `.env`,restart 就够
|
|
|
|
|
|
|
|
|
|
|
|
`config/settings.py` 第 208 到 226 行那段 P 段自己写着「全部有默认值,不新增必填 env
|
|
|
|
|
|
→ 重启 backend-api + worker-brain 即生效」。这次改的是 worker 代码,不加新配置,
|
|
|
|
|
|
所以走常规路径:`git pull` 之后 `docker compose restart backend-api worker-brain`。
|
|
|
|
|
|
**一旦哪天要往 `.env` 里真的写一个值(比如切研判独立队列),那才必须
|
|
|
|
|
|
`docker compose up -d --force-recreate`。**
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
---
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 三、新发现的一条硬约束:一跳心跳里塞不下那么多次研判
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
这条是读代码时撞出来的,不是策略取舍,是工程事实,必须处理。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
`app/scheduler.py:45` 给所有调度任务设了 `task_soft_time_limit=240, task_time_limit=300`。
|
|
|
|
|
|
自主提议扫描跑在每分钟一跳的 `intraday_exec` 里,而 `judge.request` 是**同步阻塞**的,
|
|
|
|
|
|
超时上限 `PMS_JUDGE_TIMEOUT` 默认 90 秒。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
**三只票送研判就是 270 秒,已经超过 240 秒的软超时。** 今天之所以没出事,
|
|
|
|
|
|
是因为送研判的只有已有持仓的三只票,而且多数轮次被在途去重和当日去重挡掉了。
|
|
|
|
|
|
新建仓不节流、候选池默认取前 30 只,第一跳就会把这个洞捅穿——任务被中途杀掉,
|
|
|
|
|
|
而且是在已经落了一部分表之后。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
**处理办法:给单轮扫描一个研判时间预算,而不是给每天一个开仓上限。**
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
新增 `PMS_JUDGE_TICK_BUDGET_SEC`(默认 150 秒)。每次要调研判前先看这一轮已经花了多久,
|
|
|
|
|
|
如果「已花 + 单次超时上限」会顶破预算,本轮就不再送后面的候选,
|
|
|
|
|
|
把它们写进 `skipped`,理由写「本轮研判时间预算用尽,下一跳继续」。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
这不是节流:**一条候选都没有被丢掉,也没有任何按天计的上限**,
|
|
|
|
|
|
只是把一次心跳做不完的活挪到下一分钟。候选是按分数降序排的,
|
|
|
|
|
|
所以预算用尽时先做完的一定是分数最高的那些。决策系统那侧的裁决缓存是按
|
|
|
|
|
|
`日期 + 股票 + 动作` 存半小时的,所以同一批候选在半小时内的重复扫描都是秒回,
|
|
|
|
|
|
真正慢的只有缓存过期后的第一跳。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
顺带一提,决策系统那侧还预埋了研判独立队列(接口文档 §5.2)。
|
|
|
|
|
|
现在不用开——那是给 `brain_queue` 拥堵准备的第二档,而我们这里的瓶颈是
|
|
|
|
|
|
PMS 自己的心跳时长,不是对端排队。真出现频繁超时再开,而且开它要改 `.env`,
|
|
|
|
|
|
必须 `force-recreate`。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 四、选票规则:动作引擎只管名额、钱、批次
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
候选池那边已经筛过(分数降序、档位白名单、分数与来源数与预期空间下限、同主题限额、
|
|
|
|
|
|
剔除 ST、剔除已持仓、剔除黑名单)。规则闸后面还会管一手整百、冻结、全局暂停买入、
|
|
|
|
|
|
黑名单、昨夜定性 `BAD_SIGNAL`、涨停不追、当日涨幅不追高、距 MA5 不追高、
|
|
|
|
|
|
组合与单股与持仓数与行业集中度上限、预留现金、真实可用资金。择时再管现价在不在买入区间。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
`action_engine.py:17` 的注释已经把边界钉死了——上限、一手、冻结这些不在动作引擎重复判。
|
|
|
|
|
|
所以新建仓求值器只回答三个问题:
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
1. **还能开几只**:`最大持仓数 − 当前持仓数`。没有额外的每日上限。
|
|
|
|
|
|
2. **还有多少钱可投**:`总仓上限 × 总规模 − 当前组合市值`,与单只目标金额取小。
|
|
|
|
|
|
3. **这一批买多少股**:按 `PMS_BATCH_SPLIT` 拆批,**只提底仓那一批**(默认 50%),
|
|
|
|
|
|
`lot_qty(金额, 现价)`,不足一手就不提。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
**只提底仓批**是刻意的:后续的回踩补足与盈利加仓,等持仓真建立起来之后由已有的
|
|
|
|
|
|
`eval_fill` 与 `eval_add` 自然接管。这条路上一条 `GATED` 方案都不产生。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
**价格必须是实时价。** `market.plan_price` 拿不到实时价会回落昨收,
|
|
|
|
|
|
它自己的注释写着「拿昨收当现价去做不追高这类判断会出错,只给规划期定量用」。
|
|
|
|
|
|
自主建仓要真下单,所以走 `market.get_price`,取不到就整只跳过并写进 `skipped`。
|
|
|
|
|
|
|
|
|
|
|
|
**不节流的一个自然收敛点**:择时的买入区间本身就是过滤器。
|
|
|
|
|
|
只有现价正好落在 `[支撑×0.99, 支撑+(压力−支撑)×0.3]` 里的票才会真的出手,
|
|
|
|
|
|
高于上沿一律「不追,接受买不上」。所以每天实际能建成几只,取决于当天有几只票
|
|
|
|
|
|
落在自己的区间内——这正是「让系统自己判断」。第一周要盯的就是这个数。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 五、要动的文件与改法
|
|
|
|
|
|
|
|
|
|
|
|
### PMS 侧(`factor@factorevaluation`,改完 `make deploy`)
|
|
|
|
|
|
|
|
|
|
|
|
1. **`app/core/action_engine.py`**
|
|
|
|
|
|
加 `A_OPEN = "OPEN"`,加进 `JUDGE_ACTIONS`;
|
|
|
|
|
|
新增 `eval_open()` 与 `scan_open()`;**既有 `scan()` 一个字不动**,既有单测继续全绿。
|
|
|
|
|
|
|
|
|
|
|
|
2. **`app/services/proposal_service.py`**
|
|
|
|
|
|
- `scan_and_route` 在 `ae.scan(...)` 之后追加取候选池、算名额与可投金额、调 `scan_open`,
|
|
|
|
|
|
两批候选合并走同一个 `_route_one`。
|
|
|
|
|
|
- 候选池取数失败(`PlanFeedError`)时不产生任何新建仓候选,
|
|
|
|
|
|
原因写进 `skipped`;已有持仓的四类动作照常扫描,不受影响。
|
|
|
|
|
|
- 研判时间预算:`_route_one` 之外记一个本轮起始时刻,送研判前判预算。
|
|
|
|
|
|
- `_route_one` 给 `OPEN` 三处特判:现价取候选自带的(`_pos_of` 对新票没有 `price`,
|
|
|
|
|
|
取它会是 0,规则闸直接 `PRICE_MISSING`);组合上下文改成
|
|
|
|
|
|
`caps_ctx(view, ts_code=code, is_new_name=True, sector=industry.get(code))`
|
|
|
|
|
|
——**必须显式传行业**,否则行业集中度那道硬拦截会静默跳过;
|
|
|
|
|
|
落指令前先 `pms_repo.ensure_position(code)` 建行再写 `target_pct`。
|
|
|
|
|
|
- 档位分流加 OPEN 分支(见第六节)。
|
|
|
|
|
|
|
|
|
|
|
|
3. **`app/services/exec_advisor.py`**
|
|
|
|
|
|
`_advice` 把应答 `observed` 里的支撑、压力、买入区间一并存进缓存;
|
|
|
|
|
|
当日首答把这三样锁进 `prog["ref_lock"]`;之后每答比对,
|
|
|
|
|
|
偏离超过 `PMS_OPEN_REF_DRIFT_MAX` 就把本轮判定改成等待,
|
|
|
|
|
|
并在返回的决策里带上 `ref_drift` 说明。**只对 `action == "OPEN"` 生效。**
|
|
|
|
|
|
|
|
|
|
|
|
4. **`app/services/executor.py`**
|
|
|
|
|
|
`run_tick` 的等待分支里,看到 `d.get("ref_drift")` 就额外落一条评审账本 `WARN`。
|
|
|
|
|
|
(放这里而不放 `exec_advisor`,是为了不给后者引入落表副作用。)
|
|
|
|
|
|
|
|
|
|
|
|
5. **`app/repo/pms_repo.py`**
|
|
|
|
|
|
新增只读函数,统计当日已开与在提的新仓票数(供留痕与页面显示;
|
|
|
|
|
|
不做上限,只是让人看得见今天开了几只)。
|
|
|
|
|
|
|
|
|
|
|
|
6. **`app/services/param_store.py` 与 `config/settings.py`**
|
|
|
|
|
|
新增参数(第七节),同步补 `DESC` 中文说明、`_RANGES` 或 `_range_check` 校验、
|
|
|
|
|
|
以及 `FAIL_CLOSED`。`param_store.py:46` 那条注释记着一次血的教训——
|
|
|
|
|
|
加参数不同步加白名单,`set_param` 只回 `ok=False` 不抛异常,调用方又把返回值丢了,
|
|
|
|
|
|
于是那条纪律静默失效。这次一并照做。
|
|
|
|
|
|
另外把 `PMS_JUDGE_ACTIONS` 的默认值从 `FILL,ADD,DCA,SWITCH` 改成
|
|
|
|
|
|
`FILL,ADD,DCA,SWITCH,OPEN`。
|
|
|
|
|
|
|
|
|
|
|
|
7. **测试脚本**
|
|
|
|
|
|
纯逻辑用例,不连库:名额已满、组合钱不够、买不足一手、取不到实时价、
|
|
|
|
|
|
候选池取数失败、研判预算用尽、参考位漂移超阈值、档位分流的三种取值。
|
|
|
|
|
|
|
|
|
|
|
|
### 决策系统侧(`192.168.16.188`,改完 `git pull` + `restart`)
|
|
|
|
|
|
|
|
|
|
|
|
8. **`workers/tasks_intraday.py`**
|
|
|
|
|
|
`_task_by_action` 加 OPEN 判据;OPEN 且现价缺失时回 `UNAVAILABLE`;
|
|
|
|
|
|
边界声明按动作分岔。**不动 `.env`,不加新配置,restart 即生效。**
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
---
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 六、档位:一个开关,默认开启
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
新增 `PMS_OPEN_AUTONOMY`,取值 `full` / `propose_only` / `off`,**默认 `full`**。
|
|
|
|
|
|
它同时充当总开关,不再单设第二个开关:`off` 就是不扫描新建仓。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
`_route_one` 第 132 行那行不动,在它之外给 OPEN 加一个分支:
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
- `full` → 过完两道闸就直接落指令,无人值守。
|
|
|
|
|
|
- `propose_only` → 落待确认队列。
|
|
|
|
|
|
- `off` → 根本不扫描,一条候选都不产生。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
三条无条件优先的规矩保持不变:减持方向永远自动执行;
|
|
|
|
|
|
深档补仓永远要人点头;**研判不可用(`degraded`)时一律入队待确认**——
|
|
|
|
|
|
最后这条对新建仓尤其重要,它是「拿不到意见绝不当成通过」这条纪律在新链路上的落点。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
**一条部署上的提醒**:默认 `full` 意味着 `make deploy` 一跑完,
|
|
|
|
|
|
下一个整分钟的心跳就可能真的建仓。建议**收盘后部署**,
|
|
|
|
|
|
让第一次真实运行发生在次日开盘、你在场的时候。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 七、新增参数
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
| 键 | 默认 | 说明 |
|
|
|
|
|
|
|---|---|---|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
| `PMS_OPEN_AUTONOMY` | `full` | 新建仓档位兼总开关:full 自动执行 / propose_only 待确认 / off 不扫描 |
|
2026-08-06 12:34:57 +08:00
|
|
|
|
| `PMS_OPEN_TARGET_PCT` | `0` | 自主新建仓的单股目标仓位,0 = 用 `PMS_STOCK_TARGET_DEFAULT` |
|
2026-08-06 13:05:40 +08:00
|
|
|
|
| `PMS_OPEN_MIN_SCORE` | `0` | 在候选池已有过滤之上额外的分数下限,0 = 不额外过滤 |
|
|
|
|
|
|
| `PMS_OPEN_REF_DRIFT_MAX` | `0.03` | 参考位盘中被改写的容忍幅度,超过则该票当日暂停新建仓 |
|
|
|
|
|
|
| `PMS_JUDGE_TICK_BUDGET_SEC` | `150` | 单轮提议扫描用于研判的时间预算(秒),用尽则剩下的候选留到下一跳 |
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
改默认值的既有参数:`PMS_JUDGE_ACTIONS` 从 `FILL,ADD,DCA,SWITCH` 改成
|
|
|
|
|
|
`FILL,ADD,DCA,SWITCH,OPEN`。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
`FAIL_CLOSED` 加一条 `PMS_OPEN_AUTONOMY: "off"`——参数表读不到时不自动建仓。
|
|
|
|
|
|
这与「默认开启」不冲突:默认值管正常情况,`FAIL_CLOSED` 管读不到参数表的故障情况。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 八、两个已知的交互
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
1. **命令驱动的 OPEN 拒绝记录会挡住自主 OPEN。**
|
|
|
|
|
|
`command_service._ledger_rejects` 写的是 `action="OPEN"`、`arbiter="rule"`、
|
2026-08-06 13:05:40 +08:00
|
|
|
|
`verdict="REJECT"`,而 `_rejected_today_keys` 读的正是这张表的 `(股票, 动作)`。
|
|
|
|
|
|
所以某只票今天因为一条升仓命令在规划期被上限拦过,自主新建仓当天不会再提它。
|
|
|
|
|
|
多数情况下这是对的(同一组约束),但两边算的目标金额不同,
|
|
|
|
|
|
一手不足这类拒绝口径可能不一样。先记下来,观察一天再看要不要分开。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
2. **升仓命令的候选没有排除已有在途方案的票。**
|
|
|
|
|
|
`command_service._dispatch_planner` 算出了 `exclude = _codes_with_live_plans()`,
|
2026-08-06 13:05:40 +08:00
|
|
|
|
但只传给了降仓分支。这是既有现象,不属于本次范围;
|
|
|
|
|
|
自主这条路有 `_inflight_keys` 兜底,命令那条路没有。**这次不动,先观察。**
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 九、上线与判收
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
判收标准是真机各见一次。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
| 步 | 动作 | 判收标准 |
|
|
|
|
|
|
|---|---|---|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
| 1 | 决策系统侧先上:`git pull` + `restart backend-api worker-brain` | `docker compose exec backend-api python scripts/pms_smoke.py` 里手工发一条 `action=OPEN` 的研判请求,回 PASS 或 REJECT,理由读得出是在谈建仓而不是加仓 |
|
|
|
|
|
|
| 2 | PMS 侧 `make deploy` + `make test`(**收盘后做**) | ALL SUITES PASS |
|
|
|
|
|
|
| 3 | 次日盘前把 `PMS_OPEN_AUTONOMY` 先设成 `propose_only` 看一轮 | 提议队列出现 OPEN,理由读得懂,硬数字里有名额、可投金额、批次、分数;`make t-gate` 能看到 arbiter=judge 的行 |
|
|
|
|
|
|
| 4 | 确认无误后改成 `full` | 一条 OPEN 提议直接落指令,`make t-ins` 看到 `origin_type=proposal`、`action=OPEN`、`is_command=false` |
|
|
|
|
|
|
| 5 | 盘中观察 | 现价在买入区间内时出手,`last_decision.source=A`,限价等于区间上沿;`make watch` 里那条指令的进度在推进 |
|
2026-08-06 12:34:57 +08:00
|
|
|
|
| 6 | 成交之后 | `pms_position` 出现该票、`opened_date` 有值、`status=HOLDING`;次日起 `eval_fill` 能看见它 |
|
2026-08-06 13:05:40 +08:00
|
|
|
|
| 7 | 研判预算 | 日志里出现过「本轮研判时间预算用尽」且下一跳接上了;`intraday_exec` 一次都没有触发软超时 |
|
|
|
|
|
|
| 8 | 漂移防护 | **只靠单测判收,不在生产人为制造**(盘中跑 push-pool 触发补扫风险太大)。生产侧只观察账本里有没有 `REF_DRIFT` 的 WARN 行 |
|
|
|
|
|
|
|
|
|
|
|
|
第三步那个 `propose_only` 看一轮,是我加的一道自保:默认虽然是 `full`,
|
|
|
|
|
|
但第一次真跑之前先用一轮眼睛确认提议本身是对的,代价只有一个交易日。
|
|
|
|
|
|
你要是想直接上 `full`,把第三步跳过即可,判收标准从第四步开始。
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
## 十、第一周要盯的三个数
|
|
|
|
|
|
|
|
|
|
|
|
不设人为上限之后,这三个数替代了「每天开几只」这个旋钮,用来判断规则要不要调:
|
2026-08-06 12:34:57 +08:00
|
|
|
|
|
2026-08-06 13:05:40 +08:00
|
|
|
|
1. **每天有几只候选落在自己的买入区间内**——这是实际的建仓速度,
|
|
|
|
|
|
也是「让系统自己判断」到底判成什么样的直接答案。
|
|
|
|
|
|
2. **研判的驳回率**——驳得太狠说明 OPEN 判据写窄了,一条不驳说明写宽了。
|
|
|
|
|
|
3. **`intraday_exec` 一跳的耗时分布**——预算够不够,要不要开研判独立队列。
|