292 lines
18 KiB
Markdown
292 lines
18 KiB
Markdown
# 互换价格字段命名规范决策
|
||
|
||
> 本文档是**命名规范的决策与论证**,回答"应该叫什么、为什么这么叫"。
|
||
> 现状梳理(字段流向、存储显示规则)见姊妹文档 [互换交易价格字段存储与显示规范.md](互换交易价格字段存储与显示规范.md)。
|
||
> 适用范围:互换业务(收益互换)涉及的价格、方向、时点字段。期权/远期业务字段不在本文档范围。
|
||
|
||
---
|
||
|
||
## 一、为什么命名必须先定下来
|
||
|
||
现状是**同一概念多套名字、名字与含义颠倒**,已经反复造成 Bug。三条最严重的证据:
|
||
|
||
1. **`PosiNetPrice` 名为"Net",实为"含费全价"**。真正的净价是 `PosiNetNoFeePrice`。连现状文档(《互换交易价格字段存储与显示规范》)都把它误标成"期初净价"——文档作者都被字段名骗了。
|
||
2. **两套并行命名无对应关系**:持仓表 `PosiNetPrice` ↔ 流水表 `TradingAmountFeeAvg`,持仓表 `PosiGrossPrice` ↔ 流水表 `TradingAmountAvg`,名字里没有一处对得上。
|
||
3. **前端同一中文标签绑不同字段**:`成交净价` 一处绑 `TradingAmountNet`,另一处绑 `TradingAmountNetAvg`;`期初标的价格` 录入页绑 `PosiGrossPrice`,平仓明细页绑 `PosiNetPrice`。
|
||
|
||
命名不定下来,后续每个改动都要反复核对数据流向,且极易引入新 Bug。本文档的目的是**把每个维度的命名一次性钉死,给出对比理由**。
|
||
|
||
---
|
||
|
||
## 二、三个维度的决策
|
||
|
||
互换价格有三个正交维度,必须分别命名:
|
||
|
||
| 维度 | 取值 | 现状混乱 |
|
||
|---|---|---|
|
||
| **A. 价格种类** | 全价 / 净价 | Net/Gross 颠倒;Posi vs TradingAmount 两套 |
|
||
| **B. 是否含交易费** | 含费 / 不含费 | Fee 标记位置在两套体系里相反 |
|
||
| **C. 时点** | 期初 / 期末 / 盯市 | Start/Begin/End/Initial/Final/Spot/Open 散布,无统一规则 |
|
||
|
||
外加一个**方向维度**(收取方/支付方),目前用中文枚举,trade_swap 老表用 Get/Pay。
|
||
|
||
下面逐项决策。
|
||
|
||
---
|
||
|
||
## 三、维度 A:价格种类 —— 采用 Clean / Dirty
|
||
|
||
### 候选对比
|
||
|
||
| 方案 | 优点 | 缺点 | 评价 |
|
||
|---|---|---|---|
|
||
| **`Clean / Dirty`** | 债券行业标准术语;歧义最小;与中债估值表 `dirty_price_close`/`net_price`、量化库常见命名一致 | 非 OTDer 可能需要学习成本 | **✅ 推荐** |
|
||
| `Net / Gross`(现状持仓表) | 简短 | **本项目已颠倒**(PosiNetPrice 是含费全价);Net/Gross 在金融里本身就有"净值/毛额"歧义,不专指债券净价全价 | ❌ 已证明不可行 |
|
||
| `Clean / Full` | Full 含义尚可 | 不如 Dirty 标准;中债表用 dirty,不统一 | △ |
|
||
| `净价 / 全价`(中文直译) | 业务人员熟悉 | C# 字段用中文不合规;与现有英文体系混搭更乱 | ❌ |
|
||
|
||
### 决策
|
||
|
||
**全价用 `Dirty`,净价用 `Clean`。**
|
||
|
||
理由:
|
||
1. **行业术语,零歧义**。Clean/Dirty 是债券领域全世界通用的无歧义术语(clean price = 不含应计利息,dirty price = 含应计利息)。
|
||
2. **与数据源一致**。录入默认价格来源 `china_bond_valuation` 表用的就是 `dirty_price_close`(日终估值全价)和 `net_price`(估价净值)。字段命名与数据源对齐,减少心智负担。
|
||
3. **现状 Net/Gross 已被证明失败**。它在本项目里语义颠倒,且 Net 在不同字段里含义不同(PosiNetPrice 的 Net = 含费,PosiNetNoFeePrice 的 Net = 净价),是混乱根源,必须废弃。
|
||
|
||
### 与"费"维度组合后的完整 4 象限见第五节。
|
||
|
||
---
|
||
|
||
## 四、维度 B:费用 —— 采用 Fee / NoFee
|
||
|
||
### 先澄清:"费"是什么
|
||
|
||
**"费" = 交易费用 / 佣金(TradingFee)**,由 `TradeFeeHelper.CalcPriceWithFee(price, fee, qty)` 计算:`含费价 = 不含费价 + 单位佣金`。它**不是**应计利息(应计利息体现在 Clean/Dirty 维度),也**不是**利息腿费用。
|
||
|
||
### 候选对比
|
||
|
||
| 方案 | 优点 | 缺点 | 评价 |
|
||
|---|---|---|---|
|
||
| **`Fee / NoFee`(后缀)** | 现状已在用(PosiNetNoFeePrice/TradingAmountFeeAvg);直白 | — | **✅ 推荐**(沿用现有约定,零迁移成本) |
|
||
| `Gross / Net`(用 Net 表示含费) | — | 与维度 A 的 Clean/Dirty 严重冲突,两套词打架 | ❌ |
|
||
| `WithFee / WithoutFee` | 更明确 | 太长;后缀冗余 | △ |
|
||
| `PreFee / PostFee` | 金融衍生品常见 | 偏衍生品语境,互换收益端用着别扭 | △ |
|
||
|
||
### 决策
|
||
|
||
**含费用 `Fee` 后缀,不含费用 `NoFee` 后缀。**
|
||
|
||
理由:现状流水表已经是这套(`TradingAmountAvg` 不含费 / `TradingAmountFeeAvg` 含费 / `TradingAmountNetAvg` 净价不含费 / `TradingAmountNetFeeAvg` 净价含费),**这个约定本身没问题**,问题是持仓表用了另一套颠倒的 Posi 命名。统一到 Fee/NoFee 即可。
|
||
|
||
---
|
||
|
||
## 五、维度 A+B 组合:价格 4 象限最终命名
|
||
|
||
### 决策表
|
||
|
||
| 业务含义 | **规范命名** | 现状持仓表 | 现状流水表 |
|
||
|---|---|---|---|
|
||
| 全价 · 不含费 | `XxxDirtyPrice` | `PosiGrossPrice` | `TradingAmountAvg` |
|
||
| 全价 · 含费 | `XxxDirtyFeePrice` | `PosiNetPrice` ⚠️ | `TradingAmountFeeAvg` |
|
||
| 净价 · 不含费 | `XxxCleanPrice` | `PosiNetNoFeePrice` | `TradingAmountNetAvg` |
|
||
| 净价 · 含费 | `XxxCleanFeePrice` | `PosiNetFeePrice` | `TradingAmountNetFeeAvg` |
|
||
|
||
其中 `Xxx` 是时点前缀(见第六节)。
|
||
|
||
### 命名规则一句话
|
||
|
||
> **`{时点}{Dirty|Clean}{Fee|}{Price}`** —— Dirty/Clean 选价格种类,Fee 出现表示含费、不出现表示不含费。
|
||
|
||
例:`EntryDirtyPrice`(期初全价不含费)、`EntryDirtyFeePrice`(期初全价含费)、`ExitCleanPrice`(期末净价不含费)。
|
||
|
||
---
|
||
|
||
## 六、维度 C:时点 —— 采用 Entry / Exit(+ Market)
|
||
|
||
这是你重点要求对比的部分。先看现状,再逐词对比。
|
||
|
||
### 现状:时点词散布(统计自实体字段)
|
||
|
||
| 现状词 | 出现次数(实体字段) | 用在什么字段 |
|
||
|---|---|---|
|
||
| `Start` | 36 | StartDate(起始日) |
|
||
| `Spot` | 28+4 | SpotPrice(期初价,期权/远期)、GetSpotPrice/PaySpotPrice(trade_swap) |
|
||
| `Open` | 3 | OpenPrice、OpenTradePrice(开仓价) |
|
||
| `Init/Initial` | 4 | InitYtm、InitialSpotPrice([NotMapped]) |
|
||
| `Final` | 10 | FinalPrice(期末价)、GetFinalPrice/PayFinalPrice |
|
||
| `End` | 12 | EndDate(结束日) |
|
||
| `Close` | 21 | ClosePrice(收盘价,行情侧) |
|
||
| `Maturity` | 14 | MaturityDate(到期日) |
|
||
| `Entry/Exit` | **0** | 未使用 |
|
||
|
||
**现状问题**:表达"期初价"用了 `Spot`、`Open`、`Init`、`Initial` 四种;表达"期末价"用了 `Final`、`End`、`Close` 三种;且 `Close` 还和"行情收盘价"语义重叠。
|
||
|
||
### 候选词逐个对比
|
||
|
||
#### 期初价(开仓成本价)候选
|
||
|
||
| 方案 | 含义直觉 | 优点 | 缺点 | 评价 |
|
||
|---|---|---|---|---|
|
||
| **`Entry`** | 入场/建仓 | 交易术语,明确"进入持仓那一刻";与 `Exit` 对称;**不与现有任何日期/价格词冲突** | 全新词,需迁移 | **✅ 推荐** |
|
||
| `Start` | 开始 | 与 `StartDate`(起始日)一致,延续性最好 | **与日期混淆**:`StartPrice` 容易被误读成"起始日当天的价格"而非"建仓成本价";价格 vs 日期语义纠缠 | △ |
|
||
| `Begin` | 开始 | 同 Start | 同 Start;且与 Start 二选一造成二次混乱(现已有 Start,再加 Begin 是雪上加霜) | ❌ |
|
||
| `Initial` / `Init` | 初始 | 语义尚可 | `Init` 是缩写不规整;现状 `InitYtm` 已用,但 `InitialPrice` 太长;**Init/Initial 两种并存**(现状就有 `InitYtm` 和 `InitialSpotPrice`)本身就是混乱证据 | ❌ |
|
||
| `Spot` | 即期 | 现状 trade_swap 在用 | **语义错误**:Spot 在金融里指"即期(spot vs forward)",不是"期初";互换里强行用 Spot 表示期初是误用;且与"即期价格"概念冲突 | ❌ |
|
||
| `Open` | 开仓 | 有"开仓"意味 | `OpenPrice` 在期权语境指"开盘价"(open=开盘 vs close=收盘),互换用 Open 会与行情术语打架 | ❌ |
|
||
|
||
#### 期末价(平仓/到期价)候选
|
||
|
||
| 方案 | 含义直觉 | 优点 | 缺点 | 评价 |
|
||
|---|---|---|---|---|
|
||
| **`Exit`** | 出场/了结 | 与 Entry 对称;明确"退出持仓那一刻";**不与 Close/Maturity 冲突** | 全新词,需迁移 | **✅ 推荐** |
|
||
| `Final` | 最终 | 现状在用(FinalPrice) | `Final` 在期权语境常指"到期(final/expiration)",与互换"平仓了结"语义有偏差;且 Final 给人"永远结束"的感觉,但互换可能多次部分平仓(每次都是 Exit) | △ |
|
||
| `End` | 结束 | 简短 | 与 `EndDate`(结束日)混淆,同 Start 的问题 | ❌ |
|
||
| `Close` | 收盘 | 行情侧在用(ClosePrice) | **语义已被占用**:Close = 收盘行情价;互换平仓价用 Close 会和"每日盯市收盘价"混淆。这是现状混乱之一 | ❌ |
|
||
| `Maturity` | 到期 | 债券标准术语 | 只适用于"持有到期",不适用于"提前平仓";互换平仓≠到期 | ❌(仅 MaturityDate 用于到期日) |
|
||
|
||
### 决策
|
||
|
||
**期初用 `Entry`,期末用 `Exit`,每日盯市用 `Market`。**
|
||
|
||
### 为什么是 Entry/Exit 而不是其他 —— 三条核心理由
|
||
|
||
**理由 1:Entry/Exit 是唯一一组"互相对称、且不与现有词冲突"的方案。**
|
||
|
||
| 对称组 | 冲突情况 |
|
||
|---|---|
|
||
| **Entry / Exit** | ✅ 零冲突,现有代码 0 处使用 |
|
||
| Start / End | ❌ 与 StartDate/EndDate 日期字段冲突 |
|
||
| Open / Close | ❌ 与行情开盘价/收盘价冲突 |
|
||
| Initial / Final | △ Initial 太长,Final 偏"到期"语义 |
|
||
| Begin / End | ❌ 与 Start 重复造词 |
|
||
|
||
互换价格的核心心智模型是"**Entry(建仓成本)vs Exit(了结价格)**"——盈亏 = Exit − Entry。Entry/Exit 直接表达这个对偶关系,其他词都带着别的语义包袱。
|
||
|
||
**理由 2:互换"多次部分平仓"语义下,Entry/Exit 比 Start/End/Final 更准确。**
|
||
|
||
互换持仓期间可以有多次部分平仓(每次部分平仓都是一次 Exit),不是"一次性 Start→End"。`Final`(最终)暗示"只有最后一次",`Start`/`End` 暗示"单一起止点",都不符合"多次 Exit"的现实。`Exit` 天然支持"多次出场"语义(每次平仓 = 一次 Exit)。
|
||
|
||
**理由 3:与"盯市价"清晰分层。**
|
||
|
||
| 时点 | 命名 | 含义 |
|
||
|---|---|---|
|
||
| 建仓时 | `Entry*` | 成本价(固定) |
|
||
| 平仓时 | `Exit*` | 了结成交价(每次平仓一个) |
|
||
| 持仓期间每日 | `Market*` | 盯市价(随行情变动) |
|
||
|
||
三者职责清晰:Entry 是成本基线,Exit 是实现盈亏的时点,Market 是浮动盈亏的基准。现状 `UnderlyingPrice`(盯市价)改为 `MarketDirtyPrice` 后,与 Entry/Exit 形成 `Entry / Market / Exit` 完整时点链。
|
||
|
||
### 收益率字段同理
|
||
|
||
| 含义 | 规范命名 | 现状 |
|
||
|---|---|---|
|
||
| 期初 YTM | `EntryYtm` | InitYtm |
|
||
| 平仓 YTM | `ExitYtm` | ytm / Ytm |
|
||
|
||
---
|
||
|
||
## 七、维度 D:方向 —— 收取/支付方的命名
|
||
|
||
### 现状
|
||
|
||
| 位置 | 现状命名 | 问题 |
|
||
|---|---|---|
|
||
| `SwapDirectionEnum`(V2 在用) | `收取 / 支付`(中文) | C# 字段用中文不合规;与英文代码混排突兀 |
|
||
| `trade_swap`(老表,已弃用互换主流程) | `Get* / Pay*`(如 GetSpotPrice/PaySpotPrice) | Get 语义弱,不像金融术语;你已指出这是老互换遗留 |
|
||
| `PositionTypeFlag`(多空) | `Long / Short` | ✅ 这个没问题,标准术语 |
|
||
| 业务文档/前端 | "收取方/支付方"、"固定端/浮动端" | 中文展示 OK |
|
||
|
||
### 候选对比(英文方向词)
|
||
|
||
| 方案 | 优点 | 缺点 | 评价 |
|
||
|---|---|---|---|
|
||
| **`Rcv / Pay`** | 金融工程标准(IRS 收付固定方叫 Receive/Pay);FINCAD/Bloomberg/量化库通用;3字母缩写简洁 | Rcv 需要新人熟悉"是 receive 缩写" | **✅ 推荐** |
|
||
| `Receive / Pay` | 不缩写,更明确 | Receive 较长,字段名 `ReceiveDirtyPrice` 偏长 | △ |
|
||
| `Get / Pay`(现状老表) | 现状 | Get 不是金融术语,语义弱("获得"太泛);与 Pay 不对称(Pay 是金融词,Get 是口语) | ❌ |
|
||
| `Long / Short` | 标准术语 | **语义不同**:Long/Short 是多头/空头(PositionType),不是收取/支付方(SwapDirection);互换里收取方可能是空头。**两者不能混用** | ❌(维度不同) |
|
||
| `Buy / Sell` | 通用 | 互换不是买卖关系,是交换现金流;Buy/Sell 易误导 | ❌ |
|
||
|
||
### 决策
|
||
|
||
**英文方向词用 `Rcv / Pay`,仅用于代码字段名(如 `RcvEntryDirtyPrice`);枚举值和前端展示保持中文"收取/支付"。**
|
||
|
||
理由:
|
||
1. **金融标准**。利率互换(IRS)行业里固定方/浮动方标准叫法就是 Receive fixed / Pay fixed,FINCAD、Bloomberg、主流量化库都用 Rcv/Pay。
|
||
2. **纠正老表 Get 的错误**。trade_swap 的 `Get/Pay` 里 Get 不是金融词,是早期随手起的,正好借命名规范统一为 Rcv/Pay。
|
||
3. **不与 Long/Short 冲突**。Rcv/Pay 是 SwapDirection(收取/支付哪一端现金流),Long/Short 是 PositionType(多空),两个维度独立,不能互相替代。
|
||
4. **中文枚举保留**。`SwapDirectionEnum.收取/支付` 已被业务文档、前端、监管报表广泛使用,强行改英文枚举值成本高且无收益。只在新英文字段名里用 Rcv/Pay。
|
||
|
||
> 注:当前 V2 互换主流程(swap_position/eod_swap_position)已经不按"收取/支付腿"存价格(价格存在不分腿的持仓记录里,方向用 `PosiDirection` 字段标记),所以 `Rcv/Pay` 前缀**只在需要显式区分双腿的场景**(如 trade_swap 录入、双边互换)使用,不是所有字段都加。
|
||
|
||
---
|
||
|
||
## 八、最终命名总表(一锤定音)
|
||
|
||
### 8.1 价格 4 象限(含时点前缀)
|
||
|
||
| 业务含义 | 规范命名 | 现状(持仓/流水) |
|
||
|---|---|---|
|
||
| 期初全价·不含费 | `EntryDirtyPrice` | PosiGrossPrice / — |
|
||
| 期初全价·含费 | `EntryDirtyFeePrice` | PosiNetPrice ⚠️ / — |
|
||
| 期初净价·不含费 | `EntryCleanPrice` | PosiNetNoFeePrice / — |
|
||
| 期初净价·含费 | `EntryCleanFeePrice` | PosiNetFeePrice / — |
|
||
| 期末/平仓全价·不含费 | `ExitDirtyPrice` | — / TradingAmountAvg |
|
||
| 期末/平仓全价·含费 | `ExitDirtyFeePrice` | — / TradingAmountFeeAvg |
|
||
| 期末/平仓净价·不含费 | `ExitCleanPrice` | — / TradingAmountNetAvg |
|
||
| 期末/平仓净价·含费 | `ExitCleanFeePrice` | — / TradingAmountNetFeeAvg |
|
||
| 盯市全价 | `MarketDirtyPrice` | UnderlyingPrice |
|
||
| 期初收益率 | `EntryYtm` | InitYtm |
|
||
| 期末收益率 | `ExitYtm` | ytm |
|
||
|
||
### 8.2 方向
|
||
|
||
| 业务含义 | 代码字段名 | 枚举/展示值 |
|
||
|---|---|---|
|
||
| 收取方 | `Rcv*`(如需区分腿) | 收取(SwapDirectionEnum.收取) |
|
||
| 支付方 | `Pay*`(如需区分腿) | 支付(SwapDirectionEnum.支付) |
|
||
| 多头 | `Long`(PositionTypeFlag.Long) | 多头 |
|
||
| 空头 | `Short`(PositionTypeFlag.Short) | 空头 |
|
||
|
||
### 8.3 命名规则速记
|
||
|
||
> **价格 = `{Entry|Exit|Market}{Dirty|Clean}{Fee|}Price`**
|
||
> **方向 = `Rcv|Pay`(SwapDirection)与 `Long|Short`(PositionType)是两个独立维度,不混用**
|
||
> **收益率 = `{Entry|Exit}Ytm`**
|
||
|
||
---
|
||
|
||
## 九、落地策略(渐进式,不一次性改)
|
||
|
||
直接全局重命名风险极高(DB 列 + EF 实体 + ~15 服务类 + ~20 前端文件 + 交易确认书 + Excel 导入)。建议分阶段:
|
||
|
||
| 阶段 | 动作 | 风险 | 产出 |
|
||
|---|---|---|---|
|
||
| **0. 文档定调(本文档)** | 命名规则钉死,团队达成共识 | 零 | 本文档 |
|
||
| **1. 注释止血** | 在现状最混乱的字段(PosiNetPrice/PosiGrossPrice)加 `[Obsolete]` + XML 注释,写明真实含义与规范名 | 零 | 防止再被字段名误导 |
|
||
| **2. 前端统一取值封装** | 新增 `swapPriceHelper.js`,把分散的 `item.PosiGrossPrice` 等收敛成 `getEntryDirtyPrice(item)`;先修"同标签绑不同字段"的混乱 | 低 | 前端混乱点消除 |
|
||
| **3. 新代码强制规范** | 新增字段/新功能必须用规范命名;对外 API 新增 DTO 用规范名,内部旧字段靠 Mapper 转换 | 低 | 增量规范化 |
|
||
| **4. 大重构(慎重)** | DB 列重命名 + EF 映射 + 全量替换,配合数据迁移。建议趁大版本升级做 | 高 | 彻底统一 |
|
||
|
||
**阶段 1 最该立刻做**:给 `PosiNetPrice`(含费全价,非净价)和 `PosiGrossPrice`(不含费全价)加注释,因为连现状文档都被它们误导过。
|
||
|
||
---
|
||
|
||
## 附录:现状混乱全证据清单(决策依据)
|
||
|
||
> 以下每条都有代码/DDL/前端文件佐证,是上述决策的事实基础。
|
||
|
||
| # | 混乱点 | 关键证据位置 |
|
||
|---|---|---|
|
||
| 1 | PosiNetPrice 名为 Net 实为含费全价 | `SwapTradeService.cs:372-373` 赋值:PosiNetPrice←TradingAmountFeeAvg(含费均价) |
|
||
| 2 | PosiGrossPrice 名为 Gross 实为不含费全价 | 同上:PosiGrossPrice←TradingAmountAvg(不含费均价) |
|
||
| 3 | 同字段两表 DDL 注释矛盾 | PosiNetPrice:swap_position 注释"持仓净价",eod_swap_position 注释"期初标的价格" |
|
||
| 4 | 持仓表/流水表两套命名无对应 | Posi*(Net/Gross)vs TradingAmount*(Avg/FeeAvg),名字对不上 |
|
||
| 5 | 前端同标签绑不同字段 | `成交净价` 绑 TradingAmountNet(303) 也绑 TradingAmountNetAvg(508),见 SwapflowList.js |
|
||
| 6 | 作者自曝字段语义错位 | `SwapIncome.cshtml:155`、`SwapDealService.cs:279` 注释:"TradingAmountNetAvg 字段名为期末语义,实装期初值" |
|
||
| 7 | trade_swap 第三套命名 | GetSpotPrice/PaySpotPrice/GetFinalPrice/PayFinalPrice,与 Posi* 平行无对应 |
|
||
| 8 | 时点词散布 | 期初价用了 Spot/Open/Init/Initial 四种;期末价用 Final/End/Close 三种 |
|
||
| 9 | Close 语义被占用 | ClosePrice=行情收盘价 vs 互换平仓价混用 |
|
||
| 10 | Get 不是金融术语 | trade_swap 的 Get/Pay,Get 语义弱,与 Pay 不对称 |
|
||
| 11 | 现状文档自身被误导 | 《互换交易价格字段存储与显示规范》把 PosiNetPrice 标为"期初净价",实为含费全价 |
|