fix期权复盘与数据统计双口径盈亏,并修复卖回封顶导致盈利单冻成亏损。

平仓卖光可卖币后再按账户差分结算复盘;数据统计改为历史币盈亏×卖回价;复盘展示初始权利金与盈利率,并附历史修复脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-13 09:10:13 +08:00
parent d4de6a23ae
commit 9d77a430e5
16 changed files with 2858 additions and 2242 deletions
+400 -401
View File
@@ -1,401 +1,400 @@
# 系统分析:期权主链条
> 范围:币本位期权(`OKX_OPTIONS_MARGIN_MODE=coin`)
> 配套规格:[规格-期权交易账户盈亏结算.md](./规格-期权交易账户盈亏结算.md)、[实盘说明.md](./实盘说明.md)
> 目的:从架构到开仓 / 平仓 / 展示 / 复盘 / 统计 / 对账 / 条件单,说明数据流、关键模块与边界
---
## 0. 总览
### 0.1 三条控制面
| 控制面 | 入口 | 职责 |
|--------|------|------|
| 面板 API | `/options` → `/api/options/*` | 人工开平仓、链/报价、持仓、划转、统计 |
| 监控线程 | `options_monitor_lib`(安装时拉起) | 目标位平仓、止盈平仓、条件单、持仓同步、卖回扫尾 |
| 中控 Hub | `/api/hub/options/*` | 快照、统计、远程平仓、条件单 CRUD |
### 0.2 资金主链(商业真源)
```text
USDT(交易户)
│ 开仓前快照 trading_usdt_before
▼
买 ETH/BTC(现货桥)
▼
买期权(IOC @ 卖一)
▼
持仓中(禁划转;浮盈跟盘)
▼
卖期权(买一 / 深度)
▼
卖回残留币 → 快照 trading_usdt_after
▼
realized_pnl_usdt = after − before,pnl_settled=1(冻结,永不改)
▼
复盘 / 统计只读冻结值
```
### 0.3 启动装配
```text
app.py
→ install_options_trading # options_register
→ install_options_review # options_review_register
→ register_hub_bridge_routes # hub_bridge_register
→ 监控守护线程 # options_monitor_lib
```
核心配置包:`_build_cfg`(交易所、DB、预算、报价/下单回调)。
UI:`lib/options/templates/options_panel.html` + `lib/common/static/options_panel.js`。
### 0.4 关键表(SQLite)
| 表 | 用途 |
|----|------|
| `options_trades` | 本地开平流水;含 `trading_usdt_before/after`、`realized_pnl_usdt`、`pnl_settled` |
| `options_spot_bridge` | 买币/卖回桥状态 |
| `options_target_monitors` | 目标位监控 |
| `options_cond_orders` | 条件单 |
| `options_exchange_pnl_freeze` | 实盘历史盈亏冻结辅助 |
| `options_review_*` | 复盘成交 / 笔记 / 隐藏 / 图 |
| `options_transfer_log` 等 | 划转等资金侧日志 |
---
## 1. 主链条(架构)
### 1.1 模块分层
| 层 | 代表文件 | 说明 |
|----|----------|------|
| 路由装配 | `lib/options/options_register.py` | HTTP、尺寸归一、同步持仓 |
| 开仓编排 | `options_coin_open_lib.py` | 预算→买币→开期权→写库 |
| 平仓执行 | `options_close_exec_lib.py` | 买一/深度卖出 |
| 账户结算 | `options_account_pnl_lib.py` | USDT 快照、卖回后冻结 |
| 公式结算 | `options_pnl_settle_lib.py` | 权利金侧辅助结算(有账户快照时 U 以账户为准) |
| 交易所 | `lib/exchange/okx_options_lib.py` | 链、报价、下单、持仓 |
| 模式/预算 | `options_margin_mode_lib.py` | 币本位、预算、张数规划 |
| 展示 enrichment | `options_positions_lib.py` / `options_history_lib.py` | 浮盈、平仓预览 |
| Env UI | `lib/env/env_ui_manifest.py` | 期权相关默认与文案 |
### 1.2 运行模式
- `OKX_TRADE_MODE=options`:主站偏期权。
- `OKX_OPTIONS_ENABLED`:模块开关。
- 模拟 / 实盘由顶栏 `trading.mode` + `LIVE_TRADING_ENABLED` 等控制;实盘要求现货+期权权限(见实盘说明)。
### 1.3 与盈亏规格的关系
规格文档定义**产品规则**;本分析描述**代码落点**。
凡「已实现盈亏」对外展示(复盘 / 统计 / 顶栏已实现),在新规则下均以 `pnl_settled=1` 的账户差分为准;旧单无快照字段时走遗留口径,互不回写。
---
## 2. 开仓
### 2.1 入口
| 来源 | 路径 / 调用 |
|------|-------------|
| 面板 | `POST /api/options/open` |
| 条件单触发 | `options_cond_order_lib` → 同一套 `open_coin_option_buy_full` |
前端默认尺寸模式:**按可用余额打满**(`budget_full`);复利开时 UI 切 `compound_full`。
后端 `_normalize_size_mode`:币本位保留 `budget_full` / `compound_full`;USDC 遗留路径才落到 `sheets`。
### 2.2 尺寸模式
| mode | 含义 |
|------|------|
| `budget_full` | min(可用 USDT, 单笔预算) × 预算缓冲 → 张数(默认) |
| `compound_full` | 交易户可用 × 缓冲(受全仓上限 env 约束) |
| `sheets` | 指定张数 |
| `eth` / `eth_amount` | 指定币数量反推张数 |
预算计算:`compute_coin_budget_usdt` / `plan_coin_open_by_budget`(`options_margin_mode_lib.py`)。
### 2.3 流程
```text
请求 {inst_id, mode, sheets?, eth?, target_index?, profit_exit_*}
→ 交易模式门禁 / 条件单占用锁 / 持仓笔数上限
→ 有可卖标的币 → 拒开(须先卖回)
→ coin_budget_preview → 规划张数与买币量
→ fetch_trading_usdt → 写入 trading_usdt_before
→ 现货市价买币(× SPOT_BUY_BUFFER)→ options_spot_bridge=bought
→ 期权限价 IOC @ 卖一 → 等待成交
失败 → 回滚卖币
成功 → bridge=holding,_insert_coin_trade(open, pnl_settled=0)
→ 可选:目标位 / 止盈倍数 / 微信通知
```
### 2.4 关键 env
| 变量 | 默认(代码/清单) | 作用 |
|------|-------------------|------|
| `OKX_OPTIONS_COIN_COMPOUND` | true | 是否按交易户复利 |
| `OKX_OPTIONS_COIN_BUDGET_USDT` | (单笔预算) | 打满时的封顶 |
| `OKX_OPTIONS_BUDGET_BUFFER` | 1 | 预算缓冲 |
| `OKX_OPTIONS_COIN_SPOT_BUY_BUFFER` | 1.05 | 现货多买缓冲 |
| `OKX_OPTIONS_MAX_ACTIVE_POSITIONS` | 1 | 同时持仓笔数;0=不限 |
| `OKX_OPTIONS_OPEN_FILL_TIMEOUT_SEC` | — | 开仓等待成交超时 |
### 2.5 边界
- 开仓**只快照 before**,不结算盈亏。
- 持仓或桥未完成期间:`transfer_blocked_by_position_msg` 禁止划转。
- 一次一仓是对账规格前提;上限默认 1。
---
## 3. 平仓
### 3.1 三条平仓通道
| 通道 | 触发 | 门控 | 执行 |
|------|------|------|------|
| 手动 / Hub | 用户或中控 | 一般只校验流动性 | `close_option_by_bid1(require_recycle_gate=False)` |
| 目标位 | 指数触达目标 | 回收门控(权利金倍数 / 净盈亏 + 持续秒数) | `close_option_by_bid_depth` |
| 止盈倍数 | 买一回收 ≥ 权利金×(1+mult) | 自有回收条件,不走 close_gate | `close_option_by_bid_profit_exit` |
监控循环:`run_options_target_closes` / `run_options_profit_exits`。
### 3.2 流程
```text
卖期权(买一或深度,reduce-only)
→ 部分成交:累计平仓张数
→ 全平:
settle_closed_pnl(币侧/费用;有账户快照时 U 不以公式定稿)
→ maybe_sell_spot_after_close / sell_residual
→ try_settle_after_sellback
after − before → realized_pnl_usdt,pnl_settled=1
```
卖回失败:本单「待结算」,已实现不进统计;可 `POST /api/options/spot-bridge/retry-sell` 或监控扫尾。
### 3.3 回收门控 env
| 变量 | 默认 | 作用 |
|------|------|------|
| `OKX_OPTIONS_CLOSE_GATE_MODE` | premium | `premium` / `net_pnl` |
| `OKX_OPTIONS_CLOSE_RECYCLE_MULT_COIN` | 1.05 | 币本位回收倍数 |
| `OKX_OPTIONS_CLOSE_HOLD_SECONDS` | 20 | 条件持续秒数 |
| `OKX_OPTIONS_CLOSE_NET_PNL_MIN_USDT` | 0 | net_pnl 模式下限 |
### 3.4 边界
- 手动平仓可绕过回收门控;自动目标位不可。
- 冻结后任何划转、指数、复盘同步**不得改写** `realized_pnl_usdt`。
---
## 4. 期权显示
### 4.1 页面与 API
| UI | API | 数据源 |
|----|-----|--------|
| 期权链 / 到期 / 虚实度过滤 | `GET /api/options/chain` | `build_option_chain` |
| 下单弹窗报价与估张 | `GET /api/options/quote` | `quote_option_contract` + 币本位规划 |
| 持仓卡片 | `GET /api/options/positions` | 交易所持仓 + 本地 trade enrichment |
| 余额 / 顶栏 | `GET /api/options/balances` + header pack | 交易户 / 资金户 |
| 挂单 / 目标位 | `.../orders/pending`、`.../targets` | 本地 + 交易所 |
| 中控快照 | `GET /api/hub/options/snapshot` | `build_options_hub_snapshot` |
前端主文件:`options_panel.js`、`options_position_cards.js`、`options_expiry_countdown.js`。
### 4.2 展示口径(勿与冻结混淆)
| 指标 | 口径 |
|------|------|
| 持仓浮盈 | 买一回收估算(`display_pnl_*`),跟盘变 |
| 合计盈亏 | Σ已冻结 + 当前浮盈 |
| 顶栏钱包 | 交易所余额,不是盈亏账本 |
| 下单估权利金 / 杠杆 / 打满提示 | 报价瞬间估算,非成交结果 |
### 4.3 链过滤 env
`OKX_OPTIONS_CHAIN_MAX_DTE_DAYS`、`OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED`、`OKX_OPTIONS_ITM_MAX_DIST_USD`、`OKX_OPTIONS_DEFAULT_UNDERLY`。
---
## 5. 复盘
### 5.1 入口
- 页:`/options/review`
- 注册:`options_review_register.py`
- 逻辑:`options_review_lib.py` + `options_review_db.py`
- 前端:`options_review_panel.html`、`options_review.js`
### 5.2 数据流
```text
options_trades(已平)
→ sync_options_from_local_trades / ensure_local_review_synced
→ options_review_trades(history_key ≈ local_opt:{id})
→ 笔记 options_review_entries + 上传图 / 异步图表
→ 隐藏 options_review_hidden
```
主要 API:`POST /api/options/review/sync`,`GET/DELETE .../trades`,`POST .../entry`,`POST .../upload_slot`,`GET .../stats`。
### 5.3 盈亏读取规则
| 本地单状态 | 复盘展示 |
|------------|----------|
| 有 `trading_usdt_before` 且未 `pnl_settled` | 已实现 U 待结算(不展示假冻结) |
| `pnl_settled=1` | 只读冻结 `realized_pnl_usdt` |
| 无账户快照的历史单 | 遗留口径,不强制改写成账户差分 |
复盘是**消费者**,不负责改写结算真源。
---
## 6. 统计
### 6.1 入口
| 场景 | API / 函数 |
|------|------------|
| 期权面板 KPI | `GET /api/options/stats` → `compute_options_stats_auto` |
| 复盘页统计 | `GET /api/options/review/stats` |
| 顶栏窗口统计 | `header_options_stats_for_window` |
| 中控 | `GET /api/hub/options/stats` |
核心库:`options_stats_lib.py`。
### 6.2 窗口与构成
- 窗口:`today` / `7d` / `30d` / `all`(`normalize_stats_window`)。
- 模拟:本地已平且已结算样本。
- 实盘:交易所历史 + `options_exchange_pnl_freeze` 对齐后同一套 KPI。
- 面板合计:`净已实现(仅 settled) + 持仓浮盈`。
### 6.3 边界
- 待卖回、未冻结单**不计入**已实现。
- 统计不重新推导账户差分;最多做冻结补齐 / 修复类运维函数(如 `freeze_missing_realized_pnl_usdt`),与规格「冻结后不改」一致的运维路径需谨慎使用。
---
## 7. 对账
此处「对账」= **交易账户快照结算 + 本地/交易所持仓对齐**,不是已删除的永续 `reconcile_flat`。
### 7.1 结算真源(账户账)
| 步骤 | 函数 | 结果 |
|------|------|------|
| 开仓前 | `fetch_trading_usdt` | `trading_usdt_before` |
| 卖回后 | `try_settle_after_sellback` / `settle_closed_trades_from_trading_account` | `after`、`realized_pnl_usdt`、`pnl_settled=1` |
| 持仓禁划转 | `transfer_blocked_by_position_msg` | 前端灰 + API 拒 |
公式身份(规格 §6 思路):
```text
Σ 冻结已实现 + 当前浮盈 ≈ 面板合计盈亏
Σ 冻结已实现 + 区间净划转 ≈ 钱包 USDT 变动(在一次一仓、无并行污染前提下)
```
### 7.2 持仓对齐(仓位账)
监控 / `_sync_options_trades`:
| 动作 | 含义 |
|------|------|
| `reconcile_live_open_trades` / `sync_open_options_trades` | 交易所仍有仓而本地已关 → 纠偏重开等 |
| `backfill_closed_options_realized_pnl_from_history` | 历史回填;**跳过已冻结 U** |
| `sell_spot_after_options_sync` / `sweep_spot_after_options_flat` | 平仓后残留币扫尾,推动账户结算 |
### 7.3 双账清晰
| 账本 | 看见什么 |
|------|----------|
| 钱包(顶栏) | 交易所余额,随划转变 |
| 盈亏账 | 冻结历史 + 浮盈;划转不改已冻结单 |
---
## 8. 条件单
### 8.1 定位
中控下发「价格带触发 → 自动开仓 → 挂目标/止盈 → 等平仓结束」的自动化;与面板互斥:有活跃条件单时**锁定手动开仓**(`cond_order_blocks_manual_open_msg`)。
### 8.2 入口
| API | 作用 |
|-----|------|
| `GET/POST /api/hub/options/cond_orders` | 列表 / 创建 |
| `POST .../<hub_order_id>/cancel` | 取消 |
| `POST .../exit` | 更新出场参数 |
库:`options_cond_order_lib.py`。
Tick:监控循环 `run_cond_orders_tick` → `_tick_one`。
### 8.3 生命周期
```text
Hub 创建(trigger_px, deviation_pct, 认购/认沽, 出场)
→ options_cond_orders = armed
→ 指数落入 band_bounds
→ pick_nearest_itm_contract
→ 走 open_coin_option_buy_full(同人工开仓:before 快照、买币、IOC)
→ 挂目标位 / 止盈
→ 等待仓位平坦 → _end_order
→ 周四 23:00 等策略可 expire 未触发单
```
### 8.4 与主链关系
- **不另建盈亏账本**;触发开仓后的卖回冻结与人工单相同。
- 占用期间面板开仓按钮应不可用,避免双开破坏「一次一仓」。
---
## 9. 端到端对照表
| 阶段 | 写库 / 状态 | 用户可见 | 消费方 |
|------|-------------|----------|--------|
| 开仓成功 | trade open + before + bridge holding | 持仓卡、浮盈 | 监控、禁划转 |
| 期权已平、币未卖回 | 待结算 | 提示待卖回 | 统计不含已实现 |
| 卖回成功 | after、realized、settled=1 | 复盘/统计定格 | 对账完成 |
| 条件单 armed | cond_orders | 横幅锁定手动开仓 | 监控 tick |
| 目标位/止盈触发 | 平仓执行 | 持仓消失→卖回→冻结 | 同平仓链 |
---
## 10. 关键文件索引
| 主题 | 路径 |
|------|------|
| 路由 / 开平仓 API | `lib/options/options_register.py` |
| 币本位开仓 | `lib/options/options_coin_open_lib.py` |
| 现货桥 | `lib/options/options_spot_bridge_lib.py` |
| 平仓执行 | `lib/options/options_close_exec_lib.py` |
| 回收门控 | `lib/options/options_close_gate_lib.py` |
| 目标位 | `lib/options/options_target_lib.py` |
| 止盈 | `lib/options/options_profit_exit_lib.py` |
| 账户盈亏 | `lib/options/options_account_pnl_lib.py` |
| 统计 | `lib/options/options_stats_lib.py` |
| 复盘 | `lib/options/options_review_*.py` |
| 条件单 | `lib/options/options_cond_order_lib.py` |
| 监控 | `lib/options/options_monitor_lib.py` |
| Hub 快照 | `lib/options/options_hub_lib.py` |
| Hub 路由 | `lib/hub/hub_bridge_register.py` |
| Schema | `lib/options/options_db.py` |
| 面板 | `lib/options/templates/options_panel.html`、`lib/common/static/options_panel.js` |
---
## 11. 维护时注意
1. **改盈亏口径**:先改规格文档,再改 `options_account_pnl_lib` 与复盘/统计读取,避免三处漂移。
2. **改开仓默认尺寸**:同时看前端 `currentSizeMode`、HTML `checked`、后端 `_normalize_size_mode`(币本位勿再压成 `sheets`)。
3. **改门控秒数/缓冲**:代码默认、`env_ui_manifest` 文案与 `_RUNTIME_ENV_DEFAULTS` 一并对齐;已有 `.env` 旧值不会自动覆盖。
4. **条件单与手动开仓**:任何新开仓入口都要接 `cond_order_blocks_manual_open_msg`(或等价锁)。
5. **对账前提**:一次一仓 + 持仓禁划转 + 卖回后冻结;破坏任一前提,钱包差分将无法与单笔盈亏对齐。
# 系统分析:期权主链条
> 范围:币本位期权(`OKX_OPTIONS_MARGIN_MODE=coin`)
> 配套规格:[规格-期权交易账户盈亏结算.md](./规格-期权交易账户盈亏结算.md)、[实盘说明.md](./实盘说明.md)
> 目的:从架构到开仓 / 平仓 / 展示 / 复盘 / 统计 / 对账 / 条件单,说明数据流、关键模块与边界
---
## 0. 总览
### 0.1 三条控制面
| 控制面 | 入口 | 职责 |
|--------|------|------|
| 面板 API | `/options` → `/api/options/*` | 人工开平仓、链/报价、持仓、划转、统计 |
| 监控线程 | `options_monitor_lib`(安装时拉起) | 目标位平仓、止盈平仓、条件单、持仓同步、卖回扫尾 |
| 中控 Hub | `/api/hub/options/*` | 快照、统计、远程平仓、条件单 CRUD |
### 0.2 资金主链(双口径)
```text
USDT(交易户)
│ 开仓前快照 trading_usdt_before;initial_premium = 开仓指数×权利金币量
▼
买 ETH/BTC(现货桥)
▼
买期权(IOC @ 卖一)
▼
持仓中(禁划转;浮盈跟盘)
▼
卖期权(可多次部分平;以全部平完为准)
▼
卖光可卖标的币(禁止按开仓权利金封顶)→ 快照 trading_usdt_after
▼
口径 A(复盘): realized_pnl_usdt = after − before,pnl_settled=1
口径 B(数据统计): 历史币盈亏 × 平仓卖回价(与 A 独立)
```
### 0.3 启动装配
```text
app.py
→ install_options_trading # options_register
→ install_options_review # options_review_register
→ register_hub_bridge_routes # hub_bridge_register
→ 监控守护线程 # options_monitor_lib
```
核心配置包:`_build_cfg`(交易所、DB、预算、报价/下单回调)。
UI:`lib/options/templates/options_panel.html` + `lib/common/static/options_panel.js`。
### 0.4 关键表(SQLite)
| 表 | 用途 |
|----|------|
| `options_trades` | 本地开平流水;含 `trading_usdt_before/after`、`realized_pnl_usdt`、`pnl_settled` |
| `options_spot_bridge` | 买币/卖回桥状态 |
| `options_target_monitors` | 目标位监控 |
| `options_cond_orders` | 条件单 |
| `options_exchange_pnl_freeze` | 实盘历史盈亏冻结辅助 |
| `options_review_*` | 复盘成交 / 笔记 / 隐藏 / 图 |
| `options_transfer_log` 等 | 划转等资金侧日志 |
---
## 1. 主链条(架构)
### 1.1 模块分层
| 层 | 代表文件 | 说明 |
|----|----------|------|
| 路由装配 | `lib/options/options_register.py` | HTTP、尺寸归一、同步持仓 |
| 开仓编排 | `options_coin_open_lib.py` | 预算→买币→开期权→写库 |
| 平仓执行 | `options_close_exec_lib.py` | 买一/深度卖出 |
| 账户结算 | `options_account_pnl_lib.py` | USDT 快照、卖回后冻结 |
| 公式结算 | `options_pnl_settle_lib.py` | 权利金侧辅助结算(有账户快照时 U 以账户为准) |
| 交易所 | `lib/exchange/okx_options_lib.py` | 链、报价、下单、持仓 |
| 模式/预算 | `options_margin_mode_lib.py` | 币本位、预算、张数规划 |
| 展示 enrichment | `options_positions_lib.py` / `options_history_lib.py` | 浮盈、平仓预览 |
| Env UI | `lib/env/env_ui_manifest.py` | 期权相关默认与文案 |
### 1.2 运行模式
- `OKX_TRADE_MODE=options`:主站偏期权。
- `OKX_OPTIONS_ENABLED`:模块开关。
- 模拟 / 实盘由顶栏 `trading.mode` + `LIVE_TRADING_ENABLED` 等控制;实盘要求现货+期权权限(见实盘说明)。
### 1.3 与盈亏规格的关系
规格文档定义**产品规则**;本分析描述**代码落点**。
凡「已实现盈亏」对外展示(复盘 / 统计 / 顶栏已实现),在新规则下均以 `pnl_settled=1` 的账户差分为准;旧单无快照字段时走遗留口径,互不回写。
---
## 2. 开仓
### 2.1 入口
| 来源 | 路径 / 调用 |
|------|-------------|
| 面板 | `POST /api/options/open` |
| 条件单触发 | `options_cond_order_lib` → 同一套 `open_coin_option_buy_full` |
前端默认尺寸模式:**按可用余额打满**(`budget_full`);复利开时 UI 切 `compound_full`。
后端 `_normalize_size_mode`:币本位保留 `budget_full` / `compound_full`;USDC 遗留路径才落到 `sheets`。
### 2.2 尺寸模式
| mode | 含义 |
|------|------|
| `budget_full` | min(可用 USDT, 单笔预算) × 预算缓冲 → 张数(默认) |
| `compound_full` | 交易户可用 × 缓冲(受全仓上限 env 约束) |
| `sheets` | 指定张数 |
| `eth` / `eth_amount` | 指定币数量反推张数 |
预算计算:`compute_coin_budget_usdt` / `plan_coin_open_by_budget`(`options_margin_mode_lib.py`)。
### 2.3 流程
```text
请求 {inst_id, mode, sheets?, eth?, target_index?, profit_exit_*}
→ 交易模式门禁 / 条件单占用锁 / 持仓笔数上限
→ 有可卖标的币 → 拒开(须先卖回)
→ coin_budget_preview → 规划张数与买币量
→ fetch_trading_usdt → 写入 trading_usdt_before
→ 现货市价买币(× SPOT_BUY_BUFFER)→ options_spot_bridge=bought
→ 期权限价 IOC @ 卖一 → 等待成交
失败 → 回滚卖币
成功 → bridge=holding,_insert_coin_trade(open, pnl_settled=0)
→ 可选:目标位 / 止盈倍数 / 微信通知
```
### 2.4 关键 env
| 变量 | 默认(代码/清单) | 作用 |
|------|-------------------|------|
| `OKX_OPTIONS_COIN_COMPOUND` | true | 是否按交易户复利 |
| `OKX_OPTIONS_COIN_BUDGET_USDT` | (单笔预算) | 打满时的封顶 |
| `OKX_OPTIONS_BUDGET_BUFFER` | 1 | 预算缓冲 |
| `OKX_OPTIONS_COIN_SPOT_BUY_BUFFER` | 1.05 | 现货多买缓冲 |
| `OKX_OPTIONS_MAX_ACTIVE_POSITIONS` | 1 | 同时持仓笔数;0=不限 |
| `OKX_OPTIONS_OPEN_FILL_TIMEOUT_SEC` | — | 开仓等待成交超时 |
### 2.5 边界
- 开仓**只快照 before**,不结算盈亏。
- 持仓或桥未完成期间:`transfer_blocked_by_position_msg` 禁止划转。
- 一次一仓是对账规格前提;上限默认 1。
---
## 3. 平仓
### 3.1 三条平仓通道
| 通道 | 触发 | 门控 | 执行 |
|------|------|------|------|
| 手动 / Hub | 用户或中控 | 一般只校验流动性 | `close_option_by_bid1(require_recycle_gate=False)` |
| 目标位 | 指数触达目标 | 回收门控(权利金倍数 / 净盈亏 + 持续秒数) | `close_option_by_bid_depth` |
| 止盈倍数 | 买一回收 ≥ 权利金×(1+mult) | 自有回收条件,不走 close_gate | `close_option_by_bid_profit_exit` |
监控循环:`run_options_target_closes` / `run_options_profit_exits`。
### 3.2 流程
```text
卖期权(买一或深度,reduce-only)
→ 部分成交:累计平仓张数
→ 全平:
settle_closed_pnl(币侧/费用;有账户快照时 U 不以公式定稿)
→ maybe_sell_spot_after_close / sell_residual
→ try_settle_after_sellback
after − before → realized_pnl_usdt,pnl_settled=1
```
卖回失败:本单「待结算」,已实现不进统计;可 `POST /api/options/spot-bridge/retry-sell` 或监控扫尾。
### 3.3 回收门控 env
| 变量 | 默认 | 作用 |
|------|------|------|
| `OKX_OPTIONS_CLOSE_GATE_MODE` | premium | `premium` / `net_pnl` |
| `OKX_OPTIONS_CLOSE_RECYCLE_MULT_COIN` | 1.05 | 币本位回收倍数 |
| `OKX_OPTIONS_CLOSE_HOLD_SECONDS` | 20 | 条件持续秒数 |
| `OKX_OPTIONS_CLOSE_NET_PNL_MIN_USDT` | 0 | net_pnl 模式下限 |
### 3.4 边界
- 手动平仓可绕过回收门控;自动目标位不可。
- 冻结后任何划转、指数、复盘同步**不得改写** `realized_pnl_usdt`。
---
## 4. 期权显示
### 4.1 页面与 API
| UI | API | 数据源 |
|----|-----|--------|
| 期权链 / 到期 / 虚实度过滤 | `GET /api/options/chain` | `build_option_chain` |
| 下单弹窗报价与估张 | `GET /api/options/quote` | `quote_option_contract` + 币本位规划 |
| 持仓卡片 | `GET /api/options/positions` | 交易所持仓 + 本地 trade enrichment |
| 余额 / 顶栏 | `GET /api/options/balances` + header pack | 交易户 / 资金户 |
| 挂单 / 目标位 | `.../orders/pending`、`.../targets` | 本地 + 交易所 |
| 中控快照 | `GET /api/hub/options/snapshot` | `build_options_hub_snapshot` |
前端主文件:`options_panel.js`、`options_position_cards.js`、`options_expiry_countdown.js`。
### 4.2 展示口径(勿与冻结混淆)
| 指标 | 口径 |
|------|------|
| 持仓浮盈 | 买一回收估算(`display_pnl_*`),跟盘变 |
| 合计盈亏 | Σ已冻结 + 当前浮盈 |
| 顶栏钱包 | 交易所余额,不是盈亏账本 |
| 下单估权利金 / 杠杆 / 打满提示 | 报价瞬间估算,非成交结果 |
### 4.3 链过滤 env
`OKX_OPTIONS_CHAIN_MAX_DTE_DAYS`、`OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED`、`OKX_OPTIONS_ITM_MAX_DIST_USD`、`OKX_OPTIONS_DEFAULT_UNDERLY`。
---
## 5. 复盘
### 5.1 入口
- 页:`/options/review`
- 注册:`options_review_register.py`
- 逻辑:`options_review_lib.py` + `options_review_db.py`
- 前端:`options_review_panel.html`、`options_review.js`
### 5.2 数据流
```text
options_trades(已平)
→ sync_options_from_local_trades / ensure_local_review_synced
→ options_review_trades(history_key ≈ local_opt:{id})
→ 笔记 options_review_entries + 上传图 / 异步图表
→ 隐藏 options_review_hidden
```
主要 API:`POST /api/options/review/sync`,`GET/DELETE .../trades`,`POST .../entry`,`POST .../upload_slot`,`GET .../stats`。
### 5.3 盈亏读取规则
| 本地单状态 | 复盘展示 |
|------------|----------|
| 有 `trading_usdt_before` 且未 `pnl_settled` | 已实现 U 待结算(不展示假冻结) |
| `pnl_settled=1` | 只读冻结 `realized_pnl_usdt` |
| 无账户快照的历史单 | 遗留口径,不强制改写成账户差分 |
复盘是**消费者**,不负责改写结算真源。
---
## 6. 统计
### 6.1 入口
| 场景 | API / 函数 |
|------|------------|
| 期权面板 KPI | `GET /api/options/stats` → `compute_options_stats_auto` |
| 复盘页统计 | `GET /api/options/review/stats` |
| 顶栏窗口统计 | `header_options_stats_for_window` |
| 中控 | `GET /api/hub/options/stats` |
核心库:`options_stats_lib.py`。
### 6.2 窗口与构成
- 窗口:`today` / `7d` / `30d` / `all`(`normalize_stats_window`)。
- 模拟:本地已平且已结算样本。
- 实盘:交易所历史 + `options_exchange_pnl_freeze` 对齐后同一套 KPI。
- 面板合计:`净已实现(仅 settled) + 持仓浮盈`。
### 6.3 边界
- 待卖回、未冻结单**不计入**已实现。
- 统计不重新推导账户差分;最多做冻结补齐 / 修复类运维函数(如 `freeze_missing_realized_pnl_usdt`),与规格「冻结后不改」一致的运维路径需谨慎使用。
---
## 7. 对账
此处「对账」= **交易账户快照结算 + 本地/交易所持仓对齐**,不是已删除的永续 `reconcile_flat`。
### 7.1 结算真源(账户账)
| 步骤 | 函数 | 结果 |
|------|------|------|
| 开仓前 | `fetch_trading_usdt` | `trading_usdt_before` |
| 卖回后 | `try_settle_after_sellback` / `settle_closed_trades_from_trading_account` | `after`、`realized_pnl_usdt`、`pnl_settled=1` |
| 持仓禁划转 | `transfer_blocked_by_position_msg` | 前端灰 + API 拒 |
公式身份(规格 §6 思路):
```text
Σ 冻结已实现 + 当前浮盈 ≈ 面板合计盈亏
Σ 冻结已实现 + 区间净划转 ≈ 钱包 USDT 变动(在一次一仓、无并行污染前提下)
```
### 7.2 持仓对齐(仓位账)
监控 / `_sync_options_trades`:
| 动作 | 含义 |
|------|------|
| `reconcile_live_open_trades` / `sync_open_options_trades` | 交易所仍有仓而本地已关 → 纠偏重开等 |
| `backfill_closed_options_realized_pnl_from_history` | 历史回填;**跳过已冻结 U** |
| `sell_spot_after_options_sync` / `sweep_spot_after_options_flat` | 平仓后残留币扫尾,推动账户结算 |
### 7.3 双账清晰
| 账本 | 看见什么 |
|------|----------|
| 钱包(顶栏) | 交易所余额,随划转变 |
| 盈亏账 | 冻结历史 + 浮盈;划转不改已冻结单 |
---
## 8. 条件单
### 8.1 定位
中控下发「价格带触发 → 自动开仓 → 挂目标/止盈 → 等平仓结束」的自动化;与面板互斥:有活跃条件单时**锁定手动开仓**(`cond_order_blocks_manual_open_msg`)。
### 8.2 入口
| API | 作用 |
|-----|------|
| `GET/POST /api/hub/options/cond_orders` | 列表 / 创建 |
| `POST .../<hub_order_id>/cancel` | 取消 |
| `POST .../exit` | 更新出场参数 |
库:`options_cond_order_lib.py`。
Tick:监控循环 `run_cond_orders_tick` → `_tick_one`。
### 8.3 生命周期
```text
Hub 创建(trigger_px, deviation_pct, 认购/认沽, 出场)
→ options_cond_orders = armed
→ 指数落入 band_bounds
→ pick_nearest_itm_contract
→ 走 open_coin_option_buy_full(同人工开仓:before 快照、买币、IOC)
→ 挂目标位 / 止盈
→ 等待仓位平坦 → _end_order
→ 周四 23:00 等策略可 expire 未触发单
```
### 8.4 与主链关系
- **不另建盈亏账本**;触发开仓后的卖回冻结与人工单相同。
- 占用期间面板开仓按钮应不可用,避免双开破坏「一次一仓」。
---
## 9. 端到端对照表
| 阶段 | 写库 / 状态 | 用户可见 | 消费方 |
|------|-------------|----------|--------|
| 开仓成功 | trade open + before + bridge holding | 持仓卡、浮盈 | 监控、禁划转 |
| 期权已平、币未卖回 | 待结算 | 提示待卖回 | 统计不含已实现 |
| 卖回成功 | after、realized、settled=1 | 复盘/统计定格 | 对账完成 |
| 条件单 armed | cond_orders | 横幅锁定手动开仓 | 监控 tick |
| 目标位/止盈触发 | 平仓执行 | 持仓消失→卖回→冻结 | 同平仓链 |
---
## 10. 关键文件索引
| 主题 | 路径 |
|------|------|
| 路由 / 开平仓 API | `lib/options/options_register.py` |
| 币本位开仓 | `lib/options/options_coin_open_lib.py` |
| 现货桥 | `lib/options/options_spot_bridge_lib.py` |
| 平仓执行 | `lib/options/options_close_exec_lib.py` |
| 回收门控 | `lib/options/options_close_gate_lib.py` |
| 目标位 | `lib/options/options_target_lib.py` |
| 止盈 | `lib/options/options_profit_exit_lib.py` |
| 账户盈亏 | `lib/options/options_account_pnl_lib.py` |
| 统计 | `lib/options/options_stats_lib.py` |
| 复盘 | `lib/options/options_review_*.py` |
| 条件单 | `lib/options/options_cond_order_lib.py` |
| 监控 | `lib/options/options_monitor_lib.py` |
| Hub 快照 | `lib/options/options_hub_lib.py` |
| Hub 路由 | `lib/hub/hub_bridge_register.py` |
| Schema | `lib/options/options_db.py` |
| 面板 | `lib/options/templates/options_panel.html`、`lib/common/static/options_panel.js` |
---
## 11. 维护时注意
1. **改盈亏口径**:先改规格文档,再改 `options_account_pnl_lib` 与复盘/统计读取,避免三处漂移。
2. **改开仓默认尺寸**:同时看前端 `currentSizeMode`、HTML `checked`、后端 `_normalize_size_mode`(币本位勿再压成 `sheets`)。
3. **改门控秒数/缓冲**:代码默认、`env_ui_manifest` 文案与 `_RUNTIME_ENV_DEFAULTS` 一并对齐;已有 `.env` 旧值不会自动覆盖。
4. **条件单与手动开仓**:任何新开仓入口都要接 `cond_order_blocks_manual_open_msg`(或等价锁)。
5. **对账前提**:一次一仓 + 持仓禁划转 + 卖回后冻结;破坏任一前提,钱包差分将无法与单笔盈亏对齐。
+263 -206
View File
@@ -1,206 +1,263 @@
# 规格:期权盈亏按交易账户结算(一次一仓)
> 状态:已定稿,按本文实现
> 适用范围:币本位期权(模拟盘优先落地;实盘同规则)
> 目标:复盘盈亏、数据统计盈亏与交易账户真实 USDT 变动一致,可对账、可冻结、可商用
---
## 1. 目标与原则
### 1.1 要解决什么
此前单笔盈亏混用多套口径(权利金×指数、卖回−买币预算、实时指数重算),导致:
- 持仓「合计盈亏」与复盘单笔不一致
- 数字随行情跳动
- 后期账本无法审计
### 1.2 原则
| 原则 | 说明 |
|------|------|
| 真源 | **交易账户 USDT 快照差分** 为本单已实现盈亏真源 |
| 一次一仓 | 同一账户同一时间只允许一笔期权持仓 |
| 卖回后结算 | 未卖回标的币前,本单不进入已实现 |
| 冻结 | 结算完成后永不修改 |
| 禁划转 | 持仓期间禁止资金划转(前端灰 + 后端拒) |
| 双账清晰 | 顶栏余额 = 钱包;合计/复盘盈亏 = 冻结历史 +(持仓时)浮盈 |
---
## 2. 产品规则(用户可见)
### 2.1 开仓前
1. **有可卖标的币(ETH/BTC)→ 拒开仓**
- 提示:先卖回 USDT(可走自动扫尾/卖回)。
- 粉尘:低于现货最小可卖量可忽略;达到可卖量一律视为有币并拒开。
2. **交易账户 USDT 快照** `trading_usdt_before`
- 取开仓前(买币前)交易账户 USDT。
- 开仓前若有过多次划转:以**开仓前最后一次划转完成后**的余额为准(即开仓那一刻的余额)。
3. 通过检查后才允许买币并开期权。
### 2.2 持仓中
1. **划转按钮灰色不可点**;API 同步拒绝。
2. **合计盈亏(动态)** =
`Σ已冻结历史盈亏` + `当前持仓浮盈`
3. 本单未卖回结算前:不计入「已冻结历史」;浮盈跟盘更新。
### 2.3 平仓与卖回
1. 期权平仓后触发**自动卖回**残留标的币。
2. **卖回未完成**:本单状态为「待结算」,已实现盈亏不写死。
3. **卖回成功后**:再拍交易账户 USDT 快照 `trading_usdt_after`。
### 2.4 结算与冻结
```
本单已实现盈亏(U) = trading_usdt_after − trading_usdt_before
```
1. 写入本单并标记已冻结。
2. **此后不得因划转、指数变动、复盘同步而修改。**
3. 期权复盘、数据统计的已实现盈亏均读此冻结值。
### 2.5 划转与资金
| 时机 | 行为 |
|------|------|
| 无持仓、无待卖回 | 允许划转;顶栏资金立即刷新 |
| 持仓中或待卖回 | 禁止划转 |
| 划转后 | 只影响下一笔开仓前的 `usdt_before`;**不重算**已冻结历史单 |
---
## 3. 生命周期
```text
[空仓, 无残留币]
│
├─ 有可卖币? ──是──> 拒开仓「请先卖回」
│
├─ 快照 trading_usdt_before
├─ 买币 → 开期权
│
[持仓中] 划转禁用;合计 = 冻结历史 + 浮盈
│
├─ 平仓
│
[待卖回] 已实现未结算;划转仍禁用
│
├─ 自动/重试卖回成功
├─ 快照 trading_usdt_after
├─ realized = after − before → 冻结
│
[已结算] 复盘/统计只读冻结值
```
---
## 4. 数据字段(实现约定)
在 `options_trades` 增加(或等价持久化):
| 字段 | 含义 |
|------|------|
| `trading_usdt_before` | 开仓前交易账户 USDT |
| `trading_usdt_after` | 卖回完成后交易账户 USDT |
| `realized_pnl_usdt` | 冻结已实现 = after − before(结算后写入) |
| `pnl_settled` | 0/1,是否已按账户差分结算冻结 |
说明:
- `budget_usdt` 仍表示本笔买币花费,**不得**当作开仓前账户快照。
- 币数量 `realized_pnl` 可保留供期权历史展示(币),与 U 冻结分离。
- 统计/复盘的 **U 盈亏** 只认 `pnl_settled=1` 时的 `realized_pnl_usdt`。
---
## 5. 模块行为
### 5.1 开仓 `open_coin_option_buy_full`
1. 检查一次一仓 / 未完成桥。
2. `fetch_trading_coin_available`:可卖则拒开。
3. 读取交易账户 USDT → `trading_usdt_before`。
4. 买币、下单、写库(带 before,`pnl_settled=0`)。
### 5.2 划转 `POST /api/options/transfer`
1. 若存在 `options_trades.status='open'` 或桥状态 ∈ {bought, holding, pending_sell} → **403/业务错误**。
2. 前端:持仓或待卖回时划转按钮 `disabled` + 提示「持仓中不可划转」。
### 5.3 卖回结算
在 `sell_residual_after_option_flat`(及 retry-sell)桥关闭成功后:
1. 读取交易账户 USDT → `trading_usdt_after`。
2. 对本桥关联且未结算的已平仓位:
`realized_pnl_usdt = after - before`,`pnl_settled=1`。
3. **停止**用「权利金×指数」或「卖回−预算」覆盖已结算单。
粉尘关闭桥(无法卖出):
- 仍拍 `usdt_after` 并结算(差额含粉尘损失);或
- 记 `pnl_settled` 并附 message「粉尘忽略」——实现取:拍售后余额并冻结,保证可对账。
### 5.4 数据统计
- `net_realized_pnl` / 已平相关:仅 `pnl_settled=1` 的冻结值之和。
- `total_pnl`(持仓页合计)= `net_realized_pnl` + `open_float_pnl`。
- 待结算已平单:不计入已实现,也不重复计入浮盈(浮盈仅当前期权持仓)。
- **顶栏三项**(总交易 / 胜率 / 盈亏比):与上同一过滤口径(`header_options_stats_for_window`)。
### 5.5 期权复盘
- 同步本地已平:优先读 `realized_pnl_usdt`(且已结算)。
- 未结算:复盘显示「待结算」或暂不导入已实现 U。
- 禁止再用实时指数改写已冻结 U。
### 5.6 期权历史列表
- **仍按币**展示权利金/币盈亏(不强制改成 U)。
- U 口径只用于复盘与数据统计。
---
## 6. 对账关系(商用)
```
Σ 已冻结单笔盈亏 + 当前浮盈 ≈ 合计盈亏展示
Σ 已冻结单笔盈亏 + 期间净划转 ≈ 交易账户相对「首笔 before」的累计变化
```
差异来源应可归因:粉尘、手续费计入方式、卖回滑点(已含在 after−before 内)。
禁止用展示公式回写钱包余额。
---
## 7. 非目标 / 不做
- 不用买币预算 `budget_usdt` 当单笔盈利。
- 不用「净币 × 单一指数」覆盖已结算账户差分。
- 持仓中不因行情重算已冻结历史。
- 不把期权历史列表强制改为 U 展示。
---
## 8. 验收清单
- [ ] 开仓前有可卖 BTC/ETH → 拒开并提示卖回
- [ ] 开仓写入 `trading_usdt_before`
- [ ] 持仓中划转按钮灰 + API 拒绝
- [ ] 平仓未卖回前,统计已实现不含本单
- [ ] 卖回后 `realized = after − before` 并 `pnl_settled=1`
- [ ] 再次同步复盘/统计,冻结值不变
- [ ] 合计盈亏 = 冻结历史 + 浮盈(有持仓时动态)
- [ ] 空仓划转后顶栏资金更新,历史冻结单不变
---
## 9. 修订记录
| 日期 | 说明 |
|------|------|
| 2026-10-09 | 初版定稿:交易账户快照差分 + 一次一仓 + 持仓禁划转 + 卖回后冻结 |
# 规格:期权盈亏双口径结算(一次一仓)
> 状态:已定稿(2026-10-13 修订)
> 适用范围:币本位期权(模拟盘优先;实盘同规则)
> 目标:复盘可对账、数据统计跟期权历史一致;初始权利金 / 盈利率可展示;错账可脚本修复
---
## 1. 目标与原则
### 1.1 要解决什么
此前单笔盈亏混用多套口径,且卖回按开仓权利金封顶,导致:
- 盈利单(如约 1.5 倍)在复盘被冻成亏损(账户差分在盈利币未卖回时结算)
- 复盘与数据统计共用账户冻结,与「历史币盈亏 × 卖回价」不一致
- 缺少初始权利金(U)、盈利率展示
### 1.2 原则
| 原则 | 说明 |
|------|------|
| 双口径 | **A 复盘** = 交易账户 USDT 快照差分;**B 数据统计** = 期权历史币盈亏 × 平仓卖回价 |
| 一次一仓 | 同一账户同一时间只允许一笔期权持仓 |
| 卖光再结算 | A 口径:未卖光可卖标的币前,本单不冻结 |
| 冻结 | A 结算完成后永不修改;B 按平仓卖回价冻结后不跟盘 |
| 禁划转 | 持仓 / 待卖回期间禁止资金划转 |
| 权利金不含多买 | 初始权利金 = 开仓指数 × 权利金币量(不含现货缓冲多买) |
---
## 2. 双口径定义
### 2.1 口径 A — 期权复盘(交易账户)
```
本单已实现盈亏(U) = trading_usdt_after − trading_usdt_before
```
- **消费者**:期权复盘「交易记录」「复盘记录」、复盘 KPI
- **刻意含多买缓冲 / 滑点 / 手续费**:便于交易账户对账
- **门禁**:全部期权仓位已平 **且** 标的币无可卖量(粉尘可忽略)后才拍 `after` 并冻结
### 2.2 口径 B — 期权数据统计(历史 × 卖回价)
```
统计已实现盈亏(U) = 期权历史.realized_pnl(币) × 平仓卖回价
```
- **平仓卖回价**:优先卖回成交均价 / 冻结的 `close_index_px`;缺失时再用当时指数一次性冻结
- **消费者**:期权面板「数据统计」合计/胜率/盈亏比、数据统计页 KPI、顶栏期权窗口统计
- **不含**账户多买缓冲差分;与复盘 A **独立**,禁止混用
### 2.3 初始权利金与盈利率
| 字段 | 定义 |
|------|------|
| 初始权利金(U) | `open_index_px × premium_paid`(`premium_paid` 为开仓权利金币量;**不含** `budget_usdt` 多买) |
| 盈利率(复盘) | `realized_pnl_usdt(A) / initial_premium_usdt` |
| 盈利率(统计) | `统计盈亏(B) / initial_premium_usdt`(展示层可算,不强制落库) |
复盘列表与复盘详情须展示:初始权利金(U)、盈利率。
---
## 3. 产品规则
### 3.1 开仓前
1. 有可卖标的币(ETH/BTC)→ 拒开仓(粉尘低于现货最小可卖可忽略)。
2. 拍交易账户 USDT → `trading_usdt_before`。
3. 开仓前若有多次划转:以**开仓前最后一次划转完成后**的余额为准(即开仓瞬间余额)。
4. 开仓失败:回滚卖币,并拍**最新**资金快照(不留下脏 before / 半成品桥阻挡后续)。
5. 写入 `initial_premium_usdt = open_index_px × premium_paid`(开仓成功后)。
### 3.2 持仓中
1. 划转按钮灰 + API 拒绝。
2. 面板合计盈亏(动态)= `Σ口径B已冻结` + `当前持仓浮盈`。
3. 本单未完成 A 结算前:复盘显示「待结算」,不写假 U。
### 3.3 平仓与卖回
1. 系统可多次部分平仓;**以最后一次全部平仓完成**为准进入卖回。
2. 期权空仓后:**卖光**交易账户该标的可卖币(**禁止**用开仓权利金 `coin_bought` 封顶)。
3. `coin_bought` 仅用于开仓阶段清理过量买币,不作平仓卖回上限。
4. 卖回未完成(仍有可卖币):桥保持待卖回,**不**进入 A 结算。
5. 真粉尘(低于现货最小):可关桥并进入 A 结算(差额含粉尘损失)。
### 3.4 结算与冻结(A)
1. `realized_pnl_usdt = after − before`,`pnl_settled=1`。
2. 此后不得因划转、指数、复盘同步、监控公式而修改。
3. 仅复盘读此冻结值。
### 3.5 划转
| 时机 | 行为 |
|------|------|
| 无持仓、无待卖回 | 允许划转 |
| 持仓中或待卖回 | 禁止划转 |
| 划转后 | 只影响下一笔开仓前的 `usdt_before`;不重算已冻结 A/B |
---
## 4. 状态机
```text
IDLE
│ 有可卖币? ──是──> 拒开仓
│ 快照 trading_usdt_before(划转取最后一次后余额)
▼
OPENING ──失败──> 回滚卖币 → 最新资金快照 → IDLE
│ 成功(一次一仓);写 initial_premium_usdt
▼
OPEN(禁划转)
│ 可多次部分平仓
▼
CLOSING(交易所仓位未清零则停留)
│ 仓位 = 0
▼
SELLING_SPOT(卖光可卖标的币;禁止权利金封顶)
│ 可卖币 < minSz(真粉尘)或已空
▼
SETTLE_A(after − before → 冻结;仅此态可写 pnl_settled=1)
▼
IDLE
```
**硬门禁**
| 条件 | 行为 |
|------|------|
| OPEN / CLOSING / SELLING_SPOT | 禁止划转 |
| 未进 SETTLE_A | 复盘 U 为待结算 |
| pnl_settled=1 | 禁止覆盖 A |
| SETTLE_A | 要求 open_positions=0 且无可卖标的币 |
实现上可为显式状态字段,或由 `options_trades.status` + 桥状态 + `pnl_settled` 等价表达,但**结算入口必须统一校验门禁**。
---
## 5. 数据字段
### 5.1 `options_trades`
| 字段 | 含义 |
|------|------|
| `trading_usdt_before` | 开仓前交易账户 USDT |
| `trading_usdt_after` | 卖光后交易账户 USDT |
| `realized_pnl_usdt` | **口径 A** 冻结值 |
| `pnl_settled` | 0/1,A 是否已冻结 |
| `realized_pnl` | 币数量净盈亏(期权历史展示) |
| `close_index_px` | 平仓/卖回价(口径 B 用) |
| `open_index_px` | 开仓指数 |
| `premium_paid` | 开仓权利金币量 |
| `initial_premium_usdt` | 初始权利金 U = open_index × premium_paid |
| `budget_usdt` | 买币花费(含缓冲),**不得**当权利金或 before |
### 5.2 `options_review_trades`
| 字段 | 含义 |
|------|------|
| `realized_pnl_total` | **仅口径 A**(或待结算空) |
| `initial_premium_usdt` | 初始权利金 U |
| `profit_rate` | A / 初始权利金(小数,如 1.5 = 1.5 倍) |
### 5.3 `options_exchange_pnl_freeze`
实盘统计辅助:历史 key → 口径 B 的 U 冻结。
---
## 6. 模块行为
### 6.1 开仓 `open_coin_option_buy_full`
1. 一次一仓 / 未完成桥检查;可卖币拒开。
2. `trading_usdt_before`;买币、下单、写库(`pnl_settled=0`,`initial_premium_usdt`)。
3. 桥 `coin_bought` 可记为权利金币量(供开仓清过量),**平仓卖回不得用其封顶**。
### 6.2 划转 `POST /api/options/transfer`
持仓或桥 ∈ {bought, holding, pending_sell} → 拒绝。
### 6.3 卖回与 A 结算
`sell_residual_after_option_flat`(及 retry / sweep):
1. 空仓后卖光可用标的币(`coin_amount=None`)。
2. 仍有可卖 → 桥 `pending_sell`,返回未完成,**不结算**。
3. 仅粉尘/已空 → 关桥 → `try_settle_after_sellback`(内再校验无可卖币)→ A 冻结。
### 6.4 数据统计(口径 B)
- `net_realized_pnl` / 胜率 / 盈亏比:历史币盈亏 × 卖回价。
- **不读** `pnl_settled` 账户差分。
- 数据统计页不得再打复盘 stats API 作为盈亏真源。
### 6.5 期权复盘(口径 A)
- 同步本地已平:优先 `pnl_settled=1` 的 `realized_pnl_usdt`。
- 未结算:显示待结算,不导入假 U。
- 同步 `initial_premium_usdt`、`profit_rate`。
### 6.6 期权历史列表
仍按**币**展示权利金/币盈亏;U 只用于复盘(A)与数据统计(B)。
---
## 7. 对账关系
```
口径 A: Σ 已冻结单笔 A + 期间净划转 ≈ 交易账户相对首笔 before 的累计变化
口径 B: Σ (历史币盈亏 × 卖回价) ≈ 面板数据统计已实现
```
差异应可归因:粉尘、滑点、手续费(A 内已含);B 不含多买缓冲。
---
## 8. 非目标 / 不做
- 不用 `budget_usdt` 当单笔盈利或初始权利金。
- 不用口径 A 覆盖数据统计,也不用口径 B 覆盖复盘。
- 持仓中不因行情重算已冻结 A/B。
- 不把期权历史列表强制改为 U。
---
## 9. 历史数据修复
对已错误冻结的账户差分(典型:币盈亏>0 且 A 为负,且 `coin_sold≈premium_paid≪premium_received`):
1. 回填 `initial_premium_usdt = open_index_px × premium_paid`。
2. 口径 B:按币盈亏 × 卖回价重算统计侧(含 `options_exchange_pnl_freeze`)。
3. 口径 A:卖回完整可重建则重算;否则 `pnl_settled=0` 标 `needs_resettle` / 异常,勿盲信错账。
4. 重刷 `options_review_trades.realized_pnl_total` / `profit_rate`。
脚本:`scripts/repair_options_dual_pnl.py`(dry-run → CSV → apply)。
---
## 10. 验收清单
- [ ] 盈利约 1.5 倍单:卖回含盈利币;复盘 A 为正,约等于账户差分
- [ ] 卖回不得按开仓权利金封顶
- [ ] 仍有可卖币时不 `pnl_settled=1`
- [ ] 复盘展示初始权利金、盈利率
- [ ] 数据统计 = 历史币盈亏 × 卖回价,与复盘 A 可不同
- [ ] 开仓写入 before + initial_premium;失败有最新快照/回滚
- [ ] 持仓禁划转;空仓划转不影响已冻结
- [ ] 历史错账可用脚本修复
---
## 11. 修订记录
| 日期 | 说明 |
|------|------|
| 2026-10-09 | 初版:交易账户快照差分 + 一次一仓 + 持仓禁划转 + 卖回后冻结 |
| 2026-10-13 | 修订:双口径 A/B;卖光再结算;初始权利金/盈利率;状态机;历史修复脚本 |