交易系统工程 / 手动执行
SPXW 0DTE 控制台 —— 键盘驱动的手动执行面板
运行中
面板的完整导览:每个按键做什么、屏幕每个区域显示什么,以及面板之下的机器——作为唯一下单入口的显式状态机、以 broker 真相为准的仓位记账、主动追价平仓、硬软两层止损,和一条足以还原任何交易时段的审计日志。
这是什么
SPXW 0DTE 控制台是一个键盘驱动的手动执行面板,通过 Interactive Brokers TWS 交易 SPX 周度(SPXW)当日到期期权。它刻意不含任何策略与信号:人来决策、按键,软件唯一的职责是让这个决策被正确执行——或者按照既定规则拒绝执行。技术栈同样朴素:Python asyncio 后端(ib_insync)、经 WebSocket 连接的单文件 React 前端(零构建),持久化用 SQLite。
这是 v2 版本,针对 v1 面板运行的第一周产生的十二个 PR,其中六个实质性修复集中在同一层——订单与执行状态。本地仓位状态会偷偷偏离 broker 真相;执行逻辑是一锅互相纠缠的旗标;进程一崩,所有在途意图全部丢失,因为什么都没有持久化;IOC 加本地超时的平仓模型又天生和 TWS 服务器侧的价格管理打架。与其打第七个补丁,不如围绕三个承诺重新设计这一层:显式状态机、以 broker 真相作为仓位的唯一权威、持久化意图——并把 v1 的每一条教训固化为结构性不变量,而不是靠记性。
屏幕:从上到下
布局为时间压力下的扫读顺序而设计——五个区域,越重要越靠上。
**状态条。**连接模式(LIVE 或 DEMO)、SPX 与 ES 现货价及各自的报价延迟毫秒数、数据农场健康状态、AUTO-EXIT 开关状态、区分手续费的当日盈亏,以及当前执行状态。
**行权价面板。**左 Call 右 Put。每个面板自动瞄准第一档虚值(OTM)行权价,大字显示买卖价、点差与距现货点数,外加围绕目标刻意收窄的 ±2 档小梯子——v1 的二十行梯子被砍掉了,因为实际交易的永远是平值附近。LOCK 把目标钉在某个行权价上;ATM/OTM 微调按钮把目标上下移动一档。
**下单前预览。**这个面板的招牌新增:一块持续显示此刻每个按键会提交什么的区域——确切的合约、限价、美元成本、报价年龄,以及一个安全裁决。绿色表示就绪;红色裁决会写明拦截原因(实值行权价、报价陈旧、超保费上限),此时按键会被拒绝。它源自一次具体事故:数据源冻结后 SPX 现货一直停留在陈旧价位,行权价选择器选中了一张实值合约,而屏幕上没有任何东西在买入之前把这件事暴露出来。一块渲染”按键后果”的预览面,能在这一整类错误发生之前拦住它。
**持仓条。**执行状态、持仓、均价与浮动盈亏——或者干脆一个 FLAT。
**风控条。**只读显示当前武装着的每一道保护:−50% 硬止损、带触发门槛的移动止损、保本地板、日损停止线,以及数量与保费上限。软止损被关闭时,被压制的条目会以删除线渲染——面板绝不显示一道实际上并不生效的保护。
**控制区与日志。**可点击的按钮与按键一一对应(供触屏使用),旁边是数量步进器。日志/blotter 实时滚动每一次状态转移、订单、成交与告警,最新在上,每条都带原因。底部是 EMERGENCY STOP 紧急停止条。
键盘
| 键 | 动作 |
|---|---|
F1 / F2 | 按预览的行权价买入 Call / Put。已持仓时,同向按键为加仓——钉在已持行权价上,绝不跟随漂移后的目标;反向按键会被拒绝(单方向规则)。 |
Space | 平仓:以主动追价把仓位平到 FLAT 为止。 |
Shift+Space | 紧急平仓——同样的追价,最高优先级。 |
Esc | 撤销在途的开仓单。 |
A | 切换 AUTO-EXIT(软止损层,见下文)。 |
R | 生成并打开当日交易时段报告。 |
长按 X(2 秒) | 紧急停止。刻意只杀应用、不动仓位——如果软件必须死,平仓这件事应该在 TWS 里做,而不是交给一个正在被怀疑的软件。 |
执行机器
面板之下是一台显式状态机——FLAT → OPENING → LONG → CLOSING → FLAT——它是整个代码库中唯一会提交订单的地方。每次状态转移都带原因落日志,并且天然幂等:OPENING 或 CLOSING 期间的重复指令由状态本身拒绝,而不是靠散落各处的旗标。
四个机制承担了大部分安全重量:
- **占用保持到 broker 终态。**本地超时只请求撤单;在途占用一直保持到 broker 报告终态为止。一张 IOC 单可能在撤单请求之后几秒仍然成交——提前释放再补单正是裸空的成因,所以这道守卫让它在结构上不可能发生。
- **平仓是主动追价,任何时刻只有一单在飞。**平仓单挂在 bid 上、跟随市场重新定价——但只在 broker 确认前一单终态之后,因此双卖无法存在。定价始终留在 IB 服务器侧价格带之内(不加负 padding,那会让订单被扣住数秒)。快速追价用尽有限次数后,面板降档为每 15 秒在 bid 上耐心重试直到 FLAT,同时提示操作者也可改在 TWS 里手动处理——而 −50% 硬止损全程武装在下面。
- **持续对账 broker 真相。**broker 的仓位与成交事件在每次变化时都与本地状态核对,而不是只在重连时;发现漂移立即纠正并告警。单持仓模型无法表达的 broker 状态——空头、多腿、异类合约——会进入 BLOCKED 子态锁定开仓,呼叫人工。
- **写穿式持久化。**状态机状态、仓位与已闩锁的意图(触发中的止损、已启动的日终强平、已触发的 kill-switch)在每次转移时写入 SQLite。崩溃或重启后,控制台先恢复意图、再与 broker 对账——仓位以 broker 为准,意图得以幸存。
风控引擎:硬层与软层
保护被拆成权限不同的两层。
硬层无法从面板关闭:持仓上的 −50% 硬止损、触发即强平并锁定当日的日损 kill-switch,以及日终强平加”日终后禁止重开”锁。软层——带触发门槛的移动止损,和让冲高回落的盈利单在保本附近离场、而不是坐视其往返成亏损的保本地板——放在 AUTO-EXIT 开关(A 键)之后,留给想要纯手动离场的日子。这个开关的不对称性正是设计意图:关掉软止损时每一道硬保护依然武装,而风控条会明确显示哪些被压制了。
两条新鲜度规则贯穿所有风控。止损只在新鲜报价上行动——报价陈旧或无买价时,产生限频的降级告警而不是动作,原则是”不动比乱动安全”。开仓则以 SPX 现货的年龄为门,而不是数据源的 live 标志——那个标志一旦变成 live 就永不回退,数据源冻结后它会永远显示”live”。每个风控动作都先闩锁意图、再走与手动平仓完全相同的状态机路径;不存在绕过状态机下单的旁路。
可观测性
每个交易时段写两个文件:一份人读的应用日志,一份结构化事件流(JSON Lines,东部时间戳),经队列异步落盘,日志永不阻塞下单路径。每条指令都记录 SPX 现货、选中的行权价、距现货点数、报价年龄,被拒绝时附原因码——任何决策事后都能被还原。成交进入 SQLite blotter。
每个时段还盖有一条来源戳记:会话 ID 加上一个覆盖全部影响行为配置的哈希。不同日期的日志可以合并做后续研究,而绝不会悄悄混入两套配置——paper/live 标志也随每条记录同行。一条命令即可生成任意一天的时段报告——笔数、胜率、盈亏、离场原因与被拦订单统计——数据来自 blotter 与事件流。
刻意砍掉的东西
三个 v1 功能被有意移除。实时 Greeks:手动打点时没人读 Greeks 矩阵,它却消耗一个 IV 求解器和大半性能预算——距平值点数加点差就足以支撑决策。迷你 K 线图:真正的图在另一块屏幕的 TWS 里。以及一键反手(平仓即反向开仓):旧面板上最危险的一个键,改成了两步显式操作。
同样的品味也适用于运行时:不上 Rust、不引入编译型热路径。促成重写的那些失败全是状态机正确性问题,不是吞吐问题——50 毫秒的快照循环从来不是瓶颈,工程预算于是投给了状态机、持久化与对账。
模式与韧性
控制台按配置连接 paper 或 live 的 TWS 端点,当前模式作为标志随每条日志记录;状态条徽章则区分 LIVE(真实行情)与 DEMO(合成行情)。--demo 模式完全不需要 TWS,用合成行情驱动整个面板——包括完整的买入/平仓流程——供盘后熟悉操作。当日期权链不可用时(假日、盘后),面板降级启动而不是崩溃:起得来、亮出状态、拒绝交易。前端是响应式单文件,同时是可安装的 PWA(内联 manifest、独立窗口显示),同一面板可以在同网段的手机上使用。
工程状态
引擎已完成全部规划阶段,携带 258 项测试——单元、集成、对抗性 broker 模拟(晚成交、价格带拒单、断连、部分成交、注入漂移)与崩溃恢复演练——在逐 PR 的工作流下开发。十二条不可协商的安全不变量,每一条都标注着引发它的具体日期事故,把守着执行层的每一次改动。控制台自 2026 年 7 月下旬起投入日常使用,v1 面板保留作回退。