From 4987962aca5b549f9398e53210c68c9dbfe351e1 Mon Sep 17 00:00:00 2001
From: tengyufan <1532636164@qq.com>
Date: Mon, 27 Jul 2026 11:57:20 +0800
Subject: [PATCH] =?UTF-8?q?#EQD-6597=20=E5=9B=BD=E8=81=94=E6=B0=91?=
=?UTF-8?q?=E7=94=9F-=E7=B0=BF=E8=AE=B0=E6=97=B6=E6=96=B0=E5=A2=9E?=
=?UTF-8?q?=E5=9F=BA=E6=9C=AC=E8=B4=B9=E7=8E=87=E5=AD=97=E6=AE=B5=EF=BC=88?=
=?UTF-8?q?=E5=BC=80=E5=B9=B3=E4=BB=93=E8=B4=B9=E7=94=A8=EF=BC=89=EF=BC=8C?=
=?UTF-8?q?=E5=B9=B6=E4=B8=94=E8=87=AA=E5=8A=A8=E8=AE=A1=E7=AE=97=20feat:?=
=?UTF-8?q?=20=E5=AE=8C=E5=96=84=E4=BA=92=E6=8D=A2=E5=B9=B3=E4=BB=93?=
=?UTF-8?q?=E4=BA=A4=E6=98=93=E8=B4=B9=E7=94=A8=E8=87=AA=E5=8A=A8=E8=AE=A1?=
=?UTF-8?q?=E7=AE=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 支持百分比和单位数量模式自动计算平仓交易费用
- 支持全部平仓和部分平仓联动重算
- 保持手动互换不自动填充平仓交易费用
- 补充前后端相关单测
---
.../YLErp.Core/DBModels/SwapFlowEvent.cs | 10 ++
Framework/YLErp.Core/DBModels/SwapPosition.cs | 5 +
.../SwapModule/InitUnwindTradingFeeTest.cs | 65 +++++++++++
.../Modules/SwapModule/SwapDealService.cs | 17 +++
.../Modules/SwapModule/SwapTradeService.cs | 1 +
YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml | 11 ++
YLErpWeb/Views/SwapTrade2/TradeView.cshtml | 11 ++
YLErpWeb/fe-tests/unwindSwapTrade.test.js | 92 +++++++++++++++
.../Scripts/app/swaptrade/swapTradeEdit.js | 107 +++++++++++++++---
.../Scripts/app/swaptrade/unwindSwapTrade.js | 27 +++++
10 files changed, 329 insertions(+), 17 deletions(-)
create mode 100644 UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs
create mode 100644 YLErpWeb/fe-tests/unwindSwapTrade.test.js
diff --git a/Framework/YLErp.Core/DBModels/SwapFlowEvent.cs b/Framework/YLErp.Core/DBModels/SwapFlowEvent.cs
index b9079e27..632ed8c3 100644
--- a/Framework/YLErp.Core/DBModels/SwapFlowEvent.cs
+++ b/Framework/YLErp.Core/DBModels/SwapFlowEvent.cs
@@ -228,6 +228,16 @@ namespace YLErp.DBModels
[NotMapped]
public decimal BeforeCloseFee { get; set; }
///
+ /// 基础费率(仅前端展示,不存库)
+ ///
+ [NotMapped]
+ public decimal PosiTradingFeeUnit { get; set; }
+ ///
+ /// 基础费率模式 0=百分比 1=单位数量(仅前端展示,不存库)
+ ///
+ [NotMapped]
+ public int PosiFeeType { get; set; }
+ ///
/// 持仓腿id
///
[DisplayName("持仓腿id")]
diff --git a/Framework/YLErp.Core/DBModels/SwapPosition.cs b/Framework/YLErp.Core/DBModels/SwapPosition.cs
index 44fb89d9..04a306f9 100644
--- a/Framework/YLErp.Core/DBModels/SwapPosition.cs
+++ b/Framework/YLErp.Core/DBModels/SwapPosition.cs
@@ -113,6 +113,11 @@ namespace YLErp.DBModels
[DataChange]
public decimal PosiTradingFeeUnit { get; set; }
///
+ /// 单位交易费用模式 0=百分比 1=单位数量
+ ///
+ [DataChange]
+ public int PosiFeeType { get; set; }
+ ///
/// 起始日
///
[DisplayName("起始日")]
diff --git a/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs b/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs
new file mode 100644
index 00000000..8691bb11
--- /dev/null
+++ b/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs
@@ -0,0 +1,65 @@
+using System.Reflection;
+using YLErp.DBModels;
+
+namespace YLErp.Modules.SwapModule
+{
+ [TestClass]
+ public class InitUnwindTradingFeeTest
+ {
+ private static decimal InvokeCalcInitTradingFee(swap_position position, UnwindData unwindData)
+ {
+ var method = typeof(SwapDealService).GetMethod(
+ "CalcInitTradingFee",
+ BindingFlags.NonPublic | BindingFlags.Static);
+
+ Assert.IsNotNull(method, "未找到 CalcInitTradingFee 私有静态方法");
+
+ return (decimal)method.Invoke(null, new object[] { position, unwindData });
+ }
+
+ [TestMethod]
+ public void 百分比模式_按平仓名义本金计算并四舍五入到两位()
+ {
+ var position = new swap_position
+ {
+ PosiFeeType = 0,
+ PosiTradingFeeUnit = 0.1234m
+ };
+ var unwindData = new UnwindData
+ {
+ CloseNotionalValue = 1_000_000m,
+ CloseQty = 8888m
+ };
+
+ var fee = InvokeCalcInitTradingFee(position, unwindData);
+
+ Assert.AreEqual(1234.00m, fee);
+ }
+
+ [TestMethod]
+ public void 单位数量模式_按平仓数量计算并四舍五入到两位()
+ {
+ var position = new swap_position
+ {
+ PosiFeeType = 1,
+ PosiTradingFeeUnit = 1.235m
+ };
+ var unwindData = new UnwindData
+ {
+ CloseNotionalValue = 1_000_000m,
+ CloseQty = 10m
+ };
+
+ var fee = InvokeCalcInitTradingFee(position, unwindData);
+
+ Assert.AreEqual(12.35m, fee);
+ }
+
+ [TestMethod]
+ public void 空入参_返回零()
+ {
+ Assert.AreEqual(0m, InvokeCalcInitTradingFee(null, new UnwindData()));
+ Assert.AreEqual(0m, InvokeCalcInitTradingFee(new swap_position(), null));
+ }
+ }
+}
diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs
index 05182f5f..7bf7f637 100644
--- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs
@@ -291,6 +291,9 @@ namespace YLErp.Modules.SwapModule
floatEvent.UnderlyingInstrumentType = position.UnderlyingInstrumentType;
floatEvent.CloseFee = 0;
floatEvent.BeforeCloseFee = oriPosition.PosiTradingFeePending;
+ floatEvent.TradingFee = CalcInitTradingFee(oriPosition, unwindData);
+ floatEvent.PosiTradingFeeUnit = oriPosition?.PosiTradingFeeUnit ?? 0;
+ floatEvent.PosiFeeType = oriPosition?.PosiFeeType ?? 0;
floatEvent.MarkClosePnl = 0;
floatEvent.PayDirection = position.PosiDirection;
floatEvent.PosiGrossPrice = position.PosiGrossPrice;
@@ -313,6 +316,20 @@ namespace YLErp.Modules.SwapModule
}
return unwindData;
}
+ private static decimal CalcInitTradingFee(swap_position oriPosition, UnwindData unwindData)
+ {
+ if (oriPosition == null || unwindData == null)
+ {
+ return 0;
+ }
+
+ if (oriPosition.PosiFeeType == 1)
+ {
+ return Math.Round(oriPosition.PosiTradingFeeUnit * unwindData.CloseQty, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
+ }
+
+ return Math.Round(oriPosition.PosiTradingFeeUnit / 100m * unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
+ }
///
/// 校验上日是否收盘
///
diff --git a/YLErpDAL/Modules/SwapModule/SwapTradeService.cs b/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
index ca343cbc..f32f97fe 100644
--- a/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapTradeService.cs
@@ -1375,6 +1375,7 @@ namespace YLErp.Modules.SwapModule
position.PosiTradingFee = swap.PosiTradingFee;
position.PosiTradingFee=Math.Round(position.PosiTradingFee, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
position.PosiTradingFeeUnit = swap.PosiTradingFeeUnit;
+ position.PosiFeeType = swap.PosiFeeType;
position.PosiTradingFeePending = swap.PosiTradingFeePending;
position.PosiTradingFeePending = Math.Round(position.PosiTradingFeePending, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
position.UnderlyingCode = swap.UnderlyingCode;
diff --git a/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml b/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml
index 6409fe53..0eaf1224 100644
--- a/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml
+++ b/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml
@@ -450,6 +450,7 @@
期初标的成交收益率% |
期初标的价格 |
数量 |
+ 基础费率 |
交易费用后付 |
@@ -487,6 +488,16 @@
|
{{item.underlying!=null?item.underlying.QuoteUnitString:''}}
|
+
+
+
+ %
+
+
+
+ ¥
+
+ |
我方{{item.PosiDirection==1?"支付":"收取"}}交易费用
diff --git a/YLErpWeb/Views/SwapTrade2/TradeView.cshtml b/YLErpWeb/Views/SwapTrade2/TradeView.cshtml
index 9172e401..4cdf1c8e 100644
--- a/YLErpWeb/Views/SwapTrade2/TradeView.cshtml
+++ b/YLErpWeb/Views/SwapTrade2/TradeView.cshtml
@@ -384,6 +384,7 @@
| @initYtmTitle |
}
数量 |
+ 基础费率 |
交易费用后付 |
@foreach (var item in paySwapPositions)
@@ -409,6 +410,16 @@
@item.PosiQuantity.OtcFormat(OtcFormatFlag.StockEqvNotional)
|
+
+ @if (item.PosiFeeType == 0)
+ {
+ @(item.PosiTradingFeeUnit.ToString("0.0000") + "%")
+ }
+ else
+ {
+ @item.PosiTradingFeeUnit.ToString("0.00")
+ }
+ |
@item.PosiTradingFeePending.OtcFormat(OtcFormatFlag.StockEqvNotional)
|
diff --git a/YLErpWeb/fe-tests/unwindSwapTrade.test.js b/YLErpWeb/fe-tests/unwindSwapTrade.test.js
new file mode 100644
index 00000000..b977b41e
--- /dev/null
+++ b/YLErpWeb/fe-tests/unwindSwapTrade.test.js
@@ -0,0 +1,92 @@
+const fs = require('fs');
+const path = require('path');
+const vm = require('vm');
+
+function createNumberFormat(precision) {
+ const formatter = (value) => Number(Number(value || 0).toFixed(precision));
+ formatter.precision = precision;
+ return formatter;
+}
+
+function loadUnwindHelpers() {
+ const filePath = path.join(__dirname, '../wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js');
+ const code = fs.readFileSync(filePath, 'utf8') + '\nmodule.exports = { swapPosiFeeCalc, consPosiFeeType };';
+
+ const stockEqvNotional = createNumberFormat(2);
+ const sandbox = {
+ module: { exports: {} },
+ exports: {},
+ console,
+ require,
+ window: { otcformat: { options: {} } },
+ otcformat: {
+ options: {},
+ trading: {
+ premiumRateP: { precision: 4 },
+ tradePrice: { precision: 4 },
+ notional: { precision: 6 },
+ StockEqvNotional: stockEqvNotional,
+ marginRateP: { precision: 4 },
+ umpriceP: { precision: 4 }
+ },
+ fixed6: createNumberFormat(6)
+ },
+ model: {
+ ValueDate: '2026-07-27',
+ FlowEvents: [],
+ StructureType: '',
+ TradeStartDate: ''
+ },
+ isUseApproval: false,
+ Vue: function (options) { return options; },
+ FastVue: {
+ vueDatePicker() { return {}; },
+ vueNumberInput() { return {}; }
+ },
+ tradeHelper: { IsBond() { return false; } },
+ main: {
+ post() {
+ return {
+ done() { return this; }
+ };
+ },
+ message() { }
+ },
+ SwapCalc: {
+ roundHalfAwayFromZero(value) { return value; },
+ calcCloseQtyByOriginalPercent() { return 0; }
+ },
+ _: {
+ round(value, precision) {
+ return Number(Number(value || 0).toFixed(precision || 0));
+ }
+ }
+ };
+
+ sandbox.window.otcformat = sandbox.otcformat;
+ vm.runInNewContext(code, sandbox, { filename: filePath });
+ return sandbox.module.exports;
+}
+
+function expectClose(actual, expected, tolerance) {
+ expect(Math.abs(actual - expected)).toBeLessThanOrEqual(tolerance || 1e-6);
+}
+
+describe('unwindSwapTrade 基础费率计算', () => {
+ const { swapPosiFeeCalc, consPosiFeeType } = loadUnwindHelpers();
+
+ test('百分比模式按平仓名义本金计算并保留两位', () => {
+ const result = swapPosiFeeCalc.calcTradingFee(consPosiFeeType.Percent, 0.1234, 1000000, 5000);
+ expectClose(result, 1234.00);
+ });
+
+ test('单位数量模式按平仓数量计算并保留两位', () => {
+ const result = swapPosiFeeCalc.calcTradingFee(consPosiFeeType.Unit, 1.235, 1000000, 10);
+ expectClose(result, 12.35);
+ });
+
+ test('未知模式默认按百分比模式处理', () => {
+ const result = swapPosiFeeCalc.calcTradingFee(99, 0.1, 200000, 10);
+ expectClose(result, 200.00);
+ });
+});
diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
index fa6b7011..c3eecede 100644
--- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
+++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
@@ -12,11 +12,39 @@ const inputFormatTradeAmount = Object.freeze({ precision: otcformat.trading.noti
const inputFormatSwapRate = Object.freeze({ precision: otcformat.trading.premiumRateP.precision, negative: true, append: '%' });
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 inputFormatPosiFeePercent = Object.freeze({ precision: 4, negative: true, append: '%' });
+const inputFormatPosiFeeUnit = Object.freeze({ precision: 2, negative: true, append: '' });
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
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 consPosiFeeType = Object.freeze({ Percent: 0, Unit: 1 });
+const swapPosiFeeCalc = Object.freeze({
+ normalizeFeeType(feeType) {
+ return Number(feeType) === consPosiFeeType.Unit ? consPosiFeeType.Unit : consPosiFeeType.Percent;
+ },
+ calcPending(feeType, feeUnit, stockEqvNotional, quantity) {
+ const normalizedFeeType = this.normalizeFeeType(feeType);
+ const normalizedFeeUnit = Number(feeUnit) || 0;
+ const normalizedNotional = Number(stockEqvNotional) || 0;
+ const normalizedQuantity = Number(quantity) || 0;
+ const tradingFeePending = normalizedFeeType === consPosiFeeType.Percent
+ ? normalizedFeeUnit / 100 * normalizedNotional
+ : normalizedFeeUnit * normalizedQuantity;
+ return otcformat.trading.tradeSinglePrice(tradingFeePending);
+ },
+ calcFeeUnit(feeType, tradingFeePending, stockEqvNotional, quantity) {
+ const normalizedFeeType = this.normalizeFeeType(feeType);
+ const normalizedTradingFeePending = Number(tradingFeePending) || 0;
+ const normalizedNotional = Number(stockEqvNotional) || 0;
+ const normalizedQuantity = Number(quantity) || 0;
+ if (normalizedFeeType === consPosiFeeType.Percent) {
+ return normalizedNotional === 0 ? 0 : _.round(normalizedTradingFeePending / normalizedNotional * 100, inputFormatPosiFeePercent.precision);
+ }
+ return normalizedQuantity === 0 ? 0 : _.round(normalizedTradingFeePending / normalizedQuantity, inputFormatPosiFeeUnit.precision);
+ }
+});
const consUnderlyingFlagBase = (function () {
let unSelFlag = tradeHelper.UnderlyingSelectFlag;
@@ -153,6 +181,7 @@ const vue = new Vue({
currencys: page.currencys,
getNotionalSingleFee: 0,
isSingleFee: page.Trade.trade_extend.ExtendObj.OpenFeeType == 0,
+ posiFeeModePercent: true,
observation: {//互换观察日
ObservationInterval: "",
IntervalList: [],
@@ -236,6 +265,41 @@ const vue = new Vue({
const isBond = tradeHelper.IsBond(item && item.UnderlyingInstrumentType);
return `${index}-${field}-${isBond ? 'bond' : 'other'}`;
},
+ getCurrentPosiFeeType() {
+ return this.posiFeeModePercent ? consPosiFeeType.Percent : consPosiFeeType.Unit;
+ },
+ normalizePosiFeeType(feeType) {
+ return swapPosiFeeCalc.normalizeFeeType(feeType);
+ },
+ syncPosiFeeModeByItem(item) {
+ this.posiFeeModePercent = this.normalizePosiFeeType(item && item.PosiFeeType) !== consPosiFeeType.Unit;
+ },
+ syncPayItemFeeType(item) {
+ item.PosiFeeType = this.getCurrentPosiFeeType();
+ },
+ refreshTradingFeePendingByUnit(item) {
+ this.syncPayItemFeeType(item);
+ item.PosiTradingFeePending = swapPosiFeeCalc.calcPending(
+ item.PosiFeeType,
+ item.PosiTradingFeeUnit,
+ this.trade.StockEqvNotional,
+ item.PosiQuantity
+ );
+ },
+ refreshTradingFeeUnitByPending(item) {
+ this.syncPayItemFeeType(item);
+ item.PosiTradingFeeUnit = swapPosiFeeCalc.calcFeeUnit(
+ item.PosiFeeType,
+ item.PosiTradingFeePending,
+ this.trade.StockEqvNotional,
+ item.PosiQuantity
+ );
+ },
+ refreshPayTradingFeesByUnit() {
+ this.paySwapList.forEach(item => {
+ this.refreshTradingFeePendingByUnit(item);
+ });
+ },
changeStructureType() {
this.trade.StockEqvNotional = 0;
let direction = this.trade.trade_extend.ExtendObj.Direction;
@@ -385,6 +449,7 @@ const vue = new Vue({
//变更名义本金(仅格式化,不反算数量)
changeStockEqvNotional() {
this.trade.StockEqvNotional = otcformat.trading.StockEqvNotional(this.trade.StockEqvNotional);
+ this.refreshPayTradingFeesByUnit();
//计算数量
// if (this.paySwapList.length > 0) {
// var item = this.paySwapList[0];
@@ -474,6 +539,7 @@ const vue = new Vue({
var stockEqvNotional = SwapCalc.calcStockEqvNotional(deliveryPrice, national);//名义本金=期初价格*数量*乘数
this.trade.StockEqvNotional = otcformat.trading.stockEqvNotional(stockEqvNotional);
payItem.PosiNotionalValue = this.trade.StockEqvNotional;
+ this.refreshPayTradingFeesByUnit();
}
},
//变更到期日
@@ -509,26 +575,27 @@ const vue = new Vue({
},
//变更单位交易费用
changeTradingFeeUnit(item) {
- //计算交易费用
- //if (this.trade.trade_extend.ExtendObj.OpenFeeType == 0) {//按手数收费
- // item.PosiTradingFee = item.ContractSize == 0 ? 0 : otcformat.trading.tradeSinglePrice(item.PosiQuantity * item.PosiTradingFeeUnit / item.ContractSize);
- //} else {
- // item.PosiTradingFee = otcformat.trading.tradeSinglePrice(item.PosiQuantity * item.PosiTradingFeeUnit);
- //}
-
+ this.refreshTradingFeePendingByUnit(item);
},
//变更交易费用
changeTradingFee(item) {
- //计算单位交易费用
- //if (item.PosiQuantity == 0) {
- // item.PosiTradingFeeUnit = 0;
- // return
- //}
- //if (this.trade.trade_extend.ExtendObj.OpenFeeType == 0) {//按手数收费
- // item.PosiTradingFeeUnit = item.ContractSize == 0 ? 0 : otcformat.trading.tradeSinglePrice(item.PosiTradingFee * item.ContractSize / item.PosiQuantity);
- //} else {
- // item.PosiTradingFeeUnit = otcformat.trading.tradeSinglePrice(item.PosiTradingFee / item.PosiQuantity);
- //}
+ this.refreshTradingFeeUnitByPending(item);
+ },
+ showPayAbsPrice() {
+ this.posiFeeModePercent = false;
+ this.paySwapList.forEach(item => {
+ item.PosiFeeType = consPosiFeeType.Unit;
+ item.PosiTradingFeeUnit = 0;
+ item.PosiTradingFeePending = 0;
+ });
+ },
+ showPayPercentPrice() {
+ this.posiFeeModePercent = true;
+ this.paySwapList.forEach(item => {
+ item.PosiFeeType = consPosiFeeType.Percent;
+ item.PosiTradingFeeUnit = 0;
+ item.PosiTradingFeePending = 0;
+ });
},
savetrade() {
if (!this.checkSubmitData()) {
@@ -618,6 +685,7 @@ const vue = new Vue({
x.PosiGrossPrice = thisObj.roundStorageDeliveryPrice(x, x.PosiGrossPrice);
x.PosiNetNoFeePrice = thisObj.roundStorageBondNetPriceAndYtm(x.PosiNetNoFeePrice);
x.InitYtm = x.InitYtm == null ? null : thisObj.roundStorageBondNetPriceAndYtm(x.InitYtm);
+ x.PosiFeeType = thisObj.normalizePosiFeeType(x.PosiFeeType);
thisObj.trade.swap_positions.push(x);
});
} else {
@@ -1439,6 +1507,7 @@ const vue = new Vue({
thisObj.paySwapList = thisObj.trade.swap_positions.filter(x => { if (x.UnderlyingCode != null && x.UnderlyingCode.length != 0 && x.IsInitial) return x; });
thisObj.paySwapList.forEach((val, num, arr) => {
arr[num].index = num;
+ arr[num].PosiFeeType = thisObj.normalizePosiFeeType(arr[num].PosiFeeType);
this.StockEqvNotional = val.ContractSize * val.PosiQuantity * val.PosiGrossPrice;
// D2 修复:重开(审批重开/刷新)已保存的债券成交单时,三字段互算的手动标志随页面重置而丢失;
// 若不锁,用户一旦编辑任一价格字段就会以它为源重新反算、覆盖当初保存的其他两格。
@@ -1450,6 +1519,9 @@ const vue = new Vue({
thisObj.$set(arr[num], 'bondManual', { CP: true, DP: true, YD: true });
}
});
+ if (thisObj.paySwapList.length > 0) {
+ thisObj.syncPosiFeeModeByItem(thisObj.paySwapList[0]);
+ }
}
if (thisObj.paySwapList.length == 0) {
@@ -1576,6 +1648,7 @@ const vue = new Vue({
PosiTradingFee: 0,//交易费用
PosiTradingFeePending: 0,//交易费用后付
PosiTradingFeeUnit: 0,//单位交易费用
+ PosiFeeType: thisObj.getCurrentPosiFeeType(),//单位交易费用模式
InterestDirection: 0,//利息收支方式
InterestRateDefault: 0,//计息利率
InterestMode: 0,//计息基本类型
diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js
index 3838b7ea..554329ac 100644
--- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js
+++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js
@@ -8,6 +8,22 @@ const inputFormatEqvNotional = Object.freeze({ precision: otcformat.trading.Stoc
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 });
+const consPosiFeeType = Object.freeze({ Percent: 0, Unit: 1 });
+const swapPosiFeeCalc = {
+ normalizeFeeType(feeType) {
+ return Number(feeType) === consPosiFeeType.Unit ? consPosiFeeType.Unit : consPosiFeeType.Percent;
+ },
+ calcTradingFee(feeType, feeUnit, closeNotionalValue, closeQty) {
+ const normalizedFeeType = this.normalizeFeeType(feeType);
+ const normalizedFeeUnit = Number(feeUnit) || 0;
+ const normalizedCloseNotionalValue = Number(closeNotionalValue) || 0;
+ const normalizedCloseQty = Number(closeQty) || 0;
+ const tradingFee = normalizedFeeType === consPosiFeeType.Unit
+ ? normalizedFeeUnit * normalizedCloseQty
+ : normalizedFeeUnit / 100 * normalizedCloseNotionalValue;
+ return otcformat.trading.StockEqvNotional(_.round(tradingFee, 2));
+ }
+};
let ValueDate = model.ValueDate;
const vue = new Vue({
el: '#vueDiv',
@@ -140,6 +156,7 @@ const vue = new Vue({
this.deal.CloseQty = this.calcCloseQtyByPercent(this.deal.ClosePercent);
}
this.calcTradingFeePending();
+ this.refreshTradingFeeByUnit();
this.getInterestList();
this.calcFloatClosePnl();
},
@@ -153,6 +170,13 @@ const vue = new Vue({
calcTradingFeePending() {
this.floatPosition.TradingFeePending = this.floatPosition.BeforeCloseFee * parseFloat(this.deal.ClosePercent);
},
+ refreshTradingFeeByUnit() {
+ this.floatPosition.TradingFee = swapPosiFeeCalc.calcTradingFee(
+ this.floatPosition.PosiFeeType,
+ this.floatPosition.PosiTradingFeeUnit,
+ this.deal.CloseNotionalValue,
+ this.deal.CloseQty);
+ },
changeCloseQty() {//修改平仓数量
if (parseFloat(this.deal.CloseQty) > parseFloat(this.deal.PositionQty)) {
main.message("平仓数量不能超过持仓数量");
@@ -169,6 +193,7 @@ const vue = new Vue({
// 占期初口径:平仓名义本金 = 平仓比例 × 期初名义本金(NotionalValue)
this.deal.CloseNotionalValue = otcformat.trading.StockEqvNotional(parseFloat(this.deal.ClosePercent) * parseFloat(this.deal.NotionalValue));
this.calcTradingFeePending();
+ this.refreshTradingFeeByUnit();
this.getInterestList();
this.calcFloatClosePnl();
},
@@ -187,6 +212,7 @@ const vue = new Vue({
this.deal.CloseMethod = 2;
}
this.calcTradingFeePending();
+ this.refreshTradingFeeByUnit();
this.getInterestList();
this.calcFloatClosePnl();
},
@@ -205,6 +231,7 @@ const vue = new Vue({
this.deal.CloseMethod = 2;
}
this.calcTradingFeePending();
+ this.refreshTradingFeeByUnit();
this.getInterestList();
this.calcFloatClosePnl();
},