refactor(swaptrade): 优化互换交易中的数量和金额格式化功能

- 引入UnderlyingInstrumentType字段用于精确控制格式化规则
- 添加quantityPrecision配置支持不同产品类型的数量精度设置
- 实现formatAmount和formatQuantity方法提供统一格式化接口
- 更新视图模板使用新的格式化方法替代直接数据绑定
- 重构swapPricePrecisionHelper.js支持按产品类型定制格式化规则
- 移除废弃的inputFormatTradeAmount等旧格式化配置
- 添加千分位分组显示功能提升数字可读性
This commit is contained in:
张名锐
2026-08-06 12:41:30 +08:00
parent db5dcaf149
commit 5b7f17727d
16 changed files with 218 additions and 123 deletions
+28 -26
View File
@@ -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},
};
+9 -9
View File
@@ -42,15 +42,15 @@
<tbody>
<tr>
<td>成交名义本金</td>
<td>{{deal.NotionalValue}}</td>
<td>{{formatAmount(deal.NotionalValue)}}</td>
<td>持仓名义本金</td>
<td>{{deal.PosiNotionalValue}}</td>
<td>{{formatAmount(deal.PosiNotionalValue)}}</td>
</tr>
<tr>
<td>成交数量</td>
<td> {{deal.NotionalQty}}</td>
<td> {{formatQuantity(deal.NotionalQty)}}</td>
<td>持仓数量</td>
<td> {{deal.PositionQty2}}</td>
<td> {{formatQuantity(deal.PositionQty2)}}</td>
</tr>
<tr>
<td>起始日期</td>
@@ -66,7 +66,7 @@
</tr>
<tr>
<td>平仓总额</td>
<td> {{deal.SwapCloseAmount}}</td>
<td> {{formatAmount(deal.SwapCloseAmount)}}</td>
<td></td>
<td></td>
</tr>
@@ -98,7 +98,7 @@
<td>
<vue-number-input v-model="item.InterestFee" v-bind:format="inputFormatInterestAmount" v-on:input="changeInterestAmount(item)"></vue-number-input>
</td>
<td>{{item.InterestClosePnL}}</td>
<td>{{formatAmount(item.InterestClosePnL)}}</td>
</tr>
</tbody>
</table>
@@ -126,7 +126,7 @@
<td>
<vue-number-input v-model="item.InterestFee" v-bind:format="inputFormatInterestAmount" v-on:input="changeInterestAmount(item)"></vue-number-input>
</td>
<td>{{item.InterestClosePnL}}</td>
<td>{{formatAmount(item.InterestClosePnL)}}</td>
</tr>
</tbody>
</table>
@@ -177,13 +177,13 @@
<span title="使用系统标的价格" class="glyphicon glyphicon-refresh"></span>
</a>
</td>
<td>{{floatPosition.Quantity}}</td>
<td>{{formatQuantity(floatPosition.Quantity)}}</td>
<td>
<vue-number-input v-model="floatPosition.TradingFee" v-bind:format="inputFormatInterestAmount" v-on:input="changeTradingFee"></vue-number-input>
<div class="bubble-box" style="margin-left:6px;">我方{{floatPosition.PayDirection==1?"支付":"收取"}}交易费用</div>
</td>
<td> <vue-number-input v-model="floatPosition.DividendIn" v-bind:format="inputFormatDividend" v-on:input="changeTradingFee"></vue-number-input></td>
<td style="font-size:18px;">{{floatPosition.FloatPnlSum}}</td>
<td style="font-size:18px;">{{formatAmount(floatPosition.FloatPnlSum)}}</td>
</tr>
</tbody>
</table>
@@ -28,11 +28,11 @@
<div id="commomArea" class="row" style="height:140px">
<div class="form-group col-md-4">
<label class="formlabel">成交名义本金</label>
<input class="text-box" v-model="deal.NotionalValue" readonly="readonly" type="text" />
<input class="text-box" :value="formatAmount(deal.NotionalValue)" readonly="readonly" type="text" />
</div>
<div class="form-group col-md-4">
<label class="formlabel">持仓名义本金</label>
<input class="text-box" v-model="deal.PosiNotionalValue" readonly="readonly" type="text" />
<input class="text-box" :value="formatAmount(deal.PosiNotionalValue)" readonly="readonly" type="text" />
</div>
<div class="form-group col-md-4">
<label class="formlabel">起始日期</label>
@@ -48,7 +48,7 @@
</div>
<div class="form-group col-md-4">
<label class="formlabel">平仓总额</label>
<input class="text-box" v-model="deal.SwapCloseAmount" readonly="readonly" type="text" />
<input class="text-box" :value="formatAmount(deal.SwapCloseAmount)" readonly="readonly" type="text" />
</div>
</div>
</div>
@@ -75,7 +75,7 @@
<td>
<vue-number-input v-model="item.InterestFee" v-bind:format="inputFormatEqvNotional" v-on:input="changeInterestAmount(item)"></vue-number-input>
</td>
<td>{{item.InterestClosePnL}}</td>
<td>{{formatAmount(item.InterestClosePnL)}}</td>
</tr>
</tbody>
</table>
@@ -103,7 +103,7 @@
<td>
<vue-number-input v-model="item.InterestFee" v-bind:format="inputFormatEqvNotional" v-on:input="changeInterestAmount(item)"></vue-number-input>
</td>
<td>{{item.InterestClosePnL}}</td>
<td>{{formatAmount(item.InterestClosePnL)}}</td>
</tr>
</tbody>
</table>
@@ -29,20 +29,20 @@
<div id="commomArea" class="row" style="height:140px">
<div class="form-group col-md-3">
<label class="formlabel">成交名义本金</label>
<input class="text-box" v-model="deal.NotionalValue" readonly="readonly" type="text" />
<input class="text-box" :value="formatAmount(deal.NotionalValue)" readonly="readonly" type="text" />
</div>
<div class="form-group col-md-3">
<label class="formlabel">持仓名义本金</label>
<input class="text-box" v-model="deal.PosiNotionalValue" readonly="readonly" type="text" />
<input class="text-box" :value="formatAmount(deal.PosiNotionalValue)" readonly="readonly" type="text" />
</div>
<div class="form-group col-md-3">
<label class="formlabel">成交数量</label>
<input class="text-box" v-model="deal.NotionalQty" readonly="readonly" type="text" />
<input class="text-box" :value="formatQuantity(deal.NotionalQty)" readonly="readonly" type="text" />
</div>
<div class="form-group col-md-3">
<label class="formlabel">持仓数量</label>
<input class="text-box" v-model="deal.PositionQty" readonly="readonly" type="text" />
<input class="text-box" :value="formatQuantity(deal.PositionQty)" readonly="readonly" type="text" />
</div>
<div class="form-group col-md-3">
<label class="formlabel">起始日期</label>
@@ -62,7 +62,7 @@
</div>
<div class="form-group col-md-3">
<label class="formlabel">平仓总额</label>
<input class="text-box" v-model="deal.SwapCloseAmount" readonly="readonly" type="text" />
<input class="text-box" :value="formatAmount(deal.SwapCloseAmount)" readonly="readonly" type="text" />
</div>
</div>
</div>
@@ -91,7 +91,7 @@
<td>
<vue-number-input v-model="item.InterestAmount" v-bind:format="inputFormatEqvNotional" v-on:input="changeInterestAmount(item)"></vue-number-input>
</td>
<td>{{item.InterestClosePnL}}</td>
<td>{{formatAmount(item.InterestClosePnL)}}</td>
</tr>
</tbody>
</table>
@@ -115,7 +115,7 @@
<td>
<vue-number-input v-model="item.InterestAmount" v-bind:format="inputFormatEqvNotional" v-on:input="changeInterestAmount(item)"></vue-number-input>
</td>
<td>{{item.InterestClosePnL}}</td>
<td>{{formatAmount(item.InterestClosePnL)}}</td>
</tr>
</tbody>
</table>
+11 -11
View File
@@ -33,20 +33,20 @@
<div id="commomArea" class="row" style="height:157px">
<div class="form-group col-md-3">
<label class="formlabel">成交名义本金</label>
<input class="text-box" v-model="deal.NotionalValue" readonly="readonly" type="text" />
<input class="text-box" :value="formatAmount(deal.NotionalValue)" readonly="readonly" type="text" />
</div>
<div class="form-group col-md-3">
<label class="formlabel">持仓名义本金</label>
<input class="text-box" v-model="deal.PosiNotionalValue" readonly="readonly" type="text" />
<input class="text-box" :value="formatAmount(deal.PosiNotionalValue)" readonly="readonly" type="text" />
</div>
<div class="form-group col-md-3">
<label class="formlabel">成交数量</label>
<input class="text-box" v-model="deal.NotionalQty" readonly="readonly" type="text" />
<input class="text-box" :value="formatQuantity(deal.NotionalQty)" readonly="readonly" type="text" />
</div>
<div class="form-group col-md-3">
<label class="formlabel">持仓数量</label>
<input class="text-box" v-model="deal.PositionQty2" readonly="readonly" type="text" />
<input class="text-box" :value="formatQuantity(deal.PositionQty2)" readonly="readonly" type="text" />
</div>
<div class="form-group col-md-3">
<label class="formlabel">平仓方式</label>
@@ -64,7 +64,7 @@
</div>
<div class="form-group col-md-3" v-show="deal.CloseType==1">
<label class="formlabel">平仓数量</label>
<vue-number-input v-model="deal.CloseQty" v-on:input="changeCloseQty" v-bind:format="inputFormatTradeAmount"></vue-number-input>
<vue-number-input v-model="deal.CloseQty" v-on:input="changeCloseQty" v-bind:format="getQuantityInputFormat()"></vue-number-input>
</div>
<div class="form-group col-md-3" v-show="deal.CloseType==2">
<label class="formlabel">平仓比例</label>
@@ -96,7 +96,7 @@
</div>
<div class="form-group col-md-3">
<label class="formlabel">平仓总额</label>
<input class="text-box" v-model="deal.SwapCloseAmount" readonly="readonly" type="text" />
<input class="text-box" :value="formatAmount(deal.SwapCloseAmount)" readonly="readonly" type="text" />
</div>
</div>
</div>
@@ -125,7 +125,7 @@
<td>
<vue-number-input v-model="item.InterestAmount" v-bind:format="inputFormatCloseAmount" v-on:input="changeInterestAmount(item)"></vue-number-input>
</td>
<td>{{item.InterestClosePnL}}</td>
<td>{{formatAmount(item.InterestClosePnL)}}</td>
</tr>
</tbody>
</table>
@@ -165,7 +165,7 @@
<td>
<vue-number-input v-model="item.InterestAmount" v-bind:format="inputFormatCloseAmount" v-on:input="changeInterestAmount(item)"></vue-number-input>
</td>
<td>{{item.InterestClosePnL}}</td>
<td>{{formatAmount(item.InterestClosePnL)}}</td>
</tr>
</tbody>
</table>
@@ -214,7 +214,7 @@
<span title="使用系统标的价格" class="glyphicon glyphicon-refresh"></span>
</a>
</td>
<td>{{deal.CloseQty}}</td>
<td>{{formatQuantity(deal.CloseQty)}}</td>
<td>
<vue-number-input v-model="floatPosition.TradingFee" v-bind:format="inputFormatCloseAmount" v-on:input="changeTradingFee"></vue-number-input>
<div class="bubble-box">我方{{floatPosition.PayDirection==1?"支付":"收取"}}交易费用</div>
@@ -222,8 +222,8 @@
<td>
<vue-number-input v-model="floatPosition.TradingFeePending" v-bind:format="inputFormatEqvNotional" disabled></vue-number-input>
</td>
<td>{{floatPosition.DividendIn}}</td>
<td style="font-size:18px;">{{floatPosition.FloatPnlSum}}</td>
<td>{{formatAmount(floatPosition.DividendIn)}}</td>
<td style="font-size:18px;">{{formatAmount(floatPosition.FloatPnlSum)}}</td>
</tr>
</tbody>
</table>
@@ -233,7 +233,7 @@
</div>
<div class="form-group">
<label class="formlabel ">成交数量</label>
<vue-number-input v-model="swapflow.TradingQty" v-bind:format="inputFormatTradeAmount"></vue-number-input>
<vue-number-input v-model="swapflow.TradingQty" v-bind:format="getQuantityInputFormat()"></vue-number-input>
</div>
<div class="form-group">
<label class="formlabel ">成交金额</label>
+1 -1
View File
@@ -493,7 +493,7 @@
<vue-swap-price-input :key="getPosiPriceFormatKey(item,'normalPosiGrossPrice')" v-model="item.PosiGrossPrice" v-bind:format="getPosiPriceInputFormat(item,'grossPrice')" v-on:input="changeSpotPrice(item)"></vue-swap-price-input>
</td>
<td>
<vue-number-input v-model="item.PosiQuantity" v-on:input="changeQuantity(item)" v-bind:format="inputFormatPositionQuantityFixed2"></vue-number-input>{{item.underlying!=null?item.underlying.QuoteUnitString:''}}
<vue-number-input v-model="item.PosiQuantity" v-on:input="changeQuantity(item)" v-bind:format="getQuantityInputFormat(item)"></vue-number-input>{{item.underlying!=null?item.underlying.QuoteUnitString:''}}
</td>
<td>
<template v-if="posiFeeModePercent">
+5 -5
View File
@@ -423,7 +423,7 @@
</td>
}
<td>
<span class="js-swap-common" data-value="@SwapCommonData(item.PosiQuantity)" data-kind="quantity"></span>
<span class="js-swap-common" data-value="@SwapCommonData(item.PosiQuantity)" data-kind="quantity" data-instrument-type="@item.UnderlyingInstrumentType"></span>
</td>
<td>
@if (item.PosiFeeType == 0)
@@ -646,7 +646,7 @@
</td>
}
<td>
<span class="js-swap-common" data-value="@SwapCommonData(item.PosiQuantity)" data-kind="quantity"></span>
<span class="js-swap-common" data-value="@SwapCommonData(item.PosiQuantity)" data-kind="quantity" data-instrument-type="@item.UnderlyingInstrumentType"></span>
</td>
<td><span class="js-swap-common" data-value="@SwapCommonData(item.PosiNotionalValue)" data-kind="amount"></span></td>
<td><span class="js-swap-common" data-value="@SwapCommonData(item.PosiTradingFeePending)" data-kind="amount"></span></td>
@@ -710,7 +710,7 @@
@if (tc.CloseType == 1)
{
<td class="tdRight">平仓数量</td>
<td class="color-bule"><span class="js-swap-common" data-value="@SwapCommonData(PS.Config.IsUseDisplayNotional ? tc.CloseQty * (trade.CountRatio ?? 1) : tc.CloseQty)" data-kind="quantity"></span></td>
<td class="color-bule"><span class="js-swap-common" data-value="@SwapCommonData(PS.Config.IsUseDisplayNotional ? tc.CloseQty * (trade.CountRatio ?? 1) : tc.CloseQty)" data-kind="quantity" data-instrument-type="@trade.UnderlyingInstrumentType"></span></td>
}
else
{
@@ -856,7 +856,7 @@
<td><span class="js-swap-price" data-value="@SwapPriceData(closeFloat.PosiGrossPrice)" data-instrument-type="@closeFloat.UnderlyingInstrumentType" data-field="grossPrice"></span></td>
<td><span class="js-swap-price" data-value="@SwapPriceData(closeFloat.TradingAmountAvg)" data-instrument-type="@closeFloat.UnderlyingInstrumentType" data-field="grossPrice"></span></td>
}
<td><span class="js-swap-common" data-value="@SwapCommonData(closeFloat.Quantity)" data-kind="quantity"></span></td>
<td><span class="js-swap-common" data-value="@SwapCommonData(closeFloat.Quantity)" data-kind="quantity" data-instrument-type="@closeFloat.UnderlyingInstrumentType"></span></td>
<td><span class="js-swap-common" data-value="@SwapCommonData(closeFloat.TradingFee)" data-kind="amount"></span></td>
<td><span class="js-swap-common" data-value="@SwapCommonData(closeFloat.TradingFeePending)" data-kind="amount"></span></td>
<td><span class="js-swap-common" data-value="@SwapCommonData(closeFloat.DividendIn)" data-kind="amount"></span></td>
@@ -1058,7 +1058,7 @@
<td><span class="js-swap-price" data-value="@SwapPriceData(closeFloat.TradingAmountAvg * multiplier)" data-instrument-type="@closeFloat.UnderlyingInstrumentType" data-field="grossPrice"></span></td>
}
<td><span class="js-swap-price" data-value="@SwapPriceData(closeFloat.TradingAmountAvg * multiplier)" data-instrument-type="@closeFloat.UnderlyingInstrumentType" data-field="grossPrice"></span></td>
<td><span class="js-swap-common" data-value="@SwapCommonData(closeFloat.PositionQty ?? 0)" data-kind="quantity"></span></td>
<td><span class="js-swap-common" data-value="@SwapCommonData(closeFloat.PositionQty ?? 0)" data-kind="quantity" data-instrument-type="@closeFloat.UnderlyingInstrumentType"></span></td>
<td><span class="js-swap-common" data-value="@SwapCommonData(closeFloat.TradingFee)" data-kind="amount"></span></td>
<td><span class="js-swap-common" data-value="@SwapCommonData(closeFloat.DividendIn)" data-kind="amount"></span></td>
<td><span class="js-swap-common" data-value="@SwapCommonData(closeFloat.FloatPnlSum)" data-kind="amount"></span></td>
@@ -2,9 +2,15 @@
const inputFormatTradePrice = Object.freeze({ precision: otcformat.trading.tradeSinglePrice.precision, negative: true, append: '' });
const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, negative: true, 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 formatSwapQuantity = function (value, options, rowObject) {
const row = rowObject || {};
const instrumentType = row.UnderlyingInstrumentType
|| row.InstrumentType
|| (row.position && row.position.UnderlyingInstrumentType)
|| '';
return swapPricePrecision.formatCommon('quantity', value, instrumentType);
};
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
var clients = ylotc.clients;
const consUnderlyingFlag = (function () {
@@ -864,6 +870,7 @@ var vue = new Vue({
SwapTradeNo: "",
BsType: 1,
UnderlyingCode: "",
UnderlyingInstrumentType: "",
TradingQty: 0,
TradingAmount: 0,
TradingFee: 0,
@@ -1017,6 +1024,12 @@ var vue = new Vue({
this.tradeDate = page.valueDate; //将1970/08/08转化成1970-08-08
return this.tradeDate;
},
getQuantityInputFormat() {
return swapPricePrecision.getCommonInputFormat(
'quantity',
{ append: '' },
this.swapflow.UnderlyingInstrumentType);
},
addNew() {
this.tradeDate = page.valueDate;
this.initSwapFlow();
@@ -1032,6 +1045,7 @@ var vue = new Vue({
SwapTradeNo: "",
BsType: 1,
UnderlyingCode: "",
UnderlyingInstrumentType: "",
TradingQty: 0,
TradingAmount: 0,
TradingFee: 0,
@@ -1129,6 +1143,7 @@ var vue = new Vue({
thisObj.swapflow.SwapTradeNo = item.SwapTradeNo;
thisObj.swapflow.BsType = item.BsType;
thisObj.swapflow.UnderlyingCode = item.UnderlyingCode;
thisObj.swapflow.UnderlyingInstrumentType = item.UnderlyingInstrumentType || item.InstrumentType || "";
thisObj.swapflow.TradingQty = item.TradingQty;
thisObj.swapflow.TradingAmount = item.TradingAmount;
thisObj.swapflow.TradingFee = item.TradingFee;
@@ -1158,6 +1173,7 @@ var vue = new Vue({
setUnderlyingCode(data) {
this.swapflow.ContractSize = data.ContractSize;
this.swapflow.UnderlyingName = data.Name;
this.swapflow.UnderlyingInstrumentType = data.InstrumentType || data.UnderlyingInstrumentType || "";
},
changeClient: function (client) {
this.swapflow.ClientId = client.id;
@@ -3,12 +3,12 @@ window.otcformat.options.disableGrouping = true;
const inputFormatSwapRate = swapPricePrecision.getCommonInputFormat('rate', { append: '%' });
const inputFormatTradePrice = Object.freeze({ precision: otcformat.trading.tradePrice.precision, append: '' });
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 swapInstrumentType = (model.FlowEvents || []).find(item => item && item.UnderlyingInstrumentType)?.UnderlyingInstrumentType || model.UnderlyingInstrumentType || '';
const formatSwapAmount = value => swapPricePrecision.normalizeCommon('amount', value);
const formatSwapQuantity = value => swapPricePrecision.normalizeCommon('quantity', value, swapInstrumentType);
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 });
@@ -62,6 +62,12 @@ const vue = new Vue({
});
},
methods: {
formatAmount(value) {
return swapPricePrecision.formatCommon('amount', value);
},
formatQuantity(value) {
return swapPricePrecision.formatCommon('quantity', value, swapInstrumentType);
},
isAfterMaxIncomeValueDate(valueDate) {
return valueDate && MaxIncomeValueDate && valueDate > MaxIncomeValueDate;
},
@@ -1,8 +1,9 @@
//otcformat禁止千分位分组
window.otcformat.options.disableGrouping = true;
const inputFormatEqvNotional = swapPricePrecision.getCommonInputFormat('amount', { append: '' });
const formatSwapAmount = value => swapPricePrecision.formatCommon('amount', value);
const formatSwapQuantity = value => swapPricePrecision.formatCommon('quantity', value);
const swapInstrumentType = (model.FlowEvents || []).find(item => item && item.UnderlyingInstrumentType)?.UnderlyingInstrumentType || model.UnderlyingInstrumentType || '';
const formatSwapAmount = value => swapPricePrecision.normalizeCommon('amount', value);
const formatSwapQuantity = value => swapPricePrecision.normalizeCommon('quantity', value, swapInstrumentType);
let ValueDate = model.ValueDate;
const vue = new Vue({
el: '#vueDiv',
@@ -24,6 +25,12 @@ const vue = new Vue({
this.setValueDate();
},
methods: {
formatAmount(value) {
return swapPricePrecision.formatCommon('amount', value);
},
formatQuantity(value) {
return swapPricePrecision.formatCommon('quantity', value, swapInstrumentType);
},
initDeal() {
this.interestList = model.FlowEvents.filter((item) => {
return item.InterestMode == 1 || item.InterestMode == 2 || item.InterestMode == 7 || item.InterestMode == 8 || item.InterestMode == 9;
@@ -1,52 +1,56 @@
var swapPricePrecision = (function (global) {
const defaults = Object.freeze({
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},
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},
});
@@ -130,7 +134,7 @@ var swapPricePrecision = (function (global) {
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 };
return { precision: precision, grouping: typeof rule.grouping === 'boolean' ? rule.grouping : undefined };
}
function findRule(source, instrumentType, field) {
@@ -144,13 +148,6 @@ 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 '';
@@ -161,12 +158,48 @@ var swapPricePrecision = (function (global) {
return (negative ? '-' : '') + integerPart + '.' + (parts[1] || '').padEnd(precision, '0');
}
function formatCommon(kind, value) {
function groupDecimal(value) {
if (!value) return value;
const negative = value.charAt(0) === '-';
const source = negative ? value.substring(1) : value;
const parts = source.split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return (negative ? '-' : '') + parts.join('.');
}
function getQuantityPrecision(instrumentType, fallback) {
if (!instrumentType) return fallback;
const configured = global.main && global.main.swapPricePrecision;
const typeRule = (configured || defaults)[instrumentType];
if (!typeRule || !Object.prototype.hasOwnProperty.call(typeRule, 'quantityPrecision')) return fallback;
const precision = Number(typeRule.quantityPrecision);
return Number.isInteger(precision) && precision >= 0 && precision <= 13 ? precision : fallback;
}
function getCommonRule(kind, instrumentType) {
const fallback = normalizeCommonRule(defaults.common[kind]) || { precision: 2, grouping: false };
const configured = global.main && global.main.swapPricePrecision
&& global.main.swapPricePrecision.common;
const configuredRule = normalizeCommonRule(configured && configured[kind]);
const rule = configuredRule || fallback;
if (rule.grouping === undefined) rule.grouping = fallback.grouping;
if (kind === 'quantity') rule.precision = getQuantityPrecision(instrumentType, rule.precision);
return rule;
}
function formatCommon(kind, value, instrumentType, options) {
if (value === null || value === undefined || value === '') return '';
const precision = getCommonRule(kind).precision;
if (instrumentType && typeof instrumentType === 'object') {
options = instrumentType;
instrumentType = options.instrumentType;
}
const rule = getCommonRule(kind, instrumentType);
const displayValue = kind === 'rate' ? shiftDecimal(value, 2) : value;
const formatted = formatFixed(displayValue, precision);
return kind === 'rate' && formatted ? formatted + '%' : formatted;
const formatted = formatFixed(displayValue, rule.precision);
if (!formatted) return '';
const grouping = options && options.grouping !== undefined ? !!options.grouping : rule.grouping;
const text = grouping ? groupDecimal(formatted) : formatted;
return kind === 'rate' ? text + '%' : text;
}
function format(value, instrumentType, field) {
@@ -297,16 +330,24 @@ var swapPricePrecision = (function (global) {
return Object.freeze({
getRule: getRule,
getCommonPrecision: function (kind) {
return getCommonRule(kind).precision;
getCommonPrecision: function (kind, instrumentType) {
return getCommonRule(kind, instrumentType).precision;
},
getCommonInputFormat: function (kind, options) {
return Object.assign({}, options, {
precision: getCommonRule(kind).precision,
getCommonInputFormat: function (kind, options, instrumentType) {
const inputOptions = Object.assign({}, options || {});
const type = instrumentType || inputOptions.instrumentType;
delete inputOptions.instrumentType;
const rule = getCommonRule(kind, type);
return Object.assign(inputOptions, {
precision: rule.precision,
grouping: inputOptions.grouping === undefined ? !!rule.grouping : !!inputOptions.grouping,
trimTailZeros: false
});
},
formatCommon: formatCommon,
normalizeCommon: function (kind, value, instrumentType) {
return formatCommon(kind, value, instrumentType, { grouping: false });
},
getInputFormat: function (instrumentType, field, options) {
const rule = getRule(instrumentType, field);
return rule ? Object.assign({}, options, rule) : Object.assign({}, options);
@@ -11,8 +11,6 @@ const consAssetUnits = page.canAddNewTrader ? ylotc.assetunits
const inputFormatInteger = Object.freeze({ precision: 0, append: '' });
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 = swapPricePrecision.getCommonInputFormat('amount', { negative: true, append: '', percent: false });
@@ -270,6 +268,12 @@ const vue = new Vue({
});
},
methods: {
getQuantityInputFormat(item) {
return swapPricePrecision.getCommonInputFormat(
'quantity',
{ append: '' },
item && item.UnderlyingInstrumentType);
},
getPosiPriceInputFormat(item, field) {
return swapPricePrecision.getInputFormat(
item && item.UnderlyingInstrumentType,
@@ -563,7 +567,7 @@ const vue = new Vue({
},
//变更名义本金(仅格式化,不反算数量)
changeStockEqvNotional() {
this.trade.StockEqvNotional = swapPricePrecision.formatCommon('amount', this.trade.StockEqvNotional);
this.trade.StockEqvNotional = swapPricePrecision.normalizeCommon('amount', this.trade.StockEqvNotional);
this.refreshPayTradingFeesByUnit();
//计算数量
// if (this.paySwapList.length > 0) {
@@ -651,7 +655,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 = swapPricePrecision.formatCommon('amount', stockEqvNotional);
this.trade.StockEqvNotional = swapPricePrecision.normalizeCommon('amount', stockEqvNotional);
payItem.PosiNotionalValue = this.trade.StockEqvNotional;
this.refreshPayTradingFeesByUnit();
}
@@ -11,7 +11,10 @@ function formatSwapPriceElements() {
function formatSwapCommonElements() {
$('.js-swap-common').each(function () {
this.textContent = swapPricePrecision.formatCommon(this.dataset.kind, this.dataset.value);
this.textContent = swapPricePrecision.formatCommon(
this.dataset.kind,
this.dataset.value,
this.dataset.instrumentType);
});
}
@@ -1,8 +1,9 @@
//otcformat禁止千分位分组
window.otcformat.options.disableGrouping = true;
const inputFormatEqvNotional = swapPricePrecision.getCommonInputFormat('amount', { append: '' });
const formatSwapAmount = value => swapPricePrecision.formatCommon('amount', value);
const formatSwapQuantity = value => swapPricePrecision.formatCommon('quantity', value);
const swapInstrumentType = (model.FlowEvents || []).find(item => item && item.UnderlyingInstrumentType)?.UnderlyingInstrumentType || model.UnderlyingInstrumentType || '';
const formatSwapAmount = value => swapPricePrecision.normalizeCommon('amount', value);
const formatSwapQuantity = value => swapPricePrecision.normalizeCommon('quantity', value, swapInstrumentType);
let dealDate = model.DealDate;
const vue = new Vue({
el: '#vueDiv',
@@ -23,6 +24,12 @@ const vue = new Vue({
this.dataFormat();
},
methods: {
formatAmount(value) {
return swapPricePrecision.formatCommon('amount', value);
},
formatQuantity(value) {
return swapPricePrecision.formatCommon('quantity', value, swapInstrumentType);
},
initDeal() {
this.interestList = model.FlowEvents.filter((item) => {
return item.InterestMode == 1 || item.InterestMode == 2 || item.InterestMode == 7 || item.InterestMode == 8 || item.InterestMode == 9;
@@ -3,11 +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 = 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 swapInstrumentType = (model.FlowEvents || []).find(item => item && item.UnderlyingInstrumentType)?.UnderlyingInstrumentType || model.UnderlyingInstrumentType || '';
const formatSwapAmount = value => swapPricePrecision.normalizeCommon('amount', value);
const formatSwapQuantity = value => swapPricePrecision.normalizeCommon('quantity', value, swapInstrumentType);
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 });
@@ -81,6 +81,15 @@ const vue = new Vue({
this.setUnwindDate();
},
methods: {
formatAmount(value) {
return swapPricePrecision.formatCommon('amount', value);
},
formatQuantity(value) {
return swapPricePrecision.formatCommon('quantity', value, swapInstrumentType);
},
getQuantityInputFormat() {
return swapPricePrecision.getCommonInputFormat('quantity', { append: '' }, swapInstrumentType);
},
getDeliveryPriceInputFormat() {
return swapPricePrecision.getInputFormat(
this.floatPosition && this.floatPosition.UnderlyingInstrumentType,