Files
zszq-trs/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindBondCalc.js
T

133 lines
6.3 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.
/**
* unwindBondCalc.js — 平仓页 期末标的交割全价 ↔ 期末标的结算收益率(ExitYtm) 两字段互算(EQD-6953
* ============================================================================
* 与录入页(swapTradeEdit.js + swapCalc.js 债券三字段互算)的关键差异,【勿"顺手统一"】:
* 1. 单位状态:录入页三字段(CP/DP/YD)存【存储态小数】(0.995),发计算器前 ×100、回写 ÷100
* (bondPriceToCalc/bondCalcPriceToStorage);平仓页 TradingAmountAvg 在 initDeal 已转
* 【展示态百分数】(99.5)ExitYtm 亦定义为展示态(6.3721)——发计算器不换算、回写不换算。
* 提交时的存储态转换仍由既有 getStorageDeliveryPrice()/dataFormat() 负责。
* 2. 字段集:只有 DP(TradingAmountAvg)/YD(ExitYtm) 两个字段(平仓页无净价列),
* 失败只清对方字段(录入页 applyBondCalcFailure 清另外两个,字段名也不同,故不复用)。
* 3. 估值日:用平仓日 deal.ValueDate(结算语义);录入页用互换起始日 StartDate。
* 4. 精度:ExitYtm 保留 4 位小数、四舍五入(AwayFromZero)——结算确认书导出要求
* 固定 4 位不去零(ToString("0.0000")),界面精度不得低于该要求。
*
* 浏览器:挂 window.UnwindBondCalc(需在 swapCalc.js 之后、unwindSwapTrade.js 之前加载)。
* Nodemodule.exportsUMD),供 fe-tests/unwindBondCalc.test.js 使用。
* 命名遵《互换价格字段命名规范决策文档》:平仓/了结时点 = Exit(非 End/Close/Final)。
*/
(function (root, factory) {
if (typeof module === 'object' && module.exports) {
module.exports = factory(require('./swapCalc.js'));
} else {
root.UnwindBondCalc = factory(root.SwapCalc);
}
})(typeof self !== 'undefined' ? self : this, function (SwapCalc) {
'use strict';
// driver(回车的"源"字段) → 平仓页浮动腿(floatPosition)字段名
var FIELDS = { DP: 'TradingAmountAvg', YD: 'ExitYtm' };
function hasValue(v) {
return v !== null && v !== undefined && v !== '' && !isNaN(v);
}
/**
* 构造 /Bond/CalcBond 请求体。
* ⚠️ price 必须传【展示态百分数】直传(99.5),不做录入页那种 ×100——
* 平仓页模型里两个字段本就是展示态。此约定由 fe-tests 冻结,防止后人误加换算。
* source='unwind'BondController 记进日志,便于区分录入页/平仓页来源定位问题。
*/
function getCalcRequest(underlyingCode, price, driver, valueDate) {
return {
underlyingCode: underlyingCode,
price: Number(price),
priceType: driver,
targetDate: valueDate || null,
source: 'unwind'
};
}
/**
* ExitYtm 统一取整:4 位、四舍五入(远离零),对齐后端 decimal ToString("0.0000")。
* 非数值(null/undefined/NaN)返回 null——供 write 跳过,绝不能把 null 取整成 0 回写
* (否则计算器未返回 ytm 时会清掉用户手工输入)。
*/
function roundExitYtm(value) {
if (value === null || value === undefined || isNaN(value)) return null;
return SwapCalc.roundHalfAwayFromZero(value, 4);
}
/**
* 计算成功回写(交互约定1+2):源字段保持用户手输值,仅回写对方字段(展示态直写)。
* - driver='DP'(全价回车)→ 回写 ExitYtm = ytm 取整 4 位;计算器未返回 ytm 则不覆盖。
* - driver='YD'(收益率回车)→ 回写 TradingAmountAvg = dirtyPrice;未返回则不覆盖。
* ⚠️ 调用方回写 TradingAmountAvg 后须显式重算平仓盈亏(calcFloatClosePnl)。
* 纯函数,jest 可直接测。
*/
function applyCalcResult(state, calc, driver) {
function write(field, value) {
if (value === undefined || value === null) return; // 计算器未返回则不覆盖
var cur = state[field];
if (typeof cur === 'number' && Math.abs(cur - value) < 1e-9) return; // 无变化不写
state[field] = value;
}
if (driver === 'DP') {
write('ExitYtm', roundExitYtm(calc && calc.ytm));
} else if (driver === 'YD') {
write('TradingAmountAvg', calc && calc.dirtyPrice);
}
return state;
}
/**
* 计算失败落地(交互约定2):保留源字段值(用户可就地改),清对方字段、清全部标识。
* 与录入页差异:只清"对方"一个字段(录入页清另外两个)。
* driver 非法(null/未知)时仅清标识、不动任何数值——防调用方误清源字段
* (如 setValueDate 只想清陈旧标识时误传 null,会把 TradingAmountAvg 清掉)。
*/
function applyCalcFailure(state, driver) {
if (driver === 'DP' || driver === 'YD') {
var other = driver === 'DP' ? 'YD' : 'DP';
state[FIELDS[other]] = null;
}
state.bondDriverType = null;
state.bondAuto = { DP: false, YD: false };
state.bondRev = { DP: false, YD: false };
return state;
}
/** 计算成功后的标识(交互约定2):源字段标"源",对方标"AUTO"。 */
function applyCalcSuccess(state, driver) {
state.bondDriverType = driver;
state.bondAuto = { DP: driver !== 'DP', YD: driver !== 'YD' };
state.bondRev = { DP: false, YD: false };
return state;
}
/**
* 编辑未回车(失焦/按键)的标识(交互约定3):清 源/AUTO,仅本字段累计标"REV"
* 不联动对方字段(允许计算有问题时手工覆盖 ExitYtm)。同录入页:累计 REV 不互清。
*/
function applyManualEdit(state, type) {
state.bondDriverType = null;
state.bondAuto = { DP: false, YD: false };
if (!state.bondRev || typeof state.bondRev !== 'object') {
state.bondRev = { DP: false, YD: false };
}
if (type) state.bondRev[type] = true;
return state;
}
return {
FIELDS: FIELDS,
hasValue: hasValue,
getCalcRequest: getCalcRequest,
roundExitYtm: roundExitYtm,
applyCalcResult: applyCalcResult,
applyCalcFailure: applyCalcFailure,
applyCalcSuccess: applyCalcSuccess,
applyManualEdit: applyManualEdit
};
});