//otcformat禁止千分位分组 window.otcformat.options.disableGrouping = true; const consClients = ylotc.clients; const consTraders = ylotc.traders; 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: otcformat.trading.StockEqvNotional.precision, append: '' }); const inputFormatTradeAmount = Object.freeze({ precision: otcformat.trading.notional.precision, append: '' }); const inputFormatSwapRate = Object.freeze({ precision: otcformat.trading.premiumRateP.precision, negative: true, append: '%' }); const inputFormatTradePrice = Object.freeze({ precision: otcformat.trading.tradePrice.precision, negative: true, append: '' }); const inputFormatTradeSinglePrice = Object.freeze({ precision: otcformat.trading.tradeSinglePrice.precision, negative: true, append: '', percent: false }); const inputFormatPosiFeePercent = Object.freeze({ precision: 4, negative: true, append: '%' }); const inputFormatPosiFeeUnit = Object.freeze({ precision: 2, negative: true, append: '' }); const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' }); const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: false }); const inputFormatSwapBondDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: true }); const inputFormatSwapBondNetPriceAndYtm = Object.freeze({ precision: 9, negative: true, append: '', percent: true }); const swapBondStoragePricePrecision = inputFormatSwapBondDeliveryPrice.precision + 2; const consPosiFeeType = Object.freeze({ Percent: 0, Unit: 1 }); const swapPosiFeeCalc = Object.freeze({ normalizeFeeType(feeType) { return Number(feeType) === consPosiFeeType.Unit ? consPosiFeeType.Unit : consPosiFeeType.Percent; }, calcPending(feeType, feeUnit, stockEqvNotional, quantity) { const normalizedFeeType = this.normalizeFeeType(feeType); const normalizedFeeUnit = Number(feeUnit) || 0; const normalizedNotional = Number(stockEqvNotional) || 0; const normalizedQuantity = Number(quantity) || 0; const tradingFeePending = normalizedFeeType === consPosiFeeType.Percent ? normalizedFeeUnit / 100 * normalizedNotional : normalizedFeeUnit * normalizedQuantity; return otcformat.trading.tradeSinglePrice(tradingFeePending); }, calcFeeUnit(feeType, tradingFeePending, stockEqvNotional, quantity) { const normalizedFeeType = this.normalizeFeeType(feeType); const normalizedTradingFeePending = Number(tradingFeePending) || 0; const normalizedNotional = Number(stockEqvNotional) || 0; const normalizedQuantity = Number(quantity) || 0; if (normalizedFeeType === consPosiFeeType.Percent) { return normalizedNotional === 0 ? 0 : _.round(normalizedTradingFeePending / normalizedNotional * 100, inputFormatPosiFeePercent.precision); } return normalizedQuantity === 0 ? 0 : _.round(normalizedTradingFeePending / normalizedQuantity, inputFormatPosiFeeUnit.precision); } }); const consUnderlyingFlagBase = (function () { let unSelFlag = tradeHelper.UnderlyingSelectFlag; return unSelFlag.UseForTrading | unSelFlag.IncludeMatured | unSelFlag.UsePinYinFilter | unSelFlag.IncludeBasket | unSelFlag.IncludeSynthetic | unSelFlag.CheckLaunch; }()); const consUnderlyingFlagNonBond = (function () { let unSelFlag = tradeHelper.UnderlyingSelectFlag; //临时注释,保证业务不受影响 //return consUnderlyingFlagBase | unSelFlag.Stock | unSelFlag.StockIndex | unSelFlag.StockIF | unSelFlag.Fund | unSelFlag.CommodityFutures | unSelFlag.CommoditySpot; return consUnderlyingFlagBase; }()); const consUnderlyingFlagBond = (function () { let unSelFlag = tradeHelper.UnderlyingSelectFlag; const consUnderlyingFlagNonBond = (function () { let unSelFlag = tradeHelper.UnderlyingSelectFlag; //临时注释,保证业务不受影响 //return consUnderlyingFlagBase | unSelFlag.Stock | unSelFlag.StockIndex | unSelFlag.StockIF | unSelFlag.Fund | unSelFlag.CommodityFutures | unSelFlag.CommoditySpot; return consUnderlyingFlagBase; }()); }()); const inputFormatDouble2 = Object.freeze({ precision: 2, append: '' }); var autoMarginTemplateName; //定价格式化(来自配置) const InputFormatPercent = Object.freeze({ precision: 2, append: '%' }); const consMetaDic = { '组合标的': JSON.parse(page.Trade.MetaDic['组合标的'] || '{}'), '组合标的2': JSON.parse(page.Trade.MetaDic['组合标的2'] || '{}') }; //定价格式化(来自配置) const pricingFormat = otcformat.trading; //数字格式化 const consNumberFormat = Object.freeze(new function () { this.umpriceP = pricingFormat.umpriceP; this.umprice = pricingFormat.umprice; this.premiumRateP = pricingFormat.premiumRateP; return this; }()); const createVueUnderlying = function (selectFlag) { return { props: ['value', 'index'], data() { return { autoUnderlying: null }; }, mounted() { var selFlag = tradeHelper.UnderlyingSelectFlag; this.autoUnderlying = tradeHelper.UnderlyingAutoComplete(this.$el, { SelectFlag: selFlag.UsePinYinFilter | selFlag.UseForTrading | selFlag.IncludeSynthetic | selFlag.IncludeMatured | selFlag.IncludeBasket | selFlag.CheckLaunch, BlackLimit: 2, }); this.autoUnderlying.onSelect(this.onchange); this.value && this.autoUnderlying.selectByCode(this.value); }, methods: { onchange(data) { this.$emit('change', data); this.$emit('input', data.Code); } }, destroyed() { this.autoUnderlying && this.autoUnderlying.dispose(); }, template: '' }; }; const vueUnderlyingNonBond = function () { return createVueUnderlying(consUnderlyingFlagNonBond); }; const vueUnderlyingBond = function () { return createVueUnderlying(consUnderlyingFlagBond); }; //标的选择组件 银行间回购定盘&其他利率 const vueUnderlyingRate = function () { return { props: ['value', 'index'], data() { return { autoUnderlying: null }; }, mounted() { var selFlag = tradeHelper.UnderlyingSelectFlag; var instrumentTypes = ["FixingRepoRate", "OtherRate"]; this.autoUnderlying = tradeHelper.UnderlyingAutoComplete(this.$el, { SelectFlag: selFlag.UsePinYinFilter | selFlag.UseForTrading | selFlag.IncludeSynthetic | selFlag.IncludeMatured | selFlag.IncludeBasket | selFlag.CheckLaunch, BlackLimit: 2, InstrumentTypes: instrumentTypes, UseAll: true, UseSignle: true }); this.autoUnderlying.onSelect(this.onchange); this.value && this.autoUnderlying.selectByCode(this.value); }, methods: { onchange(data) { data.index = this.index; if (this.value !== data.Code) { this.$emit('change', data); this.$emit('input', data.Code); } } }, destroyed() { this.autoUnderlying && this.autoUnderlying.dispose(); }, template: '' }; }; //ESC $(document).keyup(function (event) { if (event.keyCode === 27) { window.parent.layer.closeAll(); } }); const vue = new Vue({ el: '#tradeEdit', data: { page: page, trade: _.cloneDeep(page.Trade), viewState: { variety: tradeHelper.getEmptyVariety(), underlying: tradeHelper.getEmptyUnderlying(), synthetic: null, Extend: { TradingPlace: "柜台市场", ClearingAgency: "交易员方" } }, currencys: page.currencys, getNotionalSingleFee: 0, isSingleFee: page.Trade.trade_extend.ExtendObj.OpenFeeType == 0, posiFeeModePercent: true, observation: {//互换观察日 ObservationInterval: "", IntervalList: [], ObservationNum: 1, ObservationUnit: 'D', ObservationHolidayType: 'Following', ObservationAlignEnd: true, ObservationCalendar: 'Chn', ObservationSettlementRules: 0, DefaultTitle1Value: 0.1, ObservationDataList: [], CheckedAll: false, index: 0, IsDeductPrincipal: true, ObservationStart: page.Trade.TradeDate }, observationHolidayTypes: [ { text: "向后调整", value: "Following" }, { text: "向前调整", value: "Previous" }, { text: "不调整", value: "None" } ], alignEndTypes: [ { text: "向到期日对齐", value: true }, { text: "向开始日对齐", value: false } ], marginSwapList: [], getSwapList: [],//利息端 paySwapList: [],//浮动端, observationType: 0, client: null,//客户信息 StockEqvNotional: 0, underlying: { UnderlyingCode: '', UnderlyingName: '', UnderlyingIssuer: '', IssueSize: '', MaturityDate: '', Price: '', UnderlyingInstrumentType: '', QuoteUnitString: '' } }, watch: { }, created: function () { if (this.trade.MetaDic['组合标的']) { this.viewState.synthetic = JSON.parse(this.trade.MetaDic['组合标的']); } if (this.trade.MetaDic["交易场所"]) { this.viewState.Extend.TradingPlace = this.trade.MetaDic["交易场所"]; } if (this.trade.MetaDic["清算机构"]) { this.viewState.Extend.ClearingAgency = this.trade.MetaDic["清算机构"]; } }, mounted() { __init(this); $("#MainProtocolCode").val(this.trade.MetaDic["主协议编号"]); $("#SupProtocolCode").val(this.trade.MetaDic["补充协议编号"]); this.initSwapRateList(); // 初始化到期日期选择器下限 this.$nextTick(() => { if (this.$refs.exerciseDatePicker) { var minDate = this.trade.StartDate > this.trade.TradeDate ? this.trade.StartDate : this.trade.TradeDate; this.$refs.exerciseDatePicker.refresh(minDate, null); } }); }, methods: { roundStorageDeliveryPrice(item, price) { const precision = tradeHelper.IsBond(item && item.UnderlyingInstrumentType) ? swapBondStoragePricePrecision : inputFormatSwapDeliveryPrice.precision; return _.round(Number(price), precision); }, roundStorageBondNetPriceAndYtm(value) { return value == null ? value : _.round(Number(value), swapBondStoragePricePrecision); }, getPosiPriceFormatKey(item, field) { const index = item && item.index != null ? item.index : ''; const isBond = tradeHelper.IsBond(item && item.UnderlyingInstrumentType); return `${index}-${field}-${isBond ? 'bond' : 'other'}`; }, getCurrentPosiFeeType() { return this.posiFeeModePercent ? consPosiFeeType.Percent : consPosiFeeType.Unit; }, normalizePosiFeeType(feeType) { return swapPosiFeeCalc.normalizeFeeType(feeType); }, syncPosiFeeModeByItem(item) { this.posiFeeModePercent = this.normalizePosiFeeType(item && item.PosiFeeType) !== consPosiFeeType.Unit; }, syncPayItemFeeType(item) { item.PosiFeeType = this.getCurrentPosiFeeType(); }, refreshTradingFeePendingByUnit(item) { this.syncPayItemFeeType(item); item.PosiTradingFeePending = swapPosiFeeCalc.calcPending( item.PosiFeeType, item.PosiTradingFeeUnit, this.trade.StockEqvNotional, item.PosiQuantity ); }, refreshTradingFeeUnitByPending(item) { this.syncPayItemFeeType(item); item.PosiTradingFeeUnit = swapPosiFeeCalc.calcFeeUnit( item.PosiFeeType, item.PosiTradingFeePending, this.trade.StockEqvNotional, item.PosiQuantity ); }, refreshPayTradingFeesByUnit() { this.paySwapList.forEach(item => { this.refreshTradingFeePendingByUnit(item); }); }, changeStructureType() { this.trade.StockEqvNotional = 0; let direction = this.trade.trade_extend.ExtendObj.Direction; if (direction !== 1 && direction !== 2) { direction = 2; this.trade.trade_extend.ExtendObj.Direction = direction; } this.paySwapList = []; this.addSwapFloat(direction); //this.trade.trade_extend.ExtendObj.FlowBookMode = 0; }, //变更初始预付金收取方向 changeInitialMargin() { if (this.trade.trade_Initial_Margin.Direction == 1) { this.trade.trade_Initial_Margin.Direction = 2; } else { this.trade.trade_Initial_Margin.Direction = 1; } }, //变更交易场所 changeTradingPlace() { this.trade.MetaDic["交易场所"] = this.viewState.Extend.TradingPlace; }, //变更清算机构 changeClearingAgency() { this.trade.MetaDic["清算机构"] = this.viewState.Extend.ClearingAgency; }, //全价(DP)列输入:先按标的单价重算名义本金(非债券标的也需要),再对债券触发三字段反算。 // 组件 vue-number-input 只 $emit('input')(失焦/回车时由底层 numberInput.onchange 触发), // 故此列绑 v-on:input,一个入口同时覆盖普通标的与债券两种情形。 onDpPriceInput(item) { this.changeSpotPrice(item); // 名义本金随全价变化(非债券标的的既有逻辑) this.onBondPriceInput(item, 'DP'); // 债券:以全价为源反算净价/收益率(非债券内部会早返回) }, //债券收益互换:编辑某一浮动腿的净价/全价/收益率之一 → 该字段标记为"已手动设过"(锁定), // 并以它为源调计算器反算"尚未手动设过"的另两个字段(已手动设过的一律不回写) onBondPriceInput(item, type) { // type: 'CP'净价 / 'DP'全价 / 'YD'收益率 if (!item.isBond) return; var m = item.bondManual || { CP: false, DP: false, YD: false }; m[type] = true; this.$set(item, 'bondManual', m); // 锁定用户手填字段 this.$set(item, 'bondDriverType', type); // 以该字段为本次计算源 this.calcBondForItem(item); }, //放弃手动覆盖、以当前某字段为源重新自动反算(清空所有手动锁定标记) resetBondCalc(item) { if (!item.isBond) return; this.$set(item, 'bondManual', { CP: false, DP: false, YD: false }); // 优先沿用用户最后一次编辑锁定的源(bondDriverType),避免"净价残留脏值"时误以净价为源反算出连锁错误; // 无历史源时再按 净价→全价→收益率 回退挑一个有值的字段。 var hasVal = function (v) { return v !== null && v !== '' && !isNaN(v); }; var driver = item.bondDriverType || (hasVal(item.PosiNetNoFeePrice) ? 'CP' : hasVal(item.PosiGrossPrice) ? 'DP' : hasVal(item.InitYtm) ? 'YD' : null); this.$set(item, 'bondDriverType', driver); this.calcBondForItem(item); }, //以该浮动腿的 bondDriverType 为源调 /Bond/CalcBond;回写时跳过"已手动设过的字段"(bondManual), //仅反向填充用户尚未手动设过的字段——故用户可逐个手填全部三个而互算不会冲掉任一个 //错误处理(对齐 bond-oms-ui / zszq-bond-oms): // - 业务/参数错误(success=false 或 errCode!=0,如债券不存在/信息不全/参数非法) // 以非阻塞 toast(main.message) 反馈真实原因,绝不回写、不弹模态框、不阻止手工输入; // - 网络/服务异常:框架 main.post 已弹"请求失败",此处不再二次提示。 calcBondForItem(item) { if (!item.isBond || !item.bondDriverType) return; if (!item.UnderlyingCode) { main.message("请先选择债券标的"); return; } var price = item.bondDriverType === 'CP' ? item.PosiNetNoFeePrice : item.bondDriverType === 'DP' ? item.PosiGrossPrice : item.InitYtm; if (price === null || price === '' || isNaN(price)) return; // 编辑中(空/非数)静默,不提示 var priceType = item.bondDriverType === 'CP' ? 'CP' : item.bondDriverType === 'DP' ? 'DP' : 'YD'; var self = this; // 估值日:债券互换的"期初"净价/全价/收益率应以【互换起始日 StartDate】估值。 // 该字段在页面上本就是可选择的日期控件(vue-datepicker),用户可改;现已默认即有。 // 不再"悄悄回退到交易日(TradeDate)"——真的为空时直接报错提示,由用户填写/手动填三数(需求2)。 var targetDate = (this.trade && this.trade.StartDate) ? this.trade.StartDate : null; var sdMsg = SwapCalc.getBondStartDateMissingMsg(targetDate); if (sdMsg) { // D3 同款去重:开始日缺失时用户在价格格逐键输入会连刷相同提示,故同一文案只弹一次 if (SwapCalc.shouldShowBondErr(item, sdMsg)) main.message(sdMsg); return; // 估值日缺失:不调用计算器、不覆盖任何手工输入 } main.post('/Bond/CalcBond', { underlyingCode: item.UnderlyingCode, // 模型 item.PosiGrossPrice/PosiNetNoFeePrice/InitYtm 存的是【存储态小数】(如 0.995), // 而 bond-calc 要的是【展示态/每百元百分比】(如 99.5)。这俩靠 BondPriceConverter.ToDisplay(×100)/ToStorage(÷100) 对齐。 // 此处传给计算器前必须 bondPriceToCalc(存储→展示);回写时再 bondCalcPriceToStorage(展示→存储), // 否则计算器拿到 0.995≈1 当 1% of par 直接发散成离谱值。 price: SwapCalc.bondPriceToCalc(price), priceType: priceType, targetDate: targetDate }, { alertFn: main.message }).done(function (resp) { if (!resp || !resp.obj) return; // 业务层错误(债券不存在/信息不全/参数非法):仅提示,绝不覆盖手工输入 var err = SwapCalc.getBondCalcErrorMessage(resp.obj); if (err) { // D3 修复:不可算债券上用户逐键手填时,每次按键都触发一次失败计算并弹 toast,会连刷相同提示; // 同一错误文案连续出现只弹一次(shouldShowBondErr 维护 item._lastBondErr),抑制噪声、不影响任何计算逻辑。 if (SwapCalc.shouldShowBondErr(item, err)) main.message(err); return; } item._lastBondErr = null; // 成功则清标记,便于下次真出不同错误时仍能提示 // 以既有三字段为代理,调用纯函数(已手动设过的字段不被覆盖),再写回。 // 关键:proxy 内必须统一为【展示态】(per-100-face),因为 applyBondCalcResult 写入的是计算器返回的展示态。 // 模型字段是【存储态小数】(percent:true 下 1.00 对应界面 100),所以初始化时要 bondPriceToCalc(×100); // 若直接用存储态初始化,则用户手填字段被 applyBondCalcResult 跳过后,proxy 中仍残留存储态, // 后续再 ÷100 就会导致该字段被二次缩小(如输入 100 变成 1)。 var proxy = { cleanPrice: SwapCalc.bondPriceToCalc(item.PosiNetNoFeePrice), dirtyPrice: SwapCalc.bondPriceToCalc(item.PosiGrossPrice), ytm: SwapCalc.bondPriceToCalc(item.InitYtm) }; var manual = item.bondManual || { CP: false, DP: false, YD: false }; 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); // 名义本金依赖全价(PosiGrossPrice):以净价/收益率为源反算出的全价被回写后, // 直接赋值不会触发组件 input 事件,需在此显式重算,保持名义本金与全价一致。 if (self.calcNotional) self.calcNotional(); if (self.$forceUpdate) self.$forceUpdate(); }); }, //变更收费基本单位 changeOpenFeeType() { var thisObj = this; thisObj.paySwapList.forEach(item => { thisObj.changeTradingFeeUnit(item); }) }, //变更数量 changeQuantity(item) { this.calcNotional(); }, //变更标的单价 changeSpotPrice(item) { this.calcNotional(); }, //变更标的合约乘数 changeContractSize(item) { this.calcNotional(); }, //变更名义本金(仅格式化,不反算数量) changeStockEqvNotional() { this.trade.StockEqvNotional = otcformat.trading.StockEqvNotional(this.trade.StockEqvNotional); this.refreshPayTradingFeesByUnit(); //计算数量 // 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(); // } }, //变更初始预付金 为¥ showAbsPrice() { this.trade.trade_Initial_Margin.MarginType = 1; this.trade.trade_Initial_Margin.MarginValue = this.trade.trade_Initial_Margin.MarginValue * this.trade.StockEqvNotional; }, //变更单位交易费用 为% showPercentPrice() { this.trade.trade_Initial_Margin.MarginType = 0; this.trade.trade_Initial_Margin.MarginValue = this.trade.StockEqvNotional == 0 ? 0 : this.trade.trade_Initial_Margin.MarginValue / this.trade.StockEqvNotional; }, //成交日期变更时自动修正联动日期 onTradeDateChange() { if (this.trade.StartDate < this.trade.TradeDate) { // 开始日期 = 成交日期 + 1天,跳过节假日 var d = new Date(this.trade.TradeDate); d.setDate(d.getDate() + 1); while (ylotc.isHoliday(d)) { d.setDate(d.getDate() + 1); } this.trade.StartDate = this.formatDate(d); } // 到期日期必须 ≥ 成交日期 且 ≥ 开始日期 if (this.trade.ExerciseDate < this.trade.TradeDate || this.trade.ExerciseDate < this.trade.StartDate) { // 到期日期 = 开始日期 + 14天,跳过节假日 var sd = new Date(this.trade.StartDate); sd.setDate(sd.getDate() + 14); while (ylotc.isHoliday(sd)) { sd.setDate(sd.getDate() + 1); } this.trade.ExerciseDate = this.formatDate(sd); } this.changeTradeDate(); }, //开始日期变更时检查到期日期 onStartDateChange() { if (this.trade.ExerciseDate < this.trade.StartDate) { var sd = new Date(this.trade.StartDate); sd.setDate(sd.getDate() + 14); while (ylotc.isHoliday(sd)) { sd.setDate(sd.getDate() + 1); } this.trade.ExerciseDate = this.formatDate(sd); } this.changeTradeDate(); }, //变更交易日期 changeTradeDate(force) { this.changeObservationStart(); if (this.Obervation) { this.Obervation.ObservationStart = this.trade.StartDate; } this.calcNotional(); this.refreshDatepicker(); this.changeMarginDate(); // 动态更新开始日期和到期日期的下限 this.$nextTick(() => { if (this.$refs.startDatePicker) { this.$refs.startDatePicker.refresh(this.trade.TradeDate, null); } if (this.$refs.exerciseDatePicker) { var minExerciseDate = this.trade.StartDate > this.trade.TradeDate ? this.trade.StartDate : this.trade.TradeDate; this.$refs.exerciseDatePicker.refresh(minExerciseDate, null); } }); //this.initMarginRate(); }, calcNotional(calcPrice) { if (this.paySwapList.length > 0) { var payItem = this.paySwapList[0]; if (calcPrice) { this.getSpotPrice(payItem.UnderlyingCode, this.trade.StartDate, payItem); } var national = payItem.PosiQuantity * payItem.ContractSize; // 守卫: 名义本金必须 round 到 2 位 → 对应历史 bug f873239a(缺 _.round); 外置到 swapCalc.calcStockEqvNotional var deliveryPrice = this.roundStorageDeliveryPrice(payItem, payItem.PosiGrossPrice); var stockEqvNotional = SwapCalc.calcStockEqvNotional(deliveryPrice, national);//名义本金=期初价格*数量*乘数 this.trade.StockEqvNotional = otcformat.trading.stockEqvNotional(stockEqvNotional); payItem.PosiNotionalValue = this.trade.StockEqvNotional; this.refreshPayTradingFeesByUnit(); } }, //变更到期日 changeExerciseDate() { this.refreshDatepicker(); this.changeMarginDate(); }, //变更预付金生效日期 changeMarginDate() { var thisObj = this; thisObj.observation.ObservationStart = thisObj.trade.StartDate; var startDate = thisObj.trade.StartDate; var endDate = thisObj.trade.ExerciseDate; if (thisObj.marginSwapList) { thisObj.marginSwapList.forEach((item, index) => { if (item.HappenDate < startDate) { thisObj.marginSwapList[index].HappenDate = startDate; thisObj.changeStartMarinRateDate(thisObj.marginSwapList[index]); } else if (item.HappenDate > endDate) { thisObj.marginSwapList[index].HappenDate = endDate; thisObj.changeStartMarinRateDate(thisObj.marginSwapList[index]); } }); } }, // 变更预付金结算规则起息日期 changeStartMarinRateDate(item) { if (item.Obervation) { item.Obervation.ObservationStart = item.HappenDate; } }, //变更单位交易费用 changeTradingFeeUnit(item) { this.refreshTradingFeePendingByUnit(item); }, //变更交易费用 changeTradingFee(item) { this.refreshTradingFeeUnitByPending(item); }, showPayAbsPrice() { this.posiFeeModePercent = false; this.paySwapList.forEach(item => { item.PosiFeeType = consPosiFeeType.Unit; item.PosiTradingFeeUnit = 0; item.PosiTradingFeePending = 0; }); }, showPayPercentPrice() { this.posiFeeModePercent = true; this.paySwapList.forEach(item => { item.PosiFeeType = consPosiFeeType.Percent; item.PosiTradingFeeUnit = 0; item.PosiTradingFeePending = 0; }); }, savetrade() { if (!this.checkSubmitData()) { return false; } this.savetrade_inner(); }, savetrade_inner() { var thisObj = this; //this.paySwapList.forEach(x => { // x.SwapIntervals = this.trade.ExerciseDate + ";" + x.InterestRate; //}); this.trade.IsUsePremiumRate = true; this.trade.IsTradePricePayType = true; this.trade.UnderlyingInstrumentType = "Stock"; this.trade.swap_positions = []; var date = thisObj.trade.ExerciseDate; var errorcount = 0; if (this.trade.trade_extend.ExtendObj.InterestCalcMode == "00" || this.trade.trade_extend.ExtendObj.InterestCalcMode == "10") { date = moment(date).add(-1, 'days').format('YYYY-MM-DD'); } this.getSwapList.forEach((x,index) => { if ((x.UnderlyingCode == null || x.UnderlyingCode.length == 0) && x.SwapIntervalList.length == 0) { var interval = { Date: date, Rate: x.InterestRateDefault, Settlement: 0 } x.SwapIntervalList.push(interval); } if (x.interest_rest_days != null && x.interest_rest_days <= 0) { main.message("利息端第" + (index + 1) + "行重置频率必须大于0"); errorcount++; return false; } x.FloatRateUnderlyingCode = x.FloatRateUnderlyingCode == "--" ? null : x.FloatRateUnderlyingCode; x.InterestSwapInterval = JSON.stringify(x.SwapIntervalList); thisObj.trade.swap_positions.push(x); }); if (errorcount > 0) { return; } var margin = 0; this.marginSwapList.forEach((x, index) => { if ((x.UnderlyingCode == null || x.UnderlyingCode.length == 0) && x.SwapIntervalList.length == 0) { var interval = { Date: date, Rate: x.InterestRateDefault, Settlement: 0 } x.SwapIntervalList.push(interval); } if (!x.HappenDate) { main.message("预付金/预付金第" + (index + 1) + "行生效日期不能为空"); errorcount++; return false; } x.FloatRateUnderlyingCode = x.FloatRateUnderlyingCode == "--" ? null : x.FloatRateUnderlyingCode; margin = margin + x.InterestPrincipalFix * (x.InterestDirection == 1 ? 1 : -1); x.InterestSwapInterval = JSON.stringify(x.SwapIntervalList); thisObj.trade.swap_positions.push(x); }); if (errorcount > 0) { return; } thisObj.trade.trade_Initial_Margin.MarginType = 1; thisObj.trade.trade_Initial_Margin.MarginValue = margin; thisObj.trade.trade_Initial_Margin.Direction = margin > 0 ? 1 : 2; if (this.trade.StructureType != "多空组合") { this.paySwapList.forEach((x, index) => { if (!x.UnderlyingCode) { main.message("浮动端第" + (index + 1) + "行标的不能为空"); errorcount++; return false; } if (!x.PosiQuantity > 0) { main.message("浮动端第" + (index + 1) + "数量不能为0"); errorcount++; return false; } if (!x.PosiGrossPrice) { main.message("浮动端第" + (index + 1) + "期初价格不能为空"); 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.PosiFeeType = thisObj.normalizePosiFeeType(x.PosiFeeType); thisObj.trade.swap_positions.push(x); }); } else { this.trade.trade_extend.ExtendObj.Direction = this.paySwapList[0].PosiDirection; } if (errorcount > 0) { return; } this.trade.MetaDic = { '组合标的': consMetaDic["组合标的"] ? JSON.stringify(consMetaDic["组合标的"]) : '', '组合标的2': consMetaDic["组合标的2"] ? JSON.stringify(consMetaDic["组合标的2"]) : '', '交易场所': this.viewState.Extend.TradingPlace, '清算机构': this.viewState.Extend.ClearingAgency, "主协议编号": $("#MainProtocolCode").val(), "补充协议编号": $("#SupProtocolCode").val() }; this.trade.trade_extend.ExtendJson = JSON.stringify(this.trade.trade_extend.ExtendObj); main.post("/swaptrade2/tradeEditJson", this.trade).done(function (resp) { window.location.href = "/swaptrade2/tradeview?enid=" + resp.obj.EncryptId; if (window.parent && window.parent.reloadtrade) { window.parent.reloadtrade(); } }); }, changeMainProtocolCode() { this.getSupProtocolCode(); }, getMainProtocolCode() { var thisObj = this; $("#MainProtocolCode option").remove(); main.post("/Client/getMainProtocolCodes", { clientId: $("#ClientId").val() }, { async: false }).done(function (resp) { var obj = $("#MainProtocolCode"); if (thisObj.client.MainProtocolCode != null && thisObj.client.MainProtocolCode != '') { obj.append(""); } _.forEach(resp, (v) => { obj.append(""); }) }); this.getSupProtocolCode() }, getSupProtocolCode() { $("#SupProtocolCode option").remove(); if ($("#MainProtocolCode").val()) { main.post("/Client/getSideProtocols", { mainProtocol: $("#MainProtocolCode").val() }, { async: false }).done(function (resp) { var obj = $("#SupProtocolCode"); if (thisObj.client.SupProtocolCode != null && thisObj.client.SupProtocolCode != '') { obj.append(""); } _.forEach(resp, (v) => { obj.append(""); }) }); } }, changeInterestMode(item) { if (item.InterestMode == 7 || item.InterestMode == 8) { item.InterestType = 0; } }, changeInterestType(item) { if (!item.interest_rest_days) { item.interest_rest_days = 7; } }, setUnderlyingCode(data, item) { var underlyingCode = item.UnderlyingCode; if (underlyingCode != data.Code) { this.getSpotPrice(data.Code, this.trade.TradeDate, item); } else { item.PosiGrossPrice = item.PosiGrossPrice; item.PosiNetNoFeePrice = item.PosiNetNoFeePrice; } item.CountRatio = data.CountRatio; item.ContractSize = data.ContractSize; item.UnderlyingInstrumentType = data.InstrumentType; item.UnderlyingCode = data.Code; item.underlying = { ...this.underlying }; item.underlying.UnderlyingCode = data.Code; item.underlying.UnderlyingName = data.Name; item.underlying.UnderlyingIssuer = data.UnderlyingIssuer; item.underlying.IssueSize = data.IssueSize; item.underlying.MaturityDate = data.MaturityDate; item.underlying.Price = data.Price; item.underlying.UnderlyingInstrumentType = data.InstrumentType; item.underlying.QuoteUnitString = data.QuoteUnitString; this.trade.UnderlyingCode = underlyingCode; // 债券标的:标记该浮动腿可启用净价/全价/收益率互算 this.$set(item, 'isBond', tradeHelper.IsBond(data.InstrumentType)); // 切换标的时清空债券三字段互算的手动/源标志(D1 修复:避免旧债券手填状态污染新债券) SwapCalc.clearBondCalcFlags(item); //this.initMarginRate(); }, setFloatRateUnderlyingCode(data, item) { item.FloatRateUnderlyingCode = data.Code; if (data.Code.length > 0 && data.Code != '--') { item.IsAnnualized = true; item.interest_rest_days = 7; item.interest_rule = data.Code === 'FR007' ? -1 : 0; } else { item.interest_rule = null; } //this.getFloatRate(item); }, getFloatRate(item) { var thisObj = this; if (item.FloatRateUnderlyingCode == null || item.FloatRateUnderlyingCode == '' || item.FloatRateUnderlyingCode == '--' || thisObj.paySwapList.length == 0) { item.InterestRateDefault = 0; blurSwapRate(item); return; } var floatPositionType = thisObj.paySwapList[0].PositionType; main.post("/SwapFloatRate/MatchRate", { clientId: thisObj.trade.ClientId, underlyingCode: item.FloatRateUnderlyingCode, startDate: thisObj.trade.StartDate, endDate: thisObj.trade.ExerciseDate }) .done(function (resp) { if (resp.success) { if (resp.obj != null) { item.InterestRateDefault = floatPositionType == 1 ? resp.obj.LongPricePoint : resp.obj.ShortPricePoint; item.InterestRateDefault = item.InterestRateDefault * 0.0001; } else { item.InterestRateDefault = 0; } thisObj.blurSwapRate(item); } }); }, getSpotPrice(underlyingCode, StartDate, item) { var thisObj = this; main.post("/pricing/AjaxGetUnderlyingPrice", { underlyingCode: underlyingCode, tradeDate: StartDate }) .done(function (resp) { // 行情接口(AjaxGetUnderlyingPrice)对债券返回的 price/netPrice 本就是【存储态小数】(1.0 代表 100 元), // 与 PosiGrossPrice/PosiNetNoFeePrice 模型字段同量纲(见 PricingController: EodPrice 直接返回、 // 非 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); thisObj.calcNotional(); }); }, //年化利率变更同步修改 同行观察日利率 blurSwapRate(item) { //if (item.InterestRateDefault != 0) { // item.IsAnnualized = true; //} if (item.SwapIntervalList.length > 0) { var swapRate = item.InterestRateDefault; var same = true; var rate1 = item.SwapIntervalList[0].Rate; item.SwapIntervalList.forEach(function (item) { if (item.Rate != rate1) { same = false; } }); if (same) { var confirmMsg = "检测到互换利率发生变化,将自动覆盖观察列表"; main.confirm(confirmMsg, function () { item.SwapIntervalList.forEach(function (element, index, arr) { arr[index].Rate = swapRate; }); var observationDates = JSON.stringify(item.SwapIntervalList); item.InterestSwapInterval = observationDates; }); } else { main.message("检测到互换利率与互换利率列表不一致,请手动重新生成互换列表"); } } }, changeObservationStart() { var thisObj = this; thisObj.getSwapList.forEach((item, index) => { if (item.Obervation) { thisObj.getSwapList[index].Obervation.ObservationStart = thisObj.trade.TradeDate; } }); }, //设置观察日弹框 initObservationDates(item, type) { this.observationType = type; var thisObj = this; var observation = item.Obervation; thisObj.observation.index = item.index; thisObj.observation.IntervalList = item.SwapIntervalList; thisObj.observation.DefaultTitle1Value = item.InterestRateDefault; thisObj.observation.ObservationNum = observation ? observation.ObservationNum : 1; thisObj.observation.ObservationUnit = observation ? observation.ObservationUnit : 'D'; thisObj.observation.ObservationHolidayType = observation ? observation.ObservationHolidayType : 'Following'; thisObj.observation.ObservationAlignEnd = observation ? observation.ObservationAlignEnd : true; if (type == 1) { thisObj.observation.ObservationCalendar = observation ? observation.ObservationCalendar : 'Chn'; thisObj.observation.ObservationSettlementRules = observation ? observation.ObservationSettlementRules : 0; } thisObj.observation.IsDeductPrincipal = observation ? observation.IsDeductPrincipal : true; if (type == 1) { thisObj.observation.IsDeductPrincipal = false; } var startTime = thisObj.trade.TradeDate; var endTime = thisObj.trade.ExerciseDate; if (main.isEmpty(startTime)) { main.message("请输入开始日期"); return false; } if (main.isEmpty(endTime)) { main.message("请输入到期日期"); return false; } if (thisObj.observation.ObservationStart < startTime) { thisObj.observation.ObservationStart = startTime; } if (thisObj.observation.ObservationStart > endTime) { thisObj.observation.ObservationStart = endTime; } thisObj.observation.ObservationDataList = []; var floatRateCode = type == 1 ? (item.FloatRateUnderlyingCode || '--') : ''; thisObj.observation.IntervalList.forEach((value, num, arr) => { var val = value.Rate; var _date = ""; var m = new moment(value.Date); if (!isNaN(m.date())) { _date = m.format("YYYY-MM-DD"); } var itemChecked = thisObj.observation.IntervalList.length >= num ? (value.Settlement == 1 ? true : false) : false; var disabled = false; if (_date == thisObj.trade.ExerciseDate) { disabled = true; itemChecked = false; } var obdate = {}; if (type == 1) { var settlementDate = value.Date; if (value.SettlementDate!=null) { settlementDate = value.SettlementDate; } var m = new moment(settlementDate); obdate = { date: _date, SettlementDate: m.format("YYYY-MM-DD"), floatRateCode: floatRateCode, val: _.toString(val) ? parseFloat(consNumberFormat.umpriceP(val)) : "", itemChecked: itemChecked, disabled: disabled }; } else { obdate = { date: _date, val: _.toString(val) ? parseFloat(consNumberFormat.umpriceP(val)) : "", itemChecked: itemChecked, disabled: disabled }; } thisObj.observation.ObservationDataList.push(obdate); }); var area = type == 1 ? ['800px', '600px'] : ['750px', '600px']; layer.open({ type: 1, area: area, title: "设置互换日期", shadeClose: false, shade: 0.4, content: $("#observationInfosEdit") }); }, //计算结算日期 calcSettleDate(observationDate, settlementRules) { if (!observationDate) return ""; var m = new moment(observationDate); var settleDate = m.add(settlementRules, 'days').format("YYYY-MM-DD"); return settleDate; }, //生成观察日操作 GetObservationDates() { var thisObj = this; var observationNum = thisObj.observation.ObservationNum; var observationUnit = thisObj.observation.ObservationUnit; var observationHolidayType = thisObj.observation.ObservationHolidayType; var alignEnd = thisObj.observation.ObservationAlignEnd; var settlementRules = thisObj.observation.ObservationSettlementRules; var calendar = thisObj.observation.ObservationCalendar; var floatRateCode = this.observationType == 1 ? (thisObj.getSwapList[thisObj.observation.index]?.FloatRateUnderlyingCode || '--') : ''; thisObj.observation.ObservationDataList = []; if (this.observationType == 1) { var postData = { startDate: thisObj.observation.ObservationStart, endDate: thisObj.trade.ExerciseDate, termStr: observationNum + observationUnit, holidayAdjustment: observationHolidayType, alignEnd: alignEnd, calcMode: thisObj.trade.trade_swap.RateCalcMode, calendar: calendar, settlementRules: settlementRules }; main.post("/trade/GetSwapObservationDateList", postData).done( function (res) { $.each(res.obj, function (i) { var _date = ""; var m = new moment(this.ObservationDate); if (!isNaN(m.date())) { _date = m.format("YYYY-MM-DD"); } var _settleDate = ""; var sm = new moment(this.SettleDate); if (!isNaN(sm.date())) { _settleDate = sm.format("YYYY-MM-DD"); } var val = parseFloat(consNumberFormat.umpriceP(thisObj.observation.DefaultTitle1Value)); var itemChecked = true; var disabled = false; if (_date == thisObj.trade.ExerciseDate) { disabled = true; itemChecked = false; } var obdate = { date: _date, SettlementDate: _settleDate, floatRateCode: floatRateCode, val: val, itemChecked: itemChecked, disabled: disabled }; thisObj.observation.ObservationDataList.push(obdate); }); thisObj.initObservationCheckedAll(); }); } else { var postData = { startDate: thisObj.observation.ObservationStart, endDate: thisObj.trade.ExerciseDate, termStr: observationNum + observationUnit, holidayAdjustment: observationHolidayType, alignEnd: alignEnd, calcMode: thisObj.trade.trade_swap.RateCalcMode }; main.post("/trade/GetObservationDateList", postData).done( function (res) { $.each(res.obj, function (i) { var _date = ""; var m = new moment(this); if (!isNaN(m.date())) { _date = m.format("YYYY-MM-DD"); } var val = parseFloat(consNumberFormat.umpriceP(thisObj.observation.DefaultTitle1Value)); var itemChecked = true; var disabled = false; if (_date == thisObj.trade.ExerciseDate) { disabled = true; itemChecked = false; } var obdate = { date: _date, val: val, itemChecked: itemChecked, disabled: disabled }; thisObj.observation.ObservationDataList.push(obdate); }); thisObj.initObservationCheckedAll(); }); } }, //编辑观察日功能数据处理 SetObservationDates() { var observationStr = ""; if (this.observation.ObservationDataList != null) { this.observation.ObservationDataList.forEach(item => { observationStr = observationStr + item.date + ", " + item.SettlementDate + ", " + (item.val * 0.01).toFixed(6) + ", " + item.itemChecked + ";\n"; }); } this.observation.ObservationInterval = observationStr; $("#myModal").modal("show"); }, // 粘贴功能 pasteMe(e, rowData, index) { let copyData = ''; var thisObj = this; if (e.clipboardData || e.originalEvent) { var clipboardData = (e.clipboardData || e.originalEvent.clipboardData); copyData = clipboardData.getData('Text'); var arr = copyData.replace(/\t/g, ', ').replace(/\r/g, '').split('\n'); arr.forEach((v, i) => { if (v && !v.trim().endsWith(';')) { arr[i] = v + ";"; } }); thisObj.observation.ObservationInterval = arr.join('\r\n'); e.preventDefault(); } }, //编辑观察日模态框保存操作 saveObservationModal() { this.initObservationDataList(); $("#myModal").modal("hide"); }, //编辑观察日模态框保存 数据转换 initObservationDataList() { var thisObj = this; thisObj.observation.ObservationInterval = thisObj.observation.ObservationInterval.trim().replaceAll("\n", "").replaceAll(" ", ""); var observationDates = thisObj.observation.ObservationInterval; var items = observationDates.split(";").filter(o => o); thisObj.observation.ObservationDataList = []; var floatRateCode = thisObj.getSwapList[thisObj.observation.index]?.FloatRateUnderlyingCode || '--'; items.forEach(function (item) { if (item) { var values = item.split(","); var itemChecked = JSON.parse(values[3].trim()); var disabled = false; if (values[0] == thisObj.trade.ExerciseDate) { disabled = true; itemChecked = false; } var val = values[2].trim(); var obdate = { date: values[0], SettlementDate: values[1] || "", floatRateCode: floatRateCode, val: _.toString(val) ? parseFloat(consNumberFormat.umpriceP(val)) : "", itemChecked: itemChecked, disabled: disabled } thisObj.observation.ObservationDataList.push(obdate); } }); thisObj.initObservationCheckedAll(); }, //观察日列表初始化是否全选 initObservationCheckedAll() { var thisObj = this; var noCheckedItems = thisObj.observation.ObservationDataList.filter(item => { if ((item.itemChecked == false) && item.date != thisObj.trade.ExerciseDate) { return item; } }); this.observation.CheckedAll = noCheckedItems.length == 0 && thisObj.observation.ObservationDataList.length > 0; }, //删除单行观察日 deleteObItem(item) { var thisObj = this; var curindex = 0; for (var i = 0; i < thisObj.observation.ObservationDataList.length; i++) { if (thisObj.observation.ObservationDataList[i].date == item.date) { curindex = i; break; } } thisObj.observation.ObservationDataList.splice(curindex, 1); }, //添加观察日 addnewitem() { var thisObj = this; var obdate = {}; if (this.observationType == 1) { var floatRateCode = thisObj.getSwapList[thisObj.observation.index]?.FloatRateUnderlyingCode || '--'; obdate = { date: '', SettlementDate: '', floatRateCode: floatRateCode, val: 0, itemChecked: true, disabled: false }; } else { obdate = { date: '', val: 0, itemChecked: true, disabled: false }; } thisObj.observation.ObservationDataList.push(obdate); }, //编辑观察日模态框关闭 closeObservationModal() { $('#myModal').modal('hide'); }, //关闭互换layer层 closeParentWindow() { layer.closeAll(); }, //观察日列表确认 ObservationConfirm() { var thisObj = this; var hasTimeError = false; var hasNumberError = false; var observationArr = []; thisObj.observation.ObservationDataList.forEach(item => { var m = new moment(item.date); if (isNaN(m.date())) { hasTimeError = true; } var dateFormat = /^(-?\d+)(\.\d+)?$/; if (!dateFormat.test(item.val)) { hasNumberError = true; } var observation = { Date: item.date, Rate: item.val * 0.01, Settlement: item.itemChecked ? 1 : 0, SettlementDate: item.SettlementDate || null // 结算日期 } observationArr.push(observation); }); if (hasTimeError) { alert("日期格式有误,请输入正确格式"); return false; } if (hasNumberError) { alert("请输入正确的数字格式"); return false; } if (this.observationType == 1) { thisObj.getSwapList.forEach((val, num, arr) => { if (val.index == thisObj.observation.index) { arr[num].InterestSwapInterval = JSON.stringify(observationArr); thisObj.observation.ObservationInterval = arr[num].InterestSwapInterval; arr[num].SwapIntervalList = observationArr; arr[num].Obervation = JSON.parse(JSON.stringify(thisObj.observation)); } }); } else { thisObj.marginSwapList.forEach((val, num, arr) => { if (val.index == thisObj.observation.index) { arr[num].InterestSwapInterval = JSON.stringify(observationArr); thisObj.observation.ObservationInterval = arr[num].InterestSwapInterval; arr[num].SwapIntervalList = observationArr; arr[num].Obervation = JSON.parse(JSON.stringify(thisObj.observation)); } }); } this.closeParentWindow(); }, //互换提交数据有效性检查 checkSubmitData() { //验证输入的日期格式是否正确(包括交易日期、开始日、到期日) var ExerciseDate = this.trade.ExerciseDate; var regTime = /^[0-9]{4}-[0-1]?[0-9]{1}-[0-3]?[0-9]{1}$/; if (main.isEmpty(ExerciseDate)) { main.message("请输入到期日期"); return false; } if (!regTime.test(ExerciseDate)) { main.message("请输入正确的到期日期格式"); return false; } var TradeDate = this.trade.TradeDate; if (main.isEmpty(TradeDate)) { main.message("请输入成交日期"); return false; } if (!regTime.test(TradeDate)) { main.message("请输入正确的成交日期格式"); return false; } if (TradeDate > ExerciseDate) { main.message("到期日期不能早于成交日期"); return false; } var StartDate = this.trade.StartDate; if (main.isEmpty(StartDate)) { main.message("请输入开始日期"); return false; } if (!regTime.test(StartDate)) { main.message("请输入正确的开始日期格式"); return false; } if (StartDate < TradeDate) { main.message("开始日期不能早于成交日期"); return false; } if (StartDate > ExerciseDate) { main.message("到期日期不能早于开始日期"); return false; } if (!this.trade.AssetId) { main.message("请设置簿记账户"); return false; } if (!this.trade.ClientId) { main.message("请设置交易对手方"); return false; } if (this.trade.StructureType!="多空组合"&&(!this.trade.StockEqvNotional > 0 || this.trade.StockEqvNotional == null)) { main.message("请设置名义本金"); return false; } if (!this.trade.trade_extend.ExtendObj.AnnualDays > 0) { main.message("请设置年化天数"); return false; } if (this.trade.StructureType == "多空组合"&&!this.checkSwapRateList()) { return false; } if (!this.checkObservationDates()) { return false; } return true; }, //观察日校验 checkObservationDates() { var checkGet = true; var thisObj = this; if (thisObj.getSwapList != null) { thisObj.getSwapList.forEach((val, num, arr) => { if (checkGet && val.SwapIntervalList.length > 0) { checkGet = this.checkSwapTimeRates(val.SwapIntervalList, num + 1, '利息端'); } }); } if (!checkGet) { return false; } if (thisObj.marginSwapList != null) { thisObj.marginSwapList.forEach((val, num, arr) => { if (checkGet && val.SwapIntervalList.length > 0) { checkGet = this.checkSwapTimeRates(val.SwapIntervalList, num + 1, '预付金/预付金'); } }); } if (!checkGet) { return false; } return true; }, // 多空组合利息腿校验 checkSwapRateList() { var thisObj = this; var longInterestModelCount = 0; var shortInterestModelCount = 0; var check = true; if (thisObj.getSwapList != null) { thisObj.getSwapList.forEach((val, num, arr) => { if (val.InterestMode==7) { longInterestModelCount++; } if (val.InterestMode == 8) { shortInterestModelCount++; } }); } if (longInterestModelCount > 1) { check = false; main.message("计息基本类型为多头存续名义本金的利息腿只能有一条"); } if (shortInterestModelCount > 1) { check = false; main.message("计息基本类型为空头存续名义本金的利息腿只能有一条"); } return check; }, //观察日起始日期跟交易起始日期检查 checkSwapTimeRates(SwapIntervalList, num, title) { var StartDate = this.trade.StartDate; var ExerciseDate = this.trade.ExerciseDate; var check = true; SwapIntervalList.forEach(item => { var _date = ""; var m = new moment(item.Date); if (!isNaN(m.date())) { _date = m.format("YYYY-MM-DD"); } if (_date < StartDate) { main.message(title + "第" + num + "行互换观察日不可以早于交易开始日"); check = false; } else if (_date > ExerciseDate) { main.message(title + "第" + num + "行互换观察日不可以晚于交易到期日"); check = false; } }); return check; }, changeClient: function (client) { this.trade.ClientId = client.id; this.client = client; //if (this.client.SwapTradeType == 0) {//非DMA客户 // this.trade.trade_extend.ExtendObj.FlowBookMode = 3;//流水簿记模式为重置 //} else { // this.trade.trade_extend.ExtendObj.FlowBookMode = 2;//流水簿记模式为加权平均 暂定 //} //this.initMarginRate(); }, initMarginRate() { //初始预付金 初始话 _that = this; var swapType = this.trade.StructureType; var UnderlyingCode = ""; if (swapType != "多空组合") { swapType = "普通"; UnderlyingCode = this.paySwapList[0].UnderlyingCode; } if (page.isAdd && this.trade.ClientId != null && ((swapType == "普通" && UnderlyingCode != "") || this.trade.trade_swap.SwapType == "多空组合")) { main.post("/swapTrade/GetInitMarginRate", { ClientId: this.trade.ClientId, Type: swapType, UnderlyingCode: UnderlyingCode, tradeDate: this.trade.TradeDate }) .done(function (resp) { if (resp.obj > 0) { _that.trade.trade_Initial_Margin.MarginValue = resp.obj } }); } }, //交易审批 submit(status) { var pop = ''; if (status === 'pass') { pop = "确认通过审批?"; } if (status === 'reject') { pop = "确认拒绝?"; } var confirmFunc = function (additionalProcessing) { var pData = { tradeId: page.Trade.id, status: status, text: "" }; if (!main.isEmpty(additionalProcessing)) { pData.additionalProcessing = additionalProcessing; } main.post("/processtradelog/UpdateTradeProcessLog", pData).done( function (data) { if (data.obj && data.obj.proccessType == "AdditionalProcessing") { if (data.obj.type == "LackOfMoney") { var htmlContent = `
${data.obj.message}
`; var lackMoneyConfirmLayer = main.open2("提示", htmlContent, { area: ["430px", "175px"], btn: ['交易特批', '取消'], yes: function (index, layero) { var layerIndex = lackMoneyConfirmLayer; main.confirm("客户资金或授信不足,强制成交会导致本机构产生风险!要继续审批通过?", function () { layer.close(layerIndex); confirmFunc("LackOfMoney"); }); }, cancel: function (index, layero) { if (window.parent && window.parent.reloadtrade) { window.parent.reloadtrade(); } (parent || window).layer.closeAll(); } }); } return; } (parent || window).main.message(data.msg); if (page.generateAmendDoc) { if (data.obj && data.obj.generateChangeSuccess) { main.downloadFiles(data.obj.url); try { window.parent.reloadtrade(); } catch (e) { } } } if (page.needSendChangeEmail) { if (data.obj && data.obj.generateChangeSuccess) { main.confirm("确认发送变更确认书?", function () { main.post("/trade/SendChangeConfirmEmails", { tradeids: tradeId }).done( function (data) { if (data.success) { main.message("发送成功"); } else { main.message(data.msg); } try { window.parent.reloadtrade(); } catch (e) { } }); }); } else { try { window.parent.reloadtrade(); } catch (e) { } } } else { try { window.parent.reloadtrade(); } catch (e) { } } if (parent) { parent.layer.closeAll(); } }); } main.confirm(pop, confirmFunc); }, //初始化利息端列表 initSwapRateList() { var thisObj = this; thisObj.getSwapList = thisObj.trade.swap_positions.filter(x => { if ((x.UnderlyingCode == null || x.UnderlyingCode.length == 0) && x.IsInitial && (x.InterestMode == 1 || x.InterestMode == 2 || x.InterestMode == 7 || x.InterestMode == 8 || x.InterestMode == 9)) return x; }); thisObj.getSwapList.forEach((val, num, arr) => { arr[num].index = num; arr[num].category_tag = arr[num].category_tag || '互换利率'; // 解析 InterestSwapInterval 为 SwapIntervalList if (arr[num].InterestSwapInterval && !arr[num].SwapIntervalList) { try { arr[num].SwapIntervalList = JSON.parse(arr[num].InterestSwapInterval); } catch (e) { } } }); thisObj.marginSwapList = thisObj.trade.swap_positions.filter(x => { if ((x.UnderlyingCode == null || x.UnderlyingCode.length == 0) && x.IsInitial && (x.InterestMode == 5 || x.InterestMode == 6)) return x; }); thisObj.marginSwapList.forEach((val, num, arr) => { arr[num].index = 1000 + num; // 保证金列表使用 1000+ 偏移,避免与利息腿冲突 arr[num].HappenDate = thisObj.formatDate(arr[num].HappenDate); // 解析 InterestSwapInterval 为 SwapIntervalList if (arr[num].InterestSwapInterval && !arr[num].SwapIntervalList) { try { arr[num].SwapIntervalList = JSON.parse(arr[num].InterestSwapInterval); } catch (e) { } } }); if (thisObj.trade.StructureType == '多空组合') { thisObj.paySwapList = []; } else { thisObj.paySwapList = thisObj.trade.swap_positions.filter(x => { if (x.UnderlyingCode != null && x.UnderlyingCode.length != 0 && x.IsInitial) return x; }); thisObj.paySwapList.forEach((val, num, arr) => { arr[num].index = num; arr[num].PosiFeeType = thisObj.normalizePosiFeeType(arr[num].PosiFeeType); this.StockEqvNotional = val.ContractSize * val.PosiQuantity * val.PosiGrossPrice; // D2 修复:重开(审批重开/刷新)已保存的债券成交单时,三字段互算的手动标志随页面重置而丢失; // 若不锁,用户一旦编辑任一价格字段就会以它为源重新反算、覆盖当初保存的其他两格。 // 故在加载路径对"债券且三字段齐全(净价/全价/收益率均有值)"的标的,直接把三格标为手动锁定, // 计算器在重开期间不自动推导;用户点"重算"才清除并重新计算。 var hasV = function (v) { return v !== null && v !== undefined && v !== '' && !isNaN(Number(v)); }; var isBond = val.isBond || tradeHelper.IsBond(val.UnderlyingInstrumentType); if (isBond && hasV(val.PosiNetNoFeePrice) && hasV(val.PosiGrossPrice) && hasV(val.InitYtm)) { thisObj.$set(arr[num], 'bondManual', { CP: true, DP: true, YD: true }); } }); if (thisObj.paySwapList.length > 0) { thisObj.syncPosiFeeModeByItem(thisObj.paySwapList[0]); } } if (thisObj.paySwapList.length == 0) { thisObj.trade.trade_extend.ExtendObj.Direction = thisObj.trade.trade_extend.ExtendObj.Direction == 0 ? 2 : thisObj.trade.trade_extend.ExtendObj.Direction; var direction = thisObj.trade.trade_extend.ExtendObj.Direction; thisObj.addSwapFloat(direction); } if (thisObj.getSwapList.length == 0) { thisObj.addGetSwapRate(); } //if (thisObj.marginSwapList.length == 0) { // thisObj.addMarginSwapRate(); //} }, //利息端添加行 addGetSwapRate() { var InterestMode = 9; var thisObj = this; if (thisObj.trade.StructureType == '多空组合') { InterestMode = 1; } var getSwap = { index: thisObj.getSwapList.length,//利息序号,做删除以及观察日用 id: 0, PosiDirection: 0,//浮动收支方式 PositionType: 0,//多空方向 1:long,2:short UnderlyingCode: "",//标的代码 UnderlyingInstrumentType: "", CountRatio: 0,//乘积因子 ContractSize: 0,//合约乘数 PosiNetPrice: 0,//期初标的价格含费 PosiGrossPrice: 0,//期初标的价格不含费 PosiNetFeePrice: 0,//净价含费 PosiNetNoFeePrice: 0,//净价不含费 PosiQuantity: 0,//持仓数量 PosiTradingFeePending: 0,//交易费用后付 PosiTradingFee: 0,//交易费用 PosiTradingFeeUnit: 0,//单位交易费用 InterestDirection: 1,//利息收支方式 InterestRateDefault: 0,//计息利率 InterestMode: InterestMode,//计息基本类型 InterestPrincipalFix: 0,//计息基准 FloatRate: 0,// 浮动利率 FloatRateUnderlyingCode: "",//浮动利率标的 InterestType: 1,//计息方式 0 单利,1:复利 InterestSwapInterval: "",//观察间隔 SwapIntervalList: [],//观察间隔集合 observation: null,//观察信息, IsAnnualized: true,//是否年化 HappenDate: null,//发生日期, Currency: 'CNY',//币种 interest_rest_days: 7,//重置频率 interest_rule: null,//利率准则 category_tag: '互换利率'//类别 } thisObj.getSwapList.push(getSwap); }, //预付金添加行 addMarginSwapRate() { var thisObj = this; var getSwap = { index: thisObj.marginSwapList.length,//利息序号,做删除以及观察日用 id: 0, PosiDirection: 0,//浮动收支方式 PositionType: 0,//多空方向 1:long,2:short UnderlyingCode: "",//标的代码 UnderlyingInstrumentType:"", CountRatio: 0,//乘积因子 ContractSize: 0,//合约乘数 PosiNetPrice: 0,//期初标的价格含费 PosiGrossPrice: 0,//期初标的价格不含费 PosiNetFeePrice: 0,//净价含费 PosiNetNoFeePrice: 0,//净价不含费 PosiQuantity: 0,//持仓数量 PosiTradingFeePending: 0,//交易费用后付 PosiTradingFee: 0,//交易费用 PosiTradingFeeUnit: 0,//单位交易费用 InterestDirection: 1,//利息收支方式 InterestRateDefault: 0,//计息利率 InterestMode: 5,//计息基本类型 InterestPrincipalFix: 0,//计息基准 FloatRate: 0,// 浮动利率 FloatRateUnderlyingCode: "",//浮动利率标的 InterestType: 0,//计息方式 0 单利,1:复利 InterestSwapInterval: "",//观察间隔 SwapIntervalList: [],//观察间隔集合 observation: null,//观察信息, IsAnnualized: true,//是否年化, HappenDate: thisObj.trade.TradeDate,//发生日期, Currency: 'CNY',//币种 interest_rest_days: 7,//重置频率 interest_rule: null//利率准则 } thisObj.marginSwapList.push(getSwap); }, //浮动端添加行 addSwapFloat(direction) { var thisObj = this; var petSwap = { index: thisObj.paySwapList.length,//利息序号,做删除以及观察日用 id: 0, PosiDirection: direction,//浮动收支方式 PositionType: 1,//多空方向 1:long,2:short UnderlyingCode: "",//标的代码 underlying: { UnderlyingCode: '', UnderlyingName: '', UnderlyingIssuer: '', IssueSize: '', MaturityDate: '', Price: '', UnderlyingInstrumentType: '', QuoteUnitString: '' },//标的信息 UnderlyingInstrumentType: "", CountRatio: 1,//乘积因子 ContractSize: 1,//合约乘数 PosiNetPrice: 0,//期初标的价格含费 PosiGrossPrice: 0,//期初标的价格不含费 PosiNetFeePrice: 0,//净价含费 PosiNetNoFeePrice: 0,//净价不含费 PosiQuantity: 0,//持仓数量 PosiTradingFee: 0,//交易费用 PosiTradingFeePending: 0,//交易费用后付 PosiTradingFeeUnit: 0,//单位交易费用 PosiFeeType: thisObj.getCurrentPosiFeeType(),//单位交易费用模式 InterestDirection: 0,//利息收支方式 InterestRateDefault: 0,//计息利率 InterestMode: 0,//计息基本类型 InterestPrincipalFix: 0,//计息基准 FloatRate: 0,// 浮动利率 FloatRateUnderlyingCode: "",//浮动利率标的 InterestType: 0,//计息方式 0 单利,1:复利 InterestSwapInterval: "",//观察间隔 SwapIntervalList: [],//观察间隔集合 observation: null,//观察信息 IsAnnualized: false,//是否年化 HappenDate: null,//发生日期, Currency: 'CNY',//币种 interest_rest_days: 7,//重置频率 interest_rule: null//利率准则 } thisObj.paySwapList.push(petSwap); }, //利息端删除行 deleteSwapRate(index) { var thisObj = this; thisObj.getSwapList.splice(index, 1); thisObj.getSwapList.forEach((val, num, arr) => { arr[num].index = num; }); }, //利息端删除行 deleteMarginSwapRate(index) { var thisObj = this; thisObj.marginSwapList.splice(index, 1); thisObj.marginSwapList.forEach((val, num, arr) => { arr[num].index = num; }); }, //浮动端删除行 deleteSwapFloat(index) { var thisObj = this; thisObj.paySwapList.splice(index, 1); thisObj.paySwapList.forEach((val, num, arr) => { arr[num].index = num; }); }, formatDate(time, formatStr) { if (!formatStr) { formatStr = "YYYY-MM-DD"; } var momDate = new moment(time); if (!momDate.isValid() || typeof time === "undefined") return "/"; return momDate.format(formatStr); }, showUnderlyingInfo(item) { var underlying = item.underlying; var html = '
' + '' + '
' $(".un").popover('show'); $(".popover-body").html(html); //$(".un").popover('show'); }, refreshDatepicker() { var thisObj = this; var startDate = thisObj.trade.StartDate; var endDate = thisObj.trade.ExerciseDate; if (this.$refs.happenDateRefs) { for (let i = 0; i < this.$refs.happenDateRefs.length; i++) { this.$refs.happenDateRefs[i].refresh(startDate, endDate); } } this.$refs.observationStartRefs.refresh(startDate, endDate); }, IsBond(instType) { return tradeHelper.IsBond(instType); } }, computed: { maxTradeDate() { let exerciseDate = this.trade.ExerciseDate; return exerciseDate && exerciseDate <= page.valuedate ? exerciseDate : page.valuedate; }, }, components: { 'vue-datepicker': FastVue.vueDatePicker(), 'vue-number-input': FastVue.vueNumberInput(), 'vue-underlying-nonbond': vueUnderlyingNonBond(), 'vue-underlying-bond': vueUnderlyingBond(), 'vue-underlying-rate': vueUnderlyingRate() }, destroyed() { } }); function hideUnderlyingInfo(index) { $(".un").popover('hide'); } function __init(vue) { //交易员 if (page.canAddNewTrader) { autoTrader = FastVue.autocomplete(document.getElementById('TraderId'), { nameField: 'Name', valueField: 'id', searchField: ['Name', 'PinYin'], lookup: consTraders, onSelect(data) { vue.trade.TraderId = data.id; vue.trade.TraderName = data.Name; } }); let traderId = page.Trade.TraderId; let trader = traderId ? consTraders.find(x => x.id === traderId) : null; if (!trader && page.IsNew) { trader = consTraders[0]; trader && (vue.trade.TraderId = trader.id); } autoTrader.setData(trader); } //簿记账户 let autoAssetUnit = FastVue.autocomplete(document.getElementById('AssetId'), { nameField: 'Name', valueField: 'id', searchField: ['Name', 'PinYin'], lookup: consAssetUnits, onSelect(data) { vue.trade.AssetId = data.id; } }); let assetId = page.Trade.AssetId; let asset = assetId ? consAssetUnits.find(x => x.id === assetId) : null; if (!asset && page.IsNew) { asset = consAssetUnits[0]; asset && (vue.trade.AssetId = asset.id); } autoAssetUnit.setData(asset); //客户名称 if (page.canChangeClient) { let autoClient = FastVue.autocomplete(document.getElementById('ClientId'), { nameField: 'Name', valueField: 'id', searchField: ['Name', 'PinYin'], lookup: consClients, onSelect: function (rep) { vue.changeClient(rep); _getMainProtocolCode(rep); } }); let clientId = page.Trade.ClientId; let client = clientId ? consClients.find(x => x.id === clientId) : null; !client && page.IsNew && (client = consClients[0]); autoClient.setData(client); _getMainProtocolCode(client); } } function _getMainProtocolCode(client) { $("#MainProtocolCode option").remove(); if (client) { let clientId = typeof client === 'object' ? client.id : client; main.post("/Client/getMainProtocolCodes", { clientId: clientId }, { async: false }).done(function (resp) { var obj = $("#MainProtocolCode"); if (client.MainProtocolCode != null && client.MainProtocolCode != '') { obj.append(""); } _.forEach(resp, (v) => { obj.append(""); }) }); } $("#SupProtocolCode option").remove(); if ($("#MainProtocolCode").val()) { main.post("/Client/getSideProtocols", { mainProtocol: $("#MainProtocolCode").val() }, { async: false }).done(function (resp) { var obj = $("#SupProtocolCode"); if (client.SupProtocolCode != null && client.SupProtocolCode != '') { obj.append(""); } _.forEach(resp, (v) => { obj.append(""); }) }); } } var getObservationDatesSetting = vue.getObservationDatesSetting; $(function () { $(".un").popover({ html: true, title: function () { return "发行情况"; }, content: function () { return ""; }, placement: "right" }); $(".datepicker").change(function () { var dateVal = $(this).val(); if (!dateVal) return; dateVal = dateVal.replace(/\D/g, '').padStart(4, '0'); switch (dateVal.length) { case 4: dateVal = new Date().getFullYear() + dateVal; break; case 6: dateVal = '20' + dateVal; break; case 8: break; default: dateVal = dateVal.length > 8 ? dateVal.substring(0, 8) : ''; break; } if (dateVal) { dateVal = moment(dateVal).format('YYYY-MM-DD'); !/^\d+/.test(dateVal) && (dateVal = ''); } $(this).datepicker("setDate", dateVal); }); });