fix(swap): 解决(观察日)利率计算精度问题并优化价格精度处理

- 在SwapDealService中对GetFixedRate结果进行精度调整,解决原数据精度误差问题
- 在swapPricePrecisionHelper中添加roundDecimal方法并移除TODO注释
- 新增observationRate相关的精度处理函数,包括roundObservationRate和percent转换函数
- 创建serializeSwapIntervals函数用于处理掉期区间利率精度序列化
- 将consNumberFormat.umpriceP替换为swapPricePrecision.shiftDecimal进行百分比转换
- 使用新的精度处理函数替代原有的toFixed(6)和乘以0.01的操作
This commit is contained in:
张名锐
2026-08-03 13:21:23 +08:00
parent 2eba4ce2a9
commit 613fd66b65
3 changed files with 33 additions and 11 deletions
@@ -712,7 +712,7 @@ namespace YLErp.Modules.SwapModule
}
// 获取利率
decimal rate = GetFixedRate(position, unwindDate);
decimal rate = Math.Round(GetFixedRate(position, unwindDate), InterestCalculationPrecision, MidpointRounding.AwayFromZero); // 做精度调整 原数据有精度误差
decimal floatRate = GetFloatRate(position, preEodPosition, td.StartDate.Value, endDate, interestPeriod, swap, positionClone);
// 根据场景计算利息
@@ -42,7 +42,7 @@ var swapPricePrecision = (function (global) {
FixingRepoRate: { integerDigits: 2, precision: 4 },
RateYield: {integerDigits: 6, precision: 8},
BondIndex: {integerDigits: 6, precision: 4},
// TODO: Add InterestYield, BondIndex and GoldFutures after their enum values are confirmed.
});
function normalizeDecimal(value) {
@@ -265,6 +265,7 @@ var swapPricePrecision = (function (global) {
return rule ? Object.assign({}, options, rule) : Object.assign({}, options);
},
format: format,
roundDecimal: roundDecimal,
roundForSubmit: function (value, instrumentType, field, storagePrecisionOffset) {
const rule = getRule(instrumentType, field);
if (value === null || value === undefined || value === '' || !rule) return value;
@@ -20,6 +20,18 @@ const inputFormatPosiFeeUnit = Object.freeze({ precision: 6, negative: true, app
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: false });
const consPosiFeeType = Object.freeze({ Percent: 0, Unit: 1 });
const observationRatePrecision = 12;
const roundObservationRate = function (value) {
const rounded = swapPricePrecision.roundDecimal(value, observationRatePrecision);
return rounded === null || rounded === '' ? value : rounded;
};
const observationRateFromPercent = function (value) {
const rate = roundObservationRate(swapPricePrecision.shiftDecimal(value, -2));
return rate === null || rate === '' ? value : Number(rate);
};
const observationRateTextFromPercent = function (value) {
return roundObservationRate(swapPricePrecision.shiftDecimal(value, -2));
};
const swapPosiFeeCalc = Object.freeze({
normalizeFeeType(feeType) {
return Number(feeType) === consPosiFeeType.Unit ? consPosiFeeType.Unit : consPosiFeeType.Percent;
@@ -712,6 +724,15 @@ const vue = new Vue({
if (this.trade.trade_extend.ExtendObj.InterestCalcMode == "00" || this.trade.trade_extend.ExtendObj.InterestCalcMode == "10") {
date = moment(date).add(-1, 'days').format('YYYY-MM-DD');
}
const serializeSwapIntervals = function (position) {
position.SwapIntervalList.forEach(interval => {
const rate = roundObservationRate(interval.Rate);
if (rate !== null && rate !== '') {
interval.Rate = Number(rate);
}
});
position.InterestSwapInterval = JSON.stringify(position.SwapIntervalList);
};
this.getSwapList.forEach((x,index) => {
if ((x.UnderlyingCode == null || x.UnderlyingCode.length == 0) && x.SwapIntervalList.length == 0) {
var interval = {
@@ -727,7 +748,7 @@ const vue = new Vue({
return false;
}
x.FloatRateUnderlyingCode = x.FloatRateUnderlyingCode == "--" ? null : x.FloatRateUnderlyingCode;
x.InterestSwapInterval = JSON.stringify(x.SwapIntervalList);
serializeSwapIntervals(x);
thisObj.trade.swap_positions.push(x);
});
if (errorcount > 0) {
@@ -750,7 +771,7 @@ const vue = new Vue({
}
x.FloatRateUnderlyingCode = x.FloatRateUnderlyingCode == "--" ? null : x.FloatRateUnderlyingCode;
margin = margin + x.InterestPrincipalFix * (x.InterestDirection == 1 ? 1 : -1);
x.InterestSwapInterval = JSON.stringify(x.SwapIntervalList);
serializeSwapIntervals(x);
thisObj.trade.swap_positions.push(x);
});
if (errorcount > 0) {
@@ -1031,14 +1052,14 @@ const vue = new Vue({
date: _date,
SettlementDate: m.format("YYYY-MM-DD"),
floatRateCode: floatRateCode,
val: _.toString(val) ? parseFloat(consNumberFormat.umpriceP(val)) : "",
val: _.toString(val) ? swapPricePrecision.shiftDecimal(val, 2) : "",
itemChecked: itemChecked,
disabled: disabled
};
} else {
obdate = {
date: _date,
val: _.toString(val) ? parseFloat(consNumberFormat.umpriceP(val)) : "",
val: _.toString(val) ? swapPricePrecision.shiftDecimal(val, 2) : "",
itemChecked: itemChecked,
disabled: disabled
};
@@ -1099,7 +1120,7 @@ const vue = new Vue({
if (!isNaN(sm.date())) {
_settleDate = sm.format("YYYY-MM-DD");
}
var val = parseFloat(consNumberFormat.umpriceP(thisObj.observation.DefaultTitle1Value));
var val = swapPricePrecision.shiftDecimal(thisObj.observation.DefaultTitle1Value, 2);
var itemChecked = true;
var disabled = false;
if (_date == thisObj.trade.ExerciseDate) {
@@ -1135,7 +1156,7 @@ const vue = new Vue({
if (!isNaN(m.date())) {
_date = m.format("YYYY-MM-DD");
}
var val = parseFloat(consNumberFormat.umpriceP(thisObj.observation.DefaultTitle1Value));
var val = swapPricePrecision.shiftDecimal(thisObj.observation.DefaultTitle1Value, 2);
var itemChecked = true;
var disabled = false;
if (_date == thisObj.trade.ExerciseDate) {
@@ -1159,7 +1180,7 @@ const vue = new Vue({
var observationStr = "";
if (this.observation.ObservationDataList != null) {
this.observation.ObservationDataList.forEach(item => {
observationStr = observationStr + item.date + ", " + item.SettlementDate + ", " + (item.val * 0.01).toFixed(6) + ", " + item.itemChecked + ";\n";
observationStr = observationStr + item.date + ", " + item.SettlementDate + ", " + observationRateTextFromPercent(item.val) + ", " + item.itemChecked + ";\n";
});
}
this.observation.ObservationInterval = observationStr;
@@ -1209,7 +1230,7 @@ const vue = new Vue({
date: values[0],
SettlementDate: values[1] || "",
floatRateCode: floatRateCode,
val: _.toString(val) ? parseFloat(consNumberFormat.umpriceP(val)) : "",
val: _.toString(val) ? swapPricePrecision.shiftDecimal(val, 2) : "",
itemChecked: itemChecked,
disabled: disabled
}
@@ -1289,7 +1310,7 @@ const vue = new Vue({
}
var observation = {
Date: item.date,
Rate: item.val * 0.01,
Rate: observationRateFromPercent(item.val),
Settlement: item.itemChecked ? 1 : 0,
SettlementDate: item.SettlementDate || null // 结算日期
}