/// 新版收益互换预付金校验
@@ -370,7 +381,7 @@ namespace YLErp.Modules.SwapModule
CountRatio = underlying.CountRatio,
ContractSize = Convert.ToDecimal(underlying.ContractSize),
PosiNetPrice = flowMerge.TradingAmountFeeAvgAbs,
- PosiGrossPrice = flowMerge.TradingAmountAvg,
+ PosiGrossPrice = Math.Round(flowMerge.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
PosiNetFeePrice = flowMerge.TradingAmountNetFeeAvg ?? 0,
PosiNetNoFeePrice = flowMerge.TradingAmountNetAvg ?? 0,
PosiQuantity = flowMerge.TradingQtyAbs,
@@ -1350,8 +1361,11 @@ namespace YLErp.Modules.SwapModule
position.UnderlyingCode = swap.UnderlyingCode;
position.UnderlyingInstrumentType = swap.UnderlyingInstrumentType;
position.PosiDirection = swap.PosiDirection;
- position.PosiGrossPrice = swap.PosiGrossPrice;
- position.PosiNetPrice = swap.PosiQuantity == 0 ? 0 : (swap.PosiGrossPrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio);
+ // position.PosiGrossPrice = string.IsNullOrEmpty(swap.UnderlyingCode)
+ // ? Math.Round(swap.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero)
+ // : ValidateDeliveryPrice(swap.PosiGrossPrice, "期初交割价");
+ position.PosiGrossPrice = Math.Round(swap.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ position.PosiNetPrice = swap.PosiQuantity == 0 ? 0 : (position.PosiGrossPrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio);
position.PosiNetPrice = Math.Round(position.PosiNetPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
position.PosiNetNoFeePrice = swap.PosiNetNoFeePrice;
position.PosiNetFeePrice = swap.PosiQuantity == 0 ? 0 : (swap.PosiNetNoFeePrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio);
diff --git a/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml b/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml
index b02667f9..c86b8774 100644
--- a/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml
+++ b/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml
@@ -163,7 +163,7 @@
| {{priceFormat(floatPosition.TradingAmountNetAvg > 0 ? floatPosition.TradingAmountNetAvg : floatPosition.PosiNetPrice)}} |
-
+
diff --git a/YLErpWeb/Views/SwapTrade2/SwapUnwind.cshtml b/YLErpWeb/Views/SwapTrade2/SwapUnwind.cshtml
index 1011c3c8..e1ecadcb 100644
--- a/YLErpWeb/Views/SwapTrade2/SwapUnwind.cshtml
+++ b/YLErpWeb/Views/SwapTrade2/SwapUnwind.cshtml
@@ -200,7 +200,7 @@
|
{{priceFormat(floatPosition.PosiGrossPrice)}} |
-
+
diff --git a/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml b/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml
index 6bc78ac4..eb805b1c 100644
--- a/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml
+++ b/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml
@@ -243,7 +243,7 @@
-
+
@*
diff --git a/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml b/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml
index b8c7d03f..c62ed4c9 100644
--- a/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml
+++ b/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml
@@ -452,7 +452,7 @@
|
-
+
|
@@ -461,7 +461,7 @@
|
-
+
|
{{item.underlying!=null?item.underlying.QuoteUnitString:''}}
diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/SwapflowList.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/SwapflowList.js
index 94481d55..3af1b627 100644
--- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/SwapflowList.js
+++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/SwapflowList.js
@@ -1,5 +1,6 @@
//window.otcformat.options.disableGrouping = true;
const inputFormatTradePrice = Object.freeze({ precision: otcformat.trading.tradeSinglePrice.precision, negative: true, append: '' });
+const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '' });
const inputFormatTradeAmount = Object.freeze({ precision: otcformat.trading.notional.precision, append: '' });
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
var clients = ylotc.clients;
@@ -1198,6 +1199,7 @@ var vue = new Vue({
},
postSwapflow() {
var thisObj = this;
+ thisObj.swapflow.TradingAmountAvg = _.round(Number(thisObj.swapflow.TradingAmountAvg), 9);
main.post("/swaptrade2/SaveSwapflow", { req: thisObj.swapflow, step: thisObj.step }).done(function (resp) {
if (resp.success) {
getList();
diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js
index 7acad7c4..0e6a610d 100644
--- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js
+++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js
@@ -8,6 +8,7 @@ const inputFormatEqvNotional = Object.freeze({ precision: otcformat.trading.Stoc
const inputFormatDividend = Object.freeze({ precision: 2, append: '', negative: true });
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
const inputFormatMarginRateNoPercent = Object.freeze({ precision: otcformat.trading.umpriceP.precision, append: '', percent: true });
+const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, append: '', negative: true });
let ValueDate = model.ValueDate;
let MaxIncomeValueDate = model.MaxIncomeValueDate ? model.MaxIncomeValueDate.substr(0, 10) : ValueDate;
@@ -77,6 +78,9 @@ const vue = new Vue({
getPriceScale() {
return SwapCalc.getPriceScale(this.multiplier);
},
+ getStorageDeliveryPrice() {
+ return _.round(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), 9);
+ },
initDeal() {
var positions = model.FlowEvents.filter((item) => {
return item.UnderlyingCode;
@@ -161,7 +165,7 @@ const vue = new Vue({
{ code: thisObj.floatPosition.UnderlyingCode, valuedate: thisObj.deal.ValueDate })
.done(function (res) {
res.obj = res.obj * thisObj.multiplier;
- thisObj.floatPosition.TradingAmountAvg = otcformat.trading.tradeSinglePrice(res.obj);
+ thisObj.floatPosition.TradingAmountAvg = _.round(Number(res.obj), 9);
thisObj.calcFloatClosePnl();
});
},
@@ -180,11 +184,11 @@ const vue = new Vue({
let TradingFee = thisObj.floatPosition.TradingFee == "" ? 0 : parseFloat(thisObj.floatPosition.TradingFee);
let TradingFeePending = thisObj.floatPosition.TradingFeePending == "" ? 0 : parseFloat(thisObj.floatPosition.TradingFeePending);
let DividendIn = thisObj.floatPosition.DividendIn == "" ? 0 : parseFloat(thisObj.floatPosition.DividendIn ?? 0);
- let scale = thisObj.getPriceScale();
+ let deliveryPrice = thisObj.getStorageDeliveryPrice();
// 债券全价是单位价格,价差盈亏应按持仓数量×合约乘数计算;
// CloseNotionalValue 是期初全价折算后的名义本金,直接乘价差会重复包含期初价格。
let positionAmount = parseFloat(thisObj.floatPosition.Quantity) * parseFloat(thisObj.floatPosition.ContractSize || 1);
- thisObj.floatPosition.MarkClosePnl = positionAmount * (thisObj.floatPosition.TradingAmountAvg * scale - thisObj.initPosiGrossPrice) * floatRatio;
+ thisObj.floatPosition.MarkClosePnl = positionAmount * (deliveryPrice - thisObj.initPosiGrossPrice) * floatRatio;
thisObj.floatPosition.MarkClosePnl = otcformat.trading.StockEqvNotional(thisObj.floatPosition.MarkClosePnl);//MarkClosePnl 纯盯市不要计算交易费用和分红
// 守卫: 浮动盈亏合计必须保留 2 位小数 → 对应历史 bug 3c5f25a5(原代码缺精度保留)
// 数值由 swapCalc.calcFloatPnlSum 计算, 此处 .toFixed(2) 仅保留字符串类型以兼容下游
@@ -205,13 +209,13 @@ const vue = new Vue({
thisObj.deal.SwapRealizedPnL = pnl;
thisObj.deal.SwapMarginRebatePnl = 0;
thisObj.deal.SwapMarginAmount = 0;
- let scale = thisObj.getPriceScale();
- thisObj.floatPosition.TradingAmount = parseFloat(thisObj.floatPosition.TradingAmountAvg) * parseFloat(thisObj.deal.CloseNotionalValue) * scale;
+ let deliveryPrice = thisObj.getStorageDeliveryPrice();
+ thisObj.floatPosition.TradingAmount = deliveryPrice * parseFloat(thisObj.deal.CloseNotionalValue);
thisObj.floatPosition.CloseFee = TradingFee;
if (thisObj.deal.CloseQty > 0) {
- thisObj.floatPosition.TradingAmountFeeAvg = parseFloat(thisObj.floatPosition.TradingAmountAvg) * scale + (TradingFee / thisObj.deal.CloseQty) * floatRatio;
+ thisObj.floatPosition.TradingAmountFeeAvg = deliveryPrice + (TradingFee / thisObj.deal.CloseQty) * floatRatio;
} else {
- thisObj.floatPosition.TradingAmountFeeAvg = parseFloat(thisObj.floatPosition.TradingAmountAvg) * scale;
+ thisObj.floatPosition.TradingAmountFeeAvg = deliveryPrice;
}
this.interestList.forEach(x => {
//let interestRatio = x.InterestDirection == 1 ? 1 : -1;
@@ -277,7 +281,7 @@ const vue = new Vue({
thisObj.floatPosition.EventDate = thisObj.deal.ValueDate;
let floatPosition = _.cloneDeep(thisObj.floatPosition);
floatPosition.Quantity = 0;
- floatPosition.TradingAmountAvg = floatPosition.TradingAmountAvg * thisObj.getPriceScale();
+ floatPosition.TradingAmountAvg = thisObj.getStorageDeliveryPrice();
reqObj.FlowEvents.push(floatPosition);
var postData = { unwindData: reqObj };
var msg = "确认提交收益结算?";
diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
index dc726ce2..5c974e69 100644
--- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
+++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
@@ -14,6 +14,8 @@ const inputFormatTradePrice = Object.freeze({ precision: otcformat.trading.trade
const inputFormatTradeSinglePrice = Object.freeze({ precision: otcformat.trading.tradeSinglePrice.precision, negative: true, append: '', percent: false });
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
const inputFormatMarginRateNoPercent = Object.freeze({ precision: otcformat.trading.umpriceP.precision, append: '', percent:true });
+const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: false });
+const inputFormatSwapBondDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: true });
const consUnderlyingFlagBase = (function () {
let unSelFlag = tradeHelper.UnderlyingSelectFlag;
@@ -277,7 +279,8 @@ const vue = new Vue({
//计算数量
if (this.paySwapList.length > 0) {
var item = this.paySwapList[0];
- var notional = item.PosiGrossPrice * item.ContractSize;
+ var deliveryPrice = _.round(Number(item.PosiGrossPrice), 9);
+ var notional = deliveryPrice * item.ContractSize;
item.PosiQuantity = notional == 0 ? 0 : _.round(this.trade.StockEqvNotional / notional, page.otcFormatConfig.StockEqvNotional.precision);
this.calcNotional();
}
@@ -358,7 +361,8 @@ const vue = new Vue({
}
var national = payItem.PosiQuantity * payItem.ContractSize;
// 守卫: 名义本金必须 round 到 2 位 → 对应历史 bug f873239a(缺 _.round); 外置到 swapCalc.calcStockEqvNotional
- var stockEqvNotional = SwapCalc.calcStockEqvNotional(payItem.PosiGrossPrice, national);//名义本金=期初价格*数量*乘数
+ var deliveryPrice = _.round(Number(payItem.PosiGrossPrice), 9);
+ var stockEqvNotional = SwapCalc.calcStockEqvNotional(deliveryPrice, national);//名义本金=期初价格*数量*乘数
this.trade.StockEqvNotional = otcformat.trading.stockEqvNotional(stockEqvNotional);
payItem.PosiNotionalValue = this.trade.StockEqvNotional;
}
@@ -502,6 +506,7 @@ const vue = new Vue({
errorcount++;
return false;
}
+ x.PosiGrossPrice = _.round(Number(x.PosiGrossPrice), 9);
thisObj.trade.swap_positions.push(x);
});
} else {
@@ -628,7 +633,7 @@ const vue = new Vue({
main.post("/pricing/AjaxGetUnderlyingPrice", { underlyingCode: underlyingCode, tradeDate: StartDate })
.done(function (resp) {
item.PosiNetNoFeePrice = otcformat.trading.umprice(resp.obj.netPrice);
- item.PosiGrossPrice = otcformat.trading.umprice(resp.obj.price);
+ item.PosiGrossPrice = _.round(Number(resp.obj.price), 9);
thisObj.calcNotional();
});
},
diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js
index c7fd23ff..71553a15 100644
--- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js
+++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js
@@ -7,6 +7,7 @@ const inputFormatTradeAmount = Object.freeze({ precision: otcformat.trading.noti
const inputFormatEqvNotional = Object.freeze({ precision: otcformat.trading.StockEqvNotional.precision, append: '', negative: true });
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
const inputFormatMarginRateNoPercent = Object.freeze({ precision: otcformat.trading.umpriceP.precision, append: '', percent: true });
+const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, append: '', negative: true });
let ValueDate = model.ValueDate;
const vue = new Vue({
el: '#vueDiv',
@@ -40,6 +41,9 @@ const vue = new Vue({
getPriceScale() {
return this.multiplier == 100 ? 0.01 : 1;
},
+ getStorageDeliveryPrice() {
+ return _.round(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), 9);
+ },
initDeal() {
var positions = model.FlowEvents.filter((item) => {
return item.UnderlyingCode;
@@ -84,7 +88,7 @@ const vue = new Vue({
this.deal.SwapCloseAmount = otcformat.trading.StockEqvNotional(this.deal.SwapCloseAmount);
//this.floatPosition.PosiNetPrice = otcformat.trading.tradeSinglePrice(this.floatPosition.PosiNetPrice);
//this.floatPosition.PosiGrossPrice = otcformat.trading.tradeSinglePrice(this.floatPosition.PosiGrossPrice);
- this.floatPosition.TradingAmountAvg = otcformat.trading.tradeSinglePrice(this.floatPosition.TradingAmountAvg);
+ this.floatPosition.TradingAmountAvg = _.round(Number(this.floatPosition.TradingAmountAvg), 9);
this.floatPosition.TradingFee = otcformat.trading.StockEqvNotional(this.floatPosition.TradingFee);
this.floatPosition.TradingFeePending = otcformat.trading.StockEqvNotional(this.floatPosition.TradingFeePending);
this.floatPosition.DividendIn = parseFloat(this.floatPosition.DividendIn).toFixed(2);
@@ -212,7 +216,7 @@ const vue = new Vue({
{ code: thisObj.floatPosition.UnderlyingCode, valuedate: thisObj.deal.ValueDate })
.done(function (res) {
res.obj = res.obj * thisObj.multiplier;
- thisObj.floatPosition.TradingAmountAvg = otcformat.trading.tradeSinglePrice(res.obj);
+ thisObj.floatPosition.TradingAmountAvg = _.round(Number(res.obj), 9);
thisObj.calcFloatClosePnl();
});
},
@@ -222,8 +226,8 @@ const vue = new Vue({
let longRatio = thisObj.floatPosition.PositionType == 1 ? 1 : -1;
let TradingFee = thisObj.floatPosition.TradingFee == "" ? 0 : parseFloat(thisObj.floatPosition.TradingFee);
let TradingFeePending = thisObj.floatPosition.TradingFeePending == "" ? 0 : parseFloat(thisObj.floatPosition.TradingFeePending);
- let scale = thisObj.getPriceScale();
- thisObj.floatPosition.MarkClosePnl = Math.round(thisObj.deal.CloseQty * (thisObj.floatPosition.TradingAmountAvg * scale - thisObj.initPosiNetPrice) * floatRatio * longRatio * 10000) / 10000;
+ let deliveryPrice = thisObj.getStorageDeliveryPrice();
+ thisObj.floatPosition.MarkClosePnl = Math.round(thisObj.deal.CloseQty * (deliveryPrice - thisObj.initPosiNetPrice) * floatRatio * longRatio * 10000) / 10000;
thisObj.floatPosition.MarkClosePnl = Number(thisObj.floatPosition.MarkClosePnl.toFixed(2));//MarkClosePnl 纯盯市不要计算交易费用和分红
thisObj.floatPosition.MarkClosePnl = otcformat.trading.StockEqvNotional(thisObj.floatPosition.MarkClosePnl);
thisObj.floatPosition.FloatPnlSum = (parseFloat(thisObj.floatPosition.MarkClosePnl) + TradingFee + TradingFeePending + parseFloat(thisObj.floatPosition.DividendIn)).toFixed(2);
@@ -253,13 +257,13 @@ const vue = new Vue({
thisObj.deal.SwapRealizedPnL = pnl;
thisObj.deal.SwapMarginRebatePnl = 0;
thisObj.deal.SwapMarginAmount = 0;
- let scale = thisObj.getPriceScale();
- thisObj.floatPosition.TradingAmount = parseFloat(thisObj.floatPosition.TradingAmountAvg) * parseFloat(thisObj.deal.CloseQty) * scale;
+ let deliveryPrice = thisObj.getStorageDeliveryPrice();
+ thisObj.floatPosition.TradingAmount = deliveryPrice * parseFloat(thisObj.deal.CloseQty);
thisObj.floatPosition.CloseFee = TradingFee;
if (thisObj.deal.CloseQty == 0) {
thisObj.floatPosition.TradingAmountFeeAvg = 0;
} else {
- thisObj.floatPosition.TradingAmountFeeAvg = parseFloat(thisObj.floatPosition.TradingAmountAvg) * scale + (TradingFee / thisObj.deal.CloseQty) * ratio;
+ thisObj.floatPosition.TradingAmountFeeAvg = deliveryPrice + (TradingFee / thisObj.deal.CloseQty) * ratio;
}
this.interestList.forEach(x => {
/*let interestRatio = x.InterestDirection == 1 ? 1 : -1;*/
@@ -360,7 +364,7 @@ const vue = new Vue({
thisObj.floatPosition.EventDate = thisObj.deal.ValueDate;
let floatPosition = _.cloneDeep(thisObj.floatPosition);
floatPosition.Quantity = reqObj.CloseQty;
- floatPosition.TradingAmountAvg = floatPosition.TradingAmountAvg * thisObj.getPriceScale();
+ floatPosition.TradingAmountAvg = thisObj.getStorageDeliveryPrice();
reqObj.FlowEvents.push(floatPosition);
var postData = { unwindData: reqObj };
var msg = "确认提交平仓?";
From bbf98ba61c74360fc23b9a7dcb972767fdcd64ce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BC=A0=E5=90=8D=E9=94=90?= <1565842059@qq.com>
Date: Wed, 22 Jul 2026 18:08:04 +0800
Subject: [PATCH 3/9] =?UTF-8?q?fix(data):=20=E4=BF=AE=E6=AD=A3=E5=80=BA?=
=?UTF-8?q?=E5=88=B8=E5=B8=B8=E9=87=8F=E5=AE=9A=E4=B9=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 将 Bonds 常量值从 "Bond" 更正为 "Bonds"
---
Framework/YLErp.Core/ConsGlobal.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Framework/YLErp.Core/ConsGlobal.cs b/Framework/YLErp.Core/ConsGlobal.cs
index 049817e7..6469db05 100644
--- a/Framework/YLErp.Core/ConsGlobal.cs
+++ b/Framework/YLErp.Core/ConsGlobal.cs
@@ -121,7 +121,7 @@ namespace YLErp
public const string TBonds = "TBonds";
public const string CreditBonds = "CreditBonds"; //信用债
public const string OtherBonds = "OtherBonds"; //其它债券
- public const string Bonds = "Bond"; //债券
+ public const string Bonds = "Bonds"; //债券
public const string GoldFutures = "GoldFutures";
public const string TBFutures = "TBFutures";
public const string OtherFutures = "OtherFutures";
From 018136c615b3e8e03c345b53ae8edd4c30ad34e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BC=A0=E5=90=8D=E9=94=90?= <1565842059@qq.com>
Date: Thu, 23 Jul 2026 10:13:09 +0800
Subject: [PATCH 4/9] =?UTF-8?q?fix(swap):=20=E4=BF=AE=E5=A4=8D=E6=94=B6?=
=?UTF-8?q?=E7=9B=8A=E4=BA=92=E6=8D=A2=E4=BB=B7=E6=A0=BC=E8=BD=AC=E6=8D=A2?=
=?UTF-8?q?=E5=92=8C=E5=88=9D=E5=A7=8B=E4=BF=9D=E8=AF=81=E9=87=91=E8=AE=A1?=
=?UTF-8?q?=E7=AE=97=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 移除债券价格显示态和存储态转换的错误判断逻辑
- 简化债券价格转换条件,只对债券标的进行存储态转换
- 添加初始保证金计算前的金额精度统一处理
- 重构初始保证金准备逻辑到独立方法中
- 修复因价格单位不一致导致的浮动损益计算错误
---
.../SwapModule/SwapEodPositionService.cs | 12 +++------
.../Modules/SwapModule/SwapTradeService.cs | 27 ++++++++++++++-----
2 files changed, 24 insertions(+), 15 deletions(-)
diff --git a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
index 58509c63..cf1b3440 100644
--- a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
@@ -162,11 +162,7 @@ namespace YLErp.Modules.SwapModule
///
/// 获取用于互换浮动腿盯市的标的价格。
///
- /// 普通债券类收益互换的新录入页面将全价按小数保存,例如页面录入 20% 后
- /// PosiGrossPrice 为 0.2;而历史交易中仍可能存在直接保存为 20 的展示态价格。
- /// 中债估值正常经 EodPriceQueryService 转换后应为小数价格,但手工维护的历史
- /// 行情可能仍以展示态进入该服务,例如 2000 经一次转换后得到 20。若将 20
- /// 与 0.2 直接相减,会把 20% 的价格差误算成 1,980,000 的浮动损益。
+ /// 普通债券类收益互换的新录入页面将全价按小数保存,例如页面录入 20% 后 PosiGrossPrice 为 0.2
///
/// 因此仅当交易期初价已经是小数口径、且当前债券价明显仍处于展示态时,再做
/// 一次展示态到存储态转换。期初价本身是历史展示态口径的存量交易保持原价格,
@@ -176,10 +172,10 @@ namespace YLErp.Modules.SwapModule
{
var price = GetUnderlyingPrice(code, settleDate, out vobp);
var underlying = GetUnderlyingData(code);
- var usesStoragePrice = Math.Abs(posiGrossPrice) < 2m;
- var usesDisplayPrice = Math.Abs(price) >= 10m;
+ // var usesStoragePrice = Math.Abs(posiGrossPrice) < 2m;
+ // var usesDisplayPrice = Math.Abs(price) >= 10m;
- if (underlying?.IsBond() == true && usesStoragePrice && usesDisplayPrice)
+ if (underlying?.IsBond() == true)
{
var normalizedPrice = BondPriceConverter.ToStorage(price);
Log.Error($"互换债券日终价格按展示态返回,已转换为存储态: UnderlyingCode={code}, ValueDate={settleDate:yyyy-MM-dd}, PosiGrossPrice={posiGrossPrice}, SourcePrice={price}, NormalizedPrice={normalizedPrice}");
diff --git a/YLErpDAL/Modules/SwapModule/SwapTradeService.cs b/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
index 9f96a210..d48b339a 100644
--- a/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
@@ -381,7 +381,10 @@ namespace YLErp.Modules.SwapModule
CountRatio = underlying.CountRatio,
ContractSize = Convert.ToDecimal(underlying.ContractSize),
PosiNetPrice = flowMerge.TradingAmountFeeAvgAbs,
- PosiGrossPrice = Math.Round(flowMerge.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
+ PosiGrossPrice = Math.Round(
+ flowMerge.TradingAmountAvg,
+ underlying.IsBond() ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound,
+ MidpointRounding.AwayFromZero),
PosiNetFeePrice = flowMerge.TradingAmountNetFeeAvg ?? 0,
PosiNetNoFeePrice = flowMerge.TradingAmountNetAvg ?? 0,
PosiQuantity = flowMerge.TradingQtyAbs,
@@ -684,11 +687,7 @@ namespace YLErp.Modules.SwapModule
private bool PrepareTrade(trade req, TradeSourceEnum dataSource, underlying_manager um)
{
bool tradeNumberGenerated = false;
- req.InitialMargin = Convert.ToDouble(req.trade_Initial_Margin.MarginValue);
- if (req.trade_Initial_Margin.MarginType == 0)
- {
- req.InitialMargin = req.StockEqvNotional == 0 ? 0 : Convert.ToDouble(req.trade_Initial_Margin.MarginValue) * req.StockEqvNotional;
- };
+ PrepareInitialMargin(req);
var isAddNew = req.id == 0;
if (isAddNew)
{
@@ -731,6 +730,17 @@ namespace YLErp.Modules.SwapModule
return tradeNumberGenerated;
}
+
+ private static void PrepareInitialMargin(trade req)
+ {
+ // 初始预付金依赖最终入库的名义本金,须先统一金额精度,避免两者无法勾稽。
+ req.StockEqvNotional = Math.Round(req.StockEqvNotional, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
+ req.InitialMargin = Convert.ToDouble(req.trade_Initial_Margin.MarginValue);
+ if (req.trade_Initial_Margin.MarginType == 0)
+ {
+ req.InitialMargin = req.StockEqvNotional == 0 ? 0 : Convert.ToDouble(req.trade_Initial_Margin.MarginValue) * req.StockEqvNotional;
+ }
+ }
//准备单个交易
private trade PrepareSingleTrade(trade req, TradeSourceEnum dataSource, bool isAddNew, underlying_manager um)
{
@@ -1364,7 +1374,10 @@ namespace YLErp.Modules.SwapModule
// position.PosiGrossPrice = string.IsNullOrEmpty(swap.UnderlyingCode)
// ? Math.Round(swap.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero)
// : ValidateDeliveryPrice(swap.PosiGrossPrice, "期初交割价");
- position.PosiGrossPrice = Math.Round(swap.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ var storagePriceRound = ConsGlobal.InstrumentType.IsBond(swap.UnderlyingInstrumentType)
+ ? ConsGlobal.PriceRound
+ : ConsGlobal.SwapDeliveryPriceRound;
+ position.PosiGrossPrice = Math.Round(swap.PosiGrossPrice, storagePriceRound, MidpointRounding.AwayFromZero);
position.PosiNetPrice = swap.PosiQuantity == 0 ? 0 : (position.PosiGrossPrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio);
position.PosiNetPrice = Math.Round(position.PosiNetPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
position.PosiNetNoFeePrice = swap.PosiNetNoFeePrice;
From bc961c3411889725007aad4703984d9b554beed1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BC=A0=E5=90=8D=E9=94=90?= <1565842059@qq.com>
Date: Thu, 23 Jul 2026 11:06:50 +0800
Subject: [PATCH 5/9] =?UTF-8?q?fix(swap):=20=E4=BF=AE=E5=A4=8D=E4=BA=92?=
=?UTF-8?q?=E6=8D=A2=E4=BA=A4=E6=98=93=E4=B8=AD=E5=80=BA=E5=88=B8=E4=BB=B7?=
=?UTF-8?q?=E6=A0=BC=E7=B2=BE=E5=BA=A6=E5=A4=84=E7=90=86=E9=97=AE=E9=A2=98?=
=?UTF-8?q?=EF=BC=8C=E6=89=A9=E5=85=85=E5=88=B011=E4=BD=8D=E8=AE=A1?=
=?UTF-8?q?=E7=AE=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 统一债券价格四舍五入规则,使用AwayFromZero模式
- 为债券类型添加特殊的精度处理逻辑
- 修复前端价格显示精度计算问题
- 优化价格存储精度控制,区分债券和其他产品类型
- 修复初始化YTM和净价精度处理问题
- 更新价格验证逻辑以支持动态精度设置
---
.../RealTimeClientBanlanceService.cs | 2 +-
.../Modules/SwapModule/SwapConsumerService.cs | 8 ++++-
.../Modules/SwapModule/SwapDealService.cs | 23 +++++++++++--
.../SwapModule/SwapEodPositionService.cs | 34 +++++++++++++++----
.../SwapModule/SwapFlowEventService.cs | 34 ++++++++++++++++---
.../SwapModule/SwapFlowImportService.cs | 8 +++--
.../Modules/SwapModule/SwapFlowService.cs | 14 +++++---
.../SwapModule/SwapTradeAutoService.cs | 8 ++---
.../Modules/SwapModule/SwapTradeService.cs | 21 ++++++++----
YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml | 4 +--
.../Scripts/app/swaptrade/incomeSwapTrade.js | 3 +-
.../Scripts/app/swaptrade/swapTradeEdit.js | 24 +++++++++----
.../Scripts/app/swaptrade/unwindSwapTrade.js | 3 +-
13 files changed, 142 insertions(+), 44 deletions(-)
diff --git a/YLErpDAL/BLL/EodSettlement/RealTimeClientBanlanceService.cs b/YLErpDAL/BLL/EodSettlement/RealTimeClientBanlanceService.cs
index 2d16d4dd..aeedd67e 100644
--- a/YLErpDAL/BLL/EodSettlement/RealTimeClientBanlanceService.cs
+++ b/YLErpDAL/BLL/EodSettlement/RealTimeClientBanlanceService.cs
@@ -1386,7 +1386,7 @@ namespace YLErp.BLL.Eod
ClientId = item.client_id ?? 0,
ClientName = item.client_name,
TradingQty = (item.order_qty ?? 0) - (item.last_shares ?? 0),
- TradingAmountAvg = BondPriceConverter.ToStorage(item.full_price ?? 0),
+ TradingAmountAvg = Math.Round(BondPriceConverter.ToStorage(item.full_price ?? 0), ConsGlobal.PriceRound, MidpointRounding.AwayFromZero),
TradingAmountFeeAvg = BondPriceConverter.ToStorage(item.full_price ?? 0),
TradingFee = 0
};
diff --git a/YLErpDAL/Modules/SwapModule/SwapConsumerService.cs b/YLErpDAL/Modules/SwapModule/SwapConsumerService.cs
index 10dc3845..e5504ce7 100644
--- a/YLErpDAL/Modules/SwapModule/SwapConsumerService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapConsumerService.cs
@@ -137,7 +137,13 @@ namespace YLErp.Modules.SwapModule
swapFlow.OptTime = result.OptTime;
swapFlow.SettleDate = result.SettleDate;
swapFlow.TradingAmount = result.TradingAmount;
- swapFlow.TradingAmountAvg = Math.Round(result.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ var underlying = string.IsNullOrEmpty(result.UnderlyingCode)
+ ? null
+ : DataCacheProvider.GetUnderlyingDataSource().GetData(result.UnderlyingCode);
+ var storagePriceRound = underlying?.IsBond() == true
+ ? ConsGlobal.PriceRound
+ : ConsGlobal.SwapDeliveryPriceRound;
+ swapFlow.TradingAmountAvg = Math.Round(result.TradingAmountAvg, storagePriceRound, MidpointRounding.AwayFromZero);
swapFlow.TradingAmountFeeAvg = result.TradingAmountFeeAvg;
swapFlow.TradingAmountNet = result.TradingAmountNet;
swapFlow.TradingAmountNetFee = result.TradingAmountNetFee;
diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs
index d044e9a6..05182f5f 100644
--- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs
@@ -48,6 +48,20 @@ namespace YLErp.Modules.SwapModule
unwindData.CloseNotionalValue = Math.Round(unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
}
+ private static int GetStorageDeliveryPriceRound(swap_flow_event flowEvent)
+ {
+ if (ConsGlobal.InstrumentType.IsBond(flowEvent?.UnderlyingInstrumentType))
+ {
+ return ConsGlobal.PriceRound;
+ }
+ if (string.IsNullOrEmpty(flowEvent?.UnderlyingCode))
+ {
+ return ConsGlobal.SwapDeliveryPriceRound;
+ }
+ var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(flowEvent?.UnderlyingCode);
+ return underlying?.IsBond() == true ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound;
+ }
+
private static void ValidateDeliveryPrices(UnwindData unwindData)
{
if (unwindData.FlowEvents == null)
@@ -56,7 +70,7 @@ namespace YLErp.Modules.SwapModule
}
foreach (var item in unwindData.FlowEvents.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)))
{
- var roundedPrice = Math.Round(item.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ var roundedPrice = Math.Round(item.TradingAmountAvg, GetStorageDeliveryPriceRound(item), MidpointRounding.AwayFromZero);
if (item.TradingAmountAvg != roundedPrice)
{
throw new ServiceException($"期末交割价最多保留{ConsGlobal.SwapDeliveryPriceRound}位小数");
@@ -73,7 +87,7 @@ namespace YLErp.Modules.SwapModule
}
foreach (var item in flowEvents.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)))
{
- item.TradingAmountAvg = Math.Round(item.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ item.TradingAmountAvg = Math.Round(item.TradingAmountAvg, GetStorageDeliveryPriceRound(item), MidpointRounding.AwayFromZero);
}
}
@@ -1310,7 +1324,6 @@ namespace YLErp.Modules.SwapModule
///
public void AuotoSwapUnwind(int tradeid, decimal unwindPrice, decimal unwindPriceFee, decimal unwindNetFee, decimal unwindNet, DateTime valueDate, decimal unwindQty, decimal closeFee)
{
- unwindPrice = Math.Round(unwindPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
unwindPriceFee = decimal.Parse(unwindPriceFee.ToString("F10"));
var td = DbContext.trade.Find(tradeid);
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && !x.Invalid);
@@ -1319,6 +1332,10 @@ namespace YLErp.Modules.SwapModule
var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id);
td.trade_extend = tradeExtend;
var position = positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && !x.IsInitial).FirstOrDefault();
+ var storagePriceRound = ConsGlobal.InstrumentType.IsBond(position?.UnderlyingInstrumentType)
+ ? ConsGlobal.PriceRound
+ : ConsGlobal.SwapDeliveryPriceRound;
+ unwindPrice = Math.Round(unwindPrice, storagePriceRound, MidpointRounding.AwayFromZero);
var preDealDate = GetPreDealDate(td.id, dealDate, eventTypes);
swap_flow_event floatEvent = new swap_flow_event();
UnwindData unwindData = new UnwindData();
diff --git a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
index cf1b3440..2266fcc5 100644
--- a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
@@ -32,13 +32,29 @@ namespace YLErp.Modules.SwapModule
}
+ private int GetStorageDeliveryPriceRound(string underlyingInstrumentType, string underlyingCode)
+ {
+ if (ConsGlobal.InstrumentType.IsBond(underlyingInstrumentType))
+ {
+ return ConsGlobal.PriceRound;
+ }
+ if (string.IsNullOrEmpty(underlyingCode))
+ {
+ return ConsGlobal.SwapDeliveryPriceRound;
+ }
+ return GetUnderlyingData(underlyingCode)?.IsBond() == true
+ ? ConsGlobal.PriceRound
+ : ConsGlobal.SwapDeliveryPriceRound;
+ }
+
#region 可测试化接缝(Seams)——override 这些虚方法可在测试中替换 DB/外部调用,生产代码行为不变
/// 持久化 eod 持仓记录(生产: DbContext.Add;测试: 收集到列表)
protected virtual void PersistEodSwapPosition(eod_swap_position position)
{
- position.PosiGrossPrice = Math.Round(position.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
- position.UnderlyingPrice = Math.Round(position.UnderlyingPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ var storagePriceRound = GetStorageDeliveryPriceRound(position.UnderlyingInstrumentType, position.UnderlyingCode);
+ position.PosiGrossPrice = Math.Round(position.PosiGrossPrice, storagePriceRound, MidpointRounding.AwayFromZero);
+ position.UnderlyingPrice = Math.Round(position.UnderlyingPrice, storagePriceRound, MidpointRounding.AwayFromZero);
position.PosiNotionalValue = Math.Round(position.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
if (position.id == 0)
{
@@ -179,7 +195,7 @@ namespace YLErp.Modules.SwapModule
{
var normalizedPrice = BondPriceConverter.ToStorage(price);
Log.Error($"互换债券日终价格按展示态返回,已转换为存储态: UnderlyingCode={code}, ValueDate={settleDate:yyyy-MM-dd}, PosiGrossPrice={posiGrossPrice}, SourcePrice={price}, NormalizedPrice={normalizedPrice}");
- return Math.Round(normalizedPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ return Math.Round(normalizedPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
}
return Math.Round(price, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
@@ -1471,7 +1487,10 @@ namespace YLErp.Modules.SwapModule
newEodPayPosition.ContractSize = eventFlow.ContractSize;
newEodPayPosition.CountRatio = eventFlow.CountRatio;
newEodPayPosition.PosiNetPrice = netPrice;
- newEodPayPosition.PosiGrossPrice = Math.Round(grossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ newEodPayPosition.PosiGrossPrice = Math.Round(
+ grossPrice,
+ GetStorageDeliveryPriceRound(eventFlow.UnderlyingInstrumentType, eventFlow.UnderlyingCode),
+ MidpointRounding.AwayFromZero);
newEodPayPosition.PosiNetFeePrice = netFeePrice;
newEodPayPosition.PosiNetNoFeePrice = netNoFeePrice;
newEodPayPosition.PosiQuantity = payQty;
@@ -1770,7 +1789,10 @@ namespace YLErp.Modules.SwapModule
posiQty = 0;
}
curretEod.PosiGrossPrice = (eod.PosiGrossPrice * eod.PosiQuantity + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountAvg)) / (eod.PosiQuantity + openQty);
- curretEod.PosiGrossPrice = Math.Round(curretEod.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ curretEod.PosiGrossPrice = Math.Round(
+ curretEod.PosiGrossPrice,
+ GetStorageDeliveryPriceRound(curretEod.UnderlyingInstrumentType, curretEod.UnderlyingCode),
+ MidpointRounding.AwayFromZero);
curretEod.PosiNetPrice = (eod.PosiNetPrice * eod.PosiQuantity + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountFeeAvg)) / (eod.PosiQuantity + openQty);
curretEod.PosiNetPrice = Math.Round(curretEod.PosiNetPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
curretEod.PosiNetNoFeePrice = (eod.PosiNetNoFeePrice * eod.PosiQuantity + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountNetAvg)) / (eod.PosiQuantity + openQty);
@@ -1896,7 +1918,7 @@ namespace YLErp.Modules.SwapModule
}
if (data.IsBond())
{
- return Math.Round(BondPrice(data, settleDate, out vobp), ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ return Math.Round(BondPrice(data, settleDate, out vobp), ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
}
var price = data.Price ?? 0;
if (EodPriceQueryService.TryGetEodPrice(settleDate, code, out var eodPrice))
diff --git a/YLErpDAL/Modules/SwapModule/SwapFlowEventService.cs b/YLErpDAL/Modules/SwapModule/SwapFlowEventService.cs
index 9fafcdf7..2b28cca0 100644
--- a/YLErpDAL/Modules/SwapModule/SwapFlowEventService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapFlowEventService.cs
@@ -51,6 +51,21 @@ namespace YLErp.Modules.SwapModule
protected virtual underlying_manager GetUnderlying(string underlyingCode)
=> DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
+ private int GetStorageDeliveryPriceRound(string underlyingInstrumentType, string underlyingCode)
+ {
+ if (ConsGlobal.InstrumentType.IsBond(underlyingInstrumentType))
+ {
+ return ConsGlobal.PriceRound;
+ }
+ if (string.IsNullOrEmpty(underlyingCode))
+ {
+ return ConsGlobal.SwapDeliveryPriceRound;
+ }
+ return GetUnderlying(underlyingCode)?.IsBond() == true
+ ? ConsGlobal.PriceRound
+ : ConsGlobal.SwapDeliveryPriceRound;
+ }
+
protected virtual DateTime GetNextBusinessDay(DateTime date)
=> QdpCalendarHelper.GetNonHoliday(date);
@@ -63,7 +78,10 @@ namespace YLErp.Modules.SwapModule
{
if (!string.IsNullOrEmpty(evt.UnderlyingCode))
{
- evt.TradingAmountAvg = Math.Round(evt.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ evt.TradingAmountAvg = Math.Round(
+ evt.TradingAmountAvg,
+ GetStorageDeliveryPriceRound(evt.UnderlyingInstrumentType, evt.UnderlyingCode),
+ MidpointRounding.AwayFromZero);
}
DbContext.swap_flow_event.Add(evt);
}
@@ -325,7 +343,7 @@ namespace YLErp.Modules.SwapModule
DataState = 1,
EventDate = flow_merge.OccurTime,
UnwindDate = QdpCalendarHelper.GetNonHoliday(flow_merge.OccurTime.AddDays(1)),
- TradingAmountAvg = Math.Round(TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
+ TradingAmountAvg = Math.Round(TradingAmountAvg, GetStorageDeliveryPriceRound(underlyingInstrumentType, flow_merge.UnderlyingCode), MidpointRounding.AwayFromZero),
TradingAmountFeeAvg = TradingAmountFeeAvg,
TradingAmountNetFeeAvg = TradingAmountNetFeeAvg,
TradingAmountNetAvg = flow_merge.TradingAmountNetAvg,
@@ -379,7 +397,10 @@ namespace YLErp.Modules.SwapModule
DataState = 1,
EventDate = flow_merge.OccurTime,
UnwindDate = QdpCalendarHelper.GetNonHoliday(flow_merge.OccurTime.AddDays(1)),
- TradingAmountAvg = Math.Round(flow_merge.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
+ TradingAmountAvg = Math.Round(
+ flow_merge.TradingAmountAvg,
+ GetStorageDeliveryPriceRound(null, flow_merge.UnderlyingCode),
+ MidpointRounding.AwayFromZero),
TradingAmountFeeAvg = flow_merge.TradingAmountFeeAvg,
TradingFeePending = flow_merge.TradingFeePending,
ClientId = flow_merge.ClientId
@@ -413,7 +434,10 @@ namespace YLErp.Modules.SwapModule
DataState = (int)SwapFlowDateStateEnum.完成,
EventDate = td.TradeDate.Value,
UnwindDate = td.StartDate.Value,
- TradingAmountAvg = Math.Round(position.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
+ TradingAmountAvg = Math.Round(
+ position.PosiGrossPrice,
+ GetStorageDeliveryPriceRound(position.UnderlyingInstrumentType, position.UnderlyingCode),
+ MidpointRounding.AwayFromZero),
TradingAmountFeeAvg = position.PosiNetPrice,
TradingAmountNetFeeAvg = position.PosiNetFeePrice,
TradingAmountNetAvg = position.PosiNetNoFeePrice,
@@ -466,7 +490,7 @@ namespace YLErp.Modules.SwapModule
DataState = 100,
EventDate = td.TradeDate.Value,
UnwindDate = QdpCalendarHelper.GetNonHoliday(td.TradeDate.Value.AddDays(1)),
- TradingAmountAvg = Math.Round(flowMerge.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
+ TradingAmountAvg = Math.Round(flowMerge.TradingAmountAvg, GetStorageDeliveryPriceRound(underlyingInstrumentType, flowMerge.UnderlyingCode), MidpointRounding.AwayFromZero),
TradingAmountFeeAvg = flowMerge.TradingAmountFeeAvg,
TradingAmountNetFeeAvg = flowMerge.TradingAmountNetFeeAvg,
TradingAmountNetAvg = flowMerge.TradingAmountNetAvg,
diff --git a/YLErpDAL/Modules/SwapModule/SwapFlowImportService.cs b/YLErpDAL/Modules/SwapModule/SwapFlowImportService.cs
index 79b5ad5a..3ee3e891 100644
--- a/YLErpDAL/Modules/SwapModule/SwapFlowImportService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapFlowImportService.cs
@@ -118,7 +118,8 @@ namespace YLErp.Modules.SwapModule
swap_flow.ytm = reader.GetDecimalOrPercent("成交收益率",false,true) ?? 0;
swap_flow.TradingAmountNet = reader.GetDecimal("成交净价") ?? 0;
swap_flow.TradingAmountNetFee = TradeFeeHelper.CalcPriceWithFee(swap_flow.TradingFee, swap_flow.TradingAmountNet??0, swap_flow.TradingQty, swap_flow.BsType);
- if (underlying != null && underlying.IsBond())
+ var isBond = underlying != null && underlying.IsBond();
+ if (isBond)
{
// 成交流水债券报价(×100形式)转入库小数(×0.01),统一走 BondPriceConverter
swap_flow.TradingAmountAvg = BondPriceConverter.ToStorage(swap_flow.TradingAmountAvg);
@@ -129,7 +130,10 @@ namespace YLErp.Modules.SwapModule
if (swap_flow.TradingAmountNetFee.HasValue)
swap_flow.TradingAmountNetFee = BondPriceConverter.ToStorage(swap_flow.TradingAmountNetFee.Value);
}
- swap_flow.TradingAmountAvg = Math.Round(swap_flow.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ swap_flow.TradingAmountAvg = Math.Round(
+ swap_flow.TradingAmountAvg,
+ isBond ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound,
+ MidpointRounding.AwayFromZero);
if (!string.IsNullOrEmpty(clientName))
{
var client = DataCacheProvider.GetClientDataSource().AsQueryable(x=>x.Name== clientName).FirstOrDefault();
diff --git a/YLErpDAL/Modules/SwapModule/SwapFlowService.cs b/YLErpDAL/Modules/SwapModule/SwapFlowService.cs
index 3cb640b9..430a5e53 100644
--- a/YLErpDAL/Modules/SwapModule/SwapFlowService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapFlowService.cs
@@ -473,7 +473,7 @@ namespace YLErp.Modules.SwapModule
swap_Flow.UnderlyingCode = req.UnderlyingCode;
swap_Flow.BsType = req.BsType;
swap_Flow.ContractSize = req.ContractSize;
- swap_Flow.TradingAmountAvg = Math.Round(req.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ swap_Flow.TradingAmountAvg = req.TradingAmountAvg;
swap_Flow.TradingAmountFeeAvg = TradeFeeHelper.CalcPriceWithFee(req.TradingFee,req.TradingAmountAvg,req.TradingQty,req.BsType);
swap_Flow.ClientId = req.ClientId;
swap_Flow.ytm = req.ytm;
@@ -540,7 +540,7 @@ namespace YLErp.Modules.SwapModule
swap_Flow.BsType = req.BsType;
swap_Flow.DataState = (int)SwapFlowDateStateEnum.等待完成;
swap_Flow.ContractSize = req.ContractSize;
- swap_Flow.TradingAmountAvg = Math.Round(req.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ swap_Flow.TradingAmountAvg = req.TradingAmountAvg;
swap_Flow.TradingAmountFeeAvg = req.TradingAmountFeeAvg;
UpdateDbOption(swap_Flow);
if (req.id == 0)
@@ -695,7 +695,7 @@ namespace YLErp.Modules.SwapModule
TradingFee = gourpItem.Sum(s => s.TradingFee),
DataState = (int)SwapFlowDateStateEnum.等待完成,
TradingAmountFeeAvg = gourpItem.Average(s => s.TradingAmountFeeAvg),
- TradingAmountAvg = Math.Round(gourpItem.Average(s => s.TradingAmountAvg), ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
+ TradingAmountAvg = Math.Round(gourpItem.Average(s => s.TradingAmountAvg), ConsGlobal.PriceRound, MidpointRounding.AwayFromZero),
ContractSize = swapflow.ContractSize
};
UpdateDbOption(swap_flow_summary);
@@ -757,7 +757,8 @@ namespace YLErp.Modules.SwapModule
{
throw new ServiceException("没有找到标的信息:" + req.UnderlyingCode);
}
- if (underlying != null && underlying.IsBond())
+ var isBond = underlying.IsBond();
+ if (isBond)
{
// 债券报价(×100)转入库小数(×0.01),价格字段统一走 BondPriceConverter
req.TradingAmountAvg = BondPriceConverter.ToStorage(req.TradingAmountAvg);
@@ -769,7 +770,10 @@ namespace YLErp.Modules.SwapModule
// 数量×100(万手→手),与价格维度无关,保留常量
req.TradingQty *= ConsGlobal.bondShowPriceMultiple;
}
- req.TradingAmountAvg = Math.Round(req.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ req.TradingAmountAvg = Math.Round(
+ req.TradingAmountAvg,
+ isBond ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound,
+ MidpointRounding.AwayFromZero);
}
diff --git a/YLErpDAL/Modules/SwapModule/SwapTradeAutoService.cs b/YLErpDAL/Modules/SwapModule/SwapTradeAutoService.cs
index 1acab84e..410112e4 100644
--- a/YLErpDAL/Modules/SwapModule/SwapTradeAutoService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapTradeAutoService.cs
@@ -154,7 +154,7 @@ namespace YLErp.Modules.SwapModule
swapFlow.DataState = (int)SwapFlowDateStateEnum.等待完成;
}
// 债券报价(×100)转入库小数(×0.01),统一走 BondPriceConverter
- swapFlow.TradingAmountAvg = Math.Round(BondPriceConverter.ToStorage(item.deal_full_price ?? 0), ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ swapFlow.TradingAmountAvg = Math.Round(BondPriceConverter.ToStorage(item.deal_full_price ?? 0), ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
swapFlow.TradingAmountFeeAvg = BondPriceConverter.ToStorage(item.deal_full_price_include_fee ?? 0);
swapFlow.TradingAmount = swapFlow.TradingQty * swapFlow.ContractSize * swapFlow.TradingAmountAvg;
swapFlow.ClientId = Convert.ToInt32(item.client_id ?? 0);
@@ -451,7 +451,7 @@ namespace YLErp.Modules.SwapModule
swap_flow_summary.FirstFlowTime = swapflow.OptTime;
swap_flow_summary.SettleDate = gourpItem.Max(s => s.SettleDate);
swap_flow_summary.TradingAmountAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountAvg * s.TradingQty) / swap_flow_summary.TradingQty;
- swap_flow_summary.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ swap_flow_summary.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
swap_flow_summary.TradingAmountFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountAvg : swap_flow_summary.TradingAmountAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountFeeAvg = Math.Round(swap_flow_summary.TradingAmountFeeAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
swap_flow_summary.TradingAmountNetAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountNet * s.TradingQty) / swap_flow_summary.TradingQty;
@@ -508,7 +508,7 @@ namespace YLErp.Modules.SwapModule
swap_flow_summary.SettleDate = gourpItem.Max(s => s.SettleDate);
swap_flow_summary.TradingAmount = swap_flow_summary.TradingQty * swap_flow_summary.ContractSize;
swap_flow_summary.TradingAmountAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.FullPrice * s.TradingQty) / swap_flow_summary.TradingQty;
- swap_flow_summary.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ swap_flow_summary.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
swap_flow_summary.TradingAmountFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountAvg : swap_flow_summary.TradingAmountAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountNetAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.NetPrice * s.TradingQty) / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountNetFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountNetAvg : swap_flow_summary.TradingAmountNetAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
@@ -1082,7 +1082,7 @@ namespace YLErp.Modules.SwapModule
var ratio = flowMergeClone.BsType == 1 ? 1 : -1;
var oriRatio = flowMergeClone.BsType == 1 ? -1 : 1;
flowMergeClone.TradingFeePending = flowMergeClone.TradingQty / origin.TradingQty * origin.TradingFeePending;
- flowMergeClone.TradingAmountAvg = Math.Round(origin.TradingAmountAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
+ flowMergeClone.TradingAmountAvg = Math.Round(origin.TradingAmountAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
flowMergeClone.TradingAmountNetAvg = origin.TradingAmountNetAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty;
flowMergeClone.TradingAmountFeeAvg = flowMergeClone.TradingAmountAvg + ratio * flowMergeClone.TradingFeePending / flowMergeClone.TradingQty;
diff --git a/YLErpDAL/Modules/SwapModule/SwapTradeService.cs b/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
index d48b339a..f8e5156e 100644
--- a/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
@@ -62,6 +62,13 @@ namespace YLErp.Modules.SwapModule
return roundedPrice;
}
+ private static decimal? RoundSwapBondNetPriceAndYtm(decimal? value)
+ {
+ return value.HasValue
+ ? Math.Round(value.Value, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero)
+ : null;
+ }
+
#region 互换交易保存
///
/// 新版收益互换预付金校验
@@ -361,7 +368,7 @@ namespace YLErp.Modules.SwapModule
td.ValidState = "Valid";
td.TradeSource = "系统交易";
td.TradeStatus = ConsTrade.确认成交;
- td.InitYtm = flowMerge.InitYtm ?? 0;
+ td.InitYtm = RoundSwapBondNetPriceAndYtm(flowMerge.InitYtm) ?? 0;
return td;
}
///
@@ -400,7 +407,7 @@ namespace YLErp.Modules.SwapModule
OptId = UserInfo.UserId,
OptName = UserInfo.UserName,
UnderlyingInstrumentType = underlying.UnderlyingInstrumentType,
- InitYtm = flowMerge.InitYtm
+ InitYtm = RoundSwapBondNetPriceAndYtm(flowMerge.InitYtm)
};
td.swap_positions.Add(floatPosition);
swap_position interestPosition = new swap_position()
@@ -593,7 +600,7 @@ namespace YLErp.Modules.SwapModule
dbTrade.trade_extend = req.trade_extend;
dbTrade.swap_positions = req.swap_positions;
dbTrade.MetaDic = req.MetaDic;
- dbTrade.InitYtm = req.swap_positions.FirstOrDefault(p => p.InitYtm != null)?.InitYtm;
+ dbTrade.InitYtm = RoundSwapBondNetPriceAndYtm(req.swap_positions.FirstOrDefault(p => p.InitYtm != null)?.InitYtm);
InnerSaveTrade(false, dbTrade, changsStr, changeConfirmStatus);
return dbTrade;
@@ -1380,7 +1387,7 @@ namespace YLErp.Modules.SwapModule
position.PosiGrossPrice = Math.Round(swap.PosiGrossPrice, storagePriceRound, MidpointRounding.AwayFromZero);
position.PosiNetPrice = swap.PosiQuantity == 0 ? 0 : (position.PosiGrossPrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio);
position.PosiNetPrice = Math.Round(position.PosiNetPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
- position.PosiNetNoFeePrice = swap.PosiNetNoFeePrice;
+ position.PosiNetNoFeePrice = RoundSwapBondNetPriceAndYtm(swap.PosiNetNoFeePrice);
position.PosiNetFeePrice = swap.PosiQuantity == 0 ? 0 : (swap.PosiNetNoFeePrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio);
position.PosiNetFeePrice = Math.Round(position.PosiNetFeePrice??0, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
position.PosiNotionalValue = Math.Round(swap.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
@@ -1405,10 +1412,10 @@ namespace YLErp.Modules.SwapModule
position.FloatRateUnderlyingCode = swap.FloatRateUnderlyingCode;
position.interest_rest_days = swap.interest_rest_days;
position.interest_rule = swap.interest_rule;
- position.InitYtm = swap.InitYtm;
- if (swap.InitYtm != null && swap.InitYtm > 0)
+ position.InitYtm = RoundSwapBondNetPriceAndYtm(swap.InitYtm);
+ if (position.InitYtm != null && position.InitYtm > 0)
{
- td.InitYtm = swap.InitYtm;
+ td.InitYtm = position.InitYtm;
}
if (position.id == 0)
diff --git a/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml b/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml
index c62ed4c9..8d8b244b 100644
--- a/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml
+++ b/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml
@@ -455,10 +455,10 @@
|
-
+
|
-
+
|
diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js
index 0e6a610d..a0057f28 100644
--- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js
+++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js
@@ -79,7 +79,8 @@ const vue = new Vue({
return SwapCalc.getPriceScale(this.multiplier);
},
getStorageDeliveryPrice() {
- return _.round(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), 9);
+ const precision = inputFormatSwapDeliveryPrice.precision + (this.multiplier === 100 ? 2 : 0);
+ return SwapCalc.roundHalfAwayFromZero(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), precision);
},
initDeal() {
var positions = model.FlowEvents.filter((item) => {
diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
index 5c974e69..6c9fcdf1 100644
--- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
+++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
@@ -13,9 +13,10 @@ const inputFormatSwapRate = Object.freeze({ precision: otcformat.trading.premium
const inputFormatTradePrice = Object.freeze({ precision: otcformat.trading.tradePrice.precision, negative: true, append: '' });
const inputFormatTradeSinglePrice = Object.freeze({ precision: otcformat.trading.tradeSinglePrice.precision, negative: true, append: '', percent: false });
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
-const inputFormatMarginRateNoPercent = Object.freeze({ precision: otcformat.trading.umpriceP.precision, append: '', percent:true });
const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: false });
const inputFormatSwapBondDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: true });
+const inputFormatSwapBondNetPriceAndYtm = Object.freeze({ precision: 9, negative: true, append: '', percent: true });
+const swapBondStoragePricePrecision = inputFormatSwapBondDeliveryPrice.precision + 2;
const consUnderlyingFlagBase = (function () {
let unSelFlag = tradeHelper.UnderlyingSelectFlag;
@@ -221,6 +222,15 @@ const vue = new Vue({
});
},
methods: {
+ roundStorageDeliveryPrice(item, price) {
+ const precision = tradeHelper.IsBond(item && item.UnderlyingInstrumentType)
+ ? swapBondStoragePricePrecision
+ : inputFormatSwapDeliveryPrice.precision;
+ return _.round(Number(price), precision);
+ },
+ roundStorageBondNetPriceAndYtm(value) {
+ return value == null ? value : _.round(Number(value), swapBondStoragePricePrecision);
+ },
getPosiPriceFormatKey(item, field) {
const index = item && item.index != null ? item.index : '';
const isBond = tradeHelper.IsBond(item && item.UnderlyingInstrumentType);
@@ -279,7 +289,7 @@ const vue = new Vue({
//计算数量
if (this.paySwapList.length > 0) {
var item = this.paySwapList[0];
- var deliveryPrice = _.round(Number(item.PosiGrossPrice), 9);
+ var deliveryPrice = this.roundStorageDeliveryPrice(item, item.PosiGrossPrice);
var notional = deliveryPrice * item.ContractSize;
item.PosiQuantity = notional == 0 ? 0 : _.round(this.trade.StockEqvNotional / notional, page.otcFormatConfig.StockEqvNotional.precision);
this.calcNotional();
@@ -361,7 +371,7 @@ const vue = new Vue({
}
var national = payItem.PosiQuantity * payItem.ContractSize;
// 守卫: 名义本金必须 round 到 2 位 → 对应历史 bug f873239a(缺 _.round); 外置到 swapCalc.calcStockEqvNotional
- var deliveryPrice = _.round(Number(payItem.PosiGrossPrice), 9);
+ var deliveryPrice = this.roundStorageDeliveryPrice(payItem, payItem.PosiGrossPrice);
var stockEqvNotional = SwapCalc.calcStockEqvNotional(deliveryPrice, national);//名义本金=期初价格*数量*乘数
this.trade.StockEqvNotional = otcformat.trading.stockEqvNotional(stockEqvNotional);
payItem.PosiNotionalValue = this.trade.StockEqvNotional;
@@ -506,7 +516,9 @@ const vue = new Vue({
errorcount++;
return false;
}
- x.PosiGrossPrice = _.round(Number(x.PosiGrossPrice), 9);
+ x.PosiGrossPrice = thisObj.roundStorageDeliveryPrice(x, x.PosiGrossPrice);
+ x.PosiNetNoFeePrice = thisObj.roundStorageBondNetPriceAndYtm(x.PosiNetNoFeePrice);
+ x.InitYtm = x.InitYtm == null ? null : thisObj.roundStorageBondNetPriceAndYtm(x.InitYtm);
thisObj.trade.swap_positions.push(x);
});
} else {
@@ -632,8 +644,8 @@ const vue = new Vue({
var thisObj = this;
main.post("/pricing/AjaxGetUnderlyingPrice", { underlyingCode: underlyingCode, tradeDate: StartDate })
.done(function (resp) {
- item.PosiNetNoFeePrice = otcformat.trading.umprice(resp.obj.netPrice);
- item.PosiGrossPrice = _.round(Number(resp.obj.price), 9);
+ item.PosiNetNoFeePrice = thisObj.roundStorageBondNetPriceAndYtm(resp.obj.netPrice);
+ item.PosiGrossPrice = thisObj.roundStorageDeliveryPrice(item, resp.obj.price);
thisObj.calcNotional();
});
},
diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js
index 71553a15..3838b7ea 100644
--- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js
+++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js
@@ -42,7 +42,8 @@ const vue = new Vue({
return this.multiplier == 100 ? 0.01 : 1;
},
getStorageDeliveryPrice() {
- return _.round(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), 9);
+ const precision = inputFormatSwapDeliveryPrice.precision + (this.multiplier === 100 ? 2 : 0);
+ return SwapCalc.roundHalfAwayFromZero(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), precision);
},
initDeal() {
var positions = model.FlowEvents.filter((item) => {
From a91010589af99ff140eceb47b8c5468d1769c0be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BC=A0=E5=90=8D=E9=94=90?= <1565842059@qq.com>
Date: Thu, 23 Jul 2026 14:16:50 +0800
Subject: [PATCH 6/9] =?UTF-8?q?fix:=20Bonds=E6=9E=9A=E4=B8=BE=E5=AF=B9?=
=?UTF-8?q?=E5=BA=94=E7=9A=84=E5=80=BC=E6=94=B9=E5=9B=9EBond?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Framework/YLErp.Core/ConsGlobal.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Framework/YLErp.Core/ConsGlobal.cs b/Framework/YLErp.Core/ConsGlobal.cs
index 6469db05..049817e7 100644
--- a/Framework/YLErp.Core/ConsGlobal.cs
+++ b/Framework/YLErp.Core/ConsGlobal.cs
@@ -121,7 +121,7 @@ namespace YLErp
public const string TBonds = "TBonds";
public const string CreditBonds = "CreditBonds"; //信用债
public const string OtherBonds = "OtherBonds"; //其它债券
- public const string Bonds = "Bonds"; //债券
+ public const string Bonds = "Bond"; //债券
public const string GoldFutures = "GoldFutures";
public const string TBFutures = "TBFutures";
public const string OtherFutures = "OtherFutures";
From 32a3a45c85e87fdb68e18dc8f4ed3ff2aacb6513 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BC=A0=E5=90=8D=E9=94=90?= <1565842059@qq.com>
Date: Thu, 23 Jul 2026 14:17:26 +0800
Subject: [PATCH 7/9] =?UTF-8?q?refactor(swap):=20=E4=BF=AE=E5=A4=8D?=
=?UTF-8?q?=E5=80=BA=E5=88=B8=E6=97=A5=E7=BB=88=E4=BB=B7=E6=A0=BC=E6=98=A0?=
=?UTF-8?q?=E5=B0=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 移除 GetSwapValuationPrice 方法中的 posiGrossPrice 参数
- 删除债券价格转换相关的注释代码和日志记录
- 简化债券价格处理逻辑,直接使用原始价格进行四舍五入
- 更新所有调用 GetSwapValuationPrice 的地方以匹配新方法签名
- 移除多余的价格转换和展示态判断逻辑
---
.../SwapModule/SwapEodPositionService.cs | 24 +++++--------------
1 file changed, 6 insertions(+), 18 deletions(-)
diff --git a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
index 2266fcc5..59aa0707 100644
--- a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
@@ -175,27 +175,15 @@ namespace YLErp.Modules.SwapModule
return UnderlyingCodePrice(code, settleDate, out vobp);
}
- ///
- /// 获取用于互换浮动腿盯市的标的价格。
- ///
- /// 普通债券类收益互换的新录入页面将全价按小数保存,例如页面录入 20% 后 PosiGrossPrice 为 0.2
- ///
- /// 因此仅当交易期初价已经是小数口径、且当前债券价明显仍处于展示态时,再做
- /// 一次展示态到存储态转换。期初价本身是历史展示态口径的存量交易保持原价格,
- /// 避免修改日终估值链路后改变其既有损益。
- ///
- private decimal GetSwapValuationPrice(string code, decimal posiGrossPrice, DateTime settleDate, out decimal vobp)
+ /// 获取用于互换浮动腿盯市的标的价格。
+ private decimal GetSwapValuationPrice(string code, DateTime settleDate, out decimal vobp)
{
var price = GetUnderlyingPrice(code, settleDate, out vobp);
var underlying = GetUnderlyingData(code);
- // var usesStoragePrice = Math.Abs(posiGrossPrice) < 2m;
- // var usesDisplayPrice = Math.Abs(price) >= 10m;
if (underlying?.IsBond() == true)
{
- var normalizedPrice = BondPriceConverter.ToStorage(price);
- Log.Error($"互换债券日终价格按展示态返回,已转换为存储态: UnderlyingCode={code}, ValueDate={settleDate:yyyy-MM-dd}, PosiGrossPrice={posiGrossPrice}, SourcePrice={price}, NormalizedPrice={normalizedPrice}");
- return Math.Round(normalizedPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
+ return Math.Round(price, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
}
return Math.Round(price, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
@@ -1575,7 +1563,7 @@ namespace YLErp.Modules.SwapModule
int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
curretEod.PosiStatus = curretEod.PosiQuantity == 0 ? 1 : 0;
- var price = GetSwapValuationPrice(eod.UnderlyingCode, eod.PosiGrossPrice, dealDate, out decimal vobp);
+ var price = GetSwapValuationPrice(eod.UnderlyingCode, dealDate, out decimal vobp);
curretEod.dv01 = Dv01Helper.CalcDv01(eod.UnderlyingCode, curretEod.PosiQuantity, eod.PosiDirection, eod.PositionType, vobp);
decimal tax = um.ValueAddedTax ?? 0;
if (valueDate > td.StartDate.Value && curretEod.PosiQuantity > 0)
@@ -1667,7 +1655,7 @@ namespace YLErp.Modules.SwapModule
var dealDate = curretEod.ValueDate;
int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
- var price = GetSwapValuationPrice(eod.UnderlyingCode, eod.PosiGrossPrice, dealDate, out decimal vobp);
+ var price = GetSwapValuationPrice(eod.UnderlyingCode, dealDate, out decimal vobp);
var todayConsumedDividend = CalcConsumedDividend(curretEod, unwindEvents);
var originNotional = (decimal)td.OriginalStockEqvNotional / swapPosition.PosiNetPrice;
decimal totalPayment = CalcBondPayment(curretEod.UnderlyingCode, td.StartDate.Value, valueDate, (decimal)originNotional, shortRatio, directionRatio);
@@ -1856,7 +1844,7 @@ namespace YLErp.Modules.SwapModule
curretEod.ContractSize = position.ContractSize;
curretEod.CountRatio = position.CountRatio;
curretEod.PosiTradingFee = position.PosiTradingFee;
- curretEod.UnderlyingPrice = GetSwapValuationPrice(position.UnderlyingCode, position.PosiGrossPrice, dealDate, out decimal vobp);
+ curretEod.UnderlyingPrice = GetSwapValuationPrice(position.UnderlyingCode, dealDate, out decimal vobp);
SetPriceInfoByFlowEvent(eod, curretEod, unwindEvents, position);
curretEod.dv01 = Dv01Helper.CalcDv01(curretEod.UnderlyingCode, curretEod.PosiQuantity, curretEod.PosiDirection, curretEod.PositionType, vobp);
//if (settleDate == td.TradeDate)
From 7475bddc4483306ab5c4c80ad1c5e0aa1d0af3d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BC=A0=E5=90=8D=E9=94=90?= <1565842059@qq.com>
Date: Thu, 23 Jul 2026 16:23:52 +0800
Subject: [PATCH 8/9] =?UTF-8?q?fix(tradeHelper):=20=E6=97=A0=E6=B3=95?=
=?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=90=8E=E7=AB=AFBonds->Bond=20=E9=87=87?=
=?UTF-8?q?=E7=94=A8=E5=89=8D=E7=AB=AF=E5=85=BC=E5=AE=B9=EF=BC=8C=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D=E5=80=BA=E5=88=B8=E7=B1=BB=E5=9E=8B=E5=88=A4=E6=96=AD?=
=?UTF-8?q?=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 添加了 Bond 类型到 IsBond 函数的判断条件中
- 确保 Bond 类型能够被正确识别为债券产品
---
YLErpWeb/wwwroot/Scripts/app/tradeHelper.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/YLErpWeb/wwwroot/Scripts/app/tradeHelper.js b/YLErpWeb/wwwroot/Scripts/app/tradeHelper.js
index 75701fcb..fa58d6ff 100644
--- a/YLErpWeb/wwwroot/Scripts/app/tradeHelper.js
+++ b/YLErpWeb/wwwroot/Scripts/app/tradeHelper.js
@@ -276,6 +276,7 @@
tradeHelper.IsBond = function (instType) {
switch (instType) {
case "Bonds":
+ case "Bond":
case "TBonds":
case "CreditBonds":
case "OtherBonds":
From c4379d4b907224633705e59ce534c6dc80bb9466 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=BC=A0=E5=90=8D=E9=94=90?= <1565842059@qq.com>
Date: Thu, 23 Jul 2026 17:49:02 +0800
Subject: [PATCH 9/9] =?UTF-8?q?fix(swap):=20=E4=BF=AE=E6=AD=A3=E6=97=A5?=
=?UTF-8?q?=E7=BB=88=E4=BF=9D=E8=AF=81=E9=87=91=E6=9C=AC=E9=87=91=E8=AE=A1?=
=?UTF-8?q?=E7=AE=97=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 移除保证金本金的1/10缩放处理,改为直接使用实际本金数值
- 修正了保证金本金计算中的除以10逻辑,确保显示正确的本金金额
- 保持利息计算使用原始本金累积值,避免破坏保证金利息金额准确性
- 更新注释说明,明确当前风险页按日终保证金
---
YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
index 59aa0707..6975a5ee 100644
--- a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
@@ -2327,24 +2327,23 @@ namespace YLErp.Modules.SwapModule
item.PeriodPaymentValuation = item.FloatingUnrealizedPnl + item.position.InterestPnL;
}
// eod_swap 的保证金本金来自 trade_span;缺少 span 数据时会被保存为 0。
- // 本风险页改按日终保证金腿展示,且该页面保证金本金采用原始本金的 1/10 口径。
- // 利息仍使用原始本金累积值,不能同步缩放,否则会破坏保证金利息金额。
+ // 本风险页改按日终保证金腿的实际本金展示。
item.position.InitMarginGain = marginLegs
.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金
&& x.InterestDirection == (int)SwapDirectionEnum.收取)
- .Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
+ .Sum(x => Math.Abs(x.InterestPrincipalFix));
item.position.InitMarginLoss = marginLegs
.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金
&& x.InterestDirection == (int)SwapDirectionEnum.支付)
- .Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
+ .Sum(x => Math.Abs(x.InterestPrincipalFix));
item.position.PostionMarginGain = marginLegs
.Where(x => x.InterestMode == (int)InterestModeEnum.追加预付金
&& x.InterestDirection == (int)SwapDirectionEnum.收取)
- .Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
+ .Sum(x => Math.Abs(x.InterestPrincipalFix));
item.position.PostionMarginLoss = marginLegs
.Where(x => x.InterestMode == (int)InterestModeEnum.追加预付金
&& x.InterestDirection == (int)SwapDirectionEnum.支付)
- .Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
+ .Sum(x => Math.Abs(x.InterestPrincipalFix));
// 保证金本金方向与我方的利息现金流方向相反:原始“收取”保证金
// 表示我方占用客户资金,应向客户支付利息;支付金额按负数展示。
|