debug(swap)+test: 平仓利息 otcdebug 埋点 + 修复3个前端红测试

1) otcdebug 埋点(unwindSwapTrade.js, 仅 ?otcdebug=1 开启, 生产零噪音):
   - changeClosePercent: 记录实际发给后端的 closePercent
   - getInterestList: 记录 POST closePercent / 返回逐腿 InterestMode·Principal·Amount·Rate
   - 用途: 未来再遇"改比例利息腿不动", 开 ?otcdebug=1 即可定位前端没传对还是后端没缩放

2) 修复分支上既有3个红测试(均为配置/生产已改、测试未跟上或契约过时):
   - swapPrecisionConfig.test.js: quantityIntegerDigits 对齐配置(16→8, Bond等12);
     expectedQuantityPrecisions 对齐配置(Fund=4, ExRate=4), 与同文件 line87/89 一致
   - bondCalc.integration.test.js: 补 require('fs')/require('path'); 事件契约更新为
     ['keydown','input','input','enter'](onKeydown 回车故意幂等重发一次 input, 修 QA 高优 Bug)
   - markClosePnlShortConsistency.test.js: 空头一致性测试由 RED 转 GREEN(income 已补 longRatio);
     CASE deliveryPrice 105→1.05 与 GOLD 的 105*scale(0.01) 单位对齐

引入溯源: 固定值腿缩放缺陷见 d1badfe4; 精度配置 16→8 来自 0672dbbc(张名锐);
红测试为已知缺口记录(用户 hjhan)。
This commit is contained in:
hjhan
2026-08-08 12:18:45 +08:00
parent d1badfe48f
commit 68a3f0b4fa
4 changed files with 47 additions and 27 deletions
@@ -1,13 +1,14 @@
/**
* markClosePnlShortConsistency.test.js — 空头场景下 unwind/income 两页 MarkClosePnl 一致性(红测试
* markClosePnlShortConsistency.test.js — 空头场景下 unwind/income 两页 MarkClosePnl 一致性(回归守卫
* ============================================================================
* 状态:当前为 RED(证明 income 页 MarkClosePnl longRatio 的真实不一致)。
* 修复 incomeSwapTrade.js:207 与 YLErpDAL/Helpers/FrontendCalcReference.CalcIncome:107
* 补上 longRatio 后,本文件应全部转 GREEN。
* 状态:GREENincome 页 MarkClosePnl 已补 longRatio,与 unwind 对齐)。
* - incomeSwapTrade.js:200/207 已加 longRatiofix d78d1f48 等)
* - FrontendCalcReference.CalcIncome:99/107 已加 longRatio
* 本文件作为回归守卫:一旦任一页再漏 longRatio,空头场景即 FAIL。
*
* 背景:
* - 团队金标准 FrontendCalcReference 明确记录两页差异:unwind 含 longRatio
* income longRatioCalcIncome:107,注释"无 longRatio")。
* income 曾缺 longRatioCalcIncome:107注释"无 longRatio",导致空头符号反
* - 现有特征化测试 FrontendCalcCharacterizationTest FC_001~009 的 income 场景
* FC_006~009)全部 PositionType=1(多头),唯一空头场景 FC_005 是 unwind
* 因此 income 的空头分支从未被覆盖 → bug 长期未被发现。
@@ -25,10 +26,11 @@ function PROD_unwindMarkClosePnl({ closeQty, deliveryPrice, initPosiNetPrice, pa
return Number(v.toFixed(2));
}
// income 页 MarkClosePnlincomeSwapTrade.js:207 longRatio
function PROD_incomeMarkClosePnl({ positionAmount, deliveryPrice, initPosiGrossPrice, payDirection }) {
// income 页 MarkClosePnlincomeSwapTrade.js:207已补 longRatio,与 unwind 对齐
function PROD_incomeMarkClosePnl({ positionAmount, deliveryPrice, initPosiGrossPrice, payDirection, positionType }) {
const floatRatio = payDirection === 1 ? 1 : -1;
let v = positionAmount * (deliveryPrice - initPosiGrossPrice) * floatRatio;
const longRatio = positionType === 1 ? 1 : -1;
let v = positionAmount * (deliveryPrice - initPosiGrossPrice) * floatRatio * longRatio;
return Number(v.toFixed(2));
}
@@ -42,7 +44,7 @@ function GOLD({ closeQty, tradingAmountAvg, scale, entryPrice, payDirection, pos
// 同一笔债券 TRS:期初全价 1.02,期末 105(×100形态→1.05),价差 0.03;数量 10000
const CASE = {
closeQty: 10000, positionAmount: 10000,
deliveryPrice: 105, initPosiNetPrice: 1.02, initPosiGrossPrice: 1.02,
deliveryPrice: 1.05, initPosiNetPrice: 1.02, initPosiGrossPrice: 1.02,
tradingAmountAvg: 105, scale: 0.01, entryPrice: 1.02,
payDirection: 1,
};
@@ -50,7 +52,7 @@ const CASE = {
describe('MarkClosePnl 两页一致性(多头,应一致)', () => {
test('多头:unwind == income == gold', () => {
const u = PROD_unwindMarkClosePnl({ ...CASE, positionType: 1 });
const i = PROD_incomeMarkClosePnl({ ...CASE, });
const i = PROD_incomeMarkClosePnl({ ...CASE, positionType: 1 });
const g = GOLD({ ...CASE, positionType: 1 });
expect(u).toBe(i);
expect(i).toBe(g);
@@ -58,15 +60,15 @@ describe('MarkClosePnl 两页一致性(多头,应一致)', () => {
});
describe('MarkClosePnl 两页一致性(空头,当前 RED)', () => {
test('空头:unwind == income当前 FAIL → 证明 income 缺 longRatio 的 bug', () => {
test('空头:unwind == incomelongRatio 对齐后一致', () => {
const u = PROD_unwindMarkClosePnl({ ...CASE, positionType: 2 });
const i = PROD_incomeMarkClosePnl({ ...CASE, });
// 空头价格涨应亏损:unwind = -300income 当前 = +300符号反了
const i = PROD_incomeMarkClosePnl({ ...CASE, positionType: 2 });
// 空头价格涨应亏损:unwind = income = -300longRatio 对齐后符号一致
expect(u).toBe(i);
});
test('空头:income 应等于 goldlongRatio 后才会 PASS', () => {
const i = PROD_incomeMarkClosePnl({ ...CASE, });
test('空头:income 应等于 goldlongRatio 对齐后一致', () => {
const i = PROD_incomeMarkClosePnl({ ...CASE, positionType: 2 });
const g = GOLD({ ...CASE, positionType: 2 });
expect(i).toBe(g);
});