//客户选择组件
const vueClient = function () {
return {
props: ['value'],
data() {
return { current: { id: 0, Name: '' } };
},
mounted() {
var self = this;
var id = parseInt(this.value);
if (id) {
this.current = _.clone(ylotc.clients.find(x => x.id === id));
}
var width = 152;
for (var i = 0; i < ylotc.clients.length; i++) {
let ele = document.createElement('span')
ele.innerText = ylotc.clients[i].Name;
ele.style.fontSize = '14px';
document.documentElement.append(ele);
var charLength = ele.offsetWidth + 28;//滚动条
document.documentElement.removeChild(ele);
if (charLength > width) {
width = charLength
}
}
FastVue.autocomplete(this.$el, {
valueField: 'Name', searchField: ['Name', 'PinYin'], lookup: ylotc.clients, width: width,
onSelect(data) {
self.current = _.clone(data);
if (self.value !== data.id) {
self.value = data.id;
self.$emit('input', self.value);
}
$(self.$el).blur();
}
});
},
watch: {
value(val, old) {
if (val !== old) {
var id = parseInt(val);
if (id !== this.current.id) {
this.current = _.clone(ylotc.clients.find(x => x.id === id) || { id: 0, Name: '' });
}
$(this.$el).data('select', '').attr("placeholder", '');
}
}
},
destroyed() {
FastVue.autocomplete.dispose(this.$el);
},
template: ''
};
};
//品种选择组件
const vueVariety = function () {
return {
props: ['variety'],
mounted() {
var self = this;
FastVue.autocomplete(this.$el, {
valueField: 'Name', searchField: ['Name', 'Code', 'PinYin'],
lookup: ylotc.varieties,
onSelect(data) {
if (self.variety !== data) {
self.$emit('change-variety', data);
}
}
});
},
destroyed() {
FastVue.autocomplete.dispose(this.$el);
},
template: ''
};
};
//期权类型选择组件
const vueTradeType = function () {
return {
props: ['value'],
mounted() {
var self = this;
FastVue.autocomplete(this.$el, {
valueField: 'value', searchField: ['value', 'pinyin'],
lookup: optionTradeTypes,
onSelect(data) {
if (self.value !== data.value) {
self.value = data.value;
self.$emit('input', self.value);
}
}
});
this.$el.value = this.value || '';
},
watch: {
value(val, old) {
if (val !== old) {
$(this.$el).val(this.value).data('select', '').attr("placeholder", '');
}
}
},
destroyed() {
FastVue.autocomplete.dispose(this.$el);
},
template: ''
};
};
//标的选择组件(EQD-7049:改为服务端搜索,不再依赖全量 ylotc.underlyings,避免十几万标的整段下载卡死)
//标的选择组件(EQD-7049:改为服务端搜索,不再依赖全量 ylotc.underlyings,避免十几万标的整段下载卡死)
const vueUnderlying = function () {
const _suggestionTpl = _.template($('#underlyingSuggestionTpl').html());
// 标的缓存(同品种各实例共享):_cache 为最近一次服务端结果,_fresh 记录其对应的 品种|关键词,
// _seq 单调递增丢弃乱序/过期响应,_inflight 防同关键词重复请求(helper 收在函数内,避免全局绑定冲突)
const _cache = {};
const _fresh = {};
const _seq = {};
const _inflight = {};
function _fetch(varietyId, query, cb) {
var vid = varietyId || 0;
var q = query || '';
var key = vid + '|' + q;
if (_inflight[key]) return;
var seq = (_seq[vid] = (_seq[vid] || 0) + 1);
_inflight[key] = true;
var postData = {
FilterCode: q.toUpperCase(),
VarietyId: vid,
MaxShowLength: 20,
BlackLimit: 1,
UseForTrading: true,
IncludeMatured: true,
CheckLaunch: true
};
main.post('/frontdata/AjaxGetUnderlyingSelect', postData).done(function (res) {
delete _inflight[key];
if (_seq[vid] !== seq) return; // 已有更新的关键词发起请求,丢弃本响应
var arr = (res && (res.obj || res.data)) || [];
var norm = arr.map(function (x) {
return {
Code: x.Code,
Name: x.Name,
InstrumentType: x.InstrumentType,
VarietyId: x.VarietyId,
Disallow: !!x.Disallow,
IsCombined: !!x.IsSynthetic || !!x.IsBasket,
BlackWhiteState: x.BlackWhiteState || 0,
PinYin: x.PinYin || ''
};
});
_cache[vid] = norm;
_fresh[vid] = key;
cb && cb(norm, q);
}).fail(function () {
delete _inflight[key];
});
}
function _filter(list, query, varietyId) {
if (!query) return (list || []).slice(0, 20);
query = query.toUpperCase();
return (list || []).filter(function (x) {
if (varietyId && x.VarietyId !== varietyId) return false;
if (x.IsCombined) return false; // 与原逻辑一致:搜索时排除组合标的
return (x.Code && x.Code.toUpperCase().indexOf(query) !== -1)
|| (x.PinYin && x.PinYin.toUpperCase().indexOf(query) !== -1);
}).slice(0, 20);
}
return {
props: ['underlying'],
data() {
return { jqInput: null, autoctrl: null };
},
mounted() {
var self = this;
this.jqInput = $(this.$el).children(0);
// EQD-7049:预拉默认20条(当前品种),获得焦点时由插件自身的 onValueChange 呈现
_fetch(self.underlying.VarietyId, '');
this.autoctrl = FastVue.autocomplete(this.jqInput, {
valueField: 'Code',
lookup(query) {
var varietyId = self.underlying.VarietyId;
var vid = varietyId || 0;
var key = vid + '|' + (query || '');
if (_fresh[vid] === key) {
// 命中当前关键词的服务端结果:直接展示(服务端已按 StartsWith+品种/黑名单过滤,不再前端二次过滤)
return (_cache[vid] || []).slice(0, 20);
}
// 异步搜索。FastVue 包装的 lookup 只同步取返回值渲染,服务端结果到达后必须重新触发
// onValueChange 才会显示;重走 lookup 时命中上面的 _fresh 分支直接返回,不会循环请求
_fetch(varietyId, query, function (list, q) {
var inst = self.jqInput.autocomplete();
if (!inst || !inst.visible) return; // 下拉已关闭:留待下次获得焦点时呈现
if ((self.jqInput.val() || '').toLowerCase() !== q.toLowerCase()) return; // 输入已变化:等新关键词的响应
inst.onValueChange();
});
// 过渡兜底:服务端响应到达前用旧缓存按关键词过滤,避免搜索期间下拉空白
return _filter(_cache[vid] || [], query, varietyId);
},
onSelect(data) {
if (self.underlying !== data) {
self.$emit('change-underlying', data);
}
self.jqInput.blur();
},
formatResult(suggestion, currentValue) {
return _suggestionTpl(suggestion.data);
}
});
this.jqInput.val(this.underlying.Code);
},
methods: {
showInput() {
this.jqInput.show().focus().next().hide();
},
hideInput() {
this.jqInput.hide().next().show();
}
},
computed: {
showName() {
return (this.underlying.InstrumentType === 'Stock' || this.underlying.InstrumentType === 'StockIndex') ? this.underlying.Name : ' ';
}
},
destroyed() {
FastVue.autocomplete.dispose(this.jqInput);
},
template: '
').append(html);
$temp.find(removeClass).remove();
$temp.find('template').each(function () {
html = _getHtml($(this), removeClass);
$(this).html(html);
});
return $temp.html();
}
//定价视图呈现器
const title = Vue.compile(_getHtml($('#pricingItem_tpl'), '.pitem,.pitem-cash'));
const item = Vue.compile(_getHtml($('#pricingItem_tpl'), '.ptitle,.pitem-cash:not(.pitem)'));
const itemCash = Vue.compile(_getHtml($('#pricingItem_tpl'), '.ptitle,.pitem:not(.pitem-cash)'));
const items = Vue.compile($('#pricingItems_tpl').html() || '');
return {
title(forStatic) {
return forStatic ? title.staticRenderFns : title.render;
},
item(forStatic) {
return forStatic ? item.staticRenderFns : item.render;
},
itemCash(forStatic) {
return forStatic ? itemCash.staticRenderFns : itemCash.render;
},
items(forStatic) {
return forStatic ? items.staticRenderFns : items.render;
}
};
}());
//单个定价组件
const vueTrade = function () {
return {
props: ['trade', 'viewState', 'calcResult', 'floating'],
data() {
return {
isTitle: false,
canEditEngineName: true,
...consVueTrade.data(),
fieldState: tradeFieldMgr.getFieldState(this.floating),
tradeMarginTemplates: ylotc.tradeMarginTemplates,
binaryPayoffTypes: this.trade.ExerciseMode === 'European' ?
consBinaryPayoffTypes.European : consBinaryPayoffTypes.American,
engineNames: tradeUtils.getEngineNames(this.trade)
};
},
watch: {
'trade.Notional': {
handler(newVal, oldVal) {
this.resetSummary(resetSummaryFlags.notional);
},
immediate: false
},
'trade.TradeType': {
handler(newVal, oldVal) {
this.fieldState.onTradeTypeChanged(this.trade, oldVal, this);
this.changeEngineName();
},
immediate: true
},
'trade.ExerciseDate': {
handler(newVal, oldVal) {
this.trade.SettlementDate = newVal;
this.changeIsAnnualized();
},
immediate: false
},
'trade.UnderlyingInstrumentType': {
handler(newVal, oldVal) {
this.fieldState.setFieldState('DividendRate', (newVal === 'Stock' || newVal === 'StockIndex'));
},
immediate: true
},
'trade.PayoffType': {
handler(newVal, oldVal) {
this.changeEngineName();
},
immediate: false
},
'trade.EnhancedPrice': {
handler(newVal, oldVal) {
this.changeEngineName();
},
immediate: true
},
'trade.StrikeType': {
handler(newVal, oldVal) {
this.changeEngineName();
},
immediate: false
}
},
created: consVueTrade.created,
mounted() {
consSyntheticMap[this.trade.CalcId] = this.viewState.synthetic;
if (pageVue.ClientUsedForCalc) {
if (!this.trade.ClientId) {
ylotc.clients.length && (this.trade.ClientId = ylotc.clients[0].id);
if (this.trade.ClientId) {
this.changeClient();
}
} else {
this.updateKey.client++;
}
}
this.$nextTick(function () {
switch (this.viewState.initFlag) {
case 'first':
this.viewState.initFlag = '';
this.preloadDefaultUnderlying();
break;
case 'import1':
case 'import2':
case 'template':
!this.floating && this.updateUnderlying({ UnderlyingCode: this.trade.UnderlyingCode });
break;
}
});
topVue.simpleMode && (this.trade.UnderlyingPrice = '');
!this.floating && EventBus.addEventListener('simpleMode.changed', this.onSimpleModeChanged);
},
methods: {
...consVueTrade.methods,
//变更客户
changeClient() {
this.$emit('change-client', this.trade.ClientId);
},
synchTrade(setInitialMarginNull) {
this.$emit('synch-trade', this.trade, setInitialMarginNull);
},
sumTotal() {
this.$emit('sum-total', this.trade);
},
//变更期权类型
changeTradeType() {
this.viewState.Observation.hasValue = false;
this.viewState.KOObservation.hasValue = false;
this.changeExerciseMode('TradeType');
this.refreshEngineNames();
if (this.trade.TradeType === "现金流交易") {
tradeUtils.resetCashFlow(this.trade);
}
},
// EQD-7049:首腿自动预载默认类型标的。固收等环境下默认类型(Stock/CommodityFutures)可能没有
// 已上线标的,后端必返回"标的信息缺失"——属可容忍场景,静默失败不弹窗,留待用户自选;
// 用户主动切换类型仍走 changeInstrumentType,查询失败正常提示
preloadDefaultUnderlying() {
if (new Date().getTime() < this.updateKey.underlying + 300) return;
let instType = this.trade.UnderlyingInstrumentType;
this.viewState.variety = tradeHelper.getEmptyVariety(instType);
this.updateUnderlying({ InstrumentType: instType }, false, true);
},
//变更标的类型
changeInstrumentType() {
if (new Date().getTime() < this.updateKey.underlying + 300) return;
let instType = this.trade.UnderlyingInstrumentType;
this.viewState.variety = tradeHelper.getEmptyVariety(instType);
this.updateUnderlying({ InstrumentType: instType });
this.synchTrade();
},
//变更标的品种
changeVariety(variety) {
this.viewState.variety = variety;
this.updateUnderlying({ VarietyId: variety.id });
this.synchTrade();
},
//变更标的
changeUnderlying(underlying) {
if (underlying.Disallow) {
if (underlying.IsCombined) {
main.alert(underlying.BlackWhiteState === 1 ? "该组合中包含存在于黑名单的标的" : "该组合中包含不在白名单中的标的");
}
else {
main.alert(underlying.BlackWhiteState === 1 ? "该标的存在于黑名单中" : "该标的不在白名单中");
return;
}
}
this.viewState.underlying = underlying;
this.updateUnderlying({ UnderlyingCode: underlying.Code }, true);
this.changeEffectRatio();
this.synchTrade();
},
//变更定价模型
changeEngineName() {
// 增强亚式期权,在执行价格和增强价格不一致的情况下,默认只支持使用蒙特卡洛计算引擎
if (tradeUtils.changeEngineName(this.trade)) {
this.canEditEngineName = false;
} else {
this.canEditEngineName = true;
}
},
//更新标的
updateUnderlying(reqData, fromSelect, silent) {
let self = this;
// EQD-7049:新建空白页未选标的/品种/类型时,跳过必然失败的后端默认标的查询,避免报“标的信息缺失”
var hasQueryKey = !!(reqData.UnderlyingCode || reqData.InstrumentType || reqData.VarietyId > 0);
if (!hasQueryKey) {
!fromSelect && (self.viewState.underlying = tradeHelper.getEmptyUnderlying());
return;
}
var instTypeChanged = !!reqData.InstrumentType;
!fromSelect && (self.viewState.underlying = tradeHelper.getEmptyUnderlying());
// silent:自动预载场景失败不弹窗(alertFn 置空+抑制网络错误提示),用户主动查询仍正常提示
var req = main.post("/pricing/AjaxGetUnderlying", reqData, silent ? { alertFn: $.noop, suppressError: true } : undefined);
silent && req.fail(function (resp) { console.warn('预载默认标的失败(已忽略):', resp && resp.msg); });
req.done(function (resp) {
let trade = self.trade;
let um = resp.obj.underlying;
if (!fromSelect) {
self.viewState.underlying = {
id: um.id, Code: um.UnderlyingCode, Name: um.UnderlyingName,
InstrumentType: um.UnderlyingInstrumentType, VarietyId: um.UnderlyingTypeId,
QuoteUnitString: um.QuoteUnitString
};
}
self.updateKey.underlying = new Date().getTime();
instTypeChanged || (instTypeChanged = trade.UnderlyingInstrumentType !== um.UnderlyingInstrumentType);
trade.UnderlyingId = um.id;
trade.UnderlyingCode = um.UnderlyingCode;
trade.UnderlyingName = um.UnderlyingName;
trade.UnderlyingInstrumentType = um.UnderlyingInstrumentType;
trade.VarietyId = um.UnderlyingTypeId;
trade.CountRatio = um.CountRatio;
self.viewState.variety = ylotc.varieties.find(x => x.id === um.UnderlyingTypeId)
|| tradeHelper.getEmptyVariety(trade.UnderlyingInstrumentType);
self.updateKey.variety = new Date().getTime();
if (self.viewState.initFlag === 'import1') {
self.getTTM(false, false);
return self.viewState.initFlag = '';
}
if (self.viewState.initFlag === 'template') {
return self.viewState.initFlag = '';
}
trade.DividendRate = um.DividendRate;
self.viewState.synthetic = consSyntheticMap[trade.CalcId] = resp.obj.synthetic;
//初始预付金
trade.InitialMargin = 0;
//加载结构化交易
if (self.viewState.initFlag === 'import2') {
trade.SettlementDate = trade.ExerciseDate;
(trade.TradeOpenVolatility !== '') && (self.viewState.VolState = 'user');
(trade.TradeCloseVolatility !== '') && (self.viewState.CloseVolState = 'user');
self.changeTradeDate('import2');
if (trade.PremiumRate !== '' || trade.TradeSinglePrice !== '') {
trade.PremiumRate !== '' ? self.changePremiumRate() : self.changeTradeSinglePrice();
} else {
trade.PremiumRate = trade.TradeSinglePrice = trade.TradePrice = 0;
}
return self.viewState.initFlag = '';
}
//重置权利金
trade.PremiumRate = trade.TradeSinglePrice = trade.TradePrice = 0;
trade.SpotPrice = pricingFormat.umprice(um.Price);
//重置视图状态
_.each(tradeFieldMgr.defaultData.viewState, (val, key) => {
val === 'system' && (self.viewState[key] = "system");
});
//根据标的类型切换,权益类默认名义本金成交方式
if (um.UnderlyingInstrumentType === "Stock" || um.UnderlyingInstrumentType === "StockIndex") {
trade.IsUsePremiumRate = true;
//名义本金影响交易数量
self.changeStockEqvNotional(1e6);
if (trade.IsMoneynessOption !== "是" || !trade.Strike) {
trade.Strike = pricingFormat.umprice(um.Price);
self.showPercentStrike();
instTypeChanged = true;
}
} else {
trade.IsUsePremiumRate = false;
self.changeTradeAmount(instTypeChanged ? 1 : null);
if (trade.IsMoneynessOption === "是" || !trade.Strike) {
trade.Strike = "1";
self.showAbsStrike();
instTypeChanged = true;
}
}
//如果资产大类变更则清除掉IsMoneynessOption和IsUsePremiumRate关联的字段值
instTypeChanged && tradeUtils.onChangeInstrumentType(trade);
//到期日ExerciseDate默认为1个月之后,股票到期日不显示
trade.TradeDate = pageVue.SysDate;
trade.ExerciseDate = new moment(um.ExerciseDate).format("YYYY-MM-DD");
trade.MaturityDate = new moment(um.MaturityDate || "2029-01-01").format("YYYY-MM-DD");
trade.TradeDate > trade.ExerciseDate && (trade.TradeDate = trade.ExerciseDate);
self.changeTradeDate(true);
if (pageData.is厦门象屿) {
trade.PremiumPayDate = trade.ExerciseDate;
}
//重置计算结果
self.calcResult.reset();
if (fromSelect) {
self.resetSummary(resetSummaryFlags.all);
}
});
},
//刷新波动率,影响波动率的属性:SpotPrice,Strike,TradeDate,ExerciseDate,UnderlyingId
getVol(debounce) {
if (this.trade.TradeType === "现金流交易") return;
if (pageVue.IsTradeVol) {
let flag = 0;
if (!pageVue.SkewMapVol) {
this.viewState.VolState !== 'user' && (flag |= 1);
this.viewState.CloseVolState !== 'user' && (flag |= 2);
if (flag === 0) return;
} else {
flag = 1 | 2;
}
if (debounce === true) {
if (!this.debounceGetVol) {
this.debounceGetVol = _.debounce(function (vue) {
(flag & 1) > 0 && vue.getTradeOpenVolatility();
(flag & 2) > 0 && vue.getTradeCloseVolatility();
}, 1000, { trailing: true });
}
this.debounceGetVol(this);
} else {
(flag & 1) > 0 && this.getTradeOpenVolatility();
(flag & 2) > 0 && this.getTradeCloseVolatility();
}
} else if (this.viewState.VolState !== 'user') {
this.getTradeOpenVolatility();
this.getTradeMidVolatility();
}
this.synchTrade();
},
//重置统计数据
resetSummary(flag) {
this.$emit('reset-summary', flag);
},
//获取标的价格(计算用)
getUnderlyingPrice() {
var self = this;
return main.post("/pricing/AjaxGetUnderlyingPrice", { underlyingCode: this.trade.UnderlyingCode, tradeDate: this.trade.ValueDate })
.done(function (resp) {
self.trade.UnderlyingPrice = pricingFormat.umprice(resp.obj.price);
self.synchTrade();
});
},
//精简模式切换时的处理
onSimpleModeChanged() {
this.trade.UnderlyingPrice = '';
}
},
computed: { ...consVueTrade.computed },
components: {
'vue-margintemplatename': vueMarginTemplateName(),
'vue-niceselect': FastVue.vueNiceSelect(),
'vue-variety': vueVariety(),
'vue-tradetype': vueTradeType(),
'vue-underlying': vueUnderlying(),
'vue-datepicker': FastVue.vueDatePicker(),
'vue-number-input': FastVue.vueNumberInput(),
'vue-daycount': vueDayCount(),
'vue-client': vueClient(),
},
render: renders.item(),
staticRenderFns: renders.item(true),
destroyed() {
delete consSyntheticMap[this.trade.calcId];
topVue.simpleMode && (this.trade.UnderlyingPrice = '');
EventBus.removeEventListener('simpleMode.changed', this.onSimpleModeChanged);
}
};
};
//单个定价组件(现金流)
const vueTrade2 = function () {
return {
props: ['trade', 'viewState', 'calcResult', 'floating'],
data() {
return {
isTitle: false,
...consVueTrade.data(),
fieldState: tradeFieldMgr.getFieldState(this.floating),
};
},
watch: {
'trade.ExerciseDate': {
handler(newVal, oldVal) {
this.trade.SettlementDate = newVal;
},
immediate: false
}
},
mounted() {
if (pageVue.ClientUsedForCalc) {
if (!this.trade.ClientId) {
ylotc.clients.length && (this.trade.ClientId = ylotc.clients[0].id);
if (this.trade.ClientId) {
this.changeClient();
}
} else {
this.updateKey.client++;
}
}
this.fieldState.onTradeTypeChanged(this.trade, null, this);
this.trade.IsUsePremiumRate = true;
},
methods: {
...consVueTrade.methods,
//变更客户
changeClient() {
this.$emit('change-client', this.trade.ClientId);
},
synchTrade() {
this.$emit('synch-trade', this.trade);
},
//重置统计数据
resetSummary(flag) {
this.$emit('reset-summary', flag);
}
},
computed: { ...consVueTrade.computed },
components: {
'vue-niceselect': FastVue.vueNiceSelect(),
'vue-client': vueClient(),
'vue-datepicker': FastVue.vueDatePicker(),
'vue-number-input': FastVue.vueNumberInput(),
'vue-daycount': vueDayCount(),
},
render: renders.itemCash(),
staticRenderFns: renders.itemCash(true),
destroyed() {
}
};
};
//主视图定价头部拖动处理
const mainDragScroll = dragScroll.create({ container: '.pricing-main', yscroll: false, throttle: _.throttle });
//定价组件组合
function createVue(index, baseVue, floating) {
var datas = baseVue.datas;
var fromLocal = baseVue.fromLocal === true;
if (datas.length < 1) {
return main.alert('缺少数据');
}
var structureType = baseVue.combining && datas.length > 1 ? '结构化交易' : baseVue.structureType || '';
_.each(datas, (data, index2) => {
if (index2 > 0) {
data.trade.hideCommen = true;
}
data.trade.CalcId = index + '-' + (index2 + 1);
data.trade.ParentIndex = index;
if (!data.calcResult) {
data.calcResult = {
sourceData: null,
reset(data) {
this.sourceData = data;
if (!data) {
_.each(consCalcFields, x => this[x] = '');
}
}
};
_.each(consCalcFields, x => data.calcResult[x] = '');
} else if (baseVue.combining) {
data.calcResult = _.cloneDeep(data.calcResult);
}
if (!data.trade.UnderlyingInstrumentType) {
data.trade.UnderlyingInstrumentType = pageVue.StockFirst ? 'Stock' : 'CommodityFutures';
}
data.trade.StructureType = structureType;
});
if (datas.length === 1) {
datas[0].trade.CalcId = index.toString();
}
var debounceFloatSinglePrice = _.debounce(singlePricer.calcSinglePrice, 300);
var vue = new Vue({
data: {
index: index,
datas: datas,
floating: floating,
isSelected: true,
structureType: structureType,
showFloatingIcon: !structureType || datas.every(x => x.trade.TradeType === '香草期权')
},
computed: {
},
mounted() {
if (this.floating) {
var singlePrice = singlePricer.calcSinglePrice(this.convertForSinglePrice());
singlePrice && $('#singleprice2').text(singlePrice);
} else {
pricingVue.onMountd(this);
}
},
methods: {
getData(index) {
return this.datas[index || 0];
},
getVol() {
_.each(this.$children, x => x.getVol());
},
remove(blRemoveChain) {
this.$destroy();
this.$el.remove();
pricingVue.remove(this, blRemoveChain);
},
saveTrade() {
tradeSaver.saveTrades([this]);
},
showFloatVue() {
floatVue.show(this);
let td = this.getData(0);
setTimeout(function () {//交易详情页-定价增强价格不显示问题
if (td && td.trade.TradeType == "亚式期权" && td.trade.StrikeType != "Floating" && td.trade.PayoffType == 'EnhancedArithmeticAverage') {
$("#EnhancedPriceTitle").show();
}
}, 500);
},
screenshot() {
screenshoter.execute(this);
},
uncouple() {
pricingVue.uncouple(this);
},
convertForSinglePrice() {
return _.reduce(this.datas, (result, data) => {
var sdata = data.calcResult.sourceData;
sdata && (result || (result = [])).push({ Pv: sdata.Pv, Notional: data.trade.Notional });
return result;
}, null);
},
//重置统计数据
resetSummary(flag) {
//浮窗页面
if (this.floating && flag < resetSummaryFlags.ten) {
var singlePrice = singlePricer.calcSinglePrice(this.convertForSinglePrice());
singlePrice && $('#singleprice2').text(singlePrice);
}
//主页面
if (this.isSelected) {
var data = this.datas[this.datas.length - 1];
data.calcResult.TotalTradePrice = pageVue.GetTotalTradePrice(_.map(this.datas, x => x.trade));
data.calcResult.TotalDay1Pnl = pageVue.GetTotalDay1Pnl(_.map(this.datas, x => x.trade));
if (flag === resetSummaryFlags.initialMargin) {
data.calcResult.TotalMargin = pageVue.GetTotalMargin(_.map(this.datas, x => x.trade), this.structureType);
return summaryVue.reset(this.index, this.datas.map(x => x.calcResult));
}
singlePricer.reset(this.index, this.convertForSinglePrice());
if (flag !== resetSummaryFlags.notional) {
summaryVue.reset(this.index, this.datas.map(x => x.calcResult));
}
}
else if (flag === true) {
summaryVue.reset(this.index, null);
singlePricer.reset(this.index, null);
}
},
setCalcResult(result) {
var data = _.find(this.datas, x => x.trade.CalcId === result.CalcId);
if (!data) {
return main.alert('系统错误');
}
var trade = data.trade;
if (topVue.calcMargin && data.viewState.InitialMargin !== 'user') {
if (result.hasInitialMargin) {
trade.InitialMargin = parseFloat(result.initialMargin) + result.AccurateTradePrice * (trade.BuySell == "买入" ? -1 : 1);
} else {
trade.InitialMargin = 0;
}
}
var calcResult = data.calcResult;
var contractSize = result.contractSize;
trade.TTMDays = consNumberFormat.ttmDaysFmt(result.TTMDays);
trade.Day1Pnl = pricingFormat.tradePrice(result.Day1Pnl);
//对象缩小为具体计算结果
result = result.calcResult;
calcResult.reset(result);
//用于统计
calcResult.UnderlyingCode = trade.UnderlyingCode;
if (trade.IsUsePremiumRate) {
trade.TradePrice = result.Pv * (trade.BuySell == "买入" ? 1 : -1);
if (trade.TradeType !== "现金流交易") {
//根据总额算出期权费率
tradePricing.tradeCalc(trade, tradePricing.calcReason.Pv);
//由于期权费率会有精度损失,进行了四舍五入处理,需要再根据期权费率反算出期权费总额
//因为录入交易时是根据期权费率作为标准的
tradePricing.tradeCalc(trade, tradePricing.calcReason.PremiumRate);
}
}
else {
var pv = result.Pv * (trade.BuySell == "买入" ? 1 : -1);
trade.TradeSinglePrice = trade.Notional ? pv / trade.Notional : 0;
tradePricing.tradeCalc(trade, tradePricing.calcReason.TradeSinglePrice);
}
//result.Pv = (result.Pv < 0 ? -1 : 1) * trade.TradePrice;
if (pageObj.IsPVIncludePrincipal && trade.OriginalPrincipalSum > 0) {
result.Pv += trade.OriginalPrincipalSum * (trade.BuySell === '卖出' ? -1 : 1);
}
if (topVue.calcMargin) {
//非用户自己输入和非结构化交易
if (data.viewState.InitialMargin !== 'user' && !trade.StructureType) {
//非双向追保交易员收预付金,预付金不能小于0;交易员买入,付期权费收预付金,预付金不能小于0;交易员卖出,收期权费付预付金,预付金不能大于0
if (pageVue.ClientUsedForCalc && !data.viewState.TwoSideMargin && trade.InitialMargin < 0) {
trade.InitialMargin = 0;
}
else if (trade.BuySell == "卖出" && trade.InitialMargin > 0) {
trade.InitialMargin = 0;
}
else if (trade.BuySell == "买入" && trade.InitialMargin < 0) {
trade.InitialMargin = 0;
}
}
} else {
trade.InitialMargin = '';
}
//todo:光子模式下是否要重设TradeOpenVolatility的值
// delta gamma 用手数,其它份额 Vega,pho份额*100 qdp用的是份额计算
//现在计算的是数量 份额数量比
//手数 = 交易数量 / 交易单位 (5吨 / 手)
//交易数量 = 份额 / 比率
//手数 = 份额 / (比率 * 交易单位)
//TradeUnitValue 是手数对份额的比率 鸡蛋时为 10
var variety = data.viewState.variety;
var fe_ration = 1;
!contractSize && (contractSize = variety && variety.ContractSize ? variety.ContractSize : 1);
calcResult.Pv = pricingFormat.tradePrice(result.Pv);
calcResult.Delta = pricingFormat.greek(result.Delta / contractSize);
calcResult.GammaCash = pricingFormat.greek(result.GammaCash);
calcResult.Theta = pricingFormat.greek(fe_ration * result.Theta);
calcResult.Vega = pricingFormat.greek(fe_ration * result.Vega);
calcResult.Rho = pricingFormat.greek(fe_ration * result.Rho * 100);
calcResult.TotalMargin = 0;
calcResult.TotalTradePrice = 0;
calcResult.TotalDay1Pnl = 0;
this.resetSummary(resetSummaryFlags.all);
this.sumTotal(trade);
if (data === this.datas[this.datas.length - 1]) {
calcResult.TotalMargin = pageVue.GetTotalMargin(_.map(this.datas, x => x.trade), this.structureType);
calcResult.TotalTradePrice = pageVue.GetTotalTradePrice(_.map(this.datas, x => x.trade));
calcResult.TotalDay1Pnl = pageVue.GetTotalDay1Pnl(_.map(this.datas, x => x.trade));
}
},
changeClient(clientId) {
let client = null;
if (pageVue.ClientUsedForCalc && pageVue.TwoSideMargin) {
client = ylotc.clients.find(y => y.id === clientId);
}
_.each(this.datas, x => {
x.trade.ClientId = clientId;
x.trade.MarginOptionType = client.MarginOptionType;
x.viewState.TwoSideMargin = client && client.MarginOptionType === 1;
x.trade.MarginTemplateName = client.MarginOptionType === 3 ? "无预付金" : "系统默认";
tradeUtils.changeMarginTemplate(x.trade, []);
});
},
updateSynthetic(calcId, synthetic) {
var index = this.datas.findIndex(x => x.trade.CalcId === calcId);
if (index < 0) return false;
this.datas[index].trade.SpotPrice = synthetic.Price;
this.$children[index].changeSpotPrice(true);
return true;
},
copyThis() {
pricingVue.copyItem(this);
},
synchTrade(trade, setInitialMarginNull) {
_.each(this.datas, x => {
//隐藏的组合公共属性字段赋值处理
if (trade.ParentIndex > 0 && x.trade.ParentIndex == trade.ParentIndex && !trade.hideCommen) {
x.trade.ClientId = trade.ClientId;
x.trade.UnderlyingId = trade.UnderlyingId;
x.trade.UnderlyingCode = trade.UnderlyingCode;
x.trade.UnderlyingName = trade.UnderlyingName;
x.trade.UnderlyingInstrumentType = trade.UnderlyingInstrumentType;
x.trade.VarietyId = trade.VarietyId;
if (trade.StructureType != "蝶式组合") {
x.trade.TradeAmountV = trade.TradeAmountV;
x.trade.TradeAmount = trade.TradeAmount;
x.trade.Notional = trade.Notional;
x.trade.StockEqvNotional = trade.StockEqvNotional;
x.trade.StockEqvNotionalReal = trade.StockEqvNotionalReal;
}
x.trade.ExerciseMode = trade.ExerciseMode;
x.trade.TradeDate = trade.TradeDate;
if (trade.StructureType != "日历价差") {
x.trade.ExerciseDate = trade.ExerciseDate;
x.trade.SettlementDate = trade.SettlementDate;
}
x.trade.SpotPrice = trade.SpotPrice;
x.trade.UnderlyingPrice = trade.UnderlyingPrice;
x.trade.SettlementType = trade.SettlementType;
x.trade.NoRiskRate = trade.NoRiskRate;
x.trade.DividendRate = trade.DividendRate;
x.trade.TTMDays = trade.TTMDays;
x.trade.IsTTMSystem = trade.IsTTMSystem;
x.trade.IsAnnualized2 = trade.IsAnnualized2;
x.trade.MetaDic = trade.MetaDic;
x.trade.EngineName = trade.EngineName;
x.trade.OriginalPrincipalSum = x.trade.IsUsePremiumRate ?
_.round(x.trade.StockEqvNotional * x.trade.PrincipalRateWrite * x.trade.AnnualizeFactor, otcformat.trading.tradePrice.precision)
: _.round(x.trade.Notional * x.trade.SinglePrincipalWrite, otcformat.trading.tradePrice.precision);
x.trade.TradePrice = x.trade.IsUsePremiumRate ?
_.round(tradeHelper.GetTradePriceByPremiumRate(x.trade.PremiumRate, x.trade.StockEqvNotional, x.trade.ParticipationRate,
x.trade.OriginalPrincipalSum, x.trade.AnnualizeFactor, x.trade.BuySell, x.trade.TradeType, true), otcformat.trading.tradePrice.precision)
: _.round(tradeHelper.GetTradePriceBySinglePrice(x.trade.TradeSinglePrice, x.trade.StockEqvNotional, x.trade.Notional,
x.trade.OriginalPrincipalSum, x.trade.AnnualizeFactor, x.trade.BuySell, x.trade.TradeType, true), otcformat.trading.tradePrice.precision);
if (setInitialMarginNull) {
x.trade.InitialMargin = null;
}
}
});
this.sumTotal(trade);
},
sumTotal(trade) {
//第一条腿添加组合成交金额和组合预付金
var totalTradePrice = 0;
var totalInitialMargin = 0;
_.each(this.datas, x => {
if (trade.ParentIndex > 0 && x.trade.ParentIndex == trade.ParentIndex) {
totalTradePrice += parseFloat(x.trade.TradePrice) * (x.trade.BuySell == "买入" ? -1 : 1);
totalInitialMargin += parseFloat(x.trade.InitialMargin);
}
});
_.each(this.datas, x => {
if (x.trade.ParentIndex == trade.ParentIndex) {
x.trade.TotalTradePrice = pricingFormat.tradePrice(totalTradePrice);
x.trade.TotalInitialMargin = pricingFormat.tradePrice(totalInitialMargin);
}
});
}
},
components: {
'vue-trade': vueTrade(),
'vue-trade2': vueTrade2(),
},
destroyed() {
if (!this.floating && this.isSelected) {
summaryVue.reset(this.index, null);
singlePricer.reset(this.index, null);
}
},
render: renders.items(),
staticRenderFns: renders.items(true)
});
if (!baseVue.combining && !baseVue.selfMount) {
if (vue.floating) {
vue.$mount($('
').appendTo('#pricing-items2').get(0));
} else if (fromLocal) {
vue.$mount($('
').appendTo('#pricing-items').get(0));
} else {
vue.$mount($('
').prependTo('#pricing-items').get(0));
}
}
!floating && mainDragScroll.reset();
return vue;
}
function _setAutocomplete(elId, datas, initData, onSelectFn) {
initData = initData || {};
if (elId === "GroupClientId") {
groupVue.trade.ClientId = initData.id;
groupVue.trade.ClientName = initData.Name;
}
else if (elId === "GroupAssetId") {
groupVue.trade.AssetId = initData.id;
groupVue.trade.AssetBookName = initData.Name;
}
else if (elId === "GroupTraderId") {
groupVue.trade.TraderId = initData.id;
groupVue.trade.TraderName = initData.Name;
}
FastVue.autocomplete(document.getElementById(elId), {
valueField: 'Name', searchField: ['Name', 'PinYin'], lookup: datas,
onSelect(data) {
$('#' + elId).data('id', data.id);
if (elId === "ClientId" || elId === "GroupClientId") {
_setSalesman();
}
onSelectFn && onSelectFn(data);
},
zIndex: 300000
}).setData(initData);
$('#' + elId).data('id', initData.id);
}
function _setSalesman() {
if (pageVue.ClientUsedForCalc) {
var IsCentralClearing = $("#txtCurrentClient").attr("data-IsCentralClearing");
var CentralClearingPaltform = $("#txtCurrentClient").attr("data-CentralClearingPaltform");
var TradingPaltform = $("#txtCurrentClient").attr("data-TradingPaltform");
$("#IsCentralClearing").val(IsCentralClearing);
$("#CentralClearingPaltform").val(CentralClearingPaltform);
$("#TradingPaltform").val(TradingPaltform);
}
if ($("#ClientId").data('id') && _salesCommissionCtrl) {
_salesCommissionCtrl.changeSalesmen($("#ClientId").data('id'));
_getMainProtocolCode($("#ClientId").data('id'));
}
}
function _changeMainProtocolCode() {
_getSupProtocolCode();
}
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");
_.forEach(resp, (v) => {
obj.append("
");
})
});
}
_getSupProtocolCode()
}
function _getSupProtocolCode() {
$("#SupProtocolCode option").remove();
if ($("#MainProtocolCode").val()) {
main.post("/Client/getSideProtocols", { mainProtocol: $("#MainProtocolCode").val() }, { async: false }).done(function (resp) {
var obj = $("#SupProtocolCode");
_.forEach(resp, (v) => {
obj.append("
");
})
});
}
}
//定价组件
const pricingVue = (function () {
//创建定价标题组件
function createTitleVue(el, floating) {
var vue = new Vue({
el: el,
data: {
trade: {},
isTitle: true,
floating: floating,
fieldState: tradeFieldMgr.getTitleState(floating)
},
render: renders.title(),
staticRenderFns: renders.title(true)
});
return vue;
}
createTitleVue('#pricing-titles', false);
createTitleVue('#pricing-titles2', true);
var _index = 1;
//定价组件集合
const _tradeVues = [];
function __clear() {
if (!_tradeVues.length) return;
_index = 0;
_tradeVues.splice(0, _tradeVues.length).forEach(x => x.remove());
$('#pricing-items').parent().floatingScroll('update');
}
//清空
function clear(force) {
if (!_tradeVues.length) return;
if (force) {
__clear();
} else {
main.confirmV2("确认清空吗?").done(__clear);
}
}
//移除
function remove(tradeVue, blRemoveChain) {
if (blRemoveChain && tradeVue.floating) {
var baseVue = _tradeVues.find(x => x.index === tradeVue.index);
baseVue && baseVue.remove();
}
_.pull(_tradeVues, tradeVue);
$('#pricing-items').parent().floatingScroll('update');
}
//新增单腿
function addTrade(trade, tradeType) {
if (_tradeVues && _tradeVues[0] && _tradeVues[0].getData().trade.StructureType) {
return main.alert('不能同时添加单笔交易和组合交易');
}
_index += 1;
let baseVue = _tradeVues[0] || { datas: [tradeFieldMgr.defaultData] };
if (trade && typeof trade === 'object') {
let viewState = _.cloneDeep(tradeFieldMgr.defaultData.viewState);
viewState.initFlag = 'import1';
baseVue = { datas: [{ trade: trade, viewState: viewState }] };
} else if (tradeType === "现金流交易") {
let data = tradeFieldMgr.defaultData;
data = {
trade: _.cloneDeep(data.trade),
viewState: _.cloneDeep(data.viewState)
};
_tradeVues.length < 1 && (data.viewState.initFlag = 'first');
data.trade.IsUsePremiumRate = true;
baseVue = { datas: [data] };
trade = data.trade;
}
else {
let data = baseVue.datas[0];
data = {
trade: _.cloneDeep(data.trade),
viewState: _.cloneDeep(data.viewState)
};
_tradeVues.length < 1 && (data.viewState.initFlag = 'first');
baseVue = { datas: [data] };
trade = data.trade;
}
if (typeof tradeType === 'string' && trade.TradeType !== tradeType) {
trade.TradeType = tradeType;
tradeFieldMgr.initFieldValue(trade);
} else {
tradeFieldMgr.resetFieldState(trade);
}
if (trade.TradeAmount && !trade.TradeAmountV) {
trade.TradeAmountV = tradeHelper.getTradeAmountV(trade);
}
let vue = createVue(_index, baseVue);
_tradeVues.unshift(vue);
if (pageVue.IsImport || !new Array('香草期权', '合成价差期权').includes(trade.TradeType)) {
floatVue.show(vue);
}
}
//复制项目
function copyItem(tradeVue) {
var arrIndex = _.indexOf(_tradeVues, tradeVue);
if (arrIndex < 0) {
throw '系统错误';
}
_index += 1;
let baseVue = tradeVue;
baseVue = { datas: _.cloneDeep(baseVue.datas), structureType: baseVue.structureType, selfMount: true };
var vue = createVue(_index, baseVue);
vue.$mount($('
').insertBefore(tradeVue.$el).get(0));
_tradeVues.splice(arrIndex, 0, vue);
}
//新增策略
function addTrades(trades) {
if (_tradeVues && _tradeVues[0] && !_tradeVues[0].getData().trade.StructureType) {
return main.alert('不能同时添加单笔交易和组合交易');
}
var datas = _.map(trades, trade => {
trade = _.transform(trade, (result, val, key) => {
key in result && (result[key] = val);
}, _.cloneDeep(pageVue.Trade));
if (trade.UnderlyingInstrumentType !== 'Stock' && trade.UnderlyingInstrumentType !== 'StockIndex') {
trade.StockEqvNotionalReal = pricingFormat.StockEqvNotional(trade.Notional * trade.SpotPrice);
trade.StockEqvNotional = trade.StockEqvNotionalReal;
}
else {
trade.StockEqvNotionalReal = trade.StockEqvNotional;
}
trade.SpotPrice = trade.UnderlyingPrice;
trade.NoRiskRate = null;
trade.DividendRate = 0;
trade.ParticipationRate = 1;
trade.AnnualizeFactor = 1;
if (pageVue.NumOfSmoothingDaysCfg === 'ONE' && pageVue.IsTradeVol) {
trade.NumOfSmoothingDays = 1;
}
tradeUtils.normalizeTrade(trade);
let viewState = _.cloneDeep(tradeFieldMgr.defaultData.viewState);
viewState.initFlag = 'import2';
return { trade: trade, viewState: viewState };
});
_index += 1;
var vue = createVue(_index, { datas: datas, structureType: pageVue.getStructureType() });
_tradeVues.unshift(vue);
}
//自由组合
function combine() {
var comVues = _.filter(_tradeVues, x => x.isSelected);
if (comVues.length < 2) {
return main.alert('请选择至少两项进行组合!');
}
var firstVue = comVues[0];
var datas = _.flatMap(comVues.map(x => x.datas));
if (pageVue.ClientUsedForCalc) {
var clientId = parseInt(datas[0].trade.ClientId);
if (!_.every(datas, x => parseInt(x.trade.ClientId) === clientId)) {
return main.alert('所选项客户必须一致!');
}
}
var vue = createVue(firstVue.index, { combining: true, datas: datas, fromLocal: true });
vue.$mount($('
').insertBefore(firstVue.$el).get(0));
var index = _.indexOf(_tradeVues, firstVue);
_tradeVues[index] = vue;
_.pullAll(_tradeVues, comVues);
comVues.forEach(x => x.remove());
}
//取消组合
function uncouple(tradeVue) {
if (!tradeVue || tradeVue.floating) {
throw '系统错误';
}
var arrIndex = _.indexOf(_tradeVues, tradeVue);
if (arrIndex < 0) {
throw '系统错误';
}
var index = _index += tradeVue.datas.length;
var map = _.map(tradeVue.datas, data => {
data.trade.hideCommen = false;
var vue = createVue(index--, { combining: true, datas: [data], fromLocal: true });
vue.$mount($('
').insertBefore(tradeVue.$el).get(0));
return vue;
});
var args = [arrIndex, 1].concat(map);
Array.prototype.splice.apply(_tradeVues, args);
tradeVue.remove();
}
//波动率类型变更
function changeVolType() {
_tradeVues.forEach(x => x.getVol());
}
//获取所有交易数据
function getTrades() {
return _.flatMap(_tradeVues.map(x => x.datas)).map(x => x.trade);
}
//录入交易
function saveTrades() {
tradeSaver.saveTrades(_tradeVues.filter(x => x.isSelected));
}
//录入组合交易
function saveGroupTrades() {
tradeSaver.saveGroupTrades(_tradeVues.filter(x => x.isSelected));
}
//定价计算
function calcPrice(tradeVues) {
!tradeVues && (tradeVues = _tradeVues);
if (!tradeVues.length) {
return main.alert("请至少录入一个交易");
}
var trades = _.flatMap(tradeVues, x => x.datas).map(x => x.trade);
//检查并转换数据
trades = tradeUtils.prepareTrades(trades, pageVue.getVolType(), false);
if (trades) {
let data = {
version: pageVue.CalcVersion,
trades: trades, calcMargin: topVue.calcMargin,
calcAutocallGreeks: topVue.calcAutocallGreeks
};
main.post("/pricing/ajaxCalcPrices", data).done(function (resp) {
//tradeVues转换为calcId为key的字典
var map = tradeVues.reduce((result, cur) => {
_.each(cur.datas, x => {
result[x.trade.CalcId] = cur;
});
return result;
}, {});
//设置计算结果
_.each(resp.obj, item => {
map[item.CalcId].setCalcResult(item);
});
});
}
}
//显示组合到期收益曲线图形
function showPayoffLineChart(tradeVues) {
var selectVues = _.filter(_tradeVues, x => x.isSelected);
!tradeVues && (tradeVues = selectVues);
if (!tradeVues.length) {
return main.alert("请至少录入一个交易");
}
var trades = _.flatMap(tradeVues, x => x.datas).map(x => x.trade);
//检查并转换数据
trades = tradeUtils.prepareTrades(trades, pageVue.getVolType(), false);
window.sessionStorage.removeItem("pricing_payoff_line");
if (trades) {
main.post("/pricing/GetTradesPayoffLine", { trades: trades }).done(function (resp) {
//resp.obj
window.sessionStorage.setItem("pricing_payoff_line", JSON.stringify(resp));
window.open("/Statics/html/PayoffChart.html");
});
}
}
//显示组合Pv曲线
function showPvChart(tradeVues) {
var selectVues = _.filter(_tradeVues, x => x.isSelected);
!tradeVues && (tradeVues = selectVues);
if (!tradeVues.length) {
return main.alert("请至少录入一个交易");
}
var trades = _.flatMap(tradeVues, x => x.datas).map(x => x.trade);
//检查并转换数据
trades = tradeUtils.prepareTrades(trades, pageVue.getVolType(), false);
window.sessionStorage.removeItem("pricing_pv_line");
if (trades) {
main.post("/pricing/GetTradesPvLine", { trades: trades }).done(function (resp) {
//resp.obj
window.sessionStorage.setItem("pricing_pv_line", JSON.stringify(resp));
window.open("/Statics/html/PvChart.html");
});
}
}
function showLifeLineChart(tradeVues) {
var selectVues = _.filter(_tradeVues, x => x.isSelected);
!tradeVues && (tradeVues = selectVues);
if (!tradeVues.length) {
return main.alert("请至少选择一笔交易");
}
var trades = _.flatMap(tradeVues, x => x.datas).map(x => x.trade);
//检查并转换数据
trades = tradeUtils.prepareTrades(trades, pageVue.getVolType(), false);
window.sessionStorage.removeItem("pricing_life_lines");
if (trades) {
main.post("/pricing/GetTradesLifePvLine", { trades: trades }).done(function (resp) {
//resp.obj
window.sessionStorage.setItem("pricing_life_lines", JSON.stringify(resp));
window.open("/Statics/html/PvLifeChart.html");
});
}
}
//显示Greeks变化曲线
function showGreeksChart(tradeVues) {
var selectVues = _.filter(_tradeVues, x => x.isSelected);
!tradeVues && (tradeVues = selectVues);
if (!tradeVues.length) {
return main.alert("请至少选择一笔交易");
}
var trades = _.flatMap(tradeVues, x => x.datas).map(x => x.trade);
//检查并转换数据
trades = tradeUtils.prepareTrades(trades, pageVue.getVolType(), false);
window.sessionStorage.removeItem("pricing_greeks_line");
if (trades) {
main.post("/pricing/GetTradesGreeksForLifetime", { trades: trades }).done(function (resp) {
//resp.obj
window.sessionStorage.setItem("pricing_greeks_line", JSON.stringify(resp));
window.open("/Statics/html/GreeksChart.html");
});
}
}
//设置观察数据
function setObservation(observationNum, observationUnit, observationHolidayType, observationDates, alignEnd, calcId, couponDayInterval) {
if (!calcId) return;
let _data = null, calcId2 = calcId.replace(/#.*$/, "");
_.each(_tradeVues, vue => {
return !(_data = _.find(vue.datas, data => data.trade.CalcId === calcId2));
});
_data && consVueTrade.methods.setObservation.apply(_data, arguments);
}
function setStructure(value, calcId) {
if (!calcId) return;
let _data = null, calcId2 = calcId + "";
_.each(_tradeVues, vue => {
return !(_data = _.find(vue.datas, data => data.trade.CalcId === calcId2));
});
_data && consVueTrade.methods.setStructure.apply(_data, arguments);
}
//对冲下单
function hedgingOrder() {
var comVues = _.filter(_tradeVues, x => x.isSelected);
if (comVues.length < 1) {
return main.alert('请至少选择一项!');
}
var datas = _.flatMap(comVues.map(x => x.datas));
var instType = datas[0].trade.UnderlyingInstrumentType;
if (!_.every(datas, x => x.trade.UnderlyingInstrumentType === instType)) {
return main.alert('所选项标的类型必须一致!');
}
var rd = _.reduce(datas, (acc, cur) => {
var code = cur.trade.UnderlyingCode;
var delta = acc[code];
acc[code] = (delta || 0) + (parseFloat(cur.calcResult.Delta) || 0);
return acc;
}, {});
var items = _.reduce(rd, (acc, value, key) => {
var index = acc.length / 2;
acc.push('');
acc.push('');
return acc;
}, []);
items.push('');
$('#orderForm').html(items.join("")).submit();
}
//浏览器本地数据缓存
const localData = (new function () {
if (pageVue.IsImport) {
this.save = $.noop;
this.load = $.noop;
return;
}
this.save = function () {
var vueDatas = _tradeVues.map(x => new Object({
index: x.index,
structureType: x.structureType,
datas: x.datas.map(data => new Object({ trade: data.trade, viewState: data.viewState }))
}));
var data = {
index: _index,
VolType: pageVue.getVolType(),
SysDate: pageVue.SysDate,
vueDatas: vueDatas,
time: new Date().getTime()
};
var json = JSON.stringify(data);
localStorage.setItem("pricing-structure-" + pageVue.UserId, json);
};
this.load = function () {
//设置默认值
var saveKey = "pricing-structure-" + pageVue.UserId;
var storage = localStorage.getItem(saveKey);
if (!storage) return;
var data = JSON.parse(storage);
if (pageVue.SysDate !== data.SysDate ||
new Date().getHours > 18 && new Date(data.time).getHours() < 18) {
//需要清空缓存
localStorage.removeItem(saveKey);
}
else {
_index = data.index;
pageVue.setVolType(data.VolType);
_.each(data.vueDatas, function (x) {
x.fromLocal = true;
var vue = createVue(x.index, x);
_tradeVues.push(vue);
});
}
};
}());
//持久化定价数据
$(window).on('beforeunload', localData.save);
$(localData.load);
return {
clear: clear,
remove: remove,
addTrade: addTrade,
copyItem: copyItem,
combine: combine,
changeVolType: changeVolType,
saveTrades: saveTrades,
saveGroupTrades: saveGroupTrades,
calcPrice: calcPrice,
showPayoffLineChart: showPayoffLineChart,
showPvChart: showPvChart,
showGreeksChart: showGreeksChart,
showLifeLineChart: showLifeLineChart,
getTradeVues(onlySelected) {
return onlySelected ? _tradeVues.filter(x => x.isSelected) : _tradeVues;
},
getTradeVue(index) { return _tradeVues[index || 0]; },
saveLocalData: localData.save,
setOption: addTrades, //用于和老版兼容
setObservation: setObservation,
setStructure: setStructure,
uncouple: uncouple,
hedgingOrder: hedgingOrder,
onMountd(vue) {
_tradeVues.length && $('#pricing-items').parent().floatingScroll();
},
swap(oldIndex, newIndex) {
if (oldIndex === newIndex) return;
let vue = _tradeVues[oldIndex];
_tradeVues.splice(oldIndex, 1);
_tradeVues.splice(newIndex, 0, vue);
},
//新增模板
addTemplate(tplItems) {
tplItems.forEach(x => {
x.datas.forEach(d => {
d.viewState = _.cloneDeep(tradeFieldMgr.defaultData.viewState);
d.viewState.initFlag = 'template';
var client = ylotc.clients.find(y => y.id === d.trade.ClientId);
d.viewState.TwoSideMargin = client && client.MarginOptionType === 1;
});
_index += 1;
var vue = createVue(_index, x);
_tradeVues.unshift(vue);
});
}
};
}());
//浮窗定价组件
const floatVue = (function () {
var _tradeVue;
//关闭浮窗
function hide() {
if (_tradeVue) {
_tradeVue.remove();
_tradeVue = null;
}
$('#floatModal').modal('hide');
$(document.body).removeClass('has-float');
if (pageVue.IsImport) {
layer.closeMe();
}
}
//显示浮窗index, trade, viewState
function show(baseVue, forScreenshot) {
_tradeVue && _tradeVue.remove();
_tradeVue = createVue(baseVue.index, baseVue, true);
$(document.body).toggleClass('has-float', !forScreenshot);
$('#floatModal').modal('show');
}
function screenshot() {
screenshoter.execute(_tradeVue);
}
return {
show: show,
hide: hide,
calcPrice() {
_tradeVue && pricingVue.calcPrice([_tradeVue]);
},
saveTrade() {
_tradeVue && _tradeVue.saveTrade();
},
screenshot: screenshot,
getVue() { return _tradeVue; }
};
}());
//dom ready function
$(function () {
tradeSaver.init();
groupVue.init();
//解决上层popover不能编辑的问题
$('#floatModal').on('shown.bs.modal', function () {
$(document).off('focusin.modal');
});
$(".search-label").addClass("formlabel");
$(".formlabel").addClass("search-label");
var sortable = Sortable.create(document.getElementById('pricing-items'), {
handle: '.pricing-index-drag',
onEnd: function (evt) {
pricingVue.swap(evt.oldIndex, evt.newIndex);
},
});
if (pageVue.IsImport) {
let trade = _.cloneDeep(pageVue.Trade);
pricingVue.addTrade(trade);
setTimeout(function () {//交易详情页-定价增强价格不显示问题
if (trade.TradeType == "亚式期权" && trade.StrikeType != "Floating" && trade.PayoffType == 'EnhancedArithmeticAverage') {
$("#EnhancedPriceTitle").show();
}
}, 500);
}
document.onkeydown = hotkey;
//当onkeydown 事件发生时调用hotkey
function hotkey() {
var a = window.event.keyCode;
if ((window.event.keyCode == 81) && (event.altKey)) {
if (floatVue.getVue() === null || floatVue.getVue() === undefined) {
pricingVue.calcPrice();
} else {
floatVue.calcPrice();
}
}
else if ((window.event.keyCode == 87) && (event.altKey)) {
floatVue.screenshot();
}
}
});
//用于和老版兼容
window.vue = pricingVue;
//兼容定价计算页面观察日设置
var getObservationDatesSetting = pricingVue.setObservation;
var getStructureSetting = pricingVue.setStructure;
//连离类型:障碍(Discrete),二元(MonitorType,美式)
//补偿类型:障碍(RebateType),二元(RebateType,美式)
//定价模板
var templateVue = (function () {
if (pageVue.IsImport) return;
var debounceSearch;
return new Vue({
el: '#modalTemplate',
data: {
saveMode: true,
saveData: {
name: '',
id: 0,
_override: false,
_CommonTemplate: true
},
listData: [],
searchText: ''
},
mounted() {
this.refreshList();
debounceSearch = _.debounce(this.searchInner, 500);
},
methods: {
showSave() {
let selVues = pricingVue.getTradeVues(true);
if (!selVues.length) {
return main.alert('请勾选要保存的定价数据');
}
//模板名称自动赋值
let vueDatas = selVues.map(x => new Object({
structureType: x.structureType,
datas: x.datas.map(data => new Object({ trade: data.trade }))
}));
var client = ylotc.clients.find(y => y.id === vueDatas[0].datas[0].trade.ClientId);
let arr = [vueDatas[0].structureType, client?.Name?.substring(0, 6), vueDatas[0].datas[0].trade.UnderlyingCode, vueDatas[0].datas[0].trade.TradeDate.split('-').join(''), vueDatas[0].datas[0].trade.Strike];
for (var i = 0; i < arr.length; i++) {
//这里为过滤空的值
//这里为过滤空的值
if (!_.trim(_.toString(arr[i]))) {
arr.splice(i, 1);
i = i - 1;
}
}
let str = arr.join("-");
this.saveMode = true;
this.saveData.name = str;
this.saveData.json = '';
$('#modalTemplate').modal('show');
},
showList() {
this.saveMode = false;
$('#modalTemplate').modal('show');
},
saveTemplate() {
let selVues = pricingVue.getTradeVues(true);
if (!selVues.length) {
return main.alert('没有可保存的定价数据');
}
let name = (this.saveData.name).trim();
if (!name) {
return main.alert('请填写模板名称');
}
let self = this;
let vueDatas = selVues.map(x => new Object({
structureType: x.structureType,
datas: x.datas.map(data => new Object({ trade: data.trade }))
}));
let dataJson = JSON.stringify(vueDatas);
main.post('/pricing/AjaxSaveTemplate/v2', { name, dataJson, _override: this.saveData._override, _CommonTemplate: this.saveData._CommonTemplate }).done(function () {
$('#modalTemplate').modal('hide');
self.refreshList();
self.searchInner();
});
},
removeTemplate(sysUserConfigId) {
let $item = $(event.target).closest('.yt-template-item');
if (!$item) {
return main.alert('系统错误,没有找到所选项');
}
let self = this;
let index = $item.prevAll().length;
let names = [$item.children('a').text()];
main.confirmPost('确认要删除所选项"' + names[0] + '"吗?', '/pricing/AjaxRemoveTemplate', { sysUserConfigId }).done(function () {
self.listData.splice(index, 1);
});
},
loadTemplate() {
let name = $(event.target).text();
let TemplateType = $(event.target).next().text();
function __do() {
pricingVue.clear(true);
main.post('/pricing/ajaxGetTemplate/v2', { name, TemplateType }).done(function (resp) {
let items = JSON.parse(resp.obj);
pricingVue.addTemplate(items);
$('#modalTemplate').modal('hide');
});
}
if (pricingVue.getTradeVues().length) {
main.confirm("加载自定义模板将清空页面现有交易,是否继续?", __do);
} else {
__do();
}
},
refreshList() {
let self = this;
this.searchText = '';
main.post('/pricing/AjaxGetTemplateList').done(function (resp) {
self.listData = resp.obj.map(x => new Object({ id: x.id, name: x.ConfigName, show: true, UserId: x.UserId, ConfigType: x.ConfigType }));
});
},
searchInner() {
console.log('searchInner');
let text = this.searchText ? this.searchText.toLowerCase() : '';
this.listData.forEach(x => {
x.show = !text || x.name.toLowerCase().indexOf(text) >= 0;
});
},
searchList(action) {
if (action === 'reset') {
debounceSearch.cancel();
this.searchText = '';
this.searchInner();
} else {
debounceSearch();
}
}
}
});
}());
const customNumberFormat = { precision: 6, negative: true, };
const customNumberPercentFormat = { precision: 6, negative: true, append: '%' };
var groupVue = new Vue({
el: '#modalGroupTradeSave',
data: {
viewState: pageVue.viewState,
...consVueTrade.data(),
trade: ylotc.trade,
options: ylotc.options,
structureTypes: pageObj.structureTypes,
PropertyMap: pageObj.PropertyMap,
},
computed: { ...consVueTrade.computed },
methods: {
...consVueTrade.methods,
closeModal: function () {
$('#modalGroupTradeSave').modal('hide');
},
saveGroupTrade: function () {
if (!_trades) return;
_trades.forEach(x => {
x.AssetId = this.trade.AssetId;
x.AssetBookName = this.trade.AssetBookName;
x.TraderId = this.trade.TraderId;
x.TraderName = this.trade.TraderName;
x.TradeNumber = this.trade.TradeNumber;
x.SalesCommission = this.trade.salesCommission;
x.ClientId = this.trade.ClientId;
x.ClientName = this.trade.ClientName;
if (x.TradePremium) {
x.MetaDic["交易溢价"] = x.TradePremium;
}
x.MetaDic["交易场所"] = this.trade.TradingPlace;
x.MetaDic["清算机构"] = this.trade.ClearingAgency;
x.MetaDic["主协议编号"] = this.trade.MainProtocolCode;
x.MetaDic["补充协议编号"] = this.trade.SupProtocolCode;
x.TradeType === '现金流交易' && tradeUtils.resetCashFlow(x);
});
this.setExtendInfo();
main.post("/pricing/AjaxSaveGroupTrade", { trade: this.trade, subTrades: _trades }).done(function (resp) {
if (pageVue.IsImport) {
let url = "#/trade/tradeview?abstract=1&enid=" + resp.obj[0].EncryptId;
layer.closeMe(url);//必须以#开头
} else {
floatVue.hide();
$('#modalTradeSave').modal('hide');
//showTradeView(resp.obj);
_.each(_tradeVues, x => x.remove(true));
}
});
},
addNewProperty: function () {
this.PropertyMap[this.trade.StructureType].push({ isNew: true, ColumnName: "", ColumnDefaultValue: "", ColumnType: 0 });//0:文本
},
deleteProperty: function (index) {
this.PropertyMap[this.trade.StructureType].splice(index, 1);
},
setExtendInfo: function () {
var propertyList = [];
this.PropertyMap[this.trade.StructureType].forEach((item, index) => {
var note = { name: item.name || item.ColumnName, value: PercentColumnText.displayText(item) };
if (note.name) {
propertyList.push(note);
}
});
this.trade.ExtendInfo = this.trade.StructureType == "气囊结构" ? null : JSON.stringify(propertyList);
return this.trade.ExtendInfo;
},
changeStrucTureType: function () {
if (groupVue.trade.TradeType == "气囊结构") {
groupVue.trade.trade_airbag.KIParticipationRate = 1;
}
else {
groupVue.trade.OptionType = null;
groupVue.trade.Strike = null;
}
},
changeBuySell: function () {
groupVue.trade.TradePrice *= -1;
groupVue.trade.Day1Pnl *= -1;
groupVue.trade.TradeSinglePrice *= -1;
groupVue.trade.PremiumRate *= -1;
},
changeIsUsePremiumRate: function () {
if (groupVue.trade.IsUsePremiumRate) {
groupVue.changeStockEqvNotional();
}
else {
groupVue.changeTradeAmount();
}
},
changeStockEqvNotional: function () {
var notional = Math.abs(groupVue.trade.StockEqvNotional / groupVue.trade.SpotPrice);
groupVue.trade.Notional = pricingFormat.notional(notional);
groupVue.trade.TradeAmount = pricingFormat.notional(notional / groupVue.viewState.variety.CountRatio);
groupVue.trade.TradeSinglePrice = pricingFormat.tradeSinglePrice(groupVue.trade.TradePrice / groupVue.trade.Notional);
groupVue.trade.PremiumRate = pricingFormat.premiumRate(groupVue.trade.TradePrice / groupVue.trade.StockEqvNotional);
},
changeTradeAmount: function () {
var notional = groupVue.trade.TradeAmount * groupVue.viewState.variety.CountRatio;
groupVue.trade.Notional = pricingFormat.notional(notional);
groupVue.trade.StockEqvNotional = pricingFormat.stockEqvNotional(Math.abs(notional * groupVue.trade.SpotPrice));
groupVue.trade.TradeSinglePrice = pricingFormat.tradeSinglePrice(groupVue.trade.TradePrice / groupVue.trade.Notional);
groupVue.trade.PremiumRate = pricingFormat.premiumRate(groupVue.trade.TradePrice / groupVue.trade.StockEqvNotional);
},
init: function () {
_setAutocomplete('GroupAssetId', ylotc.assetunits, null, function (data) {
groupVue.trade.AssetId = data.id;
groupVue.trade.AssetBookName = data.Name;
});
var traders = $.grep(ylotc.traders, function (e) { return e.Name == pageVue.Trade.TraderName; });
_setAutocomplete('GroupTraderId', ylotc.traders, { Name: pageVue.Trade.TraderName, id: traders.length ? traders[0].id : 0 }, function (data) {
groupVue.trade.TraderId = data.id;
groupVue.trade.TraderName = data.Name;
});
_setAutocomplete('GroupClientId', ylotc.clients, null, function (data) {
pageVue.SecuritiesEnvironment ? _getMainProtocolCode : null;
groupVue.trade.ClientId = data.id;
groupVue.trade.ClientName = data.Name;
});
},
changeOption: function () {
this.PropertyMap[this.trade.StructureType].forEach(d => {
if (d.ColumnName != undefined) {
d.name = d.ColumnName;
}
if (d.ColumnDefaultValue != undefined) {
d.value = d.ColumnDefaultValue;
}
});
}
},
components: {
'vue-niceselect': FastVue.vueNiceSelect(),
'vue-client': vueClient(),
'vue-variety': vueVariety(),
'vue-margintemplatename': vueMarginTemplateName(),
'vue-tradetype': vueTradeType(),
'vue-underlying': vueUnderlying(),
'vue-datepicker': FastVue.vueDatePicker(),
'vue-number-input': FastVue.vueNumberInput(),
'vue-daycount': vueDayCount(),
},
});