# CHANGELOG
## 0.2.21(2026-10-22;第三条断供判据:登录后资金回报「从未到手」也会超时)
只动本 crate,依赖不变(仍 ek-ctp 0.9.1 / ek-oms 0.3.7 / ek-station 0.2.3)。
### 实连读数把口径的洞照出来了
ek-station 实连套新增 S15f(向每个在场账户 ask `{gw}.status`)第一轮读数:
- 账户 A:`logged_in=true 掉线=0 查询被拒=0 资金时刻=Some(..)` —— 一切正常。
- 账户 B(F2 那个账户):`logged_in=true handle_up=true 掉线=1 查询被拒=0(连续 0) 资金时刻=None 持仓时刻=None 错误=None`,
且这个形状在 150 秒 + 240 秒两轮等待里**一直没变**。
0.2.20 的判据是「连续被拒达阈 **或** 距最后一次资金回报超时」——第二条要求 `td_last_account_ms`
是 `Some`。**一次都没到手**时它永远是 `None`,于是 B 在界面上一直是「已装配·在线 + 资金回报尚未到手」。
「尚未到手」这句话在场,但徽标是绿的:这正是 F2 在界面上的形状 —— 一个查询彻底停摆的账户看着像活着。
### 改法:同一个 stale 阈值也管「从未到手」
`apply_status` 登录着的分支加第三条判据:距 `td_last_login_ok_ms` 超过 `ACCOUNT_STALE_SECS`(30 秒)
且资金时刻仍为 `None` → `已装配·查询断供` + 细节「资金回报从未到手(登录已 Ns)」。
刚登录那几秒不算(轮转第一拍本就要 2~6 秒,ek-ctp 0.7.0 口径);真到手即回在线,判据不粘。
**判据原料齐了(登录时刻 + 读数时刻),网关侧不需要多给任何东西** —— 这也是为什么这条修在 ek-ui 而不是 ek-ctp。
### 测试与门禁
- kittest 钉 57 → 58(`s60`:刚登录 5 秒 = 在线 + 一句话 / 登录 40 秒从未到手 = 断供带时长 / 到手即回在线)。
- `cargo fmt` 干净;`cargo clippy --all-targets -- -D warnings` = 0,`--features ctp-native` 面同样 0;
`cargo test --tests` 全绿。
## 0.2.20(2026-10-22;三条开放项收口:F4 勾选口径、Q17 运维窗移除边界、日志窗口径 + 接 ek-ctp 0.9.1 分项)
依赖随 ek-ctp 0.9.1 整链抬版(`ek-ctp` 0.9.0 → 0.9.1、`ek-oms` 0.3.7、`ek-station` 0.2.3)。
这一版没有新功能,做的是**把三条一直挂在开放项里的口径落到界面上**,并把 ek-ctp 0.9.1 的
新读数接进状态细节。
### F4:「谁查谁过滤」写死成一句话(面板底部 + 配置弹窗各一处)
- 实连 S14b 的事实:账户 B 把勾选改成「期货+期权」,公用合约表仍 852 条、期权 0 条 ——
界面上没有任何地方告诉用户这是「没生效」还是「网关没查到」。
- 现在两处都写明:**勾选只管该网关自己收录并上抛的合约;公用合约表 = 在场各网关的并集
(合约与行情公用,规划 D3);改勾选须『连接/重试』才下发到网关。** 钉 `s57` 数两处
(同一口径不得只写在一处)。
### Q17:运维窗不再对账户清册里的实例提供『移除』/『重注入』
- 账户面板的『移除』带全套守卫(活跃委托闸 + 二次确认 + 持仓提示),运维窗那条是同一动作的
**无守卫副本** —— 同一个实例两条移除路、守卫只长在一条上,等于守卫可以绕过。
- 现在:名字在账户清册里 → 运维窗那一格改写成「起停归『账户』面板(带撤单守卫)」,
实例**仍列在表里**(运维可见性不砍),给的是去处不是死钮。
- **清册外的 `ctp_*` 实例(手工起的 / 旧流程残留)仍留这条出口** —— 否则没有退路。
钉 `s56`(工厂插件 dm/oms 的『移除』『重注入』照旧在场)。
### 日志窗:溢出时把「这是窗口不是历史」说出来
- 巡回 G1 的实连证据:重加账户后 90 秒内没抓到「交易服务器登录成功」行,但同一账户的资金行
下一拍就刷新 —— 登录其实成功,是 512 行环被状态推送流挤满把登录行顶掉了。
- 原来只写「已丢弃 N 条(环形 512)」;现在同一行接上口径与去处:**「日志窗是窗口不是历史 ——
登录/掉线/查询被拒看账户面板的状态列与状态细节,别回扫这里」**。钉 `s59`。
### 接 ek-ctp 0.9.1 分项:合约查询被拒在状态细节里点名
- `apply_status` 在句尾追加「合约查询被拒 N 次」(`td_instrument_rejects > 0` 时),与断供句
并存(「查询连续被拒 3 次;合约查询被拒 2 次」),**同一件事不重复点名**。
- 为什么要点名:总数与连续串不分路是对的(消费侧要的是「查询这条路连续被拒几次」),但合约
一条没到手时下一笔单会被 ContractGate 以 `contract:unknown` 拒掉,而状态列只写「在线」就是假信息。
- 分项为 0 不加句子(健康行的状态细节不无谓变长)。钉 `s58` 三段(未达阈 / 达阈并存不重复 / 零分项不加)。
### 测试与门禁
- kittest 钉 53 → 57(`s56`–`s59`);快照 `s39`/`s40`/`s41` 重基线(面板底部多一行口径句);
`.gitignore` 补 `**/snapshots/**/*.old.png`(重基线副产物)。
- `cargo fmt` 干净;`cargo clippy --all-targets -- -D warnings` = 0,`--features ctp-native` 面同样 0;
`cargo test --tests` 全绿(98 例,其中 kittest 57)。
## 0.2.19(2026-10-22;接上网关状态口:状态列两段口径 + 『断开』钮;规划 P3 的界面半边)
依赖随 ek-ctp 0.9.0 整链抬版(`ek-ctp` 0.8.0 → 0.9.0、`ek-oms` 0.3.5 → 0.3.6、
`ek-station` 0.2.1 → 0.2.2)。0.2.18 那条「界面只有在场位,登录成败不可见」的缺口,
这一版由 ek-ctp 的 `{gw}.status` 补上 —— 规划里的 P3 至此闭合(修 U5 全条、给 F2 界面出口)。
### 状态列改两段口径:装配位 + 登录态(`state::LoginState`)
- **`AccountRow` 新增 `login: LoginState` + `status_note: Option<String>`**,状态列文案由
`status_label()` 拼:`已装配·在线` / `已装配·掉线` / `已装配·登录失败` / `已装配·查询断供` /
`已装配·已断开` / `已装配·登录态未知`。着色:在线绿、掉线与登录失败红、查询断供橙、未知与已断开灰。
- **六档的判据(`AccountRow::apply_status`)按顺序写明**:登录失败 → 登录着(才谈断供)→
主动『断开』→ 柜台掉线 → 未知。
- **`td_handle_up` 真不等于在线**:网关的"句柄已建/registerFront 已发"在掉线时仍是真
(ek-ctp DESIGN D-STATUS ①)。界面只认 `td_logged_in`,没有它就只有「未知」——
这一条有钉(s48 读数二:handle_up 真 + logged_in 假 → 必须显示「掉线」)。
- **`None` 不当 0 用**:网关没回报过资金时刻 ≠ 「0 秒前刚回报过」;登录成功但资金一次
都没到手 = 在线 + 细节「资金回报尚未到手」,不冤枉成断供(轮转第一拍本就要几秒)。
- **纯行情账户看 MD 半边**:该行 td 前置留档为空时,TD 读数永远全假,照抄只会得到「未知」
—— 那是假信息,改看 `md_logged_in`/`md_disconnects`(s53)。
### 轮询(`UiPlugin::refresh` 尾,`STATUS_POLL_TICKS = 2`)
- eTimer 每 1 秒一拍,每 2 拍向每个**已装配且在场**的账户 ask 一次 `{gw}.status`。
- **只问在场的**:实例被摘除后清册行还在,照问不误就是每次等满 2 秒超时、按账户把刷新循环拖住
(钉 c5:未装配的行 `status_note` 必须仍是 `None`)。
- 状态口在 ek-ctp 侧只读、不写日志,所以这条轮询不会冲 eLog 环(512 行环的保护不变)。
- **问不到就退回「未知」并写明原因**(`note_status_unreachable`):上一轮的「在线」不得留档
—— 问不到就是没有证据(s50)。
### 「查询断供」两判据(规划 Q10;阈值住在界面,不住在网关)
- `QUERY_REJECT_STREAK_LIMIT = 3`(连续被拒次数)、`ACCOUNT_STALE_SECS = 30`(资金回报超时秒)。
- 两判据任一命中即「查询断供」;细节行写明是哪一条(`查询连续被拒 3 次` / `资金回报已 60s 未更新`)。
- `set_status_thresholds(limit, secs)` / `status_thresholds()`:覆盖位(测试夹具用它把判据收紧;
后续要进配置文件就接这一位)。
### 『断开』钮(`UiCmd::Disconnect` → `{gw}.disconnect`;规划裁定 1)
- 只对**已装配且会话还活着**的行画这颗钮(条件不成立就不画,不是画出来再禁用;规划 Q21)。
- 回执后徽标「已断开」,**装配态不变**(实例还在,『移除』仍是另一颗钮、另一条口)。
- **恢复入口仍是『连接』**:会话停了但实例在 → 该行重新给『连接』钮 → `UiCmd::Connect` →
svctl 回「已在场」→ 插件侧改走**重投凭据**(不误判成装配失败)。
- 按了钮立刻入列并落一条日志,不等下一轮轮询(`mark_account_stopped`)。
- 未装配的行没有『断开』钮(s52)。
### 状态细节行
有话可说的行(带次数 / 带秒数 / 带原因)在面板底部逐行给一句 `状态细节 <派生名>:<原因>`。
为什么不塞进状态列:Grid 列宽实测会把长文案挤成竖排(s39 实锤)。为什么不只给告警行:
「状态口问不到」与「纯行情账户」也是事实,灰色徽标不给原因用户只能猜。健康行没有细节,
所以这一段不随账户数无谓变长。
### 执行自查(写完这一版自己审出来 3 处,当场修 + 补钉)
1. **『断开』漏了活跃委托守卫**:会话一停就撤不了单、也不再回报 —— 与『移除』要挡的是同一个
观测黑洞(规划 §5.B)。现在两颗钮共用同一道闸:有活跃委托就不画『断开』,出口仍是『先撤 N 笔』
(钉 s54:撤完即恢复,不是一次性禁用)。
2. **实例被摘除后登录态读数留着**:`sync_accounts_connected` 把行打回「未装配」时不清
`login`/`status_note`,而状态细节行只看"有没有细节"不看装配态 → 结果是在一个「未装配」
的账户下面印「掉线 2 次」,替一个已经不存在的会话说话。两处都修(状态侧作废 + 渲染侧只给
已装配的行)(钉 s55)。
3. **状态列文案与 `AccountStage::label()` 重抄了一遍**(`未装配`/`装配中…`/`装配失败` 两处字面量):
`status_label()` 改为非装配段直接走 `stage.label()`,单一事实源。顺带把配置弹窗的
「装配状态(只读)」升级成「装配/登录状态(只读)」——保存前用户就该看见这一行现在是什么状态。
### 测试
- **s37 口径翻转**(原名 `..._exposes_only_presence` → `..._shows_stage_and_login_state`):
0.2.18 钉的是「掉线/断供 各出现 1 次(只在口径句里)」,0.2.19 钉的是「真读到才上屏,
没读到一处都不该有」+「已装配·登录态未知 必须逐行在场」+『断开』钮只给已装配行。
- **新钉 s48–s55**:读数三档各自显形且旧读数不留档 / 断供两判据与阈值覆盖 / 问不到退回未知 /
『断开』入列(且不得顺手发移除令)与徽标翻转 / 未装配无『断开』钮 / 纯行情账户看 MD 半边 /
『断开』共用活跃委托守卫 / 摘除即作废读数。
- **新钉 c4/c5(总线级,`tests/ui_ctp.rs`)**:`UiCmd::Connect` 起真实例 → 服务目录含
`.status`/`.disconnect` → 轮询真拿到 `GatewayStatus`(读数里没有「问不到」= downcast 成功 =
两份 ek-ctp 没有静默断)→ 纯逻辑传输登不上时必须仍是「未知」→『断开』后徽标「已断开」、
实例仍在场、后续轮询不得把它翻回「未知」;c5 钉"未装配的行不被轮询"。
- kittest 钉数 45 → 53;s39/s40/s41 快照基线重生成(`UPDATE_SNAPSHOTS=1`)。
- 门禁:fmt / clippy(默认面 + `ctp-native` 面)0,`cargo test --tests` 全绿(89 例)。
### 未做(写明边界)
- 断供阈值仍是常量 + 覆盖位,**没有进配置文件**(Q10 只说"进配置",界面没有配置文件;
要接的是运维面板一个输入格,或 ek-ui 的可选 `--config`,都还没定)。
- 状态读数**不持久化**(裁定 2:凭据与状态都不落盘);重启后第一拍之前一律「未知」。
- 掉线没有被动事件推送(ek-ctp 侧裁定:只给 ask 口,不发总线事件)——界面靠轮询显形,
最坏 2 秒延迟。
- 运维窗对 `ctp_*` 实例的『移除』与账户面板的『移除』仍是两条路(Q17 边界未收口)。
## 0.2.18(2026-10-21;账户配置重构:四态分离 + 列表与配置弹窗;执行核查 5 处修正)
按 `docs/ek-ui-account-tabs-plan.md`(裁定 0:不做 tab-per-account,做「列表 + 配置弹窗」)
分两个 commit 落地,再做一轮执行核查(自查抓到 5 处问题,当场修 + 补钉 s46/s47)。
### Commit A(数据模型:添加 ≠ 连接 ≠ 断开 ≠ 移除,规划 §3.3)
- **`UiCmd::Connect { gateway, form }` 新增**:一条令做完 `svctl.start ctp <派生名>` +
`{gw}.configure`(插件侧顺序执行)。旧 `UiCmd::Start` / `UiCmd::CtpConfigure` 保留为低层口
(运维窗重注入仍走 `Start`),但**账户面板不再下发两令**。
- **『添加账户』只写清册,不下发任何指令**(旧口径:点添加 = 立刻 start + configure)。
行内『连接 <显示名>』/『重试 <显示名>』才是上线入口。
- **`AccountStage` 四态**:未装配 / 装配中… / 已装配·登录态未知 / 装配失败:<原因>。
状态列只说**实例装配**(svctl 在场位),登录成败、掉线、查询断供界面不可见 ——
面板底部一句口径说明点名这一缺口(开放项 `ctp.status`,规划裁定 3:等终态口,不自建过渡)。
- **装配失败不回滚清册行**:留红字原因 + 给『重试』(修 U3)。
- **每账户独立 form**(修 U4):『添加』草稿位凭据清空(账号/密码/前置),broker/AppID/AuthCode
沿用;纯 TD 账户的 md 位在**留档与下发同一份**清空(旧口径只清下发,编辑回填带着一根用不上的前置)。
- **取消编辑清空凭据**(修 U6);**`CtpForm::validate` 前置形制校验**(修 U7):
`tcp://host:port` + 端口 1..=65535;td 前置必填,**md 前置允许为空**(纯 TD 账户,单源纪律)。
- **`default_gateway()` 与下单下拉只认已装配账户**(规划 Q4):只添加未连接的账户
不进下拉、不点亮委托钮。
- **软上限 `MAX_ACCOUNTS = 8`** + **别名唯一**(规划 Q3);`ConfigureReply.product_classes`
回带留档(规划 U9 前置)。
- 钉口径重排:s13 / s31 / s32 / s33(原 s33 改名 `…_leaves_row_and_allows_retry`)/ s34 / s35 /
s36 / s37 全部按新口径重写;s39 快照重生成。
### Commit B(界面形态:7 列一览 + 非 modal 配置弹窗)
- **账户面板 = 一览**:7 列 `egui::Grid`(网关(派生名) / 别名 / 账号 / 状态 / 行情源 /
收录(勾→实) / 操作),列表上方一行「当前行情源 = …」「账户 n/8」「添加账户」入口;
达软上限后入口撤场并给提示句(不给宿主侧注定被拒的请求)。
- **配置表单从窗内内嵌改为独立非 modal 弹窗**(`account_edit_win`,680×340):
添加 / 编辑两模式;钮 = 『保存』/『保存并连接』/『取消』。
关闭或『取消』= **放弃未保存**(规划 Q24);校验被拦 = **不关弹窗** + 红字『拦下:原因』+ 日志(规划 Q26,可就地改)。
- **编辑模式 broker/账号只读** + 橙字口径句「派生注册名 = …(由 broker + 账号决定):要改这两格请『移除后重加』」(规划 Q25);
**非行情源且已锁定 → md 三位置灰** + 口径句(规划 Q12)。
- **收录列并排显示勾选意图与网关回带事实**(`勾:期货` / `实:期货+期权`),弹窗内另有只读回带行
「网关实际收录(只读回带)…装配状态(只读)…」(规划 U9)。
- **移除守卫给出口**:活跃委托 >0 时不再只堵门,给『先撤 N 笔』按钮逐笔入列 `CancelOrder`
(网关 = 该账户自己的派生名,与「全撤」同一条 oms 通道)(规划 U10)。
- **移除 / 确认移除 / 待二次确认句统一取显示名**(别名优先,空则账号)——
顺带修掉一个 Commit B 自造坑:`pending_confirm` 记槽按派生名、按钮文案按显示名,
两者对不上导致首点后按钮永远翻不到『确认移除』(s31/s38 实锤)。
- **账本残留行加『(已摘除)』标注**(修 U8):委托/成交/持仓表的「接口」列,清册非空且该网关不在清册
= 已摘除账户的残留行(摘除不清账本 = F5 实连复测口径)。口径边界:清册为空不标注;资金格不标注。
- **`push_connect` 入列即置「装配中」**(规划 Q7 界面侧一半):否则入列到出队之间行上钮还亮 = 可重复入列。
- `examples/opening_multi_tour.rs` 改走 `UiCmd::Connect`(单源裁决移到插件侧 `prepare_connect_form`)。
- 新增钉 s40–s45(添加弹窗快照 / 编辑弹窗快照含只读置灰与回带 / 别名上列表但不参与寻址 /
软上限撤钮 / 『保存并连接』一条令 / 『先撤 N 笔』逐笔入列)+ s39 重生成 + s40/s41 新快照。
### 执行核查(5 处,当场修 + 补钉 s46/s47)
1. **摘除 × 非 modal 弹窗的连带口径**(真坑,可致错配账户):弹窗开着时可以点同一行『移除』——
编辑目标消失后弹窗会**静默退化成「添加模式」**(只读句消失、字段全可编辑),点『保存』会拿旧凭据
建出一个新账户。修:摘除连带 `end_account_edit_if_editing`;『保存』前检查编辑目标仍在清册;
`begin_account_edit(Some(gw))` 对不在册的 gw 直接拒。钉 s46。
2. **下单下拉补堵已摘除网关**:清册非空时,不在清册的历史网关名(= 已摘除账户)不再进下拉;
表单里「选过它」的陈旧选择就地回落默认阶梯并落盘。否则下一笔单注定撞 `unknown:gateway`。钉 s47。
3. **同一行四个钮口径统一**:『连接/重试』也取显示名(此前只有『编辑/移除』取显示名)。
4. **撤除多余代码**:`account_grid` 行选记槽(`select_row`)是内嵌表单时代遗留,已无读者。
5. **文案口径漂移**:state.rs 文档写「实例已装配(登录态未知)」而实现是「已装配·登录态未知」
(短形是被 `egui::Grid` 列宽夹出来的 —— s39 快照实锤过长文案会竖排折字)。
### 破坏性变更 / 已知边界
- 账户面板布局与钮文案变化:`移除 <派生名>` → `移除 <显示名>`;『编辑』不再就地切换共享表单,而是开弹窗。
- `UiCmd::Connect` 是账户上线的唯一界面入口;`UiCmd::CtpConfigure` 仍可被外部宿主直接调用(低层口)。
- 仍不可见(等 `ctp.status`,规划 P3):登录成败、掉线、查询断供的逐行状态。
- 仍不做:账户持久化(规划裁定 2)、多 broker 行情源、勾选面(product_classes)多账户语义(F4)。
- kittest 查询面只有 label 口径 → **置灰/只读无法按控件断言**,只能钉口径句 + 快照(s35/s41 注释已记)。
## 0.2.17(2026-10-21;账户面板布局两处硬伤修复 + 账户配置界面核查钉 s33–s39)
- **修① 账户表与账户表单互相压字(整窗不可读)。** 账户清单原先走 `pinned_table`
= `ScrollArea::both().auto_shrink([false,false])`(吃掉整块可用高)+ 覆盖式表头带
(`render.rs:pinned_table`),而账户面板是**自动尺寸浮动窗** —— 两者凑不出正确高度:
账户行与其后的「行情源已锁定…」口径句、整张 9 输入位表单叠在同一像素上,文字互相覆盖
(前后对照见视觉快照钉 `s39_account_panel_layout`)。账户行最多个位数,不需要固定表头滚动面
→ 改普通 `egui::Grid`。⚠️ `account_grid` 不再是 ScrollArea(行选择/编辑钮口径不变)。
- **修② 前置输入位看不见自己填的内容。** 表单原先是 6 列 `Grid`:列宽在测量期被
「窗宽 ÷ 列数」夹死(实测每格 ≈120px),`desired_width` 永不生效 → 一根真实前置
`tcp://182.254.242.81:30001`(26 字符)只显示出 `tcp:/`,端口看不见也没法核对。
改为三行 `ui.horizontal` + `desired_width(210)` → 前置与端口完整可见。
- **新增现状钉 s33–s38(账户配置界面六条问题,每条都写明待修口径):**
s33 装配失败(svctl 快照无此名)→ 行留清册、界面零显形、再点添加被『已存在』拒(须先移除);
s34 共享表单串味:添加后不清空,第二账户继承上一家密码/前置,且**纯 TD 装配只清下发的那份
`md_addr`,清册留档没清**(编辑页回填带行情前置);
s35 取消编辑只解除选中不清空表单(此时改一格即建出凭据错配账户);
s36 前置为空照样建账户并下发 Start+Configure(界面不拦,只提示 md 空);
s37 面板状态列只有『已装配/未装配』(= svctl 在场位),登录成败与掉线**无任何可见位**
(实连复测 F2 那种「在线但查询整段断供」在面板上与正常账户一模一样;开放项 `ctp.status`);
s38 摘除账户后该账户的委托/持仓行仍在账本,界面无『该账户已摘除』标注。
- **新增视觉快照钉 s39**(`tests/snapshots/s39_account_panel_layout.png`):两账户 +
真形制前置一屏,布局回归闸(与实窗同字体面,BD-UI-26)。
- 门禁:fmt 0 / clippy `--all-targets --features ctp-native` 0 / 全测试绿(kittest 37 钉)。
## 0.2.16(2026-10-21;随 ek-oms 0.3.5 / ek-station 0.2.1 整链抬版:撤单归属守卫)
- **依赖抬位:`ek-oms` 0.3.4 → 0.3.5、`ek-station` 0.2.0 → 0.2.1。** 界面代码零改动。
- **为什么界面不用改:** 账户面板的「全撤」与单行撤都用**每笔单自己的网关**
(`UiCmd::CancelOrder { orderid, gateway }` → `en_orderid = {gateway}.{orderid}`),
与 `OrderData::en_orderid()` 同式 → 天然满足 ek-oms 0.3.5 的归属守卫。
- **抬版带来的实际变化:** 若界面与 oms 的 `en_orderid` 组式哪天不同式(或账户切换后残留旧网关),
现在会在 oms 显形成 `mismatch:gateway` 被拒并落日志,而不是交付到错误网关后在柜台沉默。
- 门禁:fmt 0 / clippy `--all-targets --features ctp-native` 0 / kittest 全绿(含 s30「全撤随单走」)。
## 0.2.15(2026-10-21;多账号面:账户面板;DESIGN 新条 BD-UI-39)
- **菜单「网关」→「账户」**:旧单账号 CTP 配置窗退役,收纳为账户面板的添加/编辑表单
(9 输入位 + 密码掩码 + 收录勾选原样沿用)。旧窗死面一并删:`UiShared.ctp_window`
状态位与 `ctp_window_open/set_ctp_window_open`、`render::MENU_CTP`、
`render::BTN_CTP_CONNECT`(不留"供旧测试引用"的死常量)。⚠️ 公共 API 减项。
- **`UiCmd` 三处形制变更**:⚠️ 破坏性(公共枚举):`Start { factory, name }`(新增 `name`,
空 = 旧行为)、`CtpConfigure { gateway, form }`(原单元变体)、
`CtpSubscribe { gateway, symbols }`(新增 `gateway`)。服务名随网关走
`{gw}.configure` / `{gw}.subscribe`,不再硬编码 `ctp`。
- **`parse_gateway(raw, fallback)`**:空位不再兜底成字面量 `"ctp"`,改落显式阶梯
`default_gateway()`(上次选择 → 首个已装配 → 首个);无账户 = `Err` 且「委托」钮禁用。
- **新账户清册面**:`AccountRow`(派生名 + broker + 账号 + 在场位 + 元账号标记 + 表单留档)
与 `UiShared` 的 `accounts/account/add_account/update_account_form/remove_account/
sync_accounts_connected/md_source/accounts_locked/set_md_source/subscriptions/
note_subscriptions/default_gateway`。派生名唯一入口 = `ek_ctp::derived_gateway_name`。
- **单源纪律(§5.C)**:添加时定源 —— 无源且无在场账户则新账户自动成为行情元账号
(md 前置保留),否则按纯 TD 装配(md 前置清空);订阅两入口一律经 `subscribe_target`
路由到元账号,未指定明确报错。元账号新装配后自动重放订阅集。
- **元账号连接后锁定(用户裁定)**:任何账户已装配即拒切换(`set_md_source` 回 Err)。
- **移除守卫**:有活跃委托不给移除钮(提示"先撤单才能移除");有持仓可移除但警告;
两步确认沿用 `confirm_step`。
- **撤单随单走**:行内钮 / 全撤 / 行双击三处都跟该单的 `gateway_name`,空串落阶梯;
持仓行双击把 `gateway_name` 带入下单面板(旧实现不带,平哪条腿由上一单残留决定)。
- **行情带列名**「接口」→「行情源」(公共数据只来自元账号,身份列说清)。
- 测试:s13 重写、s17/s18/s20 改、新增 s28(锁定 + 移除守卫);五张快照重基线
(s3/s10/s16/s21/s27);`examples/demo_tour.rs` 改走账户面板流程。
## 0.2.13(2026-10-20;时间列/横轴北京钟面;可独立发布)
- **`fmt_dt` 由 UTC 直显改为北京时间 +08**(表内时间列统一口径):数据面
时间戳统一存 UTC 纳秒 —— CTP 回报的自然日本就是北京钟面,展示面换回
+08,交易员看到的不再差 8 小时(旧实现会把 09:30:01 的成交显示成 01:30:01,
夜盘跨日更乱)。换算用固定偏移,不依赖本机时区设置。
- **K线窗横轴同口径**:egui_charts 0.2 的 `Bar` 只吃 `DateTime<Utc>` 且按
UTC 钟面画刻度 —— 用「换到 +08 再标回 UTC」的重标定喂柱,刻度显示北京
钟面(只挪刻度不挪序)。
- 新增单测钉 `render::tests::fmt_dt_shows_beijing_wall_clock`(含跨日钉:
北京 00:00:30 = UTC 前日 16:00:30,旧实现会把这根显示成 16:00:30)。
- 依赖零改动(ek-ctp 0.6.0 照常工作);随 ek-ctp 0.7.0 的整链抬版另起
0.2.14(见下条)。
## 0.2.14(2026-10-20;随 ek-ctp 0.7.0 整链抬版;装配代码零改动)
- **依赖:`ek-ctp` 0.6.0 → 0.7.0、`ek-oms` 0.3.2 → 0.3.3、`ek-station`
0.1.13 → 0.1.14。** 本 crate 直接 ask 网关(0.2.12 口径),downcast 纪律与
0.2.12 条目同 —— 三处必须同一份 ek-ctp。抬版动作在本次发布波次一并提交。
- **demo_tour 叙事对 0.7.0 校正**:步4.5a/4.5b 的「重传压在驱动缓存里、等合约
齐才回放」改为「重传直接上账,收尾后账本只增不减」—— 缓存回放面已退役。
## 0.2.12(2026-10-20;网关「收录勾选」上界面;整链抬版随 ek-ctp 0.6.0)
- **网关窗新增「收录勾选」:期货 `'1'` / 期权 `'2'` 两个 checkbox**(ek-ctp 0.6.0
D-PC-FILTER 的界面面)。缺省只勾期货 —— 与 0.2.11 的既有口径一致(网关只上抛期货)。
- **`CtpForm` 新增 `accept_futures` / `accept_options` + 手写 `Default`**:不能 derive,
derive 会给 `accept_futures = false`,而两个都 false 在语义上是「什么都不收录」。
`product_classes()` 把勾选译成 CTP 码表喂 `ConfigureReq.product_classes`;两个都不勾
交空表,插件侧归一化一律回落默认期货(**空 ≠ 全丢**)。
- **勾选变更即回写 `shared`**(与 `ctp_edit` 同一口径):网关窗每帧从 `shared.ctp_form()`
重读表单,不回写则这次点击下一帧就蒸发。
- **`ConfigureReply` 模式补 `..`**(0.6.0 起带 `product_classes` = 实际生效值)。刻意
**不**用它回填表单勾选:被拒时回带的是「网关当前仍在跑」的那一份,拿它覆盖用户刚点的
勾选会把他的意图吃掉。
- **依赖整链抬版:`ek-ctp` 0.6.0 / `ek-oms` 0.3.2 / `ek-station` 0.1.13。** 本 crate 直接
ask 网关(载荷类型来自 ek-ctp),网关却由 station 工厂装配 —— 三处必须链到同一份 ek-ctp。
总线载荷是进程内 `Arc<dyn Any>` downcast,不是序列化:两份 ek-ctp 的同名类型
`type_name` 字符串相同(`ask_service` 守卫放行)而 TypeId 不同(网关 `on_request` 的 arm
downcast 失败、不回复、超时)。**编译期完全不报错,坏在运行时**;完整实测记录见
ek-oms 0.3.2 / ek-station 0.1.13 条目。
- **新钉 `c3_ctp_form_product_classes_reach_gateway`**:缺省勾选回显 `收录产品类:1`,
勾上期权后回显 `收录产品类:1 2`。这条同时是「进程里两份 ek-ctp」的哨兵 —— 不同源时
日志出现的是 `ctp.configure 失败` 而不是「受理」。
- **破坏性:`CtpForm` 加两个公开字段** —— 用完整字面量构造 `CtpForm` 的调用方需补字段或
改 `..Default::default()`。仓内三处已改(`examples/demo_tour.rs` 两处、
`tests/ui_kittest.rs` 一处);`tests/ui_ctp.rs` 原本就写 `..CtpForm::default()`,未受影响。
## 0.2.11(2026-10-19;纯文档修正;crates.io + cnb tag v0.2.11)
- render.rs 两处模块注释回正(执行后自审抓出):运维窗描述仍写「流控查询」
钮(该钮已随 0.2.10 sim 退场撤除)→ 改历史注。代码行为零变动。
## 0.2.10(2026-10-19;BD-UI-38 纸面网关退场;crates.io + cnb tag v0.2.10)
- **sim 纸面网关整体退场(用户裁定)**:纸面账户功能全撤,ctp/SimNow 两套前置
(12 小时/7×24)满足需求;选前置探测用 ek-ctp `scripts/simnow-fronts.ps1 -Probe`。
依赖 ek-paper 撤除;ek-station 依赖 bump 0.1.8→0.1.12(其工厂表已无 sim)。
- **界面清 sim**:接口下拉 ∪ 集改 {ctp};`parse_gateway` 空兜底 sim→ctp;
运维窗「流控查询」钮撤(`UiCmd::QueryRate` 公共变体同步撤——API 减项);
demo 窗改 dm/oms/ui(网关经运维窗 svctl 激活);demo_tour 纸面段整段删,
实连段重编为步 5/6/7。
- **测试面**:ui_sim.rs 整删(9 钉);ui_converter_chain 网关位改测试本地
"ctp" 受理夹具(受理形制忠实 ek-ctp,成交按 CTP 回报行直发注入;oms 管线
钉数值零变);kittest/market 夹具标签 sim→ctp(委托号 SIM_*→CTP_*,
行键 `ctp.*`);s17 断言「流控查询」改运维表头证。
- **快照重基线五张**(s3/s10/s16/s21/s27):钮撤除 + 夹具标签改名的像素变,
`UPDATE_SNAPSHOTS=true` 重出;`.old.png` 备份即删。
- **kittest 环境稳健钉**:本机后装的 "GameViewer Virtual Display Adapter" 劫持
wgpu 缺省择优选中(Dx12/DX11 纹理回读 BufferAsyncError,单测串行也复现;
Vulkan/GL 实测好)——snap() 头部 Windows 缺省钉 `WGPU_BACKEND=VULKAN`
(OnceLock 单次写,先于 LazyRenderer 建实例;用户已设则尊重)。
- 62 测试全绿(71−9);clippy -D warnings + doc 净。
- 0.2.9 及更早条目为史实保留(BD-UI-36 迁出记录不动)。
## 0.2.9(2026-10-19;BD-UI-36 纸面网关迁出;crates.io + cnb tag v0.2.9)
- **sim 纸面网关迁出到独立网关包 ek-paper 0.1.0(用户裁定)**:网关的【身份是
网关】,对应 Python app/paperaccount(PAPER 网关,与 CTP 同级独立 app)——
界面包不再藏网关。src/sim.rs 删除,语义零迁移(449↔449 行逐行比对,唯一
差异 = Arc 写法面);注册名保持 "sim"(UI 下拉/demo/测试钉零涟漪)。
- 引用面改写:runtime 工厂/pick 表/QueryRate ask 类型/main demo/tests
(ui_sim、ui_converter_chain)全部改引 ek_paper::;ui_kinds 与 pick_kinds
撤自建 sim 项(防与 station 工厂双份)。
- 依赖:新增 ek-paper "0.1.0";ek-station 0.1.7→0.1.8(station 工厂表登记
"sim" ——无头宿主可起纸面流,SimNow 登不上时切换的本地模拟环境)。
- 71 测试全绿零减钉(ui_sim 9 钉改 import 不改断言)。
## 0.2.8(2026-10-19;BD-UI-35 用户实看三报;crates.io + cnb tag v0.2.8)
- **行情带状态行撤除(用户裁定「tick N 格 / 最后更新…早已去掉,你又加回」):**
考证:该状态行 0.2.0~0.2.7 从未删过(0.2.0 撤的是「行情」题字)。本轮按裁定
删除,s8 改反向钉(此文字不得回归)。
- **表头上方缝隙硬根因(egui 源码实锤):** `Visuals::clip_rect_margin` 默认
3.0(style.rs:1435),ScrollArea 内容裁剪 = inner_rect 向外扩该余量
(scroll_area.rs:771-772)——覆盖带旧版从 inner_rect 顶边起画,滚动行从带
上方 3px 余量露出。修 = 带向上/左右扩 clip_rect_margin,向下盖表头格高 +
行间距;clip 同步为带矩形。新钉 s27:滚轮下滚委托表后出快照
s27_scrolled_orders(像素扫描口径:表头文字上方带 x450..790 内
faint_bg(16,21,30) 条纹行数=0、无数据文字像素;rb0/rb1 已滚走、rb2 起)。
- **空态提示移入表格 body(用户裁定「应在表头下面,或干脆不显示」):**
委托/未成交/成交/持仓/查询合约的提示 label 移进 grid 首格 = 表头下第一行;
行情带空态(无表格)保留原样(有意例外)。
- **本轮自审抓出(未出闸即修):** 成交/持仓空态分支表头又凭记忆写错
(「成交时间/来源」vs 真实「成交额/时间」)——委托/成交/持仓/合约四表列头
全部提为函数内 const 单源(ORDER_HEADERS/TRADE_HEADERS/POS_HEADERS/
CONTRACT_HEADERS),空/非空分支同源,杜绝手写第二套。
- **测试闸加固:** ①快照回读全局串行(SNAP_LOCK,std Mutex 零新依赖):本机多
wgpu 设备并发 map_async 回读 BufferAsyncError(5 快照并发随机成组挂,
串行后连续三轮 25/25 绿)。②s27 补 apply_cjk_fonts 闸(初版漏装字体,
快照豆腐块)。
- 全组 71 绿;s3/s10/s16/s21/s27 快照按修复后重录。
## 0.2.7(2026-10-19;0.2.6 自审纠错 + 仓库卫生;crates.io + cnb tag v0.2.7)
- **纠错(0.2.6 自审抓出):** 查询合约窗空态分支表头误写另一套名
(「代码/名称/交易所/乘数/最后价」),与该表真实列头(「选(代码)/交易所/
名称/乘数/最小变动」)不一致——空表时表头与有数据时不同、且「最后价」
根本不是本表列。修 = 表头提为函数内 const 单源,空/非空两分支同源。
- **仓库卫生:** 0.2.6 误提交 7 个二分实验裁切图(.audit_*.png)已删,
gitignore 补 `.audit_*.png` 防再犯。
- README 补记 0.2.5/0.2.6 特性段(表头固定/四主题/K线最大化+ESC)——
0.2.5 时漏记。
- 自审其余结论:0.2.6 的 8 个代码 hunk 全在预定区域;测试只增不删;
行情带空态(非格表,外层 ScrollArea 保高)为有意例外,不改。
## 0.2.6(2026-10-19;表头固定三处返修,BD-UI-34 用户实看回报;crates.io + cnb tag v0.2.6)
- **竖线根因(用户报「资金/日志右边多一根竖线」):** 表头覆盖带旧版用
`ui.put` 重画全部表头格——视口外格(持仓 16 列总宽撑到 x=1222)把覆盖带
子 UI 的布局 min_rect 撑宽 → Frame 描边矩形跟着撑 → 格右边界画在行中段。
二分实验实锤:关覆盖带线即消失;换空内容线跟内容宽走(1007)。修 =
覆盖带改纯 `painter.text/rect_filled`(零布局副作用,越界文字由 clip 裁)。
资金格同时恢复带填充 ScrollArea 的 group_frame(plain Frame 会收缩到内容
宽,右边框又落行中段)。
- **表头缝露内容(用户报「滚动时表里的东西在表头缝里露一点点」):** 覆盖带
旧高 = 表头格高,未盖 grid 行间距带(item_spacing 3px)→ 滚动行从缝里露。
修 = 带高 = 表头格高 + 行间距。
- **空表缩格(用户报「未成交没东西,表格缩得很小」):** 委托/成交/持仓/查询
合约空态旧版提前 return,pinned_table 未调用。修 = 空态照常走 pinned_table
(表头 + 吃满滚动区在场,只缺数据行)。新钉 s26。
- 快照按修复后重录;s21 全量表快照即「竖线回归」的像素闸。全组 70 绿。
## 0.2.5(2026-10-19;表头固定 + 四套主题 + K线最大化/ESC,BD-UI-34;crates.io + cnb tag v0.2.5)
- **表头固定(`pinned_table`,五张表全上:行情带/委托/成交/持仓/查询合约):**
官方 DIY 模式(scroll_area.rs:412「painting a sticky header on top of
it」)——单 grid 实例保证列对齐,ScrollArea 之后用本拍捕获的表头格矩形在
滚动区顶部重画表头带(背景盖行 + Label 重画;格矩形已含滚动平移 → 横向
自动跟随、纵向永远盖顶、零拍滞后;覆盖带只画无交互 Label → 点击穿透)。
两轮机制实锤排除歧义:vertical 嵌 horizontal 高度塌缩(s3 底行钉实测
462<500)、同 id 双 grid State 互写 repaint 环(kittest max_steps 爆)。
- **四套主题(用户问「PySide6 式可换的金融向主题」):** 金融深色(默认,
深海军蓝交易终端观感)/egui深色/egui浅色/Qt灰白(PySide6 Fusion 迁移
用)。菜单钮一键循环(单层无下拉);运行期态(AtomicU8),无配置文件。
语义色(多红/空绿/隔离红/选中蓝)跨主题恒定——金融语义不随主题漂移。
现状说明:此前 = egui 默认深色骨架 + 自配语义色,并非全默认。
- **K线窗:** 打开即最大化(ViewportBuilder::with_maximized)+ ESC 键快捷
关闭(body 请求关闭 → 同一 close_kline 链;键按子窗焦点路由,主窗不误关)。
- **新钉:** r9 主题循环/默认(ui_state);s25 ESC 关 K线(Embedded 面注入
Event::Key)+ builder.maximized=Some(true)(Immediate 面 recording
renderer)。快照按新默认主题重录。全组 69 绿。
- **内部:** show_independent_window 几何参数收敛 WinGeom(clippy
too_many_arguments);行情带外层 ScrollArea 保留(resizable 面板吃满前提,
s3 实锤撤掉即塌)。
## 0.2.4(2026-10-19;四窗一型独立化 + 合约表吃满窗口,BD-UI-33;crates.io + cnb tag v0.2.4)
- **运维窗 + CTP 网关窗转独立 OS 窗(用户裁定「四窗皆可脱离主窗」):** 与
查询合约/K线同形态——原生最小化/最大化/关闭、可拖出主窗放屏幕任意处、
独立任务栏;Embedded 回退(kittest/WEB)维持主窗内弹窗同本体,测试面零改动。
- **查询合约表吃满窗口(用户实看指出):** 撤 `.max_height(260)`(内嵌弹窗
时代残留——OS 窗里表格定高 260px,放大窗口表格不跟着长);动作行(已选
计数 + 订阅钮)移到表格前,表格 ScrollArea 吃满剩余窗高:放大窗口 = 表格
变高,行溢出才出滚动条。
- **四窗一型(`show_independent_window` 助手):** 守卫(0.2.3 教训)内建于
调用方模式——位关不 show = GC 销毁;Embedded 回退返回关闭态;Immediate
面 close_requested 返回关闭态。运维/CTP/查询合约/K线四个调用点同构,
消除四份重复的 viewport 样板。
- 全组 67 绿 + clippy/doc/fmt 净;快照零变化(回退面几何保持)。
## 0.2.3(2026-10-19;查询合约窗「关不掉」根因修复,BD-UI-32 勘误;crates.io + cnb tag v0.2.3)
- **症状(用户实看):** 独立查询合约窗点 X 无反应,永远关不掉(K 线窗正常)。
- **根因(定位经源码实证,非猜测):** egui 的子 viewport 销毁判据 = 本拍未
used(context.rs end_pass「never used this pass」)。0.2.2 的调用点没按
`contracts_window_open()` 守卫——`show_viewport_immediate` 每拍无条件调用
= 每拍 used = egui/eframe 永不销毁 OS 窗;cb 虽正确翻负状态位,下一拍又被
show 回来(等效 CancelClose)。附带病灶:窗在主窗一开就自己出现(不管状态位)。
K 线窗无此病(按 kline_symbols 列表循环,出列表 = 不再 show = 销毁)。
- **修复:** 查询合约窗整段 show 包进状态位守卫;位关 → 不 show → GC 销毁。
- **测试勘误(为什么 21 绿没拦住):** kittest 走 Embedded 回退,`.open` 旗标
本就正确——无覆盖的恰是 Immediate 真窗面。新钉 s24:注入 recording
renderer(set_immediate_viewport_renderer,模拟多窗后端)+ embed=false,
三拍增量断言(位关零 show / 位开有 show / 翻负零 show)——该钉打在 0.2.2
代码上必红。
- **审计勘误:** 0.2.2 复审核穿了 eframe/egui 机制链但漏核自家调用点的守卫
——机制正确 ≠ 用法正确;复审清单补一条「新机制的每个调用点按状态位辞行」。
## 0.2.2(2026-10-19;字号与独立窗轮,BD-UI-32;crates.io + cnb tag v0.2.2)
- **全局字号上调(`apply_ui_style` 新口,用户实看裁定):** body/button 16px、
small 14px、heading 22px、monospace 15px(egui 缺省 13/12/18/12),
render_body 顶部统一施加,真实 App 与 kittest 同源。
- **查询合约窗 + K线窗 = 独立 OS 窗(用户裁定,Python 独立窗形态):**
`show_viewport_immediate`(eframe immediate viewport)——原生最小化/最大化/
关闭钮自带、可拖出主窗放屏幕任意处、独立任务栏;OS 关闭钮 = 出表
(close_requested → 状态位翻负,父窗 ≤1s 辞行销毁)。后端不支持多窗
(kittest/WEB)时 egui 自动回退 Embedded = 主窗内弹窗,同一本体——
kittest 断言面零改动。运维窗/CTP 窗维持主窗内弹窗(用户未点)。
- 快照重基线(字号);全组 66 绿 + clippy/doc 净。
## 0.2.1(2026-10-19;行交互归行扩展,BD-UI-31 续;crates.io + cnb tag v0.2.1)
- **动作归行不归列(用户裁定):** 委托/未成交/成交/持仓/查询合约窗/行情带 ——
行内**任何一格**单击都触发该行动作(选中/带入/勾选联动),双击同理
(撤单/带入/K线);标签外观不变(`click_cell` 在标签矩形上叠点击感应)。
- **行情带盘口四列带方向(`import_tick` 新口):** 买1价/买1量 → 方向卖,
卖1价/卖1量 → 方向买(吃对手挂价口径);两个价格列顺带把该价填进价格格;
其余列只带入合约+交易所,方向/价格保持表单默认(用户裁定)。
- 新钉 r8(import_tick 逐列口径五段断言);全组 66 绿 + clippy 净;快照不变
(外观未动)。
## 0.2.0(2026-10-19;界面体验轮,BD-UI-31;crates.io + cnb tag v0.2.0)
- **菜单一键直达(R1):** 「运维/网关/查询合约」从 menu_button→下拉→入口钮
两步,改为直接按钮单击弹窗(去下拉层);「帮助」保留菜单(多行内容)。
**破坏性:** pub 常量 `BTN_OPEN_OPS/BTN_OPEN_CTP/BTN_OPEN_CONTRACTS` 删除
(入口钮标签 = 菜单名本身)。
- **成交表方向上色(R2):** 方向列空绿/多红,与委托/持仓表同口径。
- **表格行选中 + 悬停跳色(R3):** 委托/未成交/成交/持仓/查询合约窗行首格
单击 = 选中行(蓝调跳底,每表一行,换行即换选,`UiShared::row_selected/
select_row` 新口);行情带选中行 = 下单面板当前合约(带入即跳色,不另存态);
全表悬停 = 淡灰底(`paint_row_bg` 行格并集半透明叠底)。行首格既有双击
动作(撤单/带入/K线)保留——双击先经 clicked,选中顺带发生,无冲突。
- **标题文字撤除(R4):** 「行情带(唯一源 = eTick. 订阅流)」「下单面板」
「盘口(合约)」三处题字整行删(带位/侧栏即身份,Python 无题同口径;
盘口括号合约代码一并删——选中合约由下单面板代码格与行情带跳色行指认)。
- 新增 kittest s23(行选中钉:委托换选 + 成交独立槽);s17 改单击直达形态;
快照重基线(菜单/去题)。全组 65 绿 + clippy 净。
## 0.1.1(2026-10-19;OMS 中枢链薄下单者,BD-UI-30;crates.io + cnb tag v0.1.1)
- **依赖 bump(强制):** `ek-oms 0.2.1 → 0.3.1`、`ek-station 0.1.5 → 0.1.7`
(station 工厂已装配 ek-risk 三闸——面板与 headless 同一装配口径)。
- **薄下单者(BD-UI-30,核心):** 下单/撤单直发 `{gw}.send_order` /
`{gw}.cancel_order` 旁路口**删除**——`UiCmd::SendOrder` 走
`oms.submit_order` 五段管线(拆单/风控/逐腿发送/记账,parent 聚合逐腿显形
进日志),`UiCmd::CancelOrder` 走 `oms.cancel_order`(守卫 4000 不可绕过;
全撤 = 界面侧逐个扇出到同一守卫口)。symbol/exchange 不再随撤单令携带
(oms 订单缓存解)。
- **「自动开平」勾选(OrderForm.auto_split,默认开):** 开 = `LockMode::Auto`
走拆单器;关 = `LockMode::None` 原样旁路(逐请求;守卫不可旁)。
kittest s22 钉勾选双向翻转 → mode 逐位。
- **ui_converter_chain 升格:** fixture 退役(dev-dep ek-converter 删除)——
真管线链(sim+dm+oms 真装配):Auto 平 3 拆 2 腿今先 → 冻结钉 → tick 撮合
→ 账本清零 + parent reference 钉;None 单腿旁路钉;ek-risk 手数闸 parent 级
零发送链钉。顺带实锤忠实初始化口径(无成交史首轮 ePositionDone. 只记时刻,
第二轮 ≥3s 才置位,原稿 1282-1285)。
- **ui_ctp_live 第 13 步:** 管线平仓钉(有仓则 CLOSE 经管线 = CLOSETODAY
实连证据;7×24 快照市无成交 = OBSERVE 如实记档)。实连 13 步全收场
(撤单守卫面 110s PASS)。
- 快照四张更新(下单面板多勾选);ui_sim 断言随日志新形(parent 聚合)回正。
## 0.1.0(已发布 crates.io 2026-10-18;布局 v5 面板化重构 + T8 集成钉)
- **converter 全链合体钉(T8,`tests/ui_converter_chain.rs`,默认门禁零网络)**:
dev-dep ek-converter 0.1.1(装版引用);真宿主 sim+dm+oms+offset 夹具(N2 形态)
——账本初始化(空 5=今 2 昨 3)→ Auto 平 3 → SHFE 今昨拆 2 腿今先
(CLOSETODAY×2 → CLOSEYESTERDAY×1)→ 两腿入 sim+oms → tick 撮合 ALLTRADED
traded 2/1 → 账本回流 pos=2 yd=2 td=0 冻结清零。运行时语义零改动(converter
以测试夹具进链,接线时机属设计裁定)。顺带实锤 `en_symbol` 口径 = 交易所
前缀在前(`SHFE.sn2709`,vt_symbol 真源)。
- **面板化重构(BD-UI-29,render_body 改收 `&egui::Context`)**:实窗 1280
演示暴露:右列裁切(定宽 250+140+480+480 ≈ 1438 > 1280 且无横向滚动 →
裁掉不可达)、底部死区 ~300px(定高 250 实渲 ~85px)、无 splitter 语义。
官方面板组合落地:TopBottomPanel(菜单)→ TopBottomPanel(行情带 240,
resizable = Python 分带 splitter)→ SidePanel::left(下单面板 + 盘口纵排,
250, resizable)→ CentralPanel(2×2 监控格,最后)→ 弹窗(面板之后)。
- **机制钉(dbg 实锤)**:horizontal 行成员可用高度初始 = interact_size,高度
分配不沿横向传播 → 格内定高 max_height 失效;修复 = `allocate_exact_size`
预留 (可用-间距)/2 矩形 + `new_child` 内开 Frame+ScrollArea(`add_sized`
收 impl Widget 不适用,已试撤)。
- **BD-UI-28⑦「下单面板帧不动」被取代**:侧栏内容天然纵向,err label 改由
ScrollArea 吸收 sizing 退让(BD-UI-27 口径不变)。
- **测试面**:s3 加结构钉(右列贴边不裁切/底行沉底无死区/侧栏在左/两列等宽
随窗分配);kittest 建构 `build_ui` → `build`(ctx 级);快照 s3/s10/s16/s21
重基线。kittest 19 钉 + 全量 60 例绿(例数不变)。
## 0.1.0(进行中;第五轮审计修正 2026-10-18)
- **回车订阅真缺陷修复(BD-UI-28①)**:原 `has_focus + key_pressed(Enter)`
口径下回车订阅**实窗从不触发**(egui 单行 TextEdit 遇 Enter 即
surrender_focus,检查时 has_focus 已 false)→ 改规范口径 `lost_focus() +
key_pressed`;回车输入带 `EX.` 前缀剥成裸码并回写表单(与查询合约窗同
口径)。新 s20 钉(focus + type_text + key_press 全链实测;空代码回车不入列
守卫同钉)。
- **悬空面清除(BD-UI-28②)**:`selected_symbol` 字段 + 两访问器删除(四处写
零读);行情带行单击 = 带入下单面板(方案「行单击 = 选中合约」落地,s8 钉
扩展);委托行/持仓行单击无动作(Python 同口径)。
- **委托行双击 = 撤单(BD-UI-28③)**:Python OrderMonitor `itemDoubleClicked→
cancel_order` 等价(活动行限定,s11 钉扩展;原双击弹 K线为偏离已撤,K线
入口唯一在行情带双击)。
- **表列补全(BD-UI-28④,Python 蓝图逐值抄录)**:委托表 13→17 列(+名称/
交易所/类型/来源)、成交表 10→12(+名称/交易所)、持仓表 11→16(+名称/
交易所/在途/市值/成本);数据字段全在场,零新增依赖。
- **tick_of 交易所感知(BD-UI-28⑤)**:交易所非空 = EX.symbol 精确匹配
(跨所同码不串);下单面板名称/随价/盘口三消费点同步接线。
- **测试面**:kittest 17→19 钉(s20 新增;s11 双击钉用 `with_step_dt(0.1)`
建构 —— kittest 每事件一帧 + 缺省 step_dt 0.25s 会把两击拉到 0.5s,超 egui
双击时限 0.3s,BD-UI-28⑥;s21 满表视觉钉)。全量 60 例绿。
- **Frame 布局泄漏修复(BD-UI-28⑦,s21 显形)**:`Frame::show` 继承
`ui.horizontal` 横向布局 → 2×2 格内 tab 行/表题/宽网格横流挤同一行(空表
期隐形)→ `group_frame` 内容强制 `ui.vertical`;委托表表题撤除(tab 即表
身份,「委托」三面降两面,s12 口径同步);快照基线 s3/s10/s16/s21 重生成。
## 0.1.0(进行中;v4 带框架 + 订阅两入口 2026-10-18,布局重做定稿)
9 字段 + 委托/全撤钮 + 盘口独立帧;右 2×2 监控格「委托|未成交」「成交」
「持仓」「资金|日志」)→ 弹窗面(egui::Window:K线/运维/CTP/查询合约)。
默认窗 1100×720 → **1280×860**。
- **删除线(界面面退役,带测试迁移)**:TicketForm/parse_ticket/ticket_form
(票据文本框;OrderForm 成唯一下单面,ui_sim 票据 4 断言迁 ui_state r7,
ui_ctp_live 下单同步迁移);回灌表单界面面(render_reload_form + reload_form
状态 + s9 钉;**UiCmd::Reload + 插件处理 + ui_market 闭环钉保留**);
CTP 窗「订阅标的」输入位 + 「订阅行情」钮上界面撤除(CtpForm.symbols 字段
保留 = state/ask 载荷面)。
- **订阅两入口(用户裁定「订阅不在网关窗定死」)**:① 下单面板代码框回车 →
`UiCmd::CtpSubscribe`(bare symbol,Python returnPressed→set_en_symbol 等价);
② 菜单「查询合约」弹窗(Python ContractManager 等价:查询框「代码或交易所,
留空 = 全部」+ 勾选 + 「订阅选中」钮;未勾选 = 不发 + 日志说明)。
- **合约数据流(plugin.rs routes += `eContract.`/`eContractFinish`)**:
ek-ctp 登录后自动 reqQryInstrument,UI 只收不催(无催查询总线口);
`UiShared.contract_store`(键 EX.symbol)+ 完成标记落日志「合约流完成」。
- **实码布局钉(实窗截图审出,egui 0.33 帧内 sizing 退让)**:下单表单 = 帧内
网格(委托/全撤/提示语都是网格行,网格后不挂面板内容 —— 否则后置行叠回网格);
盘口独立帧(140px 列);交易带整体包 ScrollArea(小窗可达,D7 口径延续)。
- **测试面**:kittest 17 钉(新 s17 菜单开窗 / s18 查询合约勾选订阅 / s19 全撤
扇出;s9 随回灌界面退役删除;s12 点击帧改 `harness.step()` 单帧钉红字;
建构面统一 `harness_for()` 1280×860);ui_state 11 钉(新 r9 合约库查询勾选 /
r10 窗位 + K线多开);ui_sim 9 钉(新 u8 tick_store 上屏 / u9 contract_store
上屏;u4 票据钉随退役删)。五闸(fmt/clippy/doc/test/release)全绿。
## 0.1.0(进行中;CJK 字体回退 2026-10-18,乱码根因修复)
- **BD-UI-26 CJK 字体回退(`src/fonts.rs`)**:epaint 0.33 缺省字体组(Hack /
Ubuntu-Light / NotoEmoji / emoji-icon)**无 CJK 字形** → 界面中文渲染成
豆腐块(用户实窗报障)。启动时按候选表从系统字体目录装载一枚 CJK 字体
(simhei → simkai → simfang → simsunb → Noto VF → Linux unifont/droid →
macOS Arial Unicode;**只收纯 .ttf/.otf** —— ab_glyph 不吃 .ttc 集合且
epaint 解析失败即 panic,复审拆掉的雷),追加到 Proportional/Monospace
两族末位作回退。两处 run_native 取帧闭包接线(runtime + pick 窗)。
装载证据行 stderr 可 grep:`[ek-ui] CJK 回退字体已装载: <path>`。
- 验收三钉:`fonts::f0`(字形 API:22 常用汉字 has_glyph)、`s16_cjk_font`
快照(汉字密集页 baseline,豆腐块再现即 diff 爆红)、s3/s10 快照重生成
(与实窗同字体面)。
## 0.1.0(进行中;sim 流控拒绝面 2026-10-17,T7 注错)
- **sim 流控超限拒绝(D4)**:`RateWindow::hit` 超限返回 false,订阅/发单/撤单
三口第 7 笔(1s 窗)被拒显形(`流控被拒:1s 窗内请求已达上限 6`),被拒笔不进
oms。原面只计数不拒绝,与 CTP 流控语义差一档。新测试 u7(6 收 1 拒)。
## 0.1.0(进行中;票据网关选择子 2026-10-17,集成测试战役 T5 前置)
- **票据网关选择子(BD-UI-25)**:票据新增「网关」位(空 = `sim` 纸面,现行为
不变;填 `ctp` = ask `ctp.send_order`/`ctp.cancel_order` 真网关)。
`UiCmd::SendOrder/CancelOrder` 随单携 `gateway`;行级撤单钮跟随委托来路
`gateway_name`(空兜底 sim)。`parse_gateway`(空 → sim;含空白 → 红字不入列)。
- 新增 `tests/ui_ctp_live.rs`(native 门 + `#[ignore]`):**无头界面控制**的
CTP 实连 12 步全流(手工 env 注入账号 → configure → 启动链六日志 → 合约 →
订阅 → 行情上屏 → 发单/挂单撤单/拒单 → 资金/持仓 → 错密码 → 改密重连 →
收场),与真实窗口同一 UiShared/UiCmd/ask 链路。
## 0.1.0(进行中;CTP 手工配置面 2026-10-17,集成测试战役 T0)
- **CTP 网关段(界面手工配置面)**:渲染新增「CTP 网关」——9 输入位(broker/td
前置/用户/密码/AppID/AuthCode/md 前置/用户/密码;密码与 AuthCode 掩码不回显)
+ `连接 CTP` 钮 + 订阅标的位 + `订阅行情` 钮。值全部人敲(界面零配置口径的
正向面:配置归插件自身),经 `UiCmd::CtpConfigure/CtpSubscribe` → ask
`ctp.configure` / `ctp.subscribe`(ek-ctp 0.3.0 插件自带配置口)。
- **资金小节**:交易监控页加「资金」表(ask `oms.get_all_accounts`;CTP 登录后
eAccount 入账即上屏;空帧占位「等网关 eAccount.」)。
- 装版升 `ek-ctp 0.3.0` / `ek-station 0.1.3`;新增 feature `ctp-native =
["ek-station/ctp-native"]`(默认关,日常构建不碰 C++;测试战役实连时开)。
- 测试 42 → 47:`tests/ui_ctp.rs` c0–c2(configure 全链/守卫日志/服务目录 5 口/
订阅受理/ctp 不在场失败不炸链)+ kittest s13(表单入列逐位 + 空订阅拦截)
+ s14(资金行读数 + 可用 = balance−frozen)。
## 0.1.0(进行中;配置面撤销 2026-10-17 用户定盘)
- **零配置口径落地**:ek-ui 不读任何配置文件。整块删除 `cfg.rs`(toml-lite/
UiConfig/locate_setting/redact)、`CfgStatus` 状态条(state/render)、
`--config` 形态与发现链(main.rs);bin 形态收敛为 `(无参)demo / --pick /
--from [--ready-file]`。测试 51 → 41(cfg 9 例 + s7 1 例随撤)。
需配置的插件(唯一 = ek-ctp 虚拟账号)配置面归插件自身,激活时在插件内完成;
**setting/ 目录整树清理**(样例与凭据档全删,工作区不留共享配置文件;
Python 侧 enkeep_trade_setting 原档只读未动)。详见 DESIGN「配置面撤销记录」。
## 0.1.0(进行中;审计修正 2026-10-17)
- `sim` 撮合两守卫(DESIGN BD-UI-22 补钉):**tick/bar 双重计价防护**
(tick 成交挂账,bar 收口预算扣回 —— 同一笔市场量只吃一次)+ **bar 单调
守卫**(重复/重放帧不重复撮合);ask 臂锁面开块(锁内造单锁外上总线)。
测试 +2(u5/u6)。
- 装版升 `ek-station = "0.1.2"`(pickfile BOM 剥离;Notepad 系清单不再拒启)。
## 0.1.0(进行中;S5)
- **依赖面**:+`ek-ctp = "0.2.0"`(默认特性,native 关;取四口契约类型)+
`ek-oms = "0.2.1"`(取 oms 查询 Req 类型)。
- `sim` 新模块:`SimGatewayPlugin`(注册名 "sim")——四口 ask 与 ek-ctp 同构
(`sim.subscribe/send_order/cancel_order` 直接复用 ek-ctp 的 Req/Reply 型;
第四口 `sim.query_rate` 自定流控查询:1s 窗 used/limit=6/remaining);
`SimMatcher` 纸面撮合(纯逻辑零总线):限价买 bar.low≤价/卖 bar.high≥价
成交于委托价,市价成交于 close/last;**手数排队** = 每 bar 的 volume 是本拍
成交预算,挂单 FIFO 吃预算,吃满排队到下一拍;成交产出 eOrder./eTrade./
ePosition. 三件套上总线(锁内撮合锁外 put,与 DmPlugin 同形);拒单口径
对齐 ek-ctp(开平 NONE/类型不支持/量非正)。
- `state`:`UiCmd::SendOrder/CancelOrder/QueryRate`;`TicketForm`/`parse_ticket`
(中文向 多/空/开/平/市价 可写;exchange v1 固定 SHFE);`TradeSnap`
(委托/成交/持仓三表帧)。
- `plugin`:refresh 尾段 ask `oms.get_all_orders/get_all_trades/get_all_positions`
组 TradeSnap(oms 不在 → 空帧不抛错);run_cmds 增三口(回执 LogRing)。
- `runtime`/`launcher_ui`/`main`:工厂表 + pick 表 + demo 缺省均含 sim
(demo 窗 = dm/oms/sim/ui,开窗即见交易监控闭环演示)。
- `render`:+交易监控段(委托表带行级撤单钮[仅 ACTIVE_STATUSES 行]/成交表/
持仓表)+ 下单票据 + 流控查询钮;解析错红字 + 日志双面(同回灌)。
- 测试:`tests/ui_sim.rs` 5 例(u0 撮合金样本 FIFO/预算/双向/市价/撤单/拒单;
u1 全链闭环 eTick→DM→eBar→Sim→eOrder/eTrade/ePosition→OMS→监控页帧逐位;
u2 撤单闭环;u3 流控计数;u4 票据解析);`ui_kittest` +s11(监控页读数逐位 +
撤单钮入列)/s12(票据提交/坏单不入列)。**kittest 点击纪律改 `click_accesskit`**
(坐标点击对折叠线下节点无效;accesskit 动作可达不可见控件)。
- 快照基线重生成(s3/s10:S5 版面变化)。
## 0.1.0(进行中;S4)
- **依赖面**:`ek-dm = "0.1.0"`、`egui-charts = "0.2.0"`、`chrono = "0.4"`
(BD-UI-7 四件套:eframe/egui 0.33.3 + egui-charts 0.2.0 + egui_kittest
0.33.3 全锁;图表层薄封装入口在本 crate)。改 K线渲染面无需直改
`ek-dm`(转换在手:ek-data BarData → egui_charts Bar)。
- `state`:`MarketStore`(eBar./eBarUpdate. 上屏地面;(symbol,interval) 格
缓存 4096 丢头保尾;帧/更新/回灌三计数 —— 刷新不丢帧统计可见)、
`UiCmd::Reload`(dm.reload 回灌指令)、`ReloadForm`/`parse_reload_csv`
(行 `dt,o,h,l,c,v`;dt epoch 秒;错行报行号)、`CellView`。
- `plugin`:routes + `eBar`./`eBarUpdate.`;`run_cmds` 增 reload 分支
(ask `dm.reload`,push=true,回执 LogRing,DM-US)。
- `runtime`:`ui_kinds` 收 `dm_rt` — **UI 宿主 dm 工厂 real_time=true**
(替换 ek-station 缺省入口;headless 仍旧 rt-off 两宿主互不波及);
`RunOpts.dm_rt`。
- `render`:+行情板(金样本数值逐位)/K线页 v1(egui-charts collapsible
首格定点,tracking 开)/回灌窗(表单 + 提交 + 错 CSV 红字即 Logan 环)。
- `main`:`[<mode>.dm] real_time` 覆写入 RunOpts。
- 测试累计 42:`ui_market` +2(金样本回放逐位对 + 回灌端到端)、
`ui_kittest` +4(s8 金样本行、s9 提交/坏 CSV 红字双面,s10 K线帧快照,
快照并发互斥钉)。
## 0.1.0(S3)
- **依赖面**:`ek-station = "0.1.1"`(pickfile 抽世落地,`pick.rs` 变薄转发
—— BD-UI-15 统一兑现)。
- `cfg`:**toml-lite** 自写解析(零依赖;Str/Int/Bool/List;段头/重名/坏行号;
引号内 `#` 与行尾注释并存),`UiConfig`(mode + `[<mode>.station] plugins`
+ per-plugin 子树)、`locate_setting`(env EK_STATION_SETTING → cwd →
cwd 祖先链找 `setting/global.toml`)、`redact/redact_line`(秘密键替 ***)。
- bin:`--config <global.toml>` 形态;优先级 `--from` > `--config` > 发现 > demo;
预启动校验(未知插件/空清单/mode 值外/解析/IO)= fatal → exit 1 拒启动
(BD「错配置拒绝启动」);缺 station 段 = Warn 状态条即 UI 红字(不进总线)。
- `state`:`CfgStatus{Ok/Warn}` + `cfg_status`(UI 面 only);`render` 顶部
配置状态条(绿/红字)。
- 测试累计 37:`cfg` +9(round-trip 真形样例含引号内 #/坏行号/subtree/脱敏/
校验三 fatal/缺段 warn/发现链/IO fatal)、`ui_kittest` +1(s7 状态条绿/红/无)。
## 0.1.0(S2)
- `state`:`UiCmd{Start/Remove}`(窗口面→总线面 指令队列)、`pending_confirm` +
`confirm_step/confirm_cancel/confirm_cancel`(二步确认 Armed/Fired/Switched)、
`is_quarantined`(状态原文快照扫描,BD-UI-17)。
- `plugin`:eTimer 先辞行 UiCmd 再拉快照;svctl.start/remove 回执进日志
(`[ops] ...` 可断言行)。
- `render`:插件表加操作列(重注入单步 / 移除二步确认)、待确认横幅 + 取消钮、
依赖图文本段、隔离行红标(RED 状态/名)。
- 测试累计 27:`ui_ops` +5(指令队列/确认状态机/隔离判定 + 真链 摘除→重注入→
ghost 被拒)、`ui_kittest` +3(s4 二步确认流程/s5 重注入直发/s6 取消+红标+依赖图);
静态快照基线随布局更新(操作列入格)。
## 0.1.0(S1b)
- `runtime`:`run_station_ui` 增加 `ready: Option<&Path>`(start 成功即写
`"ready\n"`,launcher 轮询面);bin 形态扩成 无参 / `--pick` / `--from [+--ready-file]`
四型(未知参数 usage exit 2)。
- `pick`:行式清单读写(`#` 注释/空行/空白行拒;Empty/BadLine/Io 业务错)。
- `launch`:分离式 spawn(CREATE_NO_WINDOW + stderr 管道)→ wait_ready(50ms
轮询,30s 缺省;超时 kill)/ Failure 分类 `Exited{code,stderr_tail}` /
`NoReadySignal{timeout}`;成功路径 drop Handle 分离脱管(BD-UI-14)。
- `launcher_ui`:`render_pick`(纯渲染,回调注入)+ `PickApp`(UI 线程只画与
翻位;worker 见 BD-UI-16)。
- `launcher`:`run_pick_launcher`(window join + 槽读取)/ `launch_worker`
(写清单→spawn→wait_ready);pick 表 = 缺省工厂 + ui(BD-UI-15)。
- 测试累计 19:`ui_kittest` +4(S1b)/ `launch` +5(powershell 替身:成功标记/
拒退+stderr 尾/超时 kill/清单读写规则)/ `pick_ui` +3(勾选翻位+摘要联动/
启钮禁用-触发/取消钮)。
## 0.1.0(S1b)
- `state`:`UiShared` / `LogRing`(cap 512 + spilled)/ `BusSnap`。
- `plugin`:`UiPlugin`("ui")—— eLog 环形、ePlugUp/Down 记录、eTimer 1s 拉
svctl.list + 服务目录组快照;纯观测零服务(BD-UI-13)。
- `render`:`render_body` 单渲染入口(插件表/服务目录/日志 + 清空日志就地按钮)。
- `runtime`:`ui_kinds`(缺省表 + ui 工厂,shared 同享)/ `spawn_host_watcher`
(关窗协议)/ `run_station_ui`(boot→start→eframe 主循环→join 观察线程)。
- bin `ek-ui`:dm+oms+ui 直起窗口。
- 测试:L1 `ui_state`(6);L3 `ui_kittest`(节点断言/就地交互/静态快照);
装配链 `ui_runtime`(eTimer 回路 + 关窗 close 链)。
- 依赖: ek-core 0.3.0 / ek-event 0.1.0 / ek-data 0.2.2 / ek-station 0.1.0(装版);
eframe = egui = 0.33.3;egui_kittest 0.33.3(dev,snapshot+wgpu)。