Files
zszq-trs/YLErpWeb/fe-tests/parity.test.js
T
hjhan 7f64d48699 test(fe): 接入 SwapCalc 纯计算模块 + 前端精度回归测试
- incomeSwapTrade.js / swapTradeEdit.js 改为调用 SwapCalc.*(缩放/取整/全价推导)

- 新增 fe-tests 守卫:4 个历史 bug 回归 + 8 场景对齐 C# FrontendCalcReference 金标准 + otcformat.js 精度配置守卫

- 新增 .git/hooks/pre-commit 自动运行前端守卫(失败阻断提交)
2026-07-07 12:30:19 +08:00

97 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* parity.test.js — 生产代码 vs SwapCalc 等价性证明(替换前的"零风险闸门")
* ============================================================================
* 方法:把 incomeSwapTrade.js / swapTradeEdit.js 里 4 个公式的【生产表达式逐字】
* 抄成 PROD_* 函数,与 SwapCalc.* 在 FC 场景 + 随机 + 精度边界上对比。
* 只有本文件 100% 绿灯,才允许把生产内联表达式替换为 SwapCalc.* 调用。
* 本文件只读对比,不改动任何生产代码。
*/
const SwapCalc = require('../wwwroot/Scripts/app/swaptrade/swapCalc.js');
// ---- 生产表达式逐字抄录(来源见注释行号) ----
// incomeSwapTrade.js L76
function PROD_getPriceScale(multiplier) { return multiplier == 100 ? 0.01 : 1; }
// incomeSwapTrade.js L87(仅此一处是初值推导,L156/L268 是别的逻辑,不在此对比)
function PROD_deriveTradingAmountAvg(initPosiGrossPrice, multiplier) { return initPosiGrossPrice * multiplier; }
// incomeSwapTrade.js L1794 个加数均为 2 位小数,.toFixed(2) 与 round-to-2 等价
function PROD_calcFloatPnlSum(markClosePnl, TradingFee, TradingFeePending, DividendIn) {
return (parseFloat(markClosePnl) + TradingFee + TradingFeePending + DividendIn).toFixed(2);
}
// swapTradeEdit.js L360lodash _.round(x,2) === Math.round(x*100)/100
function lodashRound(x, d) { var f = Math.pow(10, d); return Math.round(x * f) / f; }
function PROD_calcStockEqvNotional(price, national) { return lodashRound(price * national, 2); }
// 随机 2 位小数金额(模拟真实环境:所有加数都已是 2 位小数)
function rand2() { return Math.round(Math.random() * 2000000) / 100; } // 0.00 ~ 20000.00
describe('parity: getPriceScale (incomeSwapTrade.js L76)', () => {
const cases = [100, 1, 1000, 0, 200, 10];
cases.forEach((m) => {
test('multiplier=' + m, () => {
expect(PROD_getPriceScale(m)).toBe(SwapCalc.getPriceScale(m));
});
});
});
describe('parity: deriveTradingAmountAvg (incomeSwapTrade.js L87)', () => {
// FC 场景的 (grossPrice, multiplier)
const cases = [
[1.02, 100], [100, 1], [1.02, 100], [95.5, 100], [102.34, 100], [50, 1],
];
cases.forEach(([p, m]) => {
test('gross=' + p + ' mult=' + m, () => {
expect(PROD_deriveTradingAmountAvg(p, m)).toBe(SwapCalc.deriveTradingAmountAvg(p, m));
});
});
});
describe('parity: calcStockEqvNotional (swapTradeEdit.js L360)', () => {
const cases = [
[1.02, 10000], [100, 1000], [1.0235, 5000], [99.99, 100], [0.5, 200],
];
cases.forEach(([p, n]) => {
test('price=' + p + ' national=' + n, () => {
expect(PROD_calcStockEqvNotional(p, n)).toBe(SwapCalc.calcStockEqvNotional(p, n));
});
});
test('随机 200 组 2 位小数输入', () => {
for (let i = 0; i < 200; i++) {
const p = rand2(), n = Math.round(Math.random() * 100000);
expect(PROD_calcStockEqvNotional(p, n)).toBe(SwapCalc.calcStockEqvNotional(p, n));
}
});
});
describe('parity: calcFloatPnlSum (incomeSwapTrade.js L179) — 真实 2 位小数输入', () => {
// 真实场景:MarkClosePnl 已在 L178 被 otcformat 取整为 2 位;费用/分红也 2 位
const cases = [
[30, 20, 0, 0], [100.456, 1, 0.5, 0], [-5000, 0, 0, 0], [300, 100, 50, 0], [450, 100, 50, 100],
[rand2(), rand2(), rand2(), rand2()], [rand2(), rand2(), rand2(), rand2()],
];
cases.forEach((c, idx) => {
test('case#' + idx, () => {
const prod = parseFloat(PROD_calcFloatPnlSum(c[0], c[1], c[2], c[3]));
const swap = SwapCalc.calcFloatPnlSum(c[0], c[1], c[2], c[3]);
expect(prod).toBe(swap); // 2 位小数输入下 .toFixed(2) === round-to-2
});
});
test('随机 300 组 2 位小数输入(证明真实路径零差异)', () => {
for (let i = 0; i < 300; i++) {
const a = rand2(), b = rand2(), c = rand2(), d = rand2();
const prod = parseFloat(PROD_calcFloatPnlSum(a, b, c, d));
const swap = SwapCalc.calcFloatPnlSum(a, b, c, d);
expect(prod).toBe(swap);
}
});
});
// 最坏输入验证:即便喂入未取整的原始值(如 1.005),toFixed 与 round-half-away 在浮点现实下
// 同样得到 1.00,证明不存在舍入接缝。生产环境 4 个加数恒为 2 位小数,更不可能分歧。
describe('parity: 最坏输入也无舍入接缝', () => {
test('原始 1.005 输入下两者仍一致', () => {
const prod = parseFloat(PROD_calcFloatPnlSum(1.005, 0, 0, 0)); // "1.00"
const swap = SwapCalc.calcFloatPnlSum(1.005, 0, 0, 0); // 1.00
expect(prod).toBe(swap);
});
});