feat(swap): 实现互换交易价格精度控制功能 init

- 添加 swapPricePrecisionHelper.js 工具类处理价格精度格式化
- 新增 swappriceprecision.js 配置文件定义各类金融产品的精度规则
- 在 EodPositionRisks.cshtml 和 SwapIncome.cshtml 中引入新的价格格式化脚本
- 替换原有的价格格式化函数为基于产品类型的动态精度控制
- 移除旧的价格验证和标准化逻辑,改用新的精度控制机制
- 添加 vue-swap-price-input 组件用于精确的价格输入控制
- 更新 Controller 中的价格处理逻辑以支持新精度格式化方式
This commit is contained in:
张名锐
2026-07-29 09:44:04 +08:00
parent ac00548009
commit fd2a054764
18 changed files with 548 additions and 136 deletions
@@ -800,7 +800,10 @@ function exportEodSwapRows(jgrid, fileName, groupConfig, exportColumnNames) {
}
//---------------------------Formatter---------------------------------
function PriceFormat(cellValue, options, rowObject) {
return otcformat.trading.umprice(cellValue);
return swapPricePrecision.format(
cellValue,
rowObject && rowObject.eodPosition && rowObject.eodPosition.UnderlyingInstrumentType,
'grossPrice');
}
function RealizedPnlFormat(cellValue, options, rowObject) {
@@ -73,14 +73,18 @@ const vue = new Vue({
$(this.$refs.incomeValueDatePicker.$el).val(MaxIncomeValueDate);
return false;
},
// 守卫: 价格缩放因子(债券 multiplier=100 时界面为百分比态, 计算用相对价需 ÷100)
// 计算已外置到 swapCalc.getPriceScale; 改动需同步 swapCalc.test.js
getPriceScale() {
return SwapCalc.getPriceScale(this.multiplier);
getDeliveryPriceInputFormat() {
return swapPricePrecision.getInputFormat(
this.floatPosition && this.floatPosition.UnderlyingInstrumentType,
'grossPrice',
inputFormatSwapDeliveryPrice);
},
getStorageDeliveryPrice() {
const precision = inputFormatSwapDeliveryPrice.precision + (this.multiplier === 100 ? 2 : 0);
return SwapCalc.roundHalfAwayFromZero(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), precision);
return swapPricePrecision.roundForSubmit(
swapPricePrecision.shiftDecimal(this.floatPosition.TradingAmountAvg, this.multiplier === 100 ? -2 : 0),
this.floatPosition && this.floatPosition.UnderlyingInstrumentType,
'grossPrice',
this.multiplier === 100 ? 2 : 0);
},
initDeal() {
var positions = model.FlowEvents.filter((item) => {
@@ -110,8 +114,8 @@ const vue = new Vue({
return tradeHelper.IsBond(instType);
},
priceFormat(price) {
price = price * this.multiplier;
var pricef = otcformat.trading.umprice(price);
price = swapPricePrecision.shiftDecimal(price, this.multiplier === 100 ? 2 : 0);
var pricef = swapPricePrecision.format(price, this.floatPosition && this.floatPosition.UnderlyingInstrumentType, 'grossPrice');
return pricef;
},
dataFormat() {
@@ -165,8 +169,10 @@ const vue = new Vue({
main.post("/underlying_manager/GetUnderlyingPriceByCode",
{ code: thisObj.floatPosition.UnderlyingCode, valuedate: thisObj.deal.ValueDate })
.done(function (res) {
res.obj = res.obj * thisObj.multiplier;
thisObj.floatPosition.TradingAmountAvg = _.round(Number(res.obj), 9);
thisObj.floatPosition.TradingAmountAvg = swapPricePrecision.roundForSubmit(
swapPricePrecision.shiftDecimal(res.obj, thisObj.multiplier === 100 ? 2 : 0),
thisObj.floatPosition.UnderlyingInstrumentType,
'grossPrice');
thisObj.calcFloatClosePnl();
});
},
@@ -382,6 +388,7 @@ const vue = new Vue({
components: {
'vue-datepicker': FastVue.vueDatePicker(),
'vue-number-input': FastVue.vueNumberInput(),
'vue-swap-price-input': swapPricePrecision.createVueInputComponent(),
}
});
@@ -0,0 +1,249 @@
var swapPricePrecision = (function (global) {
const defaults = Object.freeze({
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 },
Bond: {
grossPrice: { integerDigits: 6, precision: 11 },
netPrice: { integerDigits: 6, precision: 11 },
yield: { integerDigits: 2, precision: 6 }
},
TBonds: {
grossPrice: { integerDigits: 6, precision: 11 },
netPrice: { integerDigits: 6, precision: 11 },
yield: { integerDigits: 2, precision: 6 }
},
CreditBonds: {
grossPrice: { integerDigits: 6, precision: 11 },
netPrice: { integerDigits: 6, precision: 11 },
yield: { integerDigits: 2, precision: 6 }
},
OtherBonds: {
grossPrice: { integerDigits: 6, precision: 11 },
netPrice: { integerDigits: 6, precision: 11 },
yield: { integerDigits: 2, precision: 6 }
},
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 }
// TODO: Add InterestYield, BondIndex and GoldFutures after their enum values are confirmed.
});
function normalizeDecimal(value) {
if (value === null || value === undefined || value === '') return '';
let text = String(value).trim();
if (/[eE]/.test(text)) {
const number = Number(text);
if (!Number.isFinite(number)) return null;
text = number.toFixed(20).replace(/0+$/, '').replace(/\.$/, '');
}
if (!/^[+-]?(?:\d+|\d*\.\d+)$/.test(text)) return null;
const negative = text.charAt(0) === '-';
text = text.replace(/^[+-]/, '');
const parts = text.split('.');
const integerPart = parts[0].replace(/^0+(?=\d)/, '') || '0';
const decimalPart = parts.length > 1 ? parts[1] : '';
const result = integerPart + (decimalPart ? '.' + decimalPart : '');
return negative && !/^0(?:\.0*)?$/.test(result) ? '-' + result : result;
}
function shiftDecimal(value, places) {
let normalized = normalizeDecimal(value);
if (!normalized || !Number.isInteger(places) || places === 0) return normalized;
const negative = normalized.charAt(0) === '-';
const parts = (negative ? normalized.substring(1) : normalized).split('.');
const integerPart = parts[0];
const decimalPart = parts.length > 1 ? parts[1] : '';
const digits = integerPart + decimalPart;
const decimalIndex = integerPart.length + places;
let text;
if (decimalIndex <= 0) text = '0.' + '0'.repeat(-decimalIndex) + digits;
else if (decimalIndex >= digits.length) text = digits + '0'.repeat(decimalIndex - digits.length);
else text = digits.substring(0, decimalIndex) + '.' + digits.substring(decimalIndex);
return normalizeDecimal((negative ? '-' : '') + text);
}
function incrementDigits(value) {
let carry = 1;
let result = '';
for (let index = value.length - 1; index >= 0; index--) {
const digit = value.charCodeAt(index) - 48 + carry;
if (digit === 10) {
result = '0' + result;
carry = 1;
} else {
result = String(digit) + result;
carry = 0;
}
}
return carry ? '1' + result : result;
}
function roundDecimal(value, precision) {
const normalized = normalizeDecimal(value);
if (normalized === null || !Number.isInteger(precision) || precision < 0) return value;
const negative = normalized.charAt(0) === '-';
const parts = (negative ? normalized.substring(1) : normalized).split('.');
let integerPart = parts[0];
const decimalPart = parts.length > 1 ? parts[1] : '';
if (decimalPart.length <= precision) return normalized;
let digits = integerPart + decimalPart.substring(0, precision);
if (decimalPart.charAt(precision) >= '5') digits = incrementDigits(digits);
if (digits.length <= precision) digits = digits.padStart(precision + 1, '0');
integerPart = precision === 0 ? digits : digits.substring(0, digits.length - precision);
const roundedDecimal = precision === 0 ? '' : digits.substring(digits.length - precision);
return normalizeDecimal((negative ? '-' : '') + integerPart + (roundedDecimal ? '.' + roundedDecimal : ''));
}
function normalizeRule(rule) {
if (!rule || typeof rule !== 'object') return null;
const integerDigits = Number(rule.integerDigits);
const precision = Number(rule.precision);
if (!Number.isInteger(integerDigits) || integerDigits < 1 || integerDigits > 18
|| !Number.isInteger(precision) || precision < 0 || precision > 13) return null;
return { integerDigits: integerDigits, precision: precision };
}
function findRule(source, instrumentType, field) {
const typeRule = source && source[instrumentType];
return typeRule ? normalizeRule(typeRule[field] || typeRule) : null;
}
function getRule(instrumentType, field) {
const fallback = findRule(defaults, instrumentType, field);
if (!fallback) return null;
return findRule(global.main && global.main.swapPricePrecision, instrumentType, field) || fallback;
}
function format(value, instrumentType, field) {
const rule = getRule(instrumentType, field);
if (value === null || value === undefined || value === '') return '';
if (!rule) return global.otcformat.trading.umprice(value);
const rounded = roundDecimal(value, rule.precision);
return rounded === null ? '' : rounded.replace(/(\.\d*?[1-9])0+$/, '$1').replace(/\.0+$/, '');
}
function normalizeInput(value, format, shouldRound) {
const options = format || {};
const maxIntegerDigits = Number(options.integerDigits) || 0;
const precision = Number(options.precision) || 0;
const source = String(value === null || value === undefined ? '' : value).trim().replaceAll(',', '').replaceAll('。', '.');
let negative = false;
let hasDot = false;
let integerPart = '';
let decimalPart = '';
for (let index = 0; index < source.length; index++) {
const ch = source.charAt(index);
if (ch >= '0' && ch <= '9') {
if (hasDot) {
if (shouldRound || decimalPart.length < precision) decimalPart += ch;
} else if (!maxIntegerDigits || integerPart.length < maxIntegerDigits) {
integerPart += ch;
}
} else if (ch === '.' && !hasDot && precision > 0) {
hasDot = true;
} else if (ch === '-' && index === 0 && options.negative) {
negative = true;
}
}
if (!integerPart && !decimalPart) return negative ? '-' : '';
const text = (negative ? '-' : '') + (integerPart || '0') + (hasDot ? '.' + decimalPart : '');
if (!shouldRound || text.endsWith('.')) return text;
return roundDecimal(text, precision);
}
function createVueInputComponent() {
return {
props: {
value: { type: [Number, String], default: '' },
format: { type: Object, default: function () { return {}; } },
disabled: { type: Boolean }
},
data: function () {
return { text: '' };
},
mounted: function () {
this.text = this.toDisplay(this.value);
},
methods: {
toDisplay: function (value) {
if (value === null || value === undefined || value === '') return '';
const displayValue = this.format && this.format.percent ? shiftDecimal(value, 2) : String(value);
return normalizeInput(displayValue, this.format, true);
},
toModel: function (value) {
if (!value || value === '-') return '';
return this.format && this.format.percent ? shiftDecimal(value, -2) : value;
},
updateValue: function (value, shouldRound, shouldCommit) {
this.text = normalizeInput(value, this.format, shouldRound);
if (this.text.endsWith('.') && !shouldRound) return;
if (shouldCommit) this.$emit('input', this.toModel(this.text));
},
onInput: function (event) {
this.updateValue(event.target.value, false, false);
// When the normalized value is unchanged, Vue skips the DOM patch.
// Write it directly so excess digits do not remain in the native input.
event.target.value = this.text;
},
onPaste: function (event) {
const clipboard = event.clipboardData || global.clipboardData;
if (!clipboard) return;
event.preventDefault();
this.updateValue(clipboard.getData('text'), true, false);
event.target.value = this.text;
},
onBlur: function () {
if (this.text.endsWith('.')) this.text = this.text.substring(0, this.text.length - 1);
this.updateValue(this.text, true, true);
event.target.value = this.text;
}
},
watch: {
value: function (value) {
const display = this.toDisplay(value);
if (display !== this.text) this.text = display;
},
format: {
deep: true,
handler: function () {
this.text = this.toDisplay(this.value);
}
}
},
template: '<input type="text" :disabled="disabled" :value="text" @input="onInput" @paste="onPaste" @blur="onBlur">'
};
}
return Object.freeze({
getRule: getRule,
getInputFormat: function (instrumentType, field, options) {
const rule = getRule(instrumentType, field);
return rule ? Object.assign({}, options, rule) : Object.assign({}, options);
},
format: format,
roundForSubmit: function (value, instrumentType, field, storagePrecisionOffset) {
const rule = getRule(instrumentType, field);
if (value === null || value === undefined || value === '' || !rule) return value;
const offset = Number.isInteger(storagePrecisionOffset) ? storagePrecisionOffset : 0;
return roundDecimal(value, rule.precision + offset);
},
shiftDecimal: shiftDecimal,
createVueInputComponent: createVueInputComponent
});
}(window));
@@ -16,9 +16,6 @@ const inputFormatPosiFeePercent = Object.freeze({ precision: 4, negative: true,
const inputFormatPosiFeeUnit = Object.freeze({ precision: 6, negative: true, append: '' });
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: false });
const inputFormatSwapBondDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: true });
const inputFormatSwapBondNetPriceAndYtm = Object.freeze({ precision: 9, negative: true, append: '', percent: true });
const swapBondStoragePricePrecision = inputFormatSwapBondDeliveryPrice.precision + 2;
const consPosiFeeType = Object.freeze({ Percent: 0, Unit: 1 });
const swapPosiFeeCalc = Object.freeze({
normalizeFeeType(feeType) {
@@ -251,19 +248,26 @@ const vue = new Vue({
});
},
methods: {
roundStorageDeliveryPrice(item, price) {
const precision = tradeHelper.IsBond(item && item.UnderlyingInstrumentType)
? swapBondStoragePricePrecision
: inputFormatSwapDeliveryPrice.precision;
return _.round(Number(price), precision);
getPosiPriceInputFormat(item, field) {
return swapPricePrecision.getInputFormat(
item && item.UnderlyingInstrumentType,
field,
Object.assign({}, inputFormatSwapDeliveryPrice, {
percent: this.trade.StructureType !== '普通收益互换'
}));
},
roundStorageBondNetPriceAndYtm(value) {
return value == null ? value : _.round(Number(value), swapBondStoragePricePrecision);
roundStoragePrice(item, price, field) {
const storagePrecisionOffset = tradeHelper.IsBond(item && item.UnderlyingInstrumentType) ? 2 : 0;
return swapPricePrecision.roundForSubmit(
price,
item && item.UnderlyingInstrumentType,
field,
storagePrecisionOffset);
},
getPosiPriceFormatKey(item, field) {
const index = item && item.index != null ? item.index : '';
const isBond = tradeHelper.IsBond(item && item.UnderlyingInstrumentType);
return `${index}-${field}-${isBond ? 'bond' : 'other'}`;
const instrumentType = item && item.UnderlyingInstrumentType ? item.UnderlyingInstrumentType : 'unknown';
return `${index}-${field}-${instrumentType}`;
},
getCurrentPosiFeeType() {
return this.posiFeeModePercent ? consPosiFeeType.Percent : consPosiFeeType.Unit;
@@ -404,7 +408,7 @@ const vue = new Vue({
return;
}
item._lastBondErr = null; // 成功则清标记,便于下次真出不同错误时仍能提示
// 以既有三字段为代理,调用纯函数(已手动设过的字段不被覆盖),再写回
// 以既有三字段为代理,调用纯函数;手工输入字段保留原始十进制字符串,避免经 Number 参与计算后丢失末位
// 关键:proxy 内必须统一为【展示态】(per-100-face),因为 applyBondCalcResult 写入的是计算器返回的展示态。
// 模型字段是【存储态小数】(percent:true 下 1.00 对应界面 100),所以初始化时要 bondPriceToCalc(×100)
// 若直接用存储态初始化,则用户手填字段被 applyBondCalcResult 跳过后,proxy 中仍残留存储态,
@@ -418,9 +422,9 @@ const vue = new Vue({
SwapCalc.applyBondCalcResult(proxy, resp.obj, manual);
// 回写前 bondCalcPriceToStorage(÷100)(展示态→存储态小数):proxy 里均为展示态;
// 模型字段存存储态(0.995),须 ÷100 落回模型,否则配合 percent:true 显示会 ×100 成离谱值。
item.PosiNetNoFeePrice = SwapCalc.bondCalcPriceToStorage(proxy.cleanPrice);
item.PosiGrossPrice = SwapCalc.bondCalcPriceToStorage(proxy.dirtyPrice);
item.InitYtm = SwapCalc.bondCalcPriceToStorage(proxy.ytm);
if (!manual.CP) item.PosiNetNoFeePrice = self.roundStoragePrice(item, SwapCalc.bondCalcPriceToStorage(proxy.cleanPrice), 'netPrice');
if (!manual.DP) item.PosiGrossPrice = self.roundStoragePrice(item, SwapCalc.bondCalcPriceToStorage(proxy.dirtyPrice), 'grossPrice');
if (!manual.YD) item.InitYtm = self.roundStoragePrice(item, SwapCalc.bondCalcPriceToStorage(proxy.ytm), 'yield');
// 名义本金依赖全价(PosiGrossPrice):以净价/收益率为源反算出的全价被回写后,
// 直接赋值不会触发组件 input 事件,需在此显式重算,保持名义本金与全价一致。
if (self.calcNotional) self.calcNotional();
@@ -453,7 +457,6 @@ const vue = new Vue({
//计算数量
// if (this.paySwapList.length > 0) {
// var item = this.paySwapList[0];
// var deliveryPrice = this.roundStorageDeliveryPrice(item, item.PosiGrossPrice);
// var notional = deliveryPrice * item.ContractSize;
// item.PosiQuantity = notional == 0 ? 0 : _.round(this.trade.StockEqvNotional / notional, page.otcFormatConfig.StockEqvNotional.precision);
// this.calcNotional();
@@ -535,7 +538,7 @@ const vue = new Vue({
}
var national = payItem.PosiQuantity * payItem.ContractSize;
// 守卫: 名义本金必须 round 到 2 位 → 对应历史 bug f873239a(缺 _.round); 外置到 swapCalc.calcStockEqvNotional
var deliveryPrice = this.roundStorageDeliveryPrice(payItem, payItem.PosiGrossPrice);
var deliveryPrice = this.roundStoragePrice(payItem, payItem.PosiGrossPrice, 'grossPrice');
var stockEqvNotional = SwapCalc.calcStockEqvNotional(deliveryPrice, national);//名义本金=期初价格*数量*乘数
this.trade.StockEqvNotional = otcformat.trading.stockEqvNotional(stockEqvNotional);
payItem.PosiNotionalValue = this.trade.StockEqvNotional;
@@ -682,9 +685,9 @@ const vue = new Vue({
errorcount++;
return false;
}
x.PosiGrossPrice = thisObj.roundStorageDeliveryPrice(x, x.PosiGrossPrice);
x.PosiNetNoFeePrice = thisObj.roundStorageBondNetPriceAndYtm(x.PosiNetNoFeePrice);
x.InitYtm = x.InitYtm == null ? null : thisObj.roundStorageBondNetPriceAndYtm(x.InitYtm);
x.PosiGrossPrice = thisObj.roundStoragePrice(x, x.PosiGrossPrice, 'grossPrice');
x.PosiNetNoFeePrice = thisObj.roundStoragePrice(x, x.PosiNetNoFeePrice, 'netPrice');
x.InitYtm = x.InitYtm == null ? null : thisObj.roundStoragePrice(x, x.InitYtm, 'yield');
x.PosiFeeType = thisObj.normalizePosiFeeType(x.PosiFeeType);
thisObj.trade.swap_positions.push(x);
});
@@ -820,8 +823,8 @@ const vue = new Vue({
// 非 EodPrice 分支 ×bondPriceMultiple=0.01)。故此处仅做精度格式化,**不可**再 bondCalcPriceToStorage(÷100)
// 否则默认价 1.0 被除成 0.01,界面 percent:true 再 ×100 显示为 1"被自动除以100"bug)。
// 计算器(/Bond/CalcBond)返回的才是展示态,其 ÷100 落库逻辑在 calcBondForItem 内处理。
item.PosiNetNoFeePrice = thisObj.roundStorageBondNetPriceAndYtm(resp.obj.netPrice);
item.PosiGrossPrice = thisObj.roundStorageDeliveryPrice(item, resp.obj.price);
item.PosiNetNoFeePrice = thisObj.roundStoragePrice(item, resp.obj.netPrice, 'netPrice');
item.PosiGrossPrice = thisObj.roundStoragePrice(item, resp.obj.price, 'grossPrice');
thisObj.calcNotional();
});
},
@@ -1739,6 +1742,7 @@ const vue = new Vue({
components: {
'vue-datepicker': FastVue.vueDatePicker(),
'vue-number-input': FastVue.vueNumberInput(),
'vue-swap-price-input': swapPricePrecision.createVueInputComponent(),
'vue-underlying-nonbond': vueUnderlyingNonBond(),
'vue-underlying-bond': vueUnderlyingBond(),
'vue-underlying-rate': vueUnderlyingRate()
@@ -1,5 +1,14 @@
const inputFormatDouble2 = Object.freeze({ precision: 2, append: '' });
function formatSwapPriceElements() {
$('.js-swap-price').each(function () {
this.textContent = swapPricePrecision.format(
this.dataset.value,
this.dataset.instrumentType,
this.dataset.field);
});
}
function deletetrade(id) { //无效化
main.confirm(page.ConfirmInfo, function () {
$.ajax({
@@ -541,6 +550,7 @@ function chk_onclick(obj) {
}
$(function () {
formatSwapPriceElements();
refreshEntryExit();
});
@@ -54,12 +54,18 @@ const vue = new Vue({
this.setUnwindDate();
},
methods: {
getPriceScale() {
return this.multiplier == 100 ? 0.01 : 1;
getDeliveryPriceInputFormat() {
return swapPricePrecision.getInputFormat(
this.floatPosition && this.floatPosition.UnderlyingInstrumentType,
'grossPrice',
inputFormatSwapDeliveryPrice);
},
getStorageDeliveryPrice() {
const precision = inputFormatSwapDeliveryPrice.precision + (this.multiplier === 100 ? 2 : 0);
return SwapCalc.roundHalfAwayFromZero(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), precision);
return swapPricePrecision.roundForSubmit(
swapPricePrecision.shiftDecimal(this.floatPosition.TradingAmountAvg, this.multiplier === 100 ? -2 : 0),
this.floatPosition && this.floatPosition.UnderlyingInstrumentType,
'grossPrice',
this.multiplier === 100 ? 2 : 0);
},
initDeal() {
var positions = model.FlowEvents.filter((item) => {
@@ -81,15 +87,15 @@ const vue = new Vue({
? this.deal.PosiNotionalValue / this.deal.NotionalValue : 1;
// 转换期末标的价格为百分比形式
if (this.floatPosition.TradingAmountAvg) {
this.floatPosition.TradingAmountAvg = this.floatPosition.TradingAmountAvg * this.multiplier;
this.floatPosition.TradingAmountAvg = swapPricePrecision.shiftDecimal(this.floatPosition.TradingAmountAvg, this.multiplier === 100 ? 2 : 0);
}
},
IsBond(instType) {
return tradeHelper.IsBond(instType);
},
priceFormat(price) {
price = price * this.multiplier;
var pricef = otcformat.trading.umprice(price);
price = swapPricePrecision.shiftDecimal(price, this.multiplier === 100 ? 2 : 0);
var pricef = swapPricePrecision.format(price, this.floatPosition && this.floatPosition.UnderlyingInstrumentType, 'grossPrice');
return pricef;
},
dataFormat() {
@@ -105,7 +111,10 @@ const vue = new Vue({
this.deal.SwapCloseAmount = otcformat.trading.StockEqvNotional(this.deal.SwapCloseAmount);
//this.floatPosition.PosiNetPrice = otcformat.trading.tradeSinglePrice(this.floatPosition.PosiNetPrice);
//this.floatPosition.PosiGrossPrice = otcformat.trading.tradeSinglePrice(this.floatPosition.PosiGrossPrice);
this.floatPosition.TradingAmountAvg = _.round(Number(this.floatPosition.TradingAmountAvg), 9);
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);
@@ -243,8 +252,10 @@ const vue = new Vue({
main.post("/underlying_manager/GetUnderlyingPriceByCode",
{ code: thisObj.floatPosition.UnderlyingCode, valuedate: thisObj.deal.ValueDate })
.done(function (res) {
res.obj = res.obj * thisObj.multiplier;
thisObj.floatPosition.TradingAmountAvg = _.round(Number(res.obj), 9);
thisObj.floatPosition.TradingAmountAvg = swapPricePrecision.roundForSubmit(
swapPricePrecision.shiftDecimal(res.obj, thisObj.multiplier === 100 ? 2 : 0),
thisObj.floatPosition.UnderlyingInstrumentType,
'grossPrice');
thisObj.calcFloatClosePnl();
});
},
@@ -492,5 +503,6 @@ const vue = new Vue({
components: {
'vue-datepicker': FastVue.vueDatePicker(),
'vue-number-input': FastVue.vueNumberInput(),
'vue-swap-price-input': swapPricePrecision.createVueInputComponent(),
}
});
+1 -1
View File
@@ -217,4 +217,4 @@ var main = main || {};
global.otcformat = _format;
}(window));
}(window));
@@ -590,4 +590,4 @@
};
};
}(window.FastVue));
}(window.FastVue));