18 KiB
互换价格字段命名规范决策
本文档是命名规范的决策与论证,回答"应该叫什么、为什么这么叫"。 现状梳理(字段流向、存储显示规则)见姊妹文档 互换交易价格字段存储与显示规范.md。 适用范围:互换业务(收益互换)涉及的价格、方向、时点字段。期权/远期业务字段不在本文档范围。
一、为什么命名必须先定下来
现状是同一概念多套名字、名字与含义颠倒,已经反复造成 Bug。三条最严重的证据:
PosiNetPrice名为"Net",实为"含费全价"。真正的净价是PosiNetNoFeePrice。连现状文档(《互换交易价格字段存储与显示规范》)都把它误标成"期初净价"——文档作者都被字段名骗了。- 两套并行命名无对应关系:持仓表
PosiNetPrice↔ 流水表TradingAmountFeeAvg,持仓表PosiGrossPrice↔ 流水表TradingAmountAvg,名字里没有一处对得上。 - 前端同一中文标签绑不同字段:
成交净价一处绑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。
理由:
- 行业术语,零歧义。Clean/Dirty 是债券领域全世界通用的无歧义术语(clean price = 不含应计利息,dirty price = 含应计利息)。
- 与数据源一致。录入默认价格来源
china_bond_valuation表用的就是dirty_price_close(日终估值全价)和net_price(估价净值)。字段命名与数据源对齐,减少心智负担。 - 现状 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);枚举值和前端展示保持中文"收取/支付"。
理由:
- 金融标准。利率互换(IRS)行业里固定方/浮动方标准叫法就是 Receive fixed / Pay fixed,FINCAD、Bloomberg、主流量化库都用 Rcv/Pay。
- 纠正老表 Get 的错误。trade_swap 的
Get/Pay里 Get 不是金融词,是早期随手起的,正好借命名规范统一为 Rcv/Pay。 - 不与 Long/Short 冲突。Rcv/Pay 是 SwapDirection(收取/支付哪一端现金流),Long/Short 是 PositionType(多空),两个维度独立,不能互相替代。
- 中文枚举保留。
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 标为"期初净价",实为含费全价 |