From 46316c140029b2ea8b951973c9a9b98eaa442368 Mon Sep 17 00:00:00 2001 From: tengyufan <1532636164@qq.com> Date: Wed, 5 Aug 2026 18:26:15 +0800 Subject: [PATCH 01/17] =?UTF-8?q?fix:=20=E6=81=A2=E5=A4=8D=E4=BA=92?= =?UTF-8?q?=E6=8D=A2=E5=B9=B3=E4=BB=93=E4=BA=A4=E6=98=93=E8=B4=B9=E7=94=A8?= =?UTF-8?q?=E6=8C=89=E5=9F=BA=E7=A1=80=E8=B4=B9=E7=8E=87=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SwapModule/InitUnwindTradingFeeTest.cs | 16 ++++++-------- .../Modules/SwapModule/SwapDealService.cs | 22 +++++++++---------- YLErpWeb/fe-tests/unwindSwapTrade.test.js | 15 ++++++++----- .../Scripts/app/swaptrade/unwindSwapTrade.js | 10 +-------- 4 files changed, 28 insertions(+), 35 deletions(-) diff --git a/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs b/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs index 0b9f7b37..8ebfb199 100644 --- a/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs +++ b/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs @@ -111,27 +111,25 @@ namespace YLErp.Modules.SwapModule } [TestMethod] - public void PartialCloseTradingFeeAndPendingFeeUseTheSameRoundedOriginalFeeAllocation() + public void ManuallyAdjustedPendingFeeDoesNotOverrideBaseRateCloseFee() { var oriPosition = new swap_position { - PosiFeeType = 0, - PosiTradingFeeUnit = 1.1234m, - PosiTradingFeePending = 113.46m + PosiFeeType = 1, + PosiTradingFeeUnit = 0.123456m, + PosiTradingFeePending = 1235.56m }; var unwindData = new UnwindData { - NotionalValue = 10098m, - CloseNotionalValue = 4039.2m, NotionalQty = 10000m, - CloseQty = 4000m + CloseQty = 10000m }; var tradingFee = InvokeCalcInitTradingFee(oriPosition, unwindData); var pendingFee = InvokeCalcInitTradingFeePending(oriPosition, new swap_position(), unwindData); - Assert.AreEqual(45.38m, tradingFee); - Assert.AreEqual(45.38m, pendingFee); + Assert.AreEqual(1234.56m, tradingFee); + Assert.AreEqual(1235.56m, pendingFee); } [TestMethod] diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 05d0a372..4248ae87 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -324,19 +324,12 @@ namespace YLErp.Modules.SwapModule return 0; } - if (oriPosition.PosiTradingFeeUnit == 0) + if (oriPosition.PosiFeeType == 1) { - return 0; + return Math.Round(oriPosition.PosiTradingFeeUnit * unwindData.CloseQty, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); } - var closeBase = oriPosition.PosiFeeType == 1 ? unwindData.CloseQty : unwindData.CloseNotionalValue; - var originalBase = oriPosition.PosiFeeType == 1 ? unwindData.NotionalQty : unwindData.NotionalValue; - if (originalBase <= 0) - { - return 0; - } - - return Math.Round(oriPosition.PosiTradingFeePending * closeBase / originalBase, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); + return Math.Round(oriPosition.PosiTradingFeeUnit / 100m * unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); } private static decimal CalcInitTradingFeePending(swap_position oriPosition, swap_position position, UnwindData unwindData) @@ -346,7 +339,14 @@ namespace YLErp.Modules.SwapModule return position?.PosiTradingFeePending ?? 0; } - return CalcInitTradingFee(oriPosition, unwindData); + var closeBase = oriPosition.PosiFeeType == 1 ? unwindData.CloseQty : unwindData.CloseNotionalValue; + var originalBase = oriPosition.PosiFeeType == 1 ? unwindData.NotionalQty : unwindData.NotionalValue; + if (originalBase <= 0) + { + return position?.PosiTradingFeePending ?? 0; + } + + return Math.Round(oriPosition.PosiTradingFeePending * closeBase / originalBase, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); } /// /// 校验上日是否收盘 diff --git a/YLErpWeb/fe-tests/unwindSwapTrade.test.js b/YLErpWeb/fe-tests/unwindSwapTrade.test.js index 53c8197d..eb87bc0d 100644 --- a/YLErpWeb/fe-tests/unwindSwapTrade.test.js +++ b/YLErpWeb/fe-tests/unwindSwapTrade.test.js @@ -43,6 +43,9 @@ function loadUnwindHelpers() { vueDatePicker() { return {}; }, vueNumberInput() { return {}; } }, + swapPricePrecision: { + createVueInputComponent() { return {}; } + }, tradeHelper: { IsBond() { return false; } }, main: { post() { @@ -112,14 +115,14 @@ describe('base-rate pending trading fee', () => { expectClose(result, 450.00); }); - test('partial close fee and pending fee both use the rounded opening fee allocation', () => { - const tradingFee = swapPosiFeeCalc.calcAllocatedTradingFee( - 113.46, consPosiFeeType.Percent, 1.1234, 4039.2, 4000, 10098, 10000); + test('a manually adjusted pending fee does not override the base-rate close fee', () => { + const tradingFee = swapPosiFeeCalc.calcTradingFee( + consPosiFeeType.Unit, 0.123456, 0, 10000); const pendingFee = swapPosiFeeCalc.calcTradingFeePending( - 113.46, consPosiFeeType.Percent, 1.1234, 4039.2, 4000, 10098, 10000, 0.4); + 1235.56, consPosiFeeType.Unit, 0.123456, 0, 10000, 0, 10000, 1); - expectClose(tradingFee, 45.38); - expectClose(pendingFee, 45.38); + expectClose(tradingFee, 1234.56); + expectClose(pendingFee, 1235.56); }); test('without a configured base rate, the legacy close-percent calculation remains', () => { diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js index 27e031a7..4e30c411 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js @@ -213,15 +213,7 @@ const vue = new Vue({ this.deal.ClosePercent); }, refreshTradingFeeByUnit() { - const allocatedFee = swapPosiFeeCalc.calcAllocatedTradingFee( - this.floatPosition.BeforeCloseFee, - this.floatPosition.PosiFeeType, - this.floatPosition.PosiTradingFeeUnit, - this.deal.CloseNotionalValue, - this.deal.CloseQty, - this.deal.NotionalValue, - this.deal.NotionalQty); - this.floatPosition.TradingFee = allocatedFee !== null ? allocatedFee : swapPosiFeeCalc.calcTradingFee( + this.floatPosition.TradingFee = swapPosiFeeCalc.calcTradingFee( this.floatPosition.PosiFeeType, this.floatPosition.PosiTradingFeeUnit, this.deal.CloseNotionalValue, From db5dcaf1495fad1ab377b2d2895a56a942a9c3f0 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, 6 Aug 2026 09:54:15 +0800 Subject: [PATCH 02/17] =?UTF-8?q?refactor(swaptrade):=20=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E7=A1=AC=E7=BC=96=E7=A0=81=E7=B2=BE=E5=BA=A6=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E4=B8=BA=E7=BB=9F=E4=B8=80=E7=9A=84=E7=B2=BE=E5=BA=A6=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 swapPricePrecision 工具替代原有的 otcformat 精度设置 - 在多个页面文件中引入 swapPricePrecision 相关脚本 - 将原有的固定精度配置替换为动态精度获取方法 - 统一金额、数量、利率等字段的格式化处理逻辑 - 优化数据格式化函数,提高数值显示的一致性和准确性 - 移除冗余的硬编码精度参数,简化代码维护复杂度 --- .../App_Data/Config/swappriceprecision.js | 5 ++ .../Views/SwapTrade2/SwapLongShortSwap.cshtml | 2 + .../SwapTrade2/SwapLongShortUnwind.cshtml | 2 + YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml | 6 +- YLErpWeb/Views/SwapTrade2/TradeView.cshtml | 89 ++++++++++--------- .../Scripts/app/swaptrade/SwapflowList.js | 43 ++++----- .../Scripts/app/swaptrade/incomeSwapTrade.js | 58 ++++++------ .../Scripts/app/swaptrade/swapLongShort.js | 38 ++++---- .../app/swaptrade/swapPricePrecisionHelper.js | 47 ++++++++++ .../Scripts/app/swaptrade/swapTradeEdit.js | 18 ++-- .../Scripts/app/swaptrade/swapTradeView.js | 9 +- .../Scripts/app/swaptrade/unwindLongShort.js | 34 +++---- .../Scripts/app/swaptrade/unwindSwapTrade.js | 68 +++++++------- 13 files changed, 248 insertions(+), 171 deletions(-) diff --git a/YLErpWeb/App_Data/Config/swappriceprecision.js b/YLErpWeb/App_Data/Config/swappriceprecision.js index ca3cca8a..2cb43923 100644 --- a/YLErpWeb/App_Data/Config/swappriceprecision.js +++ b/YLErpWeb/App_Data/Config/swappriceprecision.js @@ -1,6 +1,11 @@ // 通过 /front/swappriceprecision 加载。可按 UnderlyingInstrumentType 修改;缺项或非法值由页面精度组件回退内置默认规则。 window.main = window.main || {}; window.main.swapPricePrecision = { + common: { + amount: { precision: 2 }, + quantity: { precision: 2 }, + rate: { precision: 4 } + }, Stock: { integerDigits: 7, precision: 2 }, StockIndex: { integerDigits: 7, precision: 2 }, StockIF: { integerDigits: 7, precision: 4 }, diff --git a/YLErpWeb/Views/SwapTrade2/SwapLongShortSwap.cshtml b/YLErpWeb/Views/SwapTrade2/SwapLongShortSwap.cshtml index f004920a..1ef9f150 100644 --- a/YLErpWeb/Views/SwapTrade2/SwapLongShortSwap.cshtml +++ b/YLErpWeb/Views/SwapTrade2/SwapLongShortSwap.cshtml @@ -19,6 +19,8 @@ + + }
diff --git a/YLErpWeb/Views/SwapTrade2/SwapLongShortUnwind.cshtml b/YLErpWeb/Views/SwapTrade2/SwapLongShortUnwind.cshtml index 5257e478..c773ae03 100644 --- a/YLErpWeb/Views/SwapTrade2/SwapLongShortUnwind.cshtml +++ b/YLErpWeb/Views/SwapTrade2/SwapLongShortUnwind.cshtml @@ -20,6 +20,8 @@ + + }
diff --git a/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml b/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml index eb805b1c..9ec357e9 100644 --- a/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml +++ b/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml @@ -102,6 +102,8 @@ + + @@ -235,11 +237,11 @@
- +
- +
diff --git a/YLErpWeb/Views/SwapTrade2/TradeView.cshtml b/YLErpWeb/Views/SwapTrade2/TradeView.cshtml index e82b88bc..93efbf9f 100644 --- a/YLErpWeb/Views/SwapTrade2/TradeView.cshtml +++ b/YLErpWeb/Views/SwapTrade2/TradeView.cshtml @@ -32,6 +32,7 @@ var sr = trade.trade_extend.ExtendObj.SettlementRules; bool hideFloatingIncomeDirection = PS.Config.ErpElement.SwapFloatingIncomeReceiveOnlyMode; string SwapPriceData(decimal? value) => value?.ToString(CultureInfo.InvariantCulture) ?? string.Empty; + string SwapCommonData(object value) => value == null ? string.Empty : Convert.ToString(value, CultureInfo.InvariantCulture); } @section CSS{ @@ -134,7 +135,7 @@ 名义本金 - @trade.OriginalStockEqvNotional.OtcFormat(OtcFormatFlag.StockEqvNotional) + @* 初始预付金 @@ -277,9 +278,9 @@ @((SwapDirectionEnum)item.InterestDirection) @((InterestModeEnum)item.InterestMode) @item.HappenDate.OtcFormatDate() - @item.InterestPrincipalFix.OtcFormat(OtcFormatFlag.StockEqvNotional) + @item.Currency - @item.InterestRateDefault.OtcFormatPercent(4) + @(item.IsAnnualized ? "是" : "否") @@ -342,11 +343,11 @@ @((SwapDirectionEnum)item.InterestDirection) @((InterestModeEnum)item.InterestMode) - @interestPrice?.OtcFormat(OtcFormatFlag.StockEqvNotional) + @(string.IsNullOrEmpty(item.FloatRateUnderlyingCode) ? "无" : item.FloatRateUnderlyingCode) @sign - @item.InterestRateDefault.OtcFormatPercent(4) + @(item.IsAnnualized ? "是" : "否") @@ -422,7 +423,7 @@ } - @item.PosiQuantity.OtcFormat(OtcFormatFlag.StockEqvNotional) + @if (item.PosiFeeType == 0) @@ -435,7 +436,7 @@ } - @item.PosiTradingFeePending.OtcFormat(OtcFormatFlag.StockEqvNotional) + } @@ -507,9 +508,9 @@ @((SwapDirectionEnum)item.InterestDirection) @((InterestModeEnum)item.InterestMode) @item.HappenDate.OtcFormatDate() - @item.InterestPrincipalFix.OtcFormat(OtcFormatFlag.StockEqvNotional) + @item.Currency - @item.InterestRateDefault.OtcFormatPercent(4) + @(item.IsAnnualized ? "是" : "否") @@ -567,11 +568,11 @@ @((SwapDirectionEnum)item.InterestDirection) @((InterestModeEnum)item.InterestMode) - @interestPrice?.OtcFormat(OtcFormatFlag.StockEqvNotional) + @(string.IsNullOrEmpty(item.FloatRateUnderlyingCode) ? "无" : item.FloatRateUnderlyingCode) @sign - @item.InterestRateDefault.OtcFormatPercent(4) + @(item.IsAnnualized ? "是" : "否") @(item.InterestType == 0 ? "单利" : "复利") @@ -645,10 +646,10 @@ } - @item.PosiQuantity.OtcFormat(OtcFormatFlag.StockEqvNotional) + - @item.PosiNotionalValue.OtcFormat(OtcFormatFlag.StockEqvNotional) - @item.PosiTradingFeePending.OtcFormat(OtcFormatFlag.StockEqvNotional) + + @item.PosiStartDate.OtcFormatDate() @item.PosiMatuirityDate.OtcFormatDate() @@ -709,14 +710,14 @@ @if (tc.CloseType == 1) { 平仓数量 - @((PS.Config.IsUseDisplayNotional ? tc.CloseQty * (trade.CountRatio ?? 1) : tc.CloseQty).OtcFormatNotional()) + } else { 平仓比例 @(tc.ClosePercent.OtcFormatPercent(4)) 平仓名义本金 - @(tc.CloseNotionalValue.OtcFormat(OtcFormatFlag.StockEqvNotional)) + } @@ -734,9 +735,9 @@ 实现盈亏 - @(tc.SwapRealizedPnL.OtcFormat(OtcFormatFlag.StockEqvNotional)) + 平仓总额 - @(tc.SwapCloseAmount.OtcFormat(OtcFormatFlag.StockEqvNotional)) + @@ -762,9 +763,9 @@ @((SwapDirectionEnum)item.InterestDirection) @(item.InterestModeStr) - @(item.InterestPrincipal.OtcFormat(OtcFormatFlag.StockEqvNotional)) - @(item.InterestAmount.OtcFormatMoney()) - @(item.InterestClosePnL.OtcFormat(OtcFormatFlag.StockEqvNotional)) + + + } @@ -798,9 +799,9 @@ @(item.InterestStartDate.OtcFormatDate()) @(item.InterestEndDate.OtcFormatDate()) @(item.Rate.OtcFormatPercent())*@ - @(item.InterestFee.OtcFormat(OtcFormatFlag.StockEqvNotional)) - @(item.InterestAmount.OtcFormatMoney()) - @(item.InterestClosePnL.OtcFormat(OtcFormatFlag.StockEqvNotional)) + + + } @@ -855,11 +856,11 @@ } - @(closeFloat.Quantity.OtcFormat(OtcFormatFlag.StockEqvNotional)) - @(closeFloat.TradingFee.OtcFormat(OtcFormatFlag.StockEqvNotional)) - @(closeFloat.TradingFeePending.OtcFormat(OtcFormatFlag.StockEqvNotional)) - @(closeFloat.DividendIn.OtcFormat(OtcFormatFlag.StockEqvNotional)) - @(closeFloat.FloatPnlSum.OtcFormat(OtcFormatFlag.StockEqvNotional)) + + + + + @@ -893,9 +894,9 @@ 平仓总额 - @(eod.NotionalValue.OtcFormat(OtcFormatFlag.StockEqvNotional)) + 实现盈亏 - @(eod.TdRealizedPnL.OtcFormat(OtcFormatFlag.StockEqvNotional)) + } @@ -918,7 +919,7 @@ 互换序号 @(index++) 互换名义本金 - @(tc.PosiNotionalValue.OtcFormat(OtcFormatFlag.StockEqvNotional)) + 起始日期 @@ -934,9 +935,9 @@ 平仓总额 - @(tc.SwapCloseAmount.OtcFormat(OtcFormatFlag.StockEqvNotional)) + 实现盈亏 - @(tc.SwapRealizedPnL.OtcFormat(OtcFormatFlag.StockEqvNotional)) + @@ -962,9 +963,9 @@ var bgclass = item.InterestDirection == (int)SwapDirectionEnum.收取 ? "swapget" : "swappay"; @((SwapDirectionEnum)item.InterestDirection) - @(item.InterestFee.OtcFormat(OtcFormatFlag.StockEqvNotional)) - @(item.InterestAmount.OtcFormatMoney()) - @(item.InterestClosePnL.OtcFormat(OtcFormatFlag.StockEqvNotional)) + + + } @@ -994,9 +995,9 @@ var bgclass = item.InterestDirection == (int)SwapDirectionEnum.收取 ? "swapget" : "swappay"; @((SwapDirectionEnum)item.InterestDirection) - @(item.InterestFee.OtcFormat(OtcFormatFlag.StockEqvNotional)) - @(item.InterestAmount.OtcFormatMoney()) - @(item.InterestClosePnL.OtcFormat(OtcFormatFlag.StockEqvNotional)) + + + } @@ -1057,10 +1058,10 @@ } - @((closeFloat.PositionQty??0).OtcFormat(OtcFormatFlag.StockEqvNotional)) - @(closeFloat.TradingFee.OtcFormat(OtcFormatFlag.StockEqvNotional)) - @(closeFloat.DividendIn.OtcFormat(OtcFormatFlag.StockEqvNotional)) - @(closeFloat.FloatPnlSum.OtcFormat(OtcFormatFlag.StockEqvNotional)) + + + + diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/SwapflowList.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/SwapflowList.js index 3af1b627..6bba7673 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/SwapflowList.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/SwapflowList.js @@ -1,7 +1,10 @@ //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 inputFormatSwapAmount = swapPricePrecision.getCommonInputFormat('amount', { negative: true, append: '' }); +const inputFormatTradeAmount = swapPricePrecision.getCommonInputFormat('quantity', { append: '' }); +const formatSwapAmount = value => swapPricePrecision.formatCommon('amount', value); +const formatSwapQuantity = value => swapPricePrecision.formatCommon('quantity', value); const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' }); var clients = ylotc.clients; const consUnderlyingFlag = (function () { @@ -268,19 +271,19 @@ function getColModelGridStep1() { label: '成交数量/张数', width: 160, align: 'center', - formatter: otcformat.trading.notional + formatter: formatSwapQuantity }, { name: 'TradingAmount', label: '成交金额(元)', width: 210, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount }, { name: 'TradingFee', label: '交易费用', width: 90, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount }, { name: 'TradingAmountAvg', label: '成交全价', @@ -372,13 +375,13 @@ function getColModelGridStep2() { label: '成交数量/张数', width: 90, align: 'center', - formatter: otcformat.trading.notional + formatter: formatSwapQuantity }, { name: 'TradingAmount', label: '成交金额(元)', width: 90, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount }, { name: 'TradingAmountAvg', label: '成交均价', @@ -391,7 +394,7 @@ function getColModelGridStep2() { label: '交易费用', width: 90, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount }, { name: 'TradingAmountFeeAvg', label: '含费均价', @@ -521,13 +524,13 @@ function getColModelGridStep3() { label: '成交数量/张数', width: 160, align: 'center', - formatter: otcformat.trading.notional + formatter: formatSwapQuantity }, { name: 'TradingAmount', label: '成交金额(元)', width: 160, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount }, { name: 'ContractSize', label: '乘数', @@ -540,31 +543,31 @@ function getColModelGridStep3() { label: '交易费用佣金', width: 160, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount }, { name: 'TradingFeePending', label: '待结算交易费用佣金', width: 160, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount }, { name: 'DividendPending', label: '待结算分红收益', width: 160, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount }, { name: 'MarkClosePnl', label: '浮动端平仓盈亏·浮动', width: 160, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount }, { name: 'DividendIn', label: '浮动端平仓盈亏·分红', width: 160, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount } ]; return col; @@ -664,26 +667,26 @@ function getColModelGridStep4() { label: '名义数量', width: 160, align: 'center', - formatter: otcformat.trading.notional + formatter: formatSwapQuantity }, { name: 'position.PosiNotionalValue', label: '名义本金', width: 160, align: 'center', - formatter: otcformat.trading.StockEqvNotional + formatter: formatSwapAmount } , { name: 'position.PosiTradingFee', label: '交易费用佣金', width: 160, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount }, { name: 'position.PosiTradingFeePending', label: '待实现交易费用佣金', width: 160, align: 'center', - formatter: otcformat.trading.umprice + formatter: formatSwapAmount }, { name: 'position.PosiStartDate', label: '起始日期', @@ -940,8 +943,8 @@ var vue = new Vue({ const thisObj = this; main.post(`/swapTrade2/SearchTodayWhetherFRData?dateTime=${this.FRData.date}`).done(function (resp) { if (resp.UnderlyingCode) { // 如果这天有fr007获取值和id,隐藏警告 - thisObj.FRData.value = (resp.ReferencePrice * 100).toFixed(4); - thisObj.FRData.oldValue = (resp.ReferencePrice * 100).toFixed(4); + thisObj.FRData.value = (resp.ReferencePrice * 100).toFixed(swapPricePrecision.getCommonPrecision('rate')); + thisObj.FRData.oldValue = (resp.ReferencePrice * 100).toFixed(swapPricePrecision.getCommonPrecision('rate')); thisObj.FRData.id = resp.id; thisObj.isShowWarning = false thisObj.isFromArtifical = resp.DataSource === "人工" diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js index 8e694d16..ba2fea6a 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js @@ -1,12 +1,14 @@ //otcformat禁止千分位分组 window.otcformat.options.disableGrouping = true; -const inputFormatSwapRate = Object.freeze({ precision: otcformat.trading.premiumRateP.precision, append: '%' }); +const inputFormatSwapRate = swapPricePrecision.getCommonInputFormat('rate', { append: '%' }); const inputFormatTradePrice = Object.freeze({ precision: otcformat.trading.tradePrice.precision, append: '' }); -const inputFormatTradeAmount = Object.freeze({ precision: otcformat.trading.notional.precision, append: '' }); -const inputFormatEqvNotional = Object.freeze({ precision: 2, append: '', negative: true, trimTailZeros: false }); -const inputFormatInterestAmount = Object.freeze({ precision: 2, append: '', negative: true, trimTailZeros: false }); -const inputFormatDividend = Object.freeze({ precision: 2, append: '', negative: true }); +const inputFormatTradeAmount = swapPricePrecision.getCommonInputFormat('quantity', { append: '' }); +const inputFormatEqvNotional = swapPricePrecision.getCommonInputFormat('amount', { append: '', negative: true }); +const inputFormatInterestAmount = swapPricePrecision.getCommonInputFormat('amount', { append: '', negative: true }); +const inputFormatDividend = swapPricePrecision.getCommonInputFormat('amount', { append: '', negative: true }); +const formatSwapAmount = value => swapPricePrecision.formatCommon('amount', value); +const formatSwapQuantity = value => swapPricePrecision.formatCommon('quantity', value); 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 }); @@ -120,31 +122,31 @@ const vue = new Vue({ return pricef; }, dataFormat() { - this.deal.NotionalValue = otcformat.fixed2(parseFloat(this.deal.NotionalValue)); - this.deal.PosiNotionalValue = otcformat.fixed2(parseFloat(this.deal.PosiNotionalValue)); - this.deal.NotionalQty = otcformat.trading.notional(this.deal.NotionalQty); - this.deal.PositionQty2 = otcformat.trading.notional(this.deal.PositionQty); - this.floatPosition.Quantity = otcformat.trading.notional(this.floatPosition.Quantity); - this.floatPosition.PositionQty = otcformat.trading.notional(this.floatPosition.PositionQty); - this.deal.SwapCloseAmount = otcformat.trading.StockEqvNotional(this.deal.SwapCloseAmount); + this.deal.NotionalValue = formatSwapAmount(this.deal.NotionalValue); + this.deal.PosiNotionalValue = formatSwapAmount(this.deal.PosiNotionalValue); + this.deal.NotionalQty = formatSwapQuantity(this.deal.NotionalQty); + this.deal.PositionQty2 = formatSwapQuantity(this.deal.PositionQty); + this.floatPosition.Quantity = formatSwapQuantity(this.floatPosition.Quantity); + this.floatPosition.PositionQty = formatSwapQuantity(this.floatPosition.PositionQty); + this.deal.SwapCloseAmount = formatSwapAmount(this.deal.SwapCloseAmount); //this.floatPosition.PosiNetPrice = otcformat.trading.umprice(this.floatPosition.PosiNetPrice); //this.floatPosition.PosiGrossPrice = otcformat.trading.umprice(this.floatPosition.PosiGrossPrice); - this.floatPosition.TradingFee = otcformat.trading.StockEqvNotional(this.floatPosition.TradingFee); - this.floatPosition.DividendIn = parseFloat(this.floatPosition.DividendIn).toFixed(2); - this.floatPosition.MarkClosePnl = otcformat.trading.StockEqvNotional(this.floatPosition.MarkClosePnl); + this.floatPosition.TradingFee = formatSwapAmount(this.floatPosition.TradingFee); + this.floatPosition.DividendIn = formatSwapAmount(this.floatPosition.DividendIn); + this.floatPosition.MarkClosePnl = formatSwapAmount(this.floatPosition.MarkClosePnl); this.interestList.forEach(x => { - //x.Principal = otcformat.trading.StockEqvNotional(x.Principal); + //x.Principal = formatSwapAmount(x.Principal); //x.Rate = otcformat.fixed6(x.Rate); - x.InterestFee = otcformat.trading.StockEqvNotional(x.InterestFee); - x.InterestAmount = otcformat.trading.StockEqvNotional(x.InterestAmount); - x.InterestClosePnL = otcformat.trading.StockEqvNotional(x.InterestClosePnL); + x.InterestFee = formatSwapAmount(x.InterestFee); + x.InterestAmount = formatSwapAmount(x.InterestAmount); + x.InterestClosePnL = formatSwapAmount(x.InterestClosePnL); //x.InterestStartDate = x.InterestStartDate ? x.InterestStartDate.substr(0, 10) : ""; //x.InterestEndDate = x.InterestEndDate ? x.InterestEndDate.substr(0, 10) : ""; }); this.marginList.forEach(x => { - x.InterestFee = otcformat.trading.StockEqvNotional(x.InterestFee); - x.InterestAmount = otcformat.trading.StockEqvNotional(x.InterestAmount); - x.InterestClosePnL = otcformat.trading.StockEqvNotional(x.InterestClosePnL); + x.InterestFee = formatSwapAmount(x.InterestFee); + x.InterestAmount = formatSwapAmount(x.InterestAmount); + x.InterestClosePnL = formatSwapAmount(x.InterestClosePnL); }); }, setValueDate(e) {//修改平仓日期 @@ -183,7 +185,7 @@ const vue = new Vue({ changeInterestAmount(item) {//修改利息金额 let InterestFee = item.InterestFee == "" ? 0 : parseFloat(item.InterestFee); let interestRatio = item.InterestDirection == 1 ? 1 : -1; - item.InterestClosePnL = otcformat.trading.StockEqvNotional(parseFloat(item.InterestAmount) * interestRatio + InterestFee * interestRatio); + item.InterestClosePnL = formatSwapAmount(parseFloat(item.InterestAmount) * interestRatio + InterestFee * interestRatio); this.calcCloseAmount(); }, calcFloatClosePnl() {//计算浮动端平仓盈亏 @@ -197,7 +199,7 @@ const vue = new Vue({ // CloseNotionalValue 是期初全价折算后的名义本金,直接乘价差会重复包含期初价格。 let positionAmount = parseFloat(thisObj.floatPosition.Quantity) * parseFloat(thisObj.floatPosition.ContractSize || 1); thisObj.floatPosition.MarkClosePnl = positionAmount * (deliveryPrice - thisObj.initPosiGrossPrice) * floatRatio; - thisObj.floatPosition.MarkClosePnl = otcformat.trading.StockEqvNotional(thisObj.floatPosition.MarkClosePnl);//MarkClosePnl 纯盯市不要计算交易费用和分红 + thisObj.floatPosition.MarkClosePnl = formatSwapAmount(thisObj.floatPosition.MarkClosePnl);//MarkClosePnl 纯盯市不要计算交易费用和分红 // 守卫: 浮动盈亏合计必须保留 2 位小数 → 对应历史 bug 3c5f25a5(原代码缺精度保留) // 数值由 swapCalc.calcFloatPnlSum 计算, 此处 .toFixed(2) 仅保留字符串类型以兼容下游 thisObj.floatPosition.FloatPnlSum = SwapCalc.calcFloatPnlSum(thisObj.floatPosition.MarkClosePnl, TradingFee, TradingFeePending, DividendIn).toFixed(2); @@ -205,7 +207,7 @@ const vue = new Vue({ }, //calcClosePnL() {//计算浮动端平仓盈亏 // let pnl = parseFloat(this.floatPosition.ClosePnL) - parseFloat(this.floatPosition.TradingFee); - // this.floatPosition.ClosePnL = otcformat.trading.StockEqvNotional(pnl); + // this.floatPosition.ClosePnL = formatSwapAmount(pnl); // this.calcCloseAmount(); //}, calcCloseAmount() {//计算平仓总额=浮动收取+利息收取-浮动支付-利息支付 @@ -237,9 +239,9 @@ const vue = new Vue({ thisObj.deal.SwapMarginRebatePnl = parseFloat(thisObj.deal.SwapMarginRebatePnl) + interestAmount; thisObj.deal.SwapRealizedPnL = parseFloat(thisObj.deal.SwapRealizedPnL) + interestAmount; }); - thisObj.deal.SwapCloseAmount = otcformat.trading.StockEqvNotional(thisObj.deal.SwapCloseAmount); - thisObj.deal.SwapRealizedPnL = otcformat.trading.StockEqvNotional(thisObj.deal.SwapRealizedPnL); - thisObj.deal.SwapMarginRebatePnl = otcformat.trading.StockEqvNotional(thisObj.deal.SwapMarginRebatePnl); + thisObj.deal.SwapCloseAmount = formatSwapAmount(thisObj.deal.SwapCloseAmount); + thisObj.deal.SwapRealizedPnL = formatSwapAmount(thisObj.deal.SwapRealizedPnL); + thisObj.deal.SwapMarginRebatePnl = formatSwapAmount(thisObj.deal.SwapMarginRebatePnl); }, getInterestList() {//根据平仓日期获取利息腿信息 var thisObj = this; diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapLongShort.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapLongShort.js index 4ca4ce3b..02a97d3f 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapLongShort.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapLongShort.js @@ -1,6 +1,8 @@ //otcformat禁止千分位分组 window.otcformat.options.disableGrouping = true; -const inputFormatEqvNotional = Object.freeze({ precision: 2, append: '', trimTailZeros: false }); +const inputFormatEqvNotional = swapPricePrecision.getCommonInputFormat('amount', { append: '' }); +const formatSwapAmount = value => swapPricePrecision.formatCommon('amount', value); +const formatSwapQuantity = value => swapPricePrecision.formatCommon('quantity', value); let ValueDate = model.ValueDate; const vue = new Vue({ el: '#vueDiv', @@ -42,29 +44,29 @@ const vue = new Vue({ } }, dataFormat() { - this.deal.NotionalValue = otcformat.fixed2(parseFloat(this.deal.NotionalValue)); - this.deal.PosiNotionalValue = otcformat.fixed2(parseFloat(this.deal.PosiNotionalValue)); - this.deal.NotionalQty = otcformat.trading.notional(this.deal.NotionalQty); - this.deal.PositionQty = otcformat.trading.notional(this.deal.PositionQty); - this.deal.SwapCloseAmount = otcformat.trading.StockEqvNotional(this.deal.SwapCloseAmount); + this.deal.NotionalValue = formatSwapAmount(this.deal.NotionalValue); + this.deal.PosiNotionalValue = formatSwapAmount(this.deal.PosiNotionalValue); + this.deal.NotionalQty = formatSwapQuantity(this.deal.NotionalQty); + this.deal.PositionQty = formatSwapQuantity(this.deal.PositionQty); + this.deal.SwapCloseAmount = formatSwapAmount(this.deal.SwapCloseAmount); this.interestList.forEach(x => { - //x.Principal = otcformat.trading.StockEqvNotional(x.Principal); + //x.Principal = formatSwapAmount(x.Principal); //x.Rate = otcformat.fixed6(x.Rate); - x.InterestFee = otcformat.trading.StockEqvNotional(x.InterestFee); - x.InterestAmount = otcformat.trading.StockEqvNotional(x.InterestAmount); - x.InterestClosePnL = otcformat.trading.StockEqvNotional(x.InterestClosePnL); + x.InterestFee = formatSwapAmount(x.InterestFee); + x.InterestAmount = formatSwapAmount(x.InterestAmount); + x.InterestClosePnL = formatSwapAmount(x.InterestClosePnL); //x.InterestStartDate = x.InterestStartDate ? x.InterestStartDate.substr(0, 10) : ""; //x.InterestEndDate = x.InterestEndDate ? x.InterestEndDate.substr(0, 10) : ""; }); this.marginList.forEach(x => { - x.InterestFee = otcformat.trading.StockEqvNotional(x.InterestFee); - x.InterestAmount = otcformat.trading.StockEqvNotional(x.InterestAmount); - x.InterestClosePnL = otcformat.trading.StockEqvNotional(x.InterestClosePnL); + x.InterestFee = formatSwapAmount(x.InterestFee); + x.InterestAmount = formatSwapAmount(x.InterestAmount); + x.InterestClosePnL = formatSwapAmount(x.InterestClosePnL); }); }, changeInterestAmount(item) {//修改利息金额 let interestRatio = item.InterestDirection == 1 ? 1 : -1; - item.InterestClosePnL = otcformat.trading.StockEqvNotional(parseFloat(item.InterestAmount) * interestRatio + parseFloat(item.InterestFee)); + item.InterestClosePnL = formatSwapAmount(parseFloat(item.InterestAmount) * interestRatio + parseFloat(item.InterestFee)); this.calcCloseAmount(); }, calcCloseAmount() {//计算平仓总额=浮动收取+利息收取-浮动支付-利息支付 @@ -86,10 +88,10 @@ const vue = new Vue({ thisObj.deal.SwapRealizedPnL = parseFloat(thisObj.deal.SwapRealizedPnL) + interestAmount; thisObj.deal.SwapMarginAmount = parseFloat(thisObj.deal.SwapMarginAmount) + parseFloat(x.InterestPrincipal); }); - thisObj.deal.SwapCloseAmount = otcformat.trading.StockEqvNotional(thisObj.deal.SwapCloseAmount); - thisObj.deal.SwapRealizedPnL = otcformat.trading.StockEqvNotional(thisObj.deal.SwapRealizedPnL); - thisObj.deal.SwapMarginRebatePnl = otcformat.trading.StockEqvNotional(thisObj.deal.SwapMarginRebatePnl); - thisObj.deal.SwapMarginAmount = otcformat.trading.StockEqvNotional(thisObj.deal.SwapMarginAmount); + thisObj.deal.SwapCloseAmount = formatSwapAmount(thisObj.deal.SwapCloseAmount); + thisObj.deal.SwapRealizedPnL = formatSwapAmount(thisObj.deal.SwapRealizedPnL); + thisObj.deal.SwapMarginRebatePnl = formatSwapAmount(thisObj.deal.SwapMarginRebatePnl); + thisObj.deal.SwapMarginAmount = formatSwapAmount(thisObj.deal.SwapMarginAmount); }, getInterestList() {//根据平仓日期获取利息腿信息 var thisObj = this; diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapPricePrecisionHelper.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapPricePrecisionHelper.js index fa37a9da..5dd5cf0d 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapPricePrecisionHelper.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapPricePrecisionHelper.js @@ -1,5 +1,10 @@ var swapPricePrecision = (function (global) { const defaults = Object.freeze({ + common: { + amount: { precision: 2 }, + quantity: { precision: 2 }, + rate: { precision: 4 } + }, Stock: { integerDigits: 7, precision: 2 }, StockIndex: { integerDigits: 7, precision: 2 }, StockIF: { integerDigits: 7, precision: 4 }, @@ -121,6 +126,13 @@ var swapPricePrecision = (function (global) { return { integerDigits: integerDigits, precision: precision }; } + function normalizeCommonRule(rule) { + if (!rule || typeof rule !== 'object') return null; + const precision = Number(rule.precision); + if (!Number.isInteger(precision) || precision < 0 || precision > 13) return null; + return { precision: precision }; + } + function findRule(source, instrumentType, field) { const typeRule = source && source[instrumentType]; return typeRule ? normalizeRule(typeRule[field] || typeRule) : null; @@ -132,6 +144,31 @@ var swapPricePrecision = (function (global) { return findRule(global.main && global.main.swapPricePrecision, instrumentType, field) || fallback; } + function getCommonRule(kind) { + const fallback = normalizeCommonRule(defaults.common[kind]); + const configured = global.main && global.main.swapPricePrecision + && global.main.swapPricePrecision.common; + return normalizeCommonRule(configured && configured[kind]) || fallback || { precision: 2 }; + } + + function formatFixed(value, precision) { + const normalized = normalizeDecimal(roundDecimal(value, precision)); + if (!normalized) return ''; + const negative = normalized.charAt(0) === '-'; + const parts = (negative ? normalized.substring(1) : normalized).split('.'); + const integerPart = parts[0]; + if (precision === 0) return (negative ? '-' : '') + integerPart; + return (negative ? '-' : '') + integerPart + '.' + (parts[1] || '').padEnd(precision, '0'); + } + + function formatCommon(kind, value) { + if (value === null || value === undefined || value === '') return ''; + const precision = getCommonRule(kind).precision; + const displayValue = kind === 'rate' ? shiftDecimal(value, 2) : value; + const formatted = formatFixed(displayValue, precision); + return kind === 'rate' && formatted ? formatted + '%' : formatted; + } + function format(value, instrumentType, field) { const rule = getRule(instrumentType, field); if (value === null || value === undefined || value === '') return ''; @@ -260,6 +297,16 @@ var swapPricePrecision = (function (global) { return Object.freeze({ getRule: getRule, + getCommonPrecision: function (kind) { + return getCommonRule(kind).precision; + }, + getCommonInputFormat: function (kind, options) { + return Object.assign({}, options, { + precision: getCommonRule(kind).precision, + trimTailZeros: false + }); + }, + formatCommon: formatCommon, getInputFormat: function (instrumentType, field, options) { const rule = getRule(instrumentType, field); return rule ? Object.assign({}, options, rule) : Object.assign({}, options); diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js index 2ed290b4..64152a49 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js @@ -10,13 +10,13 @@ const consAssetUnits = page.canAddNewTrader ? ylotc.assetunits : ylotc.assetunits.filter(x => x.TraderIds.includes(page.Trade.TraderId)); const inputFormatInteger = Object.freeze({ precision: 0, append: '' }); -const inputFormatEqvNotional = Object.freeze({ precision: 2, append: '', trimTailZeros: false }); -const inputFormatTradeAmount = Object.freeze({ precision: otcformat.trading.notional.precision, append: '' }); -const inputFormatPositionQuantityFixed2 = Object.freeze({ precision: 2, append: '', trimTailZeros: false }); -const inputFormatSwapRate = Object.freeze({ precision: 4, negative: true, append: '%', trimTailZeros: false }); +const inputFormatEqvNotional = swapPricePrecision.getCommonInputFormat('amount', { append: '' }); +const inputFormatTradeAmount = swapPricePrecision.getCommonInputFormat('quantity', { append: '' }); +const inputFormatPositionQuantityFixed2 = swapPricePrecision.getCommonInputFormat('quantity', { append: '' }); +const inputFormatSwapRate = swapPricePrecision.getCommonInputFormat('rate', { 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 inputFormatTradeSinglePriceFixed2 = Object.freeze({ precision: 2, negative: true, append: '', percent: false, trimTailZeros: false }); +const inputFormatTradeSinglePrice = swapPricePrecision.getCommonInputFormat('amount', { negative: true, append: '', percent: false }); +const inputFormatTradeSinglePriceFixed2 = swapPricePrecision.getCommonInputFormat('amount', { negative: true, append: '', percent: false }); const inputFormatPosiFeePercent = Object.freeze({ precision: 4, negative: true, append: '%' }); const inputFormatPosiFeeUnit = Object.freeze({ precision: 6, negative: true, append: '' }); const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' }); @@ -37,7 +37,7 @@ const observationRateTextFromPercent = function (value) { const formatObservationRate = function (value) { if (value === null || value === undefined || value === '') return ''; const number = Number(value); - return Number.isFinite(number) ? number.toFixed(4) : value; + return Number.isFinite(number) ? number.toFixed(swapPricePrecision.getCommonPrecision('rate')) : value; }; const swapPosiFeeCalc = Object.freeze({ normalizeFeeType(feeType) { @@ -563,7 +563,7 @@ const vue = new Vue({ }, //变更名义本金(仅格式化,不反算数量) changeStockEqvNotional() { - this.trade.StockEqvNotional = otcformat.trading.StockEqvNotional(this.trade.StockEqvNotional); + this.trade.StockEqvNotional = swapPricePrecision.formatCommon('amount', this.trade.StockEqvNotional); this.refreshPayTradingFeesByUnit(); //计算数量 // if (this.paySwapList.length > 0) { @@ -651,7 +651,7 @@ const vue = new Vue({ // 守卫: 名义本金必须 round 到 2 位 → 对应历史 bug f873239a(缺 _.round); 外置到 swapCalc.calcStockEqvNotional var deliveryPrice = this.roundStoragePrice(payItem, payItem.PosiGrossPrice, 'grossPrice'); var stockEqvNotional = SwapCalc.calcStockEqvNotional(deliveryPrice, national);//名义本金=期初价格*数量*乘数 - this.trade.StockEqvNotional = otcformat.trading.stockEqvNotional(stockEqvNotional); + this.trade.StockEqvNotional = swapPricePrecision.formatCommon('amount', stockEqvNotional); payItem.PosiNotionalValue = this.trade.StockEqvNotional; this.refreshPayTradingFeesByUnit(); } diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeView.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeView.js index 003ab189..565ab713 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeView.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeView.js @@ -9,6 +9,12 @@ function formatSwapPriceElements() { }); } +function formatSwapCommonElements() { + $('.js-swap-common').each(function () { + this.textContent = swapPricePrecision.formatCommon(this.dataset.kind, this.dataset.value); + }); +} + function deletetrade(id) { //无效化 main.confirm(page.ConfirmInfo, function () { $.ajax({ @@ -502,7 +508,7 @@ var vueDetails = new Vue({ if (swapIntervals && swapIntervals.length>0) { that.SwapIntervalList = JSON.parse(swapIntervals); that.SwapIntervalList.forEach((item, index) => { - that.SwapIntervalList[index].Rate = otcformat.fixed4P(item.Rate); + that.SwapIntervalList[index].Rate = swapPricePrecision.formatCommon('rate', item.Rate); // 如果结算日期为空,默认等于观察日期 if (!item.SettlementDate) { that.SwapIntervalList[index].SettlementDate = item.Date; @@ -553,6 +559,7 @@ function chk_onclick(obj) { $(function () { formatSwapPriceElements(); + formatSwapCommonElements(); refreshEntryExit(); }); diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindLongShort.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindLongShort.js index 509177bb..48fd18f0 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindLongShort.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindLongShort.js @@ -1,6 +1,8 @@ //otcformat禁止千分位分组 window.otcformat.options.disableGrouping = true; -const inputFormatEqvNotional = Object.freeze({ precision: 2, append: '', trimTailZeros: false }); +const inputFormatEqvNotional = swapPricePrecision.getCommonInputFormat('amount', { append: '' }); +const formatSwapAmount = value => swapPricePrecision.formatCommon('amount', value); +const formatSwapQuantity = value => swapPricePrecision.formatCommon('quantity', value); let dealDate = model.DealDate; const vue = new Vue({ el: '#vueDiv', @@ -30,27 +32,27 @@ const vue = new Vue({ }); }, dataFormat() { - this.deal.NotionalValue = otcformat.fixed2(parseFloat(this.deal.NotionalValue)); - this.deal.PosiNotionalValue = otcformat.fixed2(parseFloat(this.deal.PosiNotionalValue)); - this.deal.NotionalQty = otcformat.trading.notional(this.deal.NotionalQty); - this.deal.SwapCloseAmount = otcformat.trading.StockEqvNotional(this.deal.SwapCloseAmount); - this.deal.PositionQty = otcformat.trading.notional(this.deal.PositionQty); + this.deal.NotionalValue = formatSwapAmount(this.deal.NotionalValue); + this.deal.PosiNotionalValue = formatSwapAmount(this.deal.PosiNotionalValue); + this.deal.NotionalQty = formatSwapQuantity(this.deal.NotionalQty); + this.deal.SwapCloseAmount = formatSwapAmount(this.deal.SwapCloseAmount); + this.deal.PositionQty = formatSwapQuantity(this.deal.PositionQty); this.interestList.forEach(x => { - //x.Principal = otcformat.trading.StockEqvNotional(x.Principal); + //x.Principal = formatSwapAmount(x.Principal); //x.Rate = otcformat.fixed6(x.Rate); - x.InterestAmount = otcformat.trading.StockEqvNotional(x.InterestAmount); - x.InterestClosePnL = otcformat.trading.StockEqvNotional(x.InterestClosePnL); + x.InterestAmount = formatSwapAmount(x.InterestAmount); + x.InterestClosePnL = formatSwapAmount(x.InterestClosePnL); //x.InterestStartDate = x.InterestStartDate ? x.InterestStartDate.substr(0, 10) : ""; //x.InterestEndDate = x.InterestEndDate ? x.InterestEndDate.substr(0, 10) : ""; }); this.marginList.forEach(x => { - x.InterestAmount = otcformat.trading.StockEqvNotional(x.InterestAmount); - x.InterestClosePnL = otcformat.trading.StockEqvNotional(x.InterestClosePnL); + x.InterestAmount = formatSwapAmount(x.InterestAmount); + x.InterestClosePnL = formatSwapAmount(x.InterestClosePnL); }); }, changeInterestAmount(item) {//修改利息金额 let interestRatio = item.InterestDirection == 1 ? 1 : -1; - item.InterestClosePnL = otcformat.trading.StockEqvNotional(parseFloat(item.InterestAmount) * interestRatio+ parseFloat(item.InterestFee)); + item.InterestClosePnL = formatSwapAmount(parseFloat(item.InterestAmount) * interestRatio+ parseFloat(item.InterestFee)); this.calcCloseAmount(); }, calcCloseAmount() {//计算平仓总额=浮动收取+利息收取-浮动支付-利息支付 @@ -73,10 +75,10 @@ const vue = new Vue({ thisObj.deal.SwapRealizedPnL = parseFloat(thisObj.deal.SwapRealizedPnL) + interestAmount; thisObj.deal.SwapMarginAmount = parseFloat(thisObj.deal.SwapMarginAmount) + parseFloat(x.InterestPrincipal) * interestRatio; }); - thisObj.deal.SwapCloseAmount = otcformat.trading.StockEqvNotional(thisObj.deal.SwapCloseAmount); - thisObj.deal.SwapRealizedPnL = otcformat.trading.StockEqvNotional(thisObj.deal.SwapRealizedPnL); - thisObj.deal.SwapMarginRebatePnl = otcformat.trading.StockEqvNotional(thisObj.deal.SwapMarginRebatePnl); - thisObj.deal.SwapMarginAmount = otcformat.trading.StockEqvNotional(thisObj.deal.SwapMarginAmount); + thisObj.deal.SwapCloseAmount = formatSwapAmount(thisObj.deal.SwapCloseAmount); + thisObj.deal.SwapRealizedPnL = formatSwapAmount(thisObj.deal.SwapRealizedPnL); + thisObj.deal.SwapMarginRebatePnl = formatSwapAmount(thisObj.deal.SwapMarginRebatePnl); + thisObj.deal.SwapMarginAmount = formatSwapAmount(thisObj.deal.SwapMarginAmount); }, closeTrade() {//平仓 var thisObj = this; diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js index 4e30c411..51441df1 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js @@ -3,9 +3,11 @@ window.otcformat.options.disableGrouping = true; const inputFormatSwapRate = Object.freeze({ precision: 4, append: '%', trimTailZeros: false }); const inputFormatTradePrice = Object.freeze({ precision: otcformat.trading.tradePrice.precision, append: '' }); -const inputFormatTradeAmount = Object.freeze({ precision: otcformat.trading.notional.precision, append: '' }); -const inputFormatEqvNotional = Object.freeze({ precision: 2, append: '', negative: true, trimTailZeros: false }); -const inputFormatCloseAmount = Object.freeze({ precision: 2, append: '', negative: true, trimTailZeros: false }); +const inputFormatTradeAmount = swapPricePrecision.getCommonInputFormat('quantity', { append: '' }); +const inputFormatEqvNotional = swapPricePrecision.getCommonInputFormat('amount', { append: '', negative: true }); +const inputFormatCloseAmount = swapPricePrecision.getCommonInputFormat('amount', { append: '', negative: true }); +const formatSwapAmount = value => swapPricePrecision.formatCommon('amount', value); +const formatSwapQuantity = value => swapPricePrecision.formatCommon('quantity', value); 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 }); @@ -22,7 +24,7 @@ const swapPosiFeeCalc = { const tradingFee = normalizedFeeType === consPosiFeeType.Unit ? normalizedFeeUnit * normalizedCloseQty : normalizedFeeUnit / 100 * normalizedCloseNotionalValue; - return otcformat.trading.StockEqvNotional(_.round(tradingFee, 2)); + return formatSwapAmount(_.round(tradingFee, 2)); }, calcAllocatedTradingFee(totalFee, feeType, feeUnit, closeNotionalValue, closeQty, notionalValue, notionalQty) { const normalizedFeeUnit = Number(feeUnit) || 0; @@ -37,7 +39,7 @@ const swapPosiFeeCalc = { return null; } - return otcformat.trading.StockEqvNotional(_.round((Number(totalFee) || 0) * closeBase / originalBase, 2)); + return formatSwapAmount(_.round((Number(totalFee) || 0) * closeBase / originalBase, 2)); }, calcTradingFeePending(beforeCloseFee, feeType, feeUnit, closeNotionalValue, closeQty, notionalValue, notionalQty, closePercent) { const allocatedFee = this.calcAllocatedTradingFee( @@ -124,37 +126,37 @@ const vue = new Vue({ return pricef; }, dataFormat() { - this.deal.NotionalValue = otcformat.fixed2(parseFloat(this.deal.NotionalValue)); - this.deal.PosiNotionalValue = otcformat.fixed2(parseFloat(this.deal.PosiNotionalValue)); - this.deal.CloseNotionalValue = otcformat.fixed2(parseFloat(this.deal.CloseNotionalValue)); - this.deal.NotionalQty = otcformat.trading.notional(this.deal.NotionalQty); - this.deal.PositionQty2 = otcformat.trading.notional(this.deal.PositionQty); - this.floatPosition.Quantity = otcformat.trading.notional(this.floatPosition.Quantity); - this.floatPosition.PositionQty = otcformat.trading.notional(this.floatPosition.PositionQty); - this.deal.CloseQty = otcformat.trading.notional(this.deal.CloseQty); + this.deal.NotionalValue = formatSwapAmount(this.deal.NotionalValue); + this.deal.PosiNotionalValue = formatSwapAmount(this.deal.PosiNotionalValue); + this.deal.CloseNotionalValue = formatSwapAmount(this.deal.CloseNotionalValue); + this.deal.NotionalQty = formatSwapQuantity(this.deal.NotionalQty); + this.deal.PositionQty2 = formatSwapQuantity(this.deal.PositionQty); + this.floatPosition.Quantity = formatSwapQuantity(this.floatPosition.Quantity); + this.floatPosition.PositionQty = formatSwapQuantity(this.floatPosition.PositionQty); + this.deal.CloseQty = formatSwapQuantity(this.deal.CloseQty); this.deal.ClosePercent = otcformat.fixed6(this.deal.ClosePercent); - this.deal.SwapCloseAmount = otcformat.trading.StockEqvNotional(this.deal.SwapCloseAmount); + this.deal.SwapCloseAmount = formatSwapAmount(this.deal.SwapCloseAmount); //this.floatPosition.PosiNetPrice = otcformat.trading.tradeSinglePrice(this.floatPosition.PosiNetPrice); //this.floatPosition.PosiGrossPrice = otcformat.trading.tradeSinglePrice(this.floatPosition.PosiGrossPrice); this.floatPosition.TradingAmountAvg = swapPricePrecision.roundForSubmit( this.floatPosition.TradingAmountAvg, this.floatPosition.UnderlyingInstrumentType, 'grossPrice'); - 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); - this.floatPosition.MarkClosePnl = otcformat.trading.StockEqvNotional(this.floatPosition.MarkClosePnl); + this.floatPosition.TradingFee = formatSwapAmount(this.floatPosition.TradingFee); + this.floatPosition.TradingFeePending = formatSwapAmount(this.floatPosition.TradingFeePending); + this.floatPosition.DividendIn = formatSwapAmount(this.floatPosition.DividendIn); + this.floatPosition.MarkClosePnl = formatSwapAmount(this.floatPosition.MarkClosePnl); this.interestList.forEach(x => { - //x.Principal = otcformat.trading.StockEqvNotional(x.Principal); + //x.Principal = formatSwapAmount(x.Principal); //x.Rate = otcformat.fixed6(x.Rate); - x.InterestAmount = otcformat.trading.StockEqvNotional(x.InterestAmount); - x.InterestClosePnL = otcformat.trading.StockEqvNotional(x.InterestClosePnL); + x.InterestAmount = formatSwapAmount(x.InterestAmount); + x.InterestClosePnL = formatSwapAmount(x.InterestClosePnL); //x.InterestStartDate = x.InterestStartDate ? x.InterestStartDate.substr(0, 10) : ""; //x.InterestEndDate = x.InterestEndDate ? x.InterestEndDate.substr(0, 10) : ""; }); this.marginList.forEach(x => { - x.InterestAmount = otcformat.trading.StockEqvNotional(x.InterestAmount); - x.InterestClosePnL = otcformat.trading.StockEqvNotional(x.InterestClosePnL); + x.InterestAmount = formatSwapAmount(x.InterestAmount); + x.InterestClosePnL = formatSwapAmount(x.InterestClosePnL); }); }, setValueDate(e) {//修改平仓日期 @@ -183,7 +185,7 @@ const vue = new Vue({ changeCloseMethod() {//修改平仓类型 if (this.deal.CloseMethod == 1) { this.deal.ClosePercent = this.oriClosePercent; - this.deal.CloseNotionalValue = otcformat.trading.StockEqvNotional(parseFloat(this.deal.PosiNotionalValue)); + this.deal.CloseNotionalValue = formatSwapAmount(parseFloat(this.deal.PosiNotionalValue)); this.deal.CloseQty = this.deal.PositionQty; } else { // ClosePercent 是占期初口径(A),需除以 oriClosePercent 转占剩余(B) 再乘剩余数量 @@ -233,7 +235,7 @@ const vue = new Vue({ this.deal.CloseMethod = 2; } // 占期初口径:平仓名义本金 = 平仓比例 × 期初名义本金(NotionalValue) - this.deal.CloseNotionalValue = otcformat.trading.StockEqvNotional(parseFloat(this.deal.ClosePercent) * parseFloat(this.deal.NotionalValue)); + this.deal.CloseNotionalValue = formatSwapAmount(parseFloat(this.deal.ClosePercent) * parseFloat(this.deal.NotionalValue)); this.calcTradingFeePending(); this.refreshTradingFeeByUnit(); this.getInterestList(); @@ -247,7 +249,7 @@ const vue = new Vue({ } this.deal.CloseQty = this.calcCloseQtyByPercent(this.deal.ClosePercent); // 占期初口径:平仓名义本金 = 平仓比例 × 期初名义本金(NotionalValue) - this.deal.CloseNotionalValue = otcformat.trading.StockEqvNotional(parseFloat(this.deal.ClosePercent) * parseFloat(this.deal.NotionalValue)); + this.deal.CloseNotionalValue = formatSwapAmount(parseFloat(this.deal.ClosePercent) * parseFloat(this.deal.NotionalValue)); if (parseFloat(this.deal.ClosePercent) == parseFloat(this.oriClosePercent)) { this.deal.CloseMethod = 1; } else { @@ -301,7 +303,7 @@ const vue = new Vue({ 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.MarkClosePnl = formatSwapAmount(thisObj.floatPosition.MarkClosePnl); thisObj.floatPosition.FloatPnlSum = (parseFloat(thisObj.floatPosition.MarkClosePnl) + TradingFee + TradingFeePending + parseFloat(thisObj.floatPosition.DividendIn)).toFixed(2); thisObj.calcCloseAmount(); @@ -311,12 +313,12 @@ const vue = new Vue({ }, changeInterestAmount(item) {//修改利息金额 let interestRatio = item.InterestDirection == 1 ? 1 : -1; - item.InterestClosePnL = otcformat.trading.StockEqvNotional(parseFloat(item.InterestAmount) * interestRatio + parseFloat(item.InterestFee) * interestRatio); + item.InterestClosePnL = formatSwapAmount(parseFloat(item.InterestAmount) * interestRatio + parseFloat(item.InterestFee) * interestRatio); this.calcCloseAmount(); }, //calcClosePnL() {//计算浮动端平仓盈亏 // let pnl = parseFloat(this.floatPosition.ClosePnL) - parseFloat(this.floatPosition.TradingFee); - // this.floatPosition.ClosePnL = otcformat.trading.StockEqvNotional(pnl); + // this.floatPosition.ClosePnL = formatSwapAmount(pnl); // this.calcCloseAmount(); //}, calcCloseAmount() {//计算平仓总额=浮动收取+利息收取-浮动支付-利息支付 @@ -353,10 +355,10 @@ const vue = new Vue({ }); thisObj.deal.SwapRealizedPnL = Number(thisObj.deal.SwapRealizedPnL.toFixed(2)); thisObj.deal.SwapCloseAmount = Number(thisObj.deal.SwapCloseAmount.toFixed(2)); - thisObj.deal.SwapCloseAmount = otcformat.trading.StockEqvNotional(thisObj.deal.SwapCloseAmount); - thisObj.deal.SwapRealizedPnL = otcformat.trading.StockEqvNotional(thisObj.deal.SwapRealizedPnL); - thisObj.deal.SwapMarginRebatePnl = otcformat.trading.StockEqvNotional(thisObj.deal.SwapMarginRebatePnl); - thisObj.deal.SwapMarginAmount = otcformat.trading.StockEqvNotional(thisObj.deal.SwapMarginAmount); + thisObj.deal.SwapCloseAmount = formatSwapAmount(thisObj.deal.SwapCloseAmount); + thisObj.deal.SwapRealizedPnL = formatSwapAmount(thisObj.deal.SwapRealizedPnL); + thisObj.deal.SwapMarginRebatePnl = formatSwapAmount(thisObj.deal.SwapMarginRebatePnl); + thisObj.deal.SwapMarginAmount = formatSwapAmount(thisObj.deal.SwapMarginAmount); }, getInterestList() {//根据平仓日期获取利息腿信息 var thisObj = this; From 9d436162720bf0ca51646b13a412f8512caaec82 Mon Sep 17 00:00:00 2001 From: yexuzhong <120511780@qq.com> Date: Thu, 6 Aug 2026 10:18:03 +0800 Subject: [PATCH 03/17] =?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=A4=A7=E5=B0=8F?= =?UTF-8?q?=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- YLErpDAL/Modules/EodModule/GLMSGreeksHandleService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YLErpDAL/Modules/EodModule/GLMSGreeksHandleService.cs b/YLErpDAL/Modules/EodModule/GLMSGreeksHandleService.cs index 8193aaf8..5ccfdf94 100644 --- a/YLErpDAL/Modules/EodModule/GLMSGreeksHandleService.cs +++ b/YLErpDAL/Modules/EodModule/GLMSGreeksHandleService.cs @@ -188,7 +188,7 @@ namespace YLErp.Modules.EodModule private Dictionary GetTFeatureBondInnerCode(List underlyingCodes,DateTime valueDate,DbConnection conn) { - var sql = "SELECT contractcode,deliverableinnercode,spread FROM fut_cgbderiv WHERE TradingDay = @ValueDate AND pricetype = 3 AND (contractcode,irr) IN (SELECT contractcode,MAX(irr) FROM fut_cgbderiv WHERE TradingDay = @ValueDate AND contractcode IN (@UmCodes) AND pricetype = 3 GROUP BY contractcode);"; + var sql = "SELECT contractcode,deliverableinnercode,spread FROM fut_cgbderiv WHERE tradingday = @ValueDate AND pricetype = 3 AND (contractcode,irr) IN (SELECT contractcode,MAX(irr) FROM fut_cgbderiv WHERE tradingday = @ValueDate AND contractcode IN (@UmCodes) AND pricetype = 3 GROUP BY contractcode);"; var datas = conn.Query(sql, new { ValueDate = valueDate, From c1c2ed9dda6dd21c9e1cef485ae10405f3be66fb Mon Sep 17 00:00:00 2001 From: hjhan Date: Thu, 6 Aug 2026 10:58:44 +0800 Subject: [PATCH 04/17] =?UTF-8?q?EQD-6979=20TRS=E5=90=88=E7=BA=A6=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=AE=9E=E6=97=B6=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- YLErpDAL/Model/KafkaConfig.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/YLErpDAL/Model/KafkaConfig.cs b/YLErpDAL/Model/KafkaConfig.cs index 5d144e1e..341ba482 100644 --- a/YLErpDAL/Model/KafkaConfig.cs +++ b/YLErpDAL/Model/KafkaConfig.cs @@ -57,6 +57,10 @@ namespace YLErp.Model public string AccountCapitalTopicGroupId { get; set; } public int AutoOffsetReset { get; set; } + /// + /// TRS合约数据推送topic(对外,如onebp等) + /// + public string ContractTopic { get; set; } = "onederi.trs.onebp.contract.v1"; } } From b0b04dbdeaf364c16fa4abfc1eeccaf8e4e5c168 Mon Sep 17 00:00:00 2001 From: tengyufan <1532636164@qq.com> Date: Thu, 6 Aug 2026 11:29:54 +0800 Subject: [PATCH 05/17] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BA=A4?= =?UTF-8?q?=E6=98=93=E5=AE=A1=E6=89=B9=E6=9C=9F=E5=88=9D=E6=A0=87=E7=9A=84?= =?UTF-8?q?=E4=BB=B7=E6=A0=BC=E5=8F=96=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- YLErpDAL/Modules/TradeModule/TradeBLL.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/YLErpDAL/Modules/TradeModule/TradeBLL.cs b/YLErpDAL/Modules/TradeModule/TradeBLL.cs index 2efe1960..62e42170 100644 --- a/YLErpDAL/Modules/TradeModule/TradeBLL.cs +++ b/YLErpDAL/Modules/TradeModule/TradeBLL.cs @@ -276,10 +276,10 @@ namespace YLErp.BLL } var swapPositions = db.Set() .Where(sp => swapTradeIds.Contains(sp.SwapTradeId) && sp.IsInitial && sp.UnderlyingCode != null) - .Select(sp => new { sp.SwapTradeId, sp.PosiNetPrice, sp.UnderlyingCode }) + .Select(sp => new { sp.SwapTradeId, sp.PosiGrossPrice, sp.UnderlyingCode }) .ToList(); var logger = LogFactory.GetLogger(); - var posDict = swapPositions.GroupBy(sp => sp.SwapTradeId).ToDictionary(g => g.Key, g => g.First().PosiNetPrice); + var posDict = swapPositions.GroupBy(sp => sp.SwapTradeId).ToDictionary(g => g.Key, g => g.First().PosiGrossPrice); var umProvider = DataCacheProvider.GetUnderlyingDataSource(); // 需求②:平仓/行权/互换交易,审批角色应取 CloseProcess 流程的节点角色,而非 TradeProcess var closeProcessRoles = db.approvalprocess @@ -312,14 +312,14 @@ namespace YLErp.BLL tradeLinq.TradeSinglePrice = option.OpenCommission; } - // --- 新增逻辑:针对收益互换类型,用 swap_position.PosiNetPrice 覆盖展示用的期初标的价格 --- + // --- 针对收益互换类型,用 swap_position.PosiGrossPrice 覆盖展示用的期初标的价格 --- try { - if (tradeLinq.TradeType == "收益互换" && posDict.TryGetValue(tradeLinq.id, out var netPrice) && netPrice > 0) + if (tradeLinq.TradeType == "收益互换" && posDict.TryGetValue(tradeLinq.id, out var grossPrice) && grossPrice > 0) { - // 将期初价格覆盖为互换持仓的 PosiNetPrice(仅使用 PosiDirection != 0 的期初持仓) - logger.Info($"tradeOpeningProcessQuery.DAL override: tradeId={tradeLinq.id} beforeInitialSpot={tradeLinq.InitialSpotPrice} dbPosi={netPrice}"); - tradeLinq.InitialSpotPrice = Convert.ToDouble(netPrice); + // 将期初价格覆盖为互换持仓的 PosiGrossPrice(仅使用 PosiDirection != 0 的期初持仓) + logger.Info($"tradeOpeningProcessQuery.DAL override: tradeId={tradeLinq.id} beforeInitialSpot={tradeLinq.InitialSpotPrice} dbPosi={grossPrice}"); + tradeLinq.InitialSpotPrice = Convert.ToDouble(grossPrice); } } catch (Exception ex) From 5b7f17727d9a37271f02cb62166775663f83cc48 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, 6 Aug 2026 12:41:30 +0800 Subject: [PATCH 06/17] =?UTF-8?q?refactor(swaptrade):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BA=92=E6=8D=A2=E4=BA=A4=E6=98=93=E4=B8=AD=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E9=87=8F=E5=92=8C=E9=87=91=E9=A2=9D=E6=A0=BC=E5=BC=8F=E5=8C=96?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入UnderlyingInstrumentType字段用于精确控制格式化规则 - 添加quantityPrecision配置支持不同产品类型的数量精度设置 - 实现formatAmount和formatQuantity方法提供统一格式化接口 - 更新视图模板使用新的格式化方法替代直接数据绑定 - 重构swapPricePrecisionHelper.js支持按产品类型定制格式化规则 - 移除废弃的inputFormatTradeAmount等旧格式化配置 - 添加千分位分组显示功能提升数字可读性 --- .../App_Data/Config/swappriceprecision.js | 54 ++++---- YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml | 18 +-- .../Views/SwapTrade2/SwapLongShortSwap.cshtml | 10 +- .../SwapTrade2/SwapLongShortUnwind.cshtml | 14 +- YLErpWeb/Views/SwapTrade2/SwapUnwind.cshtml | 22 ++-- YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml | 2 +- YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml | 2 +- YLErpWeb/Views/SwapTrade2/TradeView.cshtml | 10 +- .../Scripts/app/swaptrade/SwapflowList.js | 20 ++- .../Scripts/app/swaptrade/incomeSwapTrade.js | 12 +- .../Scripts/app/swaptrade/swapLongShort.js | 11 +- .../app/swaptrade/swapPricePrecisionHelper.js | 123 ++++++++++++------ .../Scripts/app/swaptrade/swapTradeEdit.js | 12 +- .../Scripts/app/swaptrade/swapTradeView.js | 5 +- .../Scripts/app/swaptrade/unwindLongShort.js | 11 +- .../Scripts/app/swaptrade/unwindSwapTrade.js | 15 ++- 16 files changed, 218 insertions(+), 123 deletions(-) diff --git a/YLErpWeb/App_Data/Config/swappriceprecision.js b/YLErpWeb/App_Data/Config/swappriceprecision.js index 2cb43923..a0d631eb 100644 --- a/YLErpWeb/App_Data/Config/swappriceprecision.js +++ b/YLErpWeb/App_Data/Config/swappriceprecision.js @@ -2,52 +2,54 @@ window.main = window.main || {}; window.main.swapPricePrecision = { common: { - amount: { precision: 2 }, - quantity: { precision: 2 }, + amount: { precision: 2, grouping: true }, + quantity: { precision: 2, grouping: true }, rate: { precision: 4 } }, - Stock: { integerDigits: 7, precision: 2 }, - StockIndex: { integerDigits: 7, precision: 2 }, - StockIF: { integerDigits: 7, precision: 4 }, - CommodityFutures: { integerDigits: 7, precision: 4 }, - CommoditySpot: { integerDigits: 7, precision: 4 }, - NewOtcStock: { integerDigits: 7, precision: 4 }, - HKStock: { integerDigits: 7, precision: 4 }, - HKStockIndex: { integerDigits: 7, precision: 4 }, - Fund: { integerDigits: 7, precision: 4 }, + Stock: { integerDigits: 7, precision: 2, quantityPrecision: 2 }, + StockIndex: { integerDigits: 7, precision: 2, quantityPrecision: 2 }, + StockIF: { integerDigits: 7, precision: 4, quantityPrecision: 2 }, + CommodityFutures: { integerDigits: 7, precision: 4, quantityPrecision: 2 }, + CommoditySpot: { integerDigits: 7, precision: 4, quantityPrecision: 2 }, + NewOtcStock: { integerDigits: 7, precision: 4, quantityPrecision: 2 }, + HKStock: { integerDigits: 7, precision: 4, quantityPrecision: 2 }, + HKStockIndex: { integerDigits: 7, precision: 4, quantityPrecision: 2 }, + Fund: { integerDigits: 7, precision: 4, quantityPrecision: 4 }, Bond: { + quantityPrecision: 0, grossPrice: { integerDigits: 6, precision: 9 }, netPrice: { integerDigits: 6, precision: 9 }, yield: { integerDigits: 2, precision: 4 } }, TBonds: { + quantityPrecision: 0, grossPrice: { integerDigits: 6, precision: 9 }, netPrice: { integerDigits: 6, precision: 9 }, yield: { integerDigits: 2, precision: 4 } }, CreditBonds: { + quantityPrecision: 0, grossPrice: { integerDigits: 6, precision: 9 }, netPrice: { integerDigits: 6, precision: 9 }, yield: { integerDigits: 2, precision: 4 } }, OtherBonds: { + quantityPrecision: 0, grossPrice: { integerDigits: 6, precision: 9 }, netPrice: { integerDigits: 6, precision: 9 }, yield: { integerDigits: 2, precision: 4 } }, - TBFutures: { integerDigits: 8, precision: 4 }, - OtherFutures: { integerDigits: 8, precision: 4 }, - GoldSpot: { integerDigits: 8, precision: 4 }, - OtherSpot: { integerDigits: 8, precision: 4 }, - AbroadFutures: { integerDigits: 8, precision: 4 }, - AbroadSpot: { integerDigits: 8, precision: 4 }, - AbroadStock: { integerDigits: 8, precision: 2 }, - AbroadStockIndex: { integerDigits: 8, precision: 4 }, - ExRate: { integerDigits: 2, precision: 8 }, - Shibor: { integerDigits: 2, precision: 4 }, - FixingRepoRate: { integerDigits: 2, precision: 4 }, - RateYield: {integerDigits: 6, precision: 8}, - BondIndex: {integerDigits: 6, precision: 4}, - - // TODO: 利率收益率(6+8)、债券指数(6+4)、黄金期货(6+4)待对应的 UnderlyingInstrumentType 枚举确认后启用。 + TBFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2 }, + OtherFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2 }, + GoldSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2 }, + OtherSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2 }, + AbroadFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2 }, + AbroadSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2 }, + AbroadStock: { integerDigits: 8, precision: 2, quantityPrecision: 2 }, + AbroadStockIndex: { integerDigits: 8, precision: 4, quantityPrecision: 2 }, + ExRate: { integerDigits: 2, precision: 8, quantityPrecision: 8 }, + Shibor: { integerDigits: 2, precision: 4, quantityPrecision: 2 }, + FixingRepoRate: { integerDigits: 2, precision: 4, quantityPrecision: 2 }, + RateYield: {integerDigits: 6, precision: 8, quantityPrecision: 2}, + BondIndex: {integerDigits: 6, precision: 4, quantityPrecision: 2}, }; diff --git a/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml b/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml index ea00430b..91d9d26f 100644 --- a/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml +++ b/YLErpWeb/Views/SwapTrade2/SwapIncome.cshtml @@ -42,15 +42,15 @@ 成交名义本金 - {{deal.NotionalValue}} + {{formatAmount(deal.NotionalValue)}} 持仓名义本金 - {{deal.PosiNotionalValue}} + {{formatAmount(deal.PosiNotionalValue)}} 成交数量 - {{deal.NotionalQty}} + {{formatQuantity(deal.NotionalQty)}} 持仓数量 - {{deal.PositionQty2}} + {{formatQuantity(deal.PositionQty2)}} 起始日期 @@ -66,7 +66,7 @@ 平仓总额 - {{deal.SwapCloseAmount}} + {{formatAmount(deal.SwapCloseAmount)}} @@ -98,7 +98,7 @@ - {{item.InterestClosePnL}} + {{formatAmount(item.InterestClosePnL)}} @@ -126,7 +126,7 @@ - {{item.InterestClosePnL}} + {{formatAmount(item.InterestClosePnL)}} @@ -177,13 +177,13 @@ - {{floatPosition.Quantity}} + {{formatQuantity(floatPosition.Quantity)}}
我方{{floatPosition.PayDirection==1?"支付":"收取"}}交易费用
- {{floatPosition.FloatPnlSum}} + {{formatAmount(floatPosition.FloatPnlSum)}} diff --git a/YLErpWeb/Views/SwapTrade2/SwapLongShortSwap.cshtml b/YLErpWeb/Views/SwapTrade2/SwapLongShortSwap.cshtml index 1ef9f150..4df23cf7 100644 --- a/YLErpWeb/Views/SwapTrade2/SwapLongShortSwap.cshtml +++ b/YLErpWeb/Views/SwapTrade2/SwapLongShortSwap.cshtml @@ -28,11 +28,11 @@
- +
- +
@@ -48,7 +48,7 @@
- +
@@ -75,7 +75,7 @@ - {{item.InterestClosePnL}} + {{formatAmount(item.InterestClosePnL)}} @@ -103,7 +103,7 @@ - {{item.InterestClosePnL}} + {{formatAmount(item.InterestClosePnL)}} diff --git a/YLErpWeb/Views/SwapTrade2/SwapLongShortUnwind.cshtml b/YLErpWeb/Views/SwapTrade2/SwapLongShortUnwind.cshtml index c773ae03..bdbc1601 100644 --- a/YLErpWeb/Views/SwapTrade2/SwapLongShortUnwind.cshtml +++ b/YLErpWeb/Views/SwapTrade2/SwapLongShortUnwind.cshtml @@ -29,20 +29,20 @@
- +
- +
- +
- +
@@ -62,7 +62,7 @@
- +
@@ -91,7 +91,7 @@ - {{item.InterestClosePnL}} + {{formatAmount(item.InterestClosePnL)}} @@ -115,7 +115,7 @@ - {{item.InterestClosePnL}} + {{formatAmount(item.InterestClosePnL)}} diff --git a/YLErpWeb/Views/SwapTrade2/SwapUnwind.cshtml b/YLErpWeb/Views/SwapTrade2/SwapUnwind.cshtml index 7f9e2ea1..4d7c979a 100644 --- a/YLErpWeb/Views/SwapTrade2/SwapUnwind.cshtml +++ b/YLErpWeb/Views/SwapTrade2/SwapUnwind.cshtml @@ -33,20 +33,20 @@
- +
- +
- +
- +
@@ -64,7 +64,7 @@
- +
@@ -96,7 +96,7 @@
- +
@@ -125,7 +125,7 @@ - {{item.InterestClosePnL}} + {{formatAmount(item.InterestClosePnL)}} @@ -165,7 +165,7 @@ - {{item.InterestClosePnL}} + {{formatAmount(item.InterestClosePnL)}} @@ -214,7 +214,7 @@ - {{deal.CloseQty}} + {{formatQuantity(deal.CloseQty)}}
我方{{floatPosition.PayDirection==1?"支付":"收取"}}交易费用
@@ -222,8 +222,8 @@ - {{floatPosition.DividendIn}} - {{floatPosition.FloatPnlSum}} + {{formatAmount(floatPosition.DividendIn)}} + {{formatAmount(floatPosition.FloatPnlSum)}} diff --git a/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml b/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml index 9ec357e9..863283eb 100644 --- a/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml +++ b/YLErpWeb/Views/SwapTrade2/SwapflowList.cshtml @@ -233,7 +233,7 @@
- +
diff --git a/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml b/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml index eead5b88..6561cbfe 100644 --- a/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml +++ b/YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml @@ -493,7 +493,7 @@ - {{item.underlying!=null?item.underlying.QuoteUnitString:''}} + {{item.underlying!=null?item.underlying.QuoteUnitString:''}}