feat期权盈亏按交易账户快照结算:一次一仓、持仓禁划转、卖回后冻结。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-09 00:29:47 +08:00
parent e0e2a94322
commit 260bfd1f29
12 changed files with 659 additions and 157 deletions
@@ -0,0 +1,205 @@
# 规格:期权盈亏按交易账户结算(一次一仓)
> 状态:已定稿,按本文实现
> 适用范围:币本位期权(模拟盘优先落地;实盘同规则)
> 目标:复盘盈亏、数据统计盈亏与交易账户真实 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`。
- 待结算已平单:不计入已实现,也不重复计入浮盈(浮盈仅当前期权持仓)。
### 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 | 初版定稿:交易账户快照差分 + 一次一仓 + 持仓禁划转 + 卖回后冻结 |