2472 lines
111 KiB
JavaScript
2472 lines
111 KiB
JavaScript
//otcformat禁止千分位分组
|
|
window.otcformat.options.disableGrouping = true;
|
|
|
|
//去掉MyJs.js中的数组扩展,防止某些js类库出错
|
|
delete Array.prototype.contains;
|
|
delete Array.prototype.remove;
|
|
|
|
//pageVue来自PricingModel对象序列化
|
|
//pageVue.TradeEdit属性来自TradeEditViewModel
|
|
//pageVue额外定义的参数:getVolType()
|
|
|
|
pageVue.DaysInYear = 365;
|
|
|
|
//定价格式化(来自配置)
|
|
const pricingFormat = otcformat.trading;
|
|
|
|
//数字格式化
|
|
const consNumberFormat = Object.freeze(new function () {
|
|
this.fixed2 = main.numberFormat(2);
|
|
this.fixed3 = main.numberFormat(3);
|
|
this.fixed4 = main.numberFormat(4);
|
|
this.fixed5 = main.numberFormat(5);
|
|
this.fixed6 = main.numberFormat(6);
|
|
|
|
this.percent = main.numberFormat({ percent: true });
|
|
|
|
this.umpriceP = main.numberFormat(pricingFormat.umpriceP.precision + 2);
|
|
this.volValueFmt = pageVue.VolMoreAccurate ? this.fixed6 : this.fixed4;
|
|
this.ttmDaysFmt = pageVue.PrecisionOfMinuteInQuote ? this.fixed5 : this.fixed4;
|
|
return this;
|
|
}());
|
|
|
|
//基本输入格式
|
|
const inputFormatInteger = Object.freeze({ precision: 0, append: '' });
|
|
const inputFormatDouble2 = Object.freeze({ precision: 2, append: '' });
|
|
const inputFormatPercent = Object.freeze({ precision: 2, append: '%' });
|
|
const inputFormatPercentN = Object.freeze({ precision: 2, append: '%', negative: true });
|
|
const inputFormatPercent4 = Object.freeze({ precision: 4, append: '%' });
|
|
//权利金输入格式
|
|
const inputFormatPremiumRate = Object.freeze({ precision: pricingFormat.premiumRateP.precision, negative: true, append: '%' });
|
|
const inputFormatSinglePrice = Object.freeze({ precision: pricingFormat.tradeSinglePrice.precision, negative: true, append: '' });
|
|
const inputFormatTradePrice = Object.freeze({ precision: 2, negative: true, append: '' });
|
|
//波动率输入格式
|
|
const inputFormatVolPercent = Object.freeze({ precision: pageVue.VolMoreAccurate ? 4 : 2, append: '%' });
|
|
//名义本金输入格式
|
|
const inputFormatEqvNotional = Object.freeze({ precision: pricingFormat.StockEqvNotional.precision, append: '' });
|
|
const inputFormatEqvNotionalReal = Object.freeze({ precision: 10, append: '' });
|
|
//预付金率输入格式
|
|
const inputFormatMarginRate = Object.freeze({ precision: pricingFormat.marginRateP.precision, append: '%' });
|
|
//期初价格
|
|
const inputFormatSpot = Object.freeze({ precision: pricingFormat.umprice.precision, append: '' });
|
|
|
|
//交易方向
|
|
const consBuysellTypes = Object.freeze(pageVue.Company === "润和"
|
|
? [{ value: '卖出', text: '客户买入' }, { value: '买入', text: '客户卖出' }]
|
|
: [{ value: '卖出', text: pageVue.CompanyName + '卖出' }, { value: '买入', text: pageVue.CompanyName + '买入' }]);
|
|
|
|
//观察方式
|
|
const consMonitorTypes = Object.freeze([{ value: '离散', text: '离散' }, { value: '连续', text: '连续' }]);
|
|
//补偿支付
|
|
const consRebateTypes = Object.freeze([{ value: 'AtHit', text: '立即' }, { value: 'AtEnd', text: '递延' }]);
|
|
//亚式均价计算
|
|
const consAsiaPayoffType = Object.freeze([{ value: 'ArithmeticAverage', text: '算术平均' }, { value: 'GeometricAverage', text: '几何平均' },
|
|
{ value: 'DiscreteArithmeticAverage', text: '算术平均(离散)' }, { value: 'EnhancedArithmeticAverage', text: '增强算术平均' }]);
|
|
//是否选项
|
|
const consFalseAsYes = Object.freeze([{ value: false, text: '是' }, { value: true, text: '否' }]);
|
|
const consTrueAsYes = Object.freeze([{ value: true, text: '是' }, { value: false, text: '否' }]);
|
|
const consFalseAsNo = Object.freeze([{ value: false, text: '否' }, { value: true, text: '是' }]);
|
|
const consCashAsPhysical = Object.freeze([{ value: 'Cash', text: 'Cash' }, { value: 'Physical', text: 'Physical' }]);
|
|
const consIsDiscreteMonitored = Object.freeze([{ value: true, text: '离散' }, { value: false, text: '连续' }]);
|
|
//敲出支付方式
|
|
const consKORebateTypes = Object.freeze([{ value: '0', text: '立即支付' }, { value: '1', text: '递延至期末支付' }]);
|
|
//票息结算方式
|
|
const consCouponPayTypes = Object.freeze([{ value: '0', text: '产生时支付' }, { value: '1', text: '敲出时支付' }, { value: '2', text: '期末支付' }]);
|
|
//障碍类型
|
|
const consBarrierTypes = Object.freeze(_.map(['上升敲入', '上升敲出', '下降敲入', '下降敲出', '双障碍敲入', '双障碍敲出'], x => new Object({ text: x, value: x })));
|
|
//二元类型
|
|
const consBinaryPayoffTypes = Object.freeze({
|
|
European: _.map(['CashOrNothing', 'AssetOrNothing'], x => new Object({ text: x, value: x })),
|
|
American: _.map(['UpOneTouch', 'DownOneTouch', 'UpNoTouch', 'DownNoTouch', 'DoubleOneTouch', 'DoubleNoTouch'], x => new Object({ text: x, value: x }))
|
|
});
|
|
const consRateType = Object.freeze([{ value: '0', text: '年化利率' }, { value: '1', text: '实际利率' }]);
|
|
const consDepositType = Object.freeze([{ value: '0', text: '资金收益' }, { value: '1', text: '成本摊还' }]);
|
|
const consRiskExposureType = Object.freeze([{ value: '0', text: '参与计算' }, { value: '1', text: '忽略' }]);
|
|
|
|
//定价模型
|
|
const consEngineNames = Object.freeze({
|
|
Default: _.map(['解析解(默认)', '蒙特卡洛'], x => new Object({ text: x, value: x })),
|
|
American: _.map(['解析解(默认)', '二叉树'], x => new Object({ text: x, value: x })),
|
|
Autocall: _.map(['积分法(默认)', '蒙特卡洛'], x => new Object({ text: x, value: x }))
|
|
});
|
|
|
|
//统计数据重置flags
|
|
const resetSummaryFlags = Object.freeze({ all: 0, notional: 1, ten: 10, initialMargin: 11 });
|
|
|
|
//帮助类(只对trade对象进行处理)
|
|
const tradeUtils = (function () {
|
|
|
|
//IsMoneynessOption影响的字段映射
|
|
const consMoneynessFieldMap = Object.freeze({
|
|
BarrierLow: 'BarrierLow', BarrierHigh: 'BarrierHigh',
|
|
StrikeHigh: 'StrikeHigh', HighStrike: 'HighStrike',
|
|
KOBarrier: 'KOBarrier', KIBarrier: 'KIBarrier',
|
|
CouponBarrier: 'CouponBarrier',
|
|
SpreadStrike: 'SpreadStrike', SpreadStrike1: 'SpreadStrike1',
|
|
SpreadStrikeAtKO: 'SpreadStrikeAtKO', SpreadStrikeAtKO1: 'SpreadStrikeAtKO1',
|
|
UpperRange: 'UpperRange', LowerRange: 'LowerRange'
|
|
});
|
|
|
|
//IsUsePremiumRate切换时绝对值与百分比的换算字段映射
|
|
const consUsePremiumRateFieldMap = Object.freeze({
|
|
Rebate: 'RebateRate', RebateHigh: 'RebateHighRate',
|
|
CashOrNothingAmount: 'CashOrNothingAmountRate',
|
|
CashOrNothingAmountHigh: 'CashOrNothingAmountHighRate'
|
|
});
|
|
|
|
//现金流交易字段(涉及很多基本要素)
|
|
const consCashFlowFields = Object.freeze(['MarginTemplateName', 'OptionType', 'SpotPrice', 'Strike', 'UnderlyingCode'
|
|
, 'UnderlyingId', 'UnderlyingInstrumentType', 'PremiumRate', 'TradeAmount', 'TradeOpenVolatility', 'ExerciseMode'
|
|
, 'MaturityDate']);
|
|
|
|
//生成波动率类型转换函数
|
|
//波动率类型为‘交易’时,根据买入方向选择交易bid(买入)或者交易Ask(卖出)的波动率曲面计算;
|
|
function _volTypeConvert(voltype, buysell) {
|
|
return voltype !== "交易" ? voltype
|
|
: buysell === "卖出" ? "报价Ask" : buysell === '买入' ? '报价Bid' : voltype;
|
|
}
|
|
|
|
//获取交易波动率
|
|
function _getTradeVol(trade, extOptions, varietyId) {
|
|
if (trade.TradeType === "现金流交易") {
|
|
return 0;
|
|
}
|
|
var result = { obj: NaN };
|
|
var VolType = _volTypeConvert(pageVue.getVolType(), trade.BuySell);
|
|
var postData = _.extend({
|
|
VolType: VolType,
|
|
Strike: trade.Strike,
|
|
SpotPrice: trade.SpotPrice,
|
|
TradeDate: trade.TradeDate,
|
|
ExerciseDate: trade.ExerciseDate,
|
|
IsMoneynessOption: trade.IsMoneynessOption,
|
|
CallPut: trade.OptionType === "看涨" ? "Call" : "Put",
|
|
UnderlyingId: trade.UnderlyingId,
|
|
UnderlyingCode: trade.UnderlyingCode,
|
|
UnderlyingName: trade.UnderlyingName,
|
|
UnderlyingTypeId: varietyId,
|
|
BaseVol: null,
|
|
BidVar: null,
|
|
AskVar: null
|
|
}, extOptions);
|
|
if (!postData.TradeDate || !postData.ExerciseDate) {
|
|
var msg = [];
|
|
!postData.TradeDate && msg.push('缺少交易日期');
|
|
!postData.ExerciseDate && msg.push('缺少到期日期');
|
|
return $.Deferred(function () {
|
|
var self = this;
|
|
setTimeout(function () {
|
|
self.resolve({ success: true, obj: '', msg: msg.join(',') });
|
|
}, 100);
|
|
}).promise();
|
|
}
|
|
return main.post("/pricing/AjaxGetVol", postData, { waitMe: false, alertFn: pageVue.SkewMapVol ? main.message : null });
|
|
}
|
|
|
|
function _isNaN(val) {
|
|
return isNaN(parseFloat(val));
|
|
}
|
|
|
|
//检查交易数据是否可以用于计算或保存
|
|
function _checkTrade(trade, usedForSave) {
|
|
var errorList = [];
|
|
if (trade.TradeType === "现金流交易") {
|
|
if (usedForSave && pageVue.TradeEdit) {
|
|
if (!trade.AssetId) {
|
|
errorList.push("簿记账户 必须填写");
|
|
}
|
|
if (!trade.TraderId) {
|
|
errorList.push("交易员 必须填写");
|
|
}
|
|
if (!trade.ClientId) {
|
|
errorList.push("客户名称 必须填写");
|
|
}
|
|
}
|
|
|
|
if (!trade.ExerciseDate) {
|
|
errorList.push("期权到期日 必须填写");
|
|
} else if (trade.ExerciseDate < trade.TradeDate) {
|
|
errorList.push("期权到期日 不能小于交易日期");
|
|
}
|
|
|
|
if (trade.SettlementDate && trade.SettlementDate < trade.ExerciseDate) {
|
|
errorList.push("期权结算日 不能小于到期日");
|
|
}
|
|
|
|
trade.ExerciseMode = "";
|
|
} else {
|
|
if (!trade.UnderlyingId || trade.UnderlyingId === '0') {
|
|
errorList.push("标的信息 必须填写");
|
|
}
|
|
|
|
if (usedForSave && pageVue.TradeEdit) {
|
|
if (!trade.AssetId) {
|
|
errorList.push("簿记账户 必须填写");
|
|
}
|
|
if (!trade.TraderId) {
|
|
errorList.push("交易员 必须填写");
|
|
}
|
|
if (!trade.ClientId) {
|
|
errorList.push("客户名称 必须填写");
|
|
}
|
|
}
|
|
|
|
if (!trade.ExerciseDate) {
|
|
errorList.push("期权到期日 必须填写");
|
|
} else if (trade.ExerciseDate < trade.TradeDate) {
|
|
errorList.push("期权到期日 不能小于交易日期");
|
|
}
|
|
|
|
if (trade.SettlementDate && trade.SettlementDate < trade.ExerciseDate) {
|
|
errorList.push("期权结算日 不能小于到期日");
|
|
}
|
|
|
|
if (trade.TradeType !== '自定义交易') {
|
|
|
|
if (pageVue.IsTradeVol) {
|
|
if (!_.trim(trade.TradeOpenVolatility)) {
|
|
errorList.push("成交波动率 必须填写");
|
|
}
|
|
if (usedForSave && !_.trim(trade.TradeCloseVolatility)) {
|
|
errorList.push("目标波动率 必须填写");
|
|
}
|
|
|
|
if (!_.trim(trade.NumOfSmoothingDays)) {
|
|
if (usedForSave && (pageVue.IsGuoJun || pageVue.SkewMapVol)) {
|
|
errorList.push("平滑过渡天数 必须填写");
|
|
} else {
|
|
trade.NumOfSmoothingDays = 1;
|
|
}
|
|
} else if (!/^\d+$/.test(trade.NumOfSmoothingDays)) {
|
|
errorList.push("平滑过渡天数 只能为整数");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (trade.UnderlyingInstrumentType === "Stock" || trade.UnderlyingInstrumentType === "StockIndex") {
|
|
if (!trade.Notional) {
|
|
errorList.push("成交份额 必须填写");
|
|
} else if (trade.Notional < 0) {
|
|
errorList.push("成交份额 不能小于0");
|
|
}
|
|
if (!trade.StockEqvNotional) {
|
|
errorList.push("名义本金 必须填写");
|
|
}
|
|
}
|
|
else {
|
|
if (!trade.TradeAmount) {
|
|
errorList.push("交易数量 必须填写");
|
|
} else if (trade.TradeAmount < 0) {
|
|
errorList.push("交易数量 不能小于0");
|
|
}
|
|
}
|
|
|
|
//执行价格可以随意输入
|
|
if (!trade.Strike) {
|
|
trade.Strike = 0;
|
|
}
|
|
|
|
//if (usedForSave && trade.TradeSinglePrice < 0) {
|
|
// errorList.push("权利金 不能小于0");
|
|
//}
|
|
if (!pageVue.TradeEdit && 0 === (parseFloat(trade.TTMDays) || 0)) {
|
|
errorList.push("TTMDays 不能为0");
|
|
}
|
|
|
|
switch (trade.TradeType) {
|
|
case "障碍期权": {
|
|
if (_isNaN(trade.BarrierLow)) {
|
|
errorList.push("障碍价格 必须填写");
|
|
}
|
|
if (!trade.BarrierType) {
|
|
errorList.push("障碍类型 必须填写");
|
|
} else if (trade.BarrierType.startsWith("双") && _isNaN(trade.BarrierHigh)) {
|
|
errorList.push("高障碍价格 必须填写");
|
|
}
|
|
break;
|
|
}
|
|
case "双鲨期权": {
|
|
if (_isNaN(trade.BarrierLow)) {
|
|
errorList.push("障碍价格 必须填写");
|
|
}
|
|
if (_isNaN(trade.BarrierHigh)) {
|
|
errorList.push("高障碍价格 必须填写");
|
|
}
|
|
break;
|
|
}
|
|
case "气囊结构": {
|
|
if (_isNaN(trade.BarrierLow)) {
|
|
errorList.push("障碍价格 必须填写");
|
|
}
|
|
break;
|
|
}
|
|
case "二元期权": {
|
|
if (trade.PayoffType !== "AssetOrNothing" && _isNaN(trade.IsUsePremiumRate ? trade.CashOrNothingAmountRate : trade.CashOrNothingAmount)) {
|
|
errorList.push("补偿金额 必须填写");
|
|
}
|
|
break;
|
|
}
|
|
case "雪球期权": {
|
|
_isNaN(trade.KOBarrier) && errorList.push("敲出障碍价格 必须填写");
|
|
if (trade.KOPayoffType > 0) { //KOPayoffType可能为string类型
|
|
_isNaN(trade.SpreadStrikeAtKO1) && errorList.push("敲出行权价1 必须填写");
|
|
trade.KOPayoffType > 1 && _isNaN(trade.SpreadStrikeAtKO) && errorList.push("敲出行权价2 必须填写");
|
|
}
|
|
break;
|
|
}
|
|
case "凤凰期权": {
|
|
_isNaN(trade.KOBarrier) && errorList.push("敲出障碍价格 必须填写");
|
|
_isNaN(trade.KIBarrier) && errorList.push("敲入障碍价格 必须填写");
|
|
break;
|
|
}
|
|
case "亚式期权": {
|
|
if (trade.StrikeType === 'Segmented' && trade.PayoffType !== 'EnhancedArithmeticAverage') {
|
|
errorList.push("行权价类型为'分段式'时,均价计算类型必须为'增强算术平均'");
|
|
}
|
|
if (trade.PayoffType === 'EnhancedArithmeticAverage' && trade.StrikeType === 'Floating') {
|
|
errorList.push("均价计算类型为'增强算术平均'时,行权价类型不能为'浮动行权价'");
|
|
}
|
|
if (trade.PayoffType === 'EnhancedArithmeticAverage' && trade.StrikeType != 'Floating' && !_isNaN(trade.EnhancedPrice) && trade.EnhancedPrice == 0) {
|
|
errorList.push("均价计算类型为'增强算术平均'且行权价类型不为'浮动行权价'时,增强价格必须填写");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return errorList;
|
|
}
|
|
|
|
//IsMoneynessOption切换处理,配置:consMoneynessFieldMap
|
|
function _moneynessSwitch(trade) {
|
|
let spotPrice = parseFloat(trade.SpotPrice) || 0;
|
|
let isSpotZero = Math.abs(spotPrice) < 1e-4;
|
|
let fmt = trade.IsMoneynessOption === "是" ? consNumberFormat.umpriceP : pricingFormat.umprice;
|
|
if (trade.IsMoneynessOption === "是") {
|
|
trade.Strike = isSpotZero ? 1 : fmt(trade.Strike / spotPrice);
|
|
trade.EnhancedPrice = isSpotZero ? 1 : fmt(trade.EnhancedPrice / spotPrice);
|
|
_.each(consMoneynessFieldMap, (perKey, absKey) => {
|
|
var absValue = parseFloat(trade[absKey]);
|
|
if (isSpotZero || !absValue) {
|
|
trade[absKey] = '', trade[perKey] = '';
|
|
} else {
|
|
trade[perKey] = fmt(absValue / spotPrice);
|
|
}
|
|
});
|
|
} else {
|
|
//避免受到onChangeInstrumentType影响
|
|
trade.Strike = fmt(trade.Strike * spotPrice);
|
|
trade.EnhancedPrice = fmt(trade.EnhancedPrice * spotPrice);
|
|
_.each(consMoneynessFieldMap, (perKey, absKey) => {
|
|
var perVal = parseFloat(trade[perKey]);
|
|
if (isSpotZero || !perVal) {
|
|
trade[absKey] = '', trade[perKey] = '';
|
|
} else {
|
|
trade[absKey] = fmt(perVal * spotPrice);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
//IsUsePremiumRate切换处理,配置:consUsePremiumRateFieldMap
|
|
function _usePremiumRateSwitch(trade) {
|
|
let spotPrice = Math.abs(parseFloat(trade.SpotPrice)) || 0;
|
|
let isSpotZero = spotPrice < 1e-4;
|
|
let fmt = trade.IsUsePremiumRate ? pricingFormat.premiumRate : pricingFormat.tradeSinglePrice;
|
|
if (trade.IsUsePremiumRate) {
|
|
trade.PremiumRate = isSpotZero ? '' : fmt(trade.TradeSinglePrice / spotPrice);
|
|
_.each(consUsePremiumRateFieldMap, (perKey, absKey) => {
|
|
var absValue = parseFloat(trade[absKey]);
|
|
if (isSpotZero || !absValue) {
|
|
trade[absKey] = '', trade[perKey] = '';
|
|
} else {
|
|
trade[perKey] = fmt(absValue / spotPrice);
|
|
}
|
|
});
|
|
} else {
|
|
trade.TradeSinglePrice = fmt(spotPrice * trade.PremiumRate);
|
|
_.each(consUsePremiumRateFieldMap, (perKey, absKey) => {
|
|
var perVal = parseFloat(trade[perKey]);
|
|
if (isSpotZero || !perVal) {
|
|
trade[absKey] = '', trade[perKey] = '';
|
|
} else {
|
|
trade[absKey] = fmt(perVal * spotPrice);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
//计算或保存时需要根据IsMoneynessOption转换隐含字段
|
|
//转换处理和moneynessSwitch正好反过来(如果字段不同,相同时不做转换)
|
|
function _transforCalcOrSave(trade) {
|
|
let spotPrice = Math.abs(parseFloat(trade.SpotPrice)) || 0;
|
|
let isSpotZero = spotPrice < 1e-4;
|
|
let IsMoneynessOption = trade.IsMoneynessOption === "是";
|
|
let fmt = IsMoneynessOption ? pricingFormat.umprice : consNumberFormat.umpriceP;
|
|
_.each(consMoneynessFieldMap, (perKey, absKey) => {
|
|
if (absKey !== perKey) {
|
|
if (IsMoneynessOption) {
|
|
//从百分比转换绝对值
|
|
var perVal = trade[perKey];
|
|
if (isSpotZero || !perVal) {
|
|
trade[absKey] = '';
|
|
} else {
|
|
trade[absKey] = fmt(perVal * spotPrice);
|
|
}
|
|
} else {
|
|
//从绝对值转换百分比
|
|
var absValue = parseFloat(trade[absKey]);
|
|
if (isSpotZero || !absValue) {
|
|
trade[absKey] = '', trade[perKey] = '';
|
|
} else {
|
|
trade[perKey] = fmt(absValue / spotPrice);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
fmt = trade.IsUsePremiumRate ? pricingFormat.tradeSinglePrice : pricingFormat.premiumRate;
|
|
_.each(consUsePremiumRateFieldMap, (perKey, absKey) => {
|
|
if (absKey !== perKey) {
|
|
if (trade.IsUsePremiumRate) {
|
|
//从百分比转换绝对值
|
|
var perVal = trade[perKey];
|
|
if (isSpotZero || !perVal) {
|
|
trade[absKey] = '';
|
|
} else {
|
|
trade[absKey] = fmt(perVal * spotPrice);
|
|
}
|
|
} else {
|
|
//从绝对值转换百分比
|
|
var absValue = parseFloat(trade[absKey]);
|
|
if (isSpotZero || !absValue) {
|
|
trade[absKey] = '', trade[perKey] = '';
|
|
} else {
|
|
trade[perKey] = fmt(absValue / spotPrice);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
//为计算和保存准备交易数据
|
|
function _prepareTrades(trades, volType, usedForSave) {
|
|
|
|
if (!Array.isArray(trades)) {
|
|
throw '请确保参数有效:trades';
|
|
}
|
|
|
|
//复制后检查交易数据
|
|
var checkResults = [], clones = [];
|
|
|
|
trades.forEach(trade => {
|
|
var clone = _.clone(trade);
|
|
_transforCalcOrSave(clone);
|
|
!('NoRiskRate' in clone) && (clone.NoRiskRate = 0);
|
|
//skew模式下不传voltype则根据var处理
|
|
clone.VolType = pageVue.SkewMapVol ? '' : _volTypeConvert(volType, clone.buysell);
|
|
clone.StartDate = clone.TradeDate;
|
|
clones.push(clone);
|
|
var errors = _checkTrade(clone, usedForSave);
|
|
if (errors.length) {
|
|
checkResults.push({ index: clone.CalcId, errors: errors });
|
|
} else if (clone.TradeType === "障碍期权") {
|
|
clone.BarrierHigh && !clone.BarrierType.startsWith("双") && (clone.BarrierHigh = '');
|
|
clone.RebateHigh && !clone.BarrierType.startsWith("双障碍敲出") && (clone.RebateHigh = '');
|
|
clone.RebateHighRate && !clone.BarrierType.startsWith("双障碍敲出") && (clone.RebateHighRate = '');
|
|
} else if (clone.TradeType === "亚式期权") {
|
|
clone.Strike && clone.StrikeType === "Floating" && (clone.Strike = '');
|
|
} else if (clone.TradeType === "累计期权") {
|
|
if (pageData.ShowMultiplier) {
|
|
let strikeGearingFactor = parseFloat(clone.StrikeGearingFactor);
|
|
Number.isNaN(strikeGearingFactor) && (strikeGearingFactor = 1);
|
|
if (clone.OptionType === '看涨') {
|
|
clone.CallMultiplier = 1;
|
|
clone.PutMultiplier = strikeGearingFactor;
|
|
} else {
|
|
clone.PutMultiplier = 1;
|
|
clone.CallMultiplier = strikeGearingFactor;
|
|
}
|
|
}
|
|
}
|
|
if (pageData.showCCR) {
|
|
clone.MetaDic["ccr_k"] = clone.ccr_k || "1";
|
|
clone.MetaDic["ignoreRiskExposure"] = clone.ignoreRiskExposure || "0";
|
|
}
|
|
});
|
|
|
|
//显示检查错误
|
|
if (checkResults.length) {
|
|
let isTradeEdit = usedForSave
|
|
var htmlResults = checkResults.map(x => {
|
|
if (pageVue.TradeEdit) {
|
|
return x.errors.map(x => '<tr><td>' + x + '</td></tr>');
|
|
}
|
|
var errmsg = x.errors.join('<br>');
|
|
return `<tr><th>${x.index}</th><td>${errmsg}</td></tr>`;
|
|
});
|
|
$('#checkErrorTable').find('tbody').html(htmlResults.join());
|
|
$('#checkErrorModal').modal('show');
|
|
return false;
|
|
}
|
|
|
|
return clones;
|
|
}
|
|
|
|
//更改交易的资产类型时的处理
|
|
function onChangeInstrumentType(trade) {
|
|
_.each(consMoneynessFieldMap, (perKey, absKey) => {
|
|
trade[absKey] = '', trade[perKey] = '';
|
|
});
|
|
|
|
_.each(consUsePremiumRateFieldMap, (perKey, absKey) => {
|
|
trade[absKey] = '', trade[perKey] = '';
|
|
});
|
|
}
|
|
|
|
function _getEngineNames(trade) {
|
|
if (trade.TradeType === '凤凰期权' || trade.TradeType === '雪球期权') {
|
|
return consEngineNames.Autocall;
|
|
} else if (trade.TradeType === '香草期权' && trade.ExerciseMode === 'American') {
|
|
return consEngineNames.American;
|
|
}
|
|
else {
|
|
return consEngineNames.Default;
|
|
}
|
|
}
|
|
|
|
//变更预付金模板
|
|
function changeMarginTemplate(trade, tradeMarginTemplates) {
|
|
if (trade.MarginTemplateName === "系统默认") {
|
|
trade.MarginType = 0;
|
|
trade.MarginRate = 0;
|
|
trade.PositionMarginRate = 0;
|
|
}
|
|
else if (trade.MarginTemplateName === "无预付金") {
|
|
trade.MarginType = 1;
|
|
trade.MarginRate = 0;
|
|
trade.PositionMarginRate = 0;
|
|
}
|
|
else {
|
|
tradeMarginTemplates.forEach(x => {
|
|
if (trade.MarginTemplateName === x.Name) {
|
|
trade.MarginType = x.MarginType;
|
|
trade.MarginRate = x.InitialMarginRatio;
|
|
if (trade.MarginType == 3) {
|
|
trade.PositionMarginRate = 0;
|
|
}
|
|
else {
|
|
trade.PositionMarginRate = x.PositionMarginRatio;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return {
|
|
getTradeVol: _getTradeVol,
|
|
checkTrade: _checkTrade,
|
|
prepareTrades: _prepareTrades,
|
|
moneynessSwitch: _moneynessSwitch,
|
|
usePremiumRateSwitch: _usePremiumRateSwitch,
|
|
onChangeInstrumentType: onChangeInstrumentType,
|
|
getEngineNames: _getEngineNames,
|
|
normalizeTrade(trade) {
|
|
_.each(trade, (val, key) => {
|
|
//标准化trade日期数据
|
|
typeof val === 'string' && key.endsWith('Date') && (trade[key] = val.substr(0, 10));
|
|
});
|
|
},
|
|
resetCashFlow(trade) {
|
|
trade.IsUsePremiumRate = true;
|
|
for (var f of consCashFlowFields) {
|
|
trade[f] = '';
|
|
}
|
|
trade.RateType || (trade.RateType = 0);
|
|
trade.DepositType || (trade.DepositType = 0);
|
|
},
|
|
changeMarginTemplate: changeMarginTemplate
|
|
};
|
|
}());
|
|
|
|
//交易字段管理
|
|
const tradeFieldMgr = (new function () {
|
|
|
|
pageVue.Trade.EncryptId = '';
|
|
|
|
tradeUtils.normalizeTrade(pageVue.Trade);
|
|
|
|
//默认数据(初次添加定价时使用)
|
|
const DefaultData = {
|
|
trade: pageVue.Trade,
|
|
viewState: {
|
|
variety: tradeHelper.getEmptyVariety(),
|
|
underlying: tradeHelper.getEmptyUnderlying(),
|
|
ShowTitle: false,
|
|
VolState: 'system',
|
|
MidVolState: 'system',
|
|
CloseVolState: 'system',
|
|
SpotPrice: 'system',
|
|
TTMDays: 'system',
|
|
InitialMargin: 'system',
|
|
TradeOpenVolatility: 'system',
|
|
NoRiskRate: 'system',
|
|
DividendRate: 'system',
|
|
TwoSideMargin: false,
|
|
Structure: "",
|
|
CashOrPhysical: "Cash",
|
|
Observation: {
|
|
hasValue: false,
|
|
ObservationDates: '',
|
|
ObservationNum: 1, ObservationUnit: 'D',
|
|
ObservationHolidayType: 'Following', ObservationAlignEnd: true
|
|
},
|
|
KOObservation: {
|
|
hasValue: false,
|
|
ObservationDates: '', KOObservationDates: '',
|
|
ObservationNum: 1, ObservationUnit: 'D', CouponDayInterval: 0,
|
|
ObservationHolidayType: 'Following', ObservationAlignEnd: true
|
|
},
|
|
KOObservationSettle: {
|
|
hasValue: false,
|
|
ObservationDates: '',
|
|
ObservationNum: 1, ObservationUnit: 'D',
|
|
ObservationHolidayType: 'Following', ObservationAlignEnd: true
|
|
},
|
|
synthetic: null,
|
|
initFlag: '',
|
|
AnnualizeFactor: { ttmDays: 0, daysInYear: pageVue.DaysInYear, refDays: 0 },
|
|
AnnualizeFactor2: { ttmDays: 0, daysInYear: pageVue.DaysInYear, refDays: 0 },
|
|
Extend: { TradingPlace: "柜台市场", ClearingAgency: "交易员方" }
|
|
}
|
|
};
|
|
|
|
this.defaultData = DefaultData;
|
|
|
|
//期权基本要素字段(是否展示)
|
|
const BasicFields = {
|
|
ExerciseMode(trade) {
|
|
switch (trade.TradeType) {
|
|
default: return true;
|
|
case '凤凰期权': case '雪球期权': case '自定义交易': case '现金流交易':
|
|
trade.ExerciseMode = "European"; return false;
|
|
case '二元期权': return trade.ExerciseMode === "European" ? 1 : 11;
|
|
}
|
|
},
|
|
OptionType(trade) {
|
|
switch (trade.TradeType) {
|
|
default: return true;
|
|
case '区间累积期权': case '自定义交易': case '双鲨期权':
|
|
case '气囊结构': case '收益增强结构': case '现金流交易': return false;
|
|
case '二元期权': return trade.ExerciseMode === 'European';
|
|
}
|
|
},
|
|
SettlementType(trade) {
|
|
return trade.TradeType !== '自定义交易';
|
|
},
|
|
DividendRate(trade) {
|
|
return (trade.UnderlyingInstrumentType === 'Stock' || trade.UnderlyingInstrumentType === 'StockIndex');
|
|
},
|
|
ShowTotal(trade) {
|
|
return trade.StructureType;
|
|
}
|
|
};
|
|
|
|
//期权奇异字段配置
|
|
const OptionFields = Object.freeze({
|
|
"障碍期权": {
|
|
BarrierOption: true, ObservationDates: '', ObservationDatesShow: '占位符',
|
|
BarrierType: "上升敲入", BarrierLow: '', BarrierHigh: '',
|
|
MonitorType: "离散", RebateType: "AtHit", Rebate: '', RebateRate: '', BarrierShift: '',
|
|
RebateHigh: '', RebateHighRate: '',
|
|
IsBarrierType: {
|
|
init: '', visible(trade) { return trade.BarrierType === "双障碍敲出"; }
|
|
},
|
|
//MonitorType替代了Discrete,BarrierLow替代了BarrierPrice,BarrierHigh替代了UpperBarrierPrice
|
|
},
|
|
"双鲨期权": {
|
|
DBSharkOption: true, ObservationDates: '', ObservationDatesShow: '占位符',
|
|
MonitorType: '离散', BarrierLow: '', BarrierHigh: '', StrikeHigh: '',
|
|
RebateType: 'AtHit', Rebate: '', RebateRate: '', RebateHigh: '', RebateHighRate: ''
|
|
//MonitorType替代了Discrete
|
|
},
|
|
"二元期权": {
|
|
BinaryOption: true, PayoffType: '', ObservationDates: '', ObservationDatesShow: '占位符',
|
|
BarrierHigh: '', MonitorType: "离散", RebateType: "AtHit",
|
|
CashOrNothingAmount: '', CashOrNothingAmountRate: '',
|
|
CashOrNothingAmountHigh: '', CashOrNothingAmountHighRate: '',
|
|
PayoffTypeShow: {
|
|
init: '', visible(trade) { return (trade.PayoffType || '').startsWith('Double'); }
|
|
}
|
|
//BarrierHigh替代了UpperBarrier
|
|
},
|
|
"亚式期权": {
|
|
AsiaOption: true,
|
|
PayoffType: 'ArithmeticAverage',
|
|
StrikeType: "Fixed", StrikeGearingFactor: 1,
|
|
AveragingPeriodStartDate: pageVue.SysDate,
|
|
EnhancedPrice: 0,
|
|
},
|
|
"彩虹期权": {
|
|
RainbowOption: true
|
|
},
|
|
"合成价差期权": {
|
|
ComSpreadOption: true
|
|
},
|
|
"区间累积期权": {
|
|
RangeAcc: true, ObservationDatesShow: true,
|
|
ObservationDates: '', UpperRange: '', LowerRange: '', BonusRate: ''
|
|
},
|
|
"凤凰期权": {
|
|
AutoCall: true,
|
|
IsFixedCoupon: false, Coupon: '', CouponBarrier: '',
|
|
CouponPayType: 0, KOObservationDates: '', KOBarrier: '',
|
|
ObservationDates: '', KIBarrier: '', IncludeCouponAfterKI: true, KIPayoffType: 1,
|
|
SpreadStrike1: {
|
|
init: '', visible(trade) { return trade.KIPayoffType > 0; }
|
|
},
|
|
SpreadStrike: {
|
|
init: '', visible(trade) { return trade.KIPayoffType === 2 || trade.KIPayoffType === 4; }
|
|
},
|
|
beforeCheck(trade) {
|
|
return {
|
|
KIPayoffType: parseInt(trade.KIPayoffType) || 1
|
|
};
|
|
}
|
|
},
|
|
"雪球期权": {
|
|
SnowBall: true,
|
|
KOBarrier: '', KOObservationDates: '',
|
|
KOPayoffType: 0, KORebateType: 0, Coupon: '',
|
|
IsFixedCoupon: {
|
|
init: false, visible(trade) { return trade.KOPayoffType === 0; }
|
|
},
|
|
KORebate: {
|
|
init: '', visible(trade) { return trade.KOPayoffType === 0; }
|
|
},
|
|
AnnualizedPremiumRate: {
|
|
init: '', visible(trade) { return trade.KOPayoffType === 0; }
|
|
},
|
|
SpreadStrikeAtKO1: {
|
|
init: '', visible(trade) { return trade.KOPayoffType > 0; }
|
|
},
|
|
SpreadStrikeAtKO: {
|
|
init: '', visible(trade) { return trade.KOPayoffType > 1; }
|
|
},
|
|
KIBarrier: '', ObservationDates: '', KIPayoffType: 0,
|
|
//SpreadStrike替代了SpreadStrikeAtMaturity
|
|
SpreadStrike1: {
|
|
init: '', visible(trade) { return trade.KIPayoffType > 0 || trade.IsInitialKnockedIn; }
|
|
},
|
|
SpreadStrike: {
|
|
init: '', visible(trade) { return trade.KIPayoffType === 2 || trade.KIPayoffType === 4; }
|
|
},
|
|
beforeCheck(trade) {
|
|
return {
|
|
KOPayoffType: parseInt(trade.KOPayoffType) || 0,
|
|
KIPayoffType: parseInt(trade.KIPayoffType) || 0
|
|
};
|
|
}
|
|
},
|
|
"气囊结构": {
|
|
AirBag: true, BarrierLow: '', KIParticipationRate: '',
|
|
HasPayoffLimit: false, HighStrike: '', IsDiscreteMonitored: false
|
|
//BarrierLow替代了Barrier
|
|
},
|
|
"收益增强结构": {
|
|
UnEnhance: true, AnnualizedEnhanceRate: ''
|
|
},
|
|
"现金流交易": {
|
|
Cashflow: true, ProfitRate: '', RateType: "0", DepositType: "0", PrepayRatio: '0'
|
|
},
|
|
"自定义交易": { ObservationDatesShow: '占位符', Custom: true },
|
|
"累计期权": {
|
|
Accumulator: true, PayoffType: '浮动', IsFixedCoupon: true, CouponDayCount: 'Act365',
|
|
StrikeGearingFactor: 1, CallMultiplier: 1, PutMultiplier: 1, EarlyTerminate: false, SettlementMode: '现金当日',
|
|
KOObservationDates: '', CouponPercent: true, AccumuType: '', AccumuTradeAmount: ''
|
|
},
|
|
"结构化产品": {
|
|
Structure: true
|
|
}
|
|
});
|
|
|
|
//期权字段状态
|
|
const OptionFieldState = _.reduce(OptionFields, (acc, cur) => {
|
|
return _.reduce(cur, (acc, val, key) => { acc[key] = false; return acc; }, acc);
|
|
}, {});
|
|
|
|
for (k of Object.keys(BasicFields)) {
|
|
OptionFieldState[k] = true;
|
|
}
|
|
|
|
//浮窗标题状态
|
|
const FloatingTitleState = Object.assign({}, OptionFieldState);
|
|
|
|
//获取标题状态
|
|
this.getTitleState = function (floating) {
|
|
return FloatingTitleState;
|
|
//return floating ? FloatingTitleState : OptionFieldState;
|
|
};
|
|
|
|
//重置字段状态
|
|
function __resetFieldState(trade, blreset) {
|
|
let self = this;
|
|
for ([k, v] of Object.entries(BasicFields)) {
|
|
self[k] = v(trade);
|
|
}
|
|
let fields = OptionFields[trade.TradeType];
|
|
if (!fields) return;
|
|
let chkobj = !blreset && fields.beforeCheck ? fields.beforeCheck(trade) || trade : trade;
|
|
_.each(fields, (val, key) => {
|
|
let blset = blreset && key in trade;
|
|
if (typeof val === 'object') {
|
|
self[key] = val.visible(chkobj);
|
|
|
|
if (blset || !self[key]) {
|
|
trade[key] = val.init;
|
|
}
|
|
} else {
|
|
self[key] = true;
|
|
blset && (trade[key] = val);
|
|
}
|
|
});
|
|
|
|
if (!blreset) return;
|
|
|
|
if (trade.TradeType === '二元期权') {
|
|
trade.PayoffType = trade.ExerciseMode === 'European' ? 'CashOrNothing' : 'UpOneTouch';
|
|
trade.RebateAnnualizedAtKO = trade.ExerciseMode !== 'European';
|
|
} else if (trade.TradeType === '现金流交易') {
|
|
trade.IsUsePremiumRate = true;
|
|
} else if (trade.TradeType === '累计期权') {
|
|
trade.IsAnnualized = false;
|
|
trade.ParticipationRate = '';
|
|
}
|
|
}
|
|
|
|
const AnnualCallControl = [null, 0];
|
|
|
|
//获取字段状态
|
|
this.getFieldState = function (floating) {
|
|
|
|
let clone = Object.assign({}, OptionFieldState);
|
|
|
|
//重置字段状态
|
|
clone.resetFieldState = function (trade, blreset) {
|
|
__resetFieldState.call(this, trade, blreset);
|
|
//if (floating) {
|
|
Object.assign(FloatingTitleState, this);
|
|
//}
|
|
};
|
|
|
|
//交易类型变更时重置交易字段值和状态
|
|
clone.onTradeTypeChanged = function (trade, blReset, vueCaller) {
|
|
let self = this, resetValue = !!blReset;
|
|
let oldAnnualFlag = this.SnowBall || this.AutoCall;
|
|
_.each(OptionFieldState, (val, key) => {
|
|
self[key] = 0;
|
|
resetValue && key in trade && !(key in BasicFields) && (trade[key] = '');
|
|
});
|
|
//这里更新可以避免不同期权类型相同字段的困扰
|
|
this.resetFieldState(trade, resetValue);
|
|
//切换年化系数的两种表现形式
|
|
let newAnnualFlag = this.SnowBall || this.AutoCall;
|
|
if (resetValue && (oldAnnualFlag ^ newAnnualFlag) === 1) {
|
|
if ((AnnualCallControl[0] !== trade || AnnualCallControl[1] + 300 < new Date().getTime())) {
|
|
AnnualCallControl[0] = trade;
|
|
AnnualCallControl[1] = new Date().getTime();
|
|
if (newAnnualFlag) {
|
|
trade.IsAnnualized2 = trade.IsAnnualized;
|
|
trade.IsAnnualized = false;
|
|
trade.AnnualizeFactor = null;
|
|
trade.MetaDic['AnnualizeFactor'] = '';
|
|
} else {
|
|
trade.IsAnnualized = trade.IsAnnualized2;
|
|
trade.IsAnnualized2 = false;
|
|
trade.AnnualizeFactor2 = null;
|
|
trade.MetaDic['AnnualizeFactor2'] = '';
|
|
}
|
|
}
|
|
if (vueCaller) {
|
|
var arr = [vueCaller.AnnualizeFactor, vueCaller.AnnualizeFactor2];
|
|
newAnnualFlag && arr.reverse();
|
|
Object.assign(arr[0], arr[1]);
|
|
Object.assign(arr[1], { ttmDays: 0, daysInYear: pageVue.DaysInYear });
|
|
vueCaller.changeIsAnnualized();
|
|
}
|
|
}
|
|
};
|
|
|
|
//设置字段状态
|
|
clone.setFieldState = function (field, blvalue) {
|
|
if (field in this) {
|
|
this[field] = blvalue;
|
|
FloatingTitleState[field] = blvalue;
|
|
//floating && (FloatingTitleState[field] = blvalue);
|
|
}
|
|
};
|
|
|
|
return clone;
|
|
};
|
|
|
|
this.initFieldValue = function (trade) {
|
|
__resetFieldState(trade, true);
|
|
};
|
|
|
|
this.resetFieldState = function (trade) {
|
|
__resetFieldState(trade, false);
|
|
};
|
|
|
|
_.each([pageVue.Trade, DefaultData, DefaultData.viewState], x => Object.freeze(x));
|
|
|
|
}());
|
|
|
|
const consSyntheticMap = {};
|
|
|
|
const consVueTrade = {
|
|
data() {
|
|
return {
|
|
updateKey: { variety: 0, underlying: 0, client: 0, instrumentType: 0, KIPayoffType: 0 },
|
|
AnnualizeFactor: { ttmDays: 0, daysInYear: pageVue.DaysInYear, refDays: 0 },
|
|
AnnualizeFactor2: { ttmDays: 0, daysInYear: pageVue.DaysInYear, refDays: 0 }
|
|
};
|
|
},
|
|
created() {
|
|
if (this.trade.MetaDic['组合标的']) {
|
|
this.viewState.synthetic = JSON.parse(this.trade.MetaDic['组合标的']);
|
|
}
|
|
this.AnnualizeFactor = this.viewState.AnnualizeFactor;
|
|
this.AnnualizeFactor2 = this.viewState.AnnualizeFactor2;
|
|
if (!this.floating) {
|
|
//在修改或组合报价导入时带入年化系数
|
|
for (var key of ['AnnualizeFactor', 'AnnualizeFactor2']) {
|
|
let meta = this.trade.MetaDic[key];
|
|
if (meta) {
|
|
let index = meta.indexOf('/')
|
|
if (index > 0) {
|
|
this[key].ttmDays = parseFloat(meta.substring(0, index)) || 0;
|
|
this[key].daysInYear = parseFloat(meta.substring(index + 1)) || pageVue.DaysInYear;
|
|
}
|
|
}
|
|
}
|
|
this.AnnualizeFactor.refDays = this.AnnualizeFactor2.refDays = this.getAnnualDays() || 0;
|
|
}
|
|
this.viewState.Observation.ObservationDates = this.trade.ObservationDates || "";
|
|
if (this.trade.KOObservationDates && (this.trade.TradeType === '雪球期权' || this.trade.TradeType === "累计期权" && this.trade.PayoffType === '固定')) {
|
|
var arr = this.trade.KOObservationDates.split(';').filter(O => O);
|
|
this.viewState.KOObservation.ObservationDates = [].concat(arr[0], this.trade.KOObservationSettleDates, arr[1], (arr[2] || "0")).join(';');
|
|
} else {
|
|
this.viewState.KOObservation.ObservationDates = this.trade.KOObservationDates || "";
|
|
}
|
|
this.viewState.KOObservationSettle.ObservationDates = this.trade.KOObservationSettleDates || "";
|
|
|
|
if (this.trade.TradeType === '结构化产品') {
|
|
this.viewState.Observation.ObservationDates = this.trade.MetaDic["observationDate"];
|
|
this.viewState.Structure = this.trade.MetaDic["structures"];
|
|
this.viewState.CashOrPhysical = this.trade.MetaDic["cashOrPhysical"];
|
|
}
|
|
|
|
this.viewState.Extend.TradingPlace = this.trade.MetaDic["交易场所"] || "";
|
|
this.viewState.Extend.ClearingAgency = this.trade.MetaDic["清算机构"] || "";
|
|
if (pageData.showCCR) {
|
|
this.trade.ccr_k = this.trade.MetaDic["ccr_k"] || "1";
|
|
this.trade.ignoreRiskExposure = this.trade.MetaDic["ignoreRiskExposure"] || "0";
|
|
}
|
|
this.trade.StockEqvNotionalMax = null;
|
|
this.trade.TradeAmountV = tradeHelper.getTradeAmountV(this.trade, this.trade.Notional, this.trade.CountRatio);
|
|
|
|
if (this.trade.TradeDate && this.trade.ExerciseDate) {
|
|
this.getday();
|
|
}
|
|
|
|
},
|
|
methods: {
|
|
getVol() { },
|
|
//变更买入卖出
|
|
changeBuySell(resetInitialMargin) {
|
|
this.getVol();
|
|
this.changeBuySellEx();
|
|
if (resetInitialMargin) {
|
|
this.trade.InitialMargin = null;
|
|
}
|
|
},
|
|
changeBuySellEx() { },
|
|
//变更资金类型
|
|
changeDepositType() {
|
|
let trade = this.trade
|
|
if (trade.DepositType === "0") {
|
|
this.getNoRiskRate();
|
|
this.changeFieldState();
|
|
} else {
|
|
trade.NoRiskRate = 0;
|
|
this.changeFieldState();
|
|
}
|
|
},
|
|
//变更行权模式
|
|
changeExerciseMode(flag) {
|
|
if (this.trade.TradeType === '二元期权') {
|
|
if (this.trade.ExerciseMode === 'European') {
|
|
this.binaryPayoffTypes = consBinaryPayoffTypes.European;
|
|
this.trade.PayoffType = 'CashOrNothing';
|
|
this.trade.MonitorType = this.trade.RebateType = "";
|
|
this.trade.RebateAnnualizedAtKO = false;
|
|
} else {
|
|
this.binaryPayoffTypes = consBinaryPayoffTypes.American;
|
|
this.trade.PayoffType = 'UpOneTouch';
|
|
this.trade.MonitorType = "离散";
|
|
this.trade.RebateType = "AtHit";
|
|
}
|
|
}
|
|
if (flag !== 'TradeType') {
|
|
this.getVol();
|
|
this.changeFieldState();
|
|
}
|
|
this.refreshEngineNames();
|
|
this.synchTrade();
|
|
},
|
|
//刷新定价模型名称
|
|
refreshEngineNames() {
|
|
this.engineNames = tradeUtils.getEngineNames(this.trade);
|
|
},
|
|
//获取组合标的价格
|
|
getSyntheticPrices(underlyingCode) {
|
|
let self = this;
|
|
main.post('/pricing/AjaxGetSyntheticPriceModel', { underlyingCode: underlyingCode }, false).done(function (resp) {
|
|
self.viewState.synthetic = resp.obj;
|
|
self.trade.SpotPrice = resp.obj.Price;
|
|
});
|
|
},
|
|
//变更标的价格
|
|
changeSpotPrice(isUserInput) {
|
|
this.viewState.SpotPrice = isUserInput ? "user" : "system";
|
|
let SpotPrice = this.trade.SpotPrice || 0;
|
|
if (!SpotPrice || SpotPrice === '-') {
|
|
SpotPrice = 0;
|
|
} else {
|
|
let precision = pricingFormat.umprice.precision || 0;
|
|
if (precision < 2) precision = 2;
|
|
let regex = new RegExp('^-?\\d*(\\.\\d{0,' + precision + '})?');
|
|
let match = regex.exec(SpotPrice);
|
|
if (match) {
|
|
if (match[0] !== SpotPrice) {
|
|
this.trade.SpotPrice = match[0];
|
|
SpotPrice = parseFloat(match[0]);
|
|
}
|
|
} else {
|
|
this.trade.SpotPrice = SpotPrice = 0;
|
|
}
|
|
}
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.SpotPrice2, this.viewState.variety.CountRatio);
|
|
this.getVol(true);
|
|
this.synchTrade();
|
|
},
|
|
blurSpotPrice() {
|
|
!this.trade.SpotPrice && (this.trade.SpotPrice !== 0) && (this.trade.SpotPrice = 0);
|
|
},
|
|
//更改成交方式
|
|
changeIsUsePremiumRate() {
|
|
tradeUtils.usePremiumRateSwitch(this.trade);
|
|
},
|
|
//变更相对行权价
|
|
changeIsMoneynessOption() {
|
|
tradeUtils.moneynessSwitch(this.trade);
|
|
this.changeKOBarrier();
|
|
},
|
|
//变更执行价格
|
|
changeStrike() {
|
|
this.getVol(true);
|
|
},
|
|
//变更敲入行权价1
|
|
changeSpreadStrike1() {
|
|
this.trade.Strike = this.trade.SpreadStrike1;
|
|
},
|
|
//获取标的价格
|
|
getSpotPrice(blSetStrike) {
|
|
var self = this;
|
|
return main.post("/pricing/AjaxGetUnderlyingPrice", { underlyingCode: this.trade.UnderlyingCode, tradeDate: this.trade.PremiumPayDate })
|
|
.done(function (resp) {
|
|
self.viewState.synthetic = consSyntheticMap[self.trade.CalcId] = resp.obj.synthetic;
|
|
self.trade.SpotPrice = pricingFormat.umprice(resp.obj.price);
|
|
if (blSetStrike === true && self.trade.IsMoneynessOption !== "是") {
|
|
self.trade.Strike = self.trade.SpotPrice;
|
|
}
|
|
self.changeSpotPrice(false);
|
|
});
|
|
},
|
|
//获取品种最小变动价格
|
|
getPriceTick() {
|
|
return this.trade.UnderlyingInstrumentType === 'Stock' ? 0.01 :
|
|
this.viewState.variety ? parseFloat(this.viewState.variety.PriceTick) || 0.1 : 0.1;
|
|
},
|
|
//绝对值执行价格
|
|
showAbsStrike() {
|
|
this.trade.IsMoneynessOption = "否";
|
|
tradeUtils.moneynessSwitch(this.trade);
|
|
this.changeKOBarrier();
|
|
},
|
|
//百分比执行价格
|
|
showPercentStrike() {
|
|
this.trade.IsMoneynessOption = "是";
|
|
tradeUtils.moneynessSwitch(this.trade);
|
|
this.changeKOBarrier();
|
|
},
|
|
//变更交易日期
|
|
changeTradeDate(force) {
|
|
this.trade.ValueDate = this.trade.TradeDate;
|
|
if (!pageData.is厦门象屿) {
|
|
this.trade.PremiumPayDate = this.trade.TradeDate;
|
|
}
|
|
if (pageVue.IsCheck) return;
|
|
this.getTTM(force);
|
|
this.getday();
|
|
this.getVol();
|
|
this.getNoRiskRate();
|
|
this.changeIsAnnualized();
|
|
force !== 'import2' && this.getSpotPrice(true);
|
|
this.synchTrade();
|
|
},
|
|
//变更定价日期
|
|
changeValueDate() {
|
|
this.getTTM(true);
|
|
this.synchTrade();
|
|
},
|
|
//变更到期日期
|
|
changeExerciseDate() {
|
|
if (pageData.is厦门象屿) {
|
|
this.trade.PremiumPayDate = this.trade.ExerciseDate;
|
|
}
|
|
if (pageVue.IsCheck) return;
|
|
this.getTTM();
|
|
this.getday();
|
|
this.getVol();
|
|
this.getNoRiskRate();
|
|
this.synchTrade();
|
|
},
|
|
//变更结算日期
|
|
changeSettlementDate() {
|
|
this.changeIsAnnualized();
|
|
this.getTTM();
|
|
this.synchTrade();
|
|
},
|
|
//更改权利金费率
|
|
changePremiumRate() {
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.PremiumRate, this.viewState.variety.CountRatio);
|
|
this.sumTotal();
|
|
},
|
|
//更改权利金单价
|
|
changeTradeSinglePrice() {
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.TradeSinglePrice, this.viewState.variety.CountRatio);
|
|
this.sumTotal();
|
|
},
|
|
//显示权利金绝对值
|
|
showAbsPrice() {
|
|
this.trade.IsUsePremiumRate = false;
|
|
tradeUtils.usePremiumRateSwitch(this.trade);
|
|
this.sumTotal();
|
|
},
|
|
//显示权利金百分比
|
|
showPercentPrice() {
|
|
this.trade.IsUsePremiumRate = true;
|
|
tradeUtils.usePremiumRateSwitch(this.trade);
|
|
this.sumTotal();
|
|
},
|
|
//更改交易总额
|
|
changeTradePrice() {
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.TradePrice, this.viewState.variety.CountRatio);
|
|
this.sumTotal();
|
|
},
|
|
//更改交易份额
|
|
changeNotional(value) {
|
|
(typeof value === 'number') && (this.trade.Notional = value);
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.Notional, this.viewState.variety.CountRatio);
|
|
this.trade.InitialMargin = null;
|
|
this.synchTrade(true);
|
|
},
|
|
//更改有效交易数量
|
|
changeTradeAmount(value) {
|
|
(typeof value === 'number') && (this.trade.TradeAmount = value);
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.TradeAmount, this.viewState.variety.CountRatio);
|
|
this.trade.InitialMargin = null;
|
|
this.synchTrade(true);
|
|
},
|
|
//更改交易数量
|
|
changeTradeAmountV(value) {
|
|
(typeof value === 'number') && (this.trade.TradeAmountV = value);
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.TradeAmountV, this.viewState.variety.CountRatio);
|
|
this.synchTrade(true);
|
|
},
|
|
changeAccumuTradeAmount(value) {
|
|
(typeof value === 'number') && (this.trade.AccumuTradeAmount = value);
|
|
|
|
this.trade.OriginalAccumuTradeAmount = this.trade.AccumuTradeAmount;
|
|
this.trade.TradeAmountV = this.trade.AccumuTradeAmount;
|
|
this.changeTradeAmountV(this.trade.TradeAmountV);
|
|
},
|
|
//变更名义本金
|
|
changeStockEqvNotional(value) {
|
|
(typeof value === 'number') && (this.trade.StockEqvNotional = value);
|
|
if (this.trade.TradeType === "现金流交易") {
|
|
return;
|
|
}
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.StockEqvNotional, this.viewState.variety.CountRatio);
|
|
this.trade.InitialMargin = null;
|
|
this.synchTrade(true);
|
|
},
|
|
//变更有效名义本金
|
|
changeStockEqvNotionalReal(value) {
|
|
(typeof value === 'number') && (this.trade.StockEqvNotionalReal = value);
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.StockEqvNotionalReal, this.viewState.variety.CountRatio);
|
|
this.trade.InitialMargin = null;
|
|
this.synchTrade(true);
|
|
},
|
|
//获取期权年化天数
|
|
getAnnualDays() {
|
|
let dateFrom = _.trim(this.trade.TradeDate).substring(0, 10);
|
|
let dateTo = _.trim(this.trade.SettlementDate).substring(0, 10);
|
|
if (dateFrom && dateTo) {
|
|
dateFrom = new Date(dateFrom).getTime();
|
|
dateTo = new Date(dateTo).getTime();
|
|
return 1 + (dateTo - dateFrom) / 1000 / 3600 / 24;
|
|
}
|
|
return false;
|
|
},
|
|
//更改期权年化
|
|
changeIsAnnualized() {
|
|
let IsAnnualized2 = this.fieldState.SnowBall || this.fieldState.AutoCall;
|
|
if (!(IsAnnualized2 ? this.trade.IsAnnualized2 : this.trade.IsAnnualized)) {
|
|
//|| pageVue.TradeEdit && this.AnnualizeFactor.ttmDays > 0
|
|
return this.changeAnnualizeFactor();
|
|
}
|
|
let factor = this[IsAnnualized2 ? "AnnualizeFactor2" : "AnnualizeFactor"];
|
|
let days = this.getAnnualDays();
|
|
if (days !== false) {
|
|
factor.ttmDays = factor.refDays = days;
|
|
this.changeAnnualizeFactor();
|
|
} else {
|
|
factor.refDays = 0;
|
|
}
|
|
this.synchTrade();
|
|
},
|
|
//更改期权年化ttmday与参考日期间隔的差值
|
|
changeAnnualDayDiff(diff) {
|
|
if (this.annualDayDiff !== diff) {
|
|
this.AnnualizeFactor.ttmDays = this.AnnualizeFactor.refDays + diff;
|
|
this.changeAnnualizeFactor();
|
|
}
|
|
},
|
|
//更改期权年化ttmday与参考日期间隔的差值
|
|
changeAnnualDayDiff2(diff) {
|
|
if (this.annualDayDiff2 !== diff) {
|
|
this.AnnualizeFactor2.ttmDays = this.AnnualizeFactor2.refDays + diff;
|
|
this.changeAnnualizeFactor();
|
|
}
|
|
},
|
|
//更改期权年化
|
|
changeAnnualizeFactor() {
|
|
let IsAnnualized2 = this.fieldState.SnowBall || this.fieldState.AutoCall;
|
|
let propKey = IsAnnualized2 ? 'AnnualizeFactor2' : 'AnnualizeFactor';
|
|
let factor = IsAnnualized2 ? this.AnnualizeFactor2 : this.AnnualizeFactor;
|
|
if (IsAnnualized2 ? this.trade.IsAnnualized2 : this.trade.IsAnnualized) {
|
|
this.trade[propKey] = Math.abs(factor.ttmDays / factor.daysInYear);
|
|
this.trade.MetaDic[propKey] = factor.ttmDays.toString() + '/' + factor.daysInYear.toString();
|
|
} else {
|
|
this.trade[propKey] = 1;
|
|
this.trade.MetaDic[propKey] = '';
|
|
factor.ttmDays = 0;
|
|
factor.daysInYear = pageVue.DaysInYear;
|
|
}
|
|
this.changeEffectRatio();
|
|
this.synchTrade();
|
|
},
|
|
//变更参与率/年化系数/保底收益率
|
|
changeEffectRatio() {
|
|
if (this.trade.IsUsePremiumRate) {
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.StockEqvNotional, this.viewState.variety.CountRatio);
|
|
}
|
|
else {
|
|
if (this.trade.TradeAmountV > 0) {
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.TradeAmountV, this.viewState.variety.CountRatio);
|
|
}
|
|
else {
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.TradeAmount, this.viewState.variety.CountRatio);
|
|
}
|
|
}
|
|
this.trade.InitialMargin = null;
|
|
},
|
|
changePrincipalSum() {
|
|
tradePricing.tradeCalc(this.trade, tradePricing.calcReason.OriginalPrincipalSum, this.viewState.variety.CountRatio);
|
|
this.trade.InitialMargin = null;
|
|
},
|
|
//变更成交波动率
|
|
changeOpenVolatility() {
|
|
this.viewState.VolState = "user";
|
|
this.synchTrade();
|
|
},
|
|
//变更Mid波动率
|
|
changeOpenMidVolatility() {
|
|
this.viewState.MidVolState = "user";
|
|
this.synchTrade();
|
|
},
|
|
//变更目标波动率
|
|
changeCloseVolatility() {
|
|
this.viewState.CloseVolState = "user";
|
|
},
|
|
//变更TTMDays
|
|
changeTTM() {
|
|
this.viewState.TTMDays = "user";
|
|
this.trade.IsTTMSystem = false;
|
|
if (pageData.is厦门象屿) {
|
|
this.trade.NumOfSmoothingDays = this.trade.TTMDays >= 10 ? 5 : 0;
|
|
}
|
|
this.synchTrade();
|
|
},
|
|
//获取交易日期和到期日期之间工作日的长度
|
|
getTTM(force, changeNumOfSmoothingDays = true) {
|
|
if (force) {
|
|
this.viewState.TTMDays = "system";
|
|
this.trade.IsTTMSystem = true;
|
|
}
|
|
var self = this;
|
|
if (this.viewState.TTMDays !== 'user') {
|
|
var data = {
|
|
from: this.trade.ValueDate || this.trade.TradeDate,
|
|
to: this.trade.ExerciseDate,
|
|
varietyid: this.viewState.variety.id
|
|
};
|
|
if (data.from && data.to) {
|
|
main.post("/calendar/DaysInPeriod", data, false).done(function (resp) {
|
|
self.trade.TTMDays = consNumberFormat.ttmDaysFmt(resp.obj);
|
|
var days = Number.parseInt(self.trade.TTMDays);
|
|
if (changeNumOfSmoothingDays && pageVue.NumOfSmoothingDaysCfg === 'TTM') {
|
|
self.trade.NumOfSmoothingDays = days < 1 ? 1 : days;
|
|
if (pageData.is厦门象屿) {
|
|
self.trade.NumOfSmoothingDays = days >= 10 ? 5 : 0;
|
|
}
|
|
}
|
|
if (pageData.is厦门象屿) {
|
|
self.trade.NumOfSmoothingDays = days >= 10 ? 5 : 0;
|
|
}
|
|
});
|
|
} else {
|
|
self.trade.TTMDays = '';
|
|
}
|
|
}
|
|
},
|
|
getday() {
|
|
var self = this;
|
|
var data = {
|
|
from: this.trade.TradeDate || this.trade.ValueDate,
|
|
to: this.trade.ExerciseDate
|
|
};
|
|
if (data.from && data.to) {
|
|
main.post("/calendar/GetDay", data, false).done(function (resp) {
|
|
var TradingDays = Number.parseInt(resp.obj.TradingDays);
|
|
var Weekdays = Number.parseInt(resp.obj.Weekdays);
|
|
var PublicHolidays = Weekdays - TradingDays;
|
|
self.trade.TradingDays = TradingDays;
|
|
self.trade.Weekdays = Weekdays;
|
|
self.trade.PublicHolidays = PublicHolidays;
|
|
});
|
|
} else {
|
|
self.trade.Weekdays = '';
|
|
self.trade.TradingDays = '';
|
|
self.trade.PublicHolidays = '';
|
|
}
|
|
},
|
|
//变更初始预付金
|
|
changeInitialMargin() {
|
|
this.viewState.InitialMargin = 'user';
|
|
this.resetSummary(resetSummaryFlags.initialMargin);
|
|
this.sumTotal();
|
|
},
|
|
//获取系统计算的初始预付金
|
|
getInitialMargin() {
|
|
if (this.trade.TradeType === "自定义交易") {
|
|
this.trade.TradeOpenVolatility = null;
|
|
this.trade.Vol = null;
|
|
}
|
|
//检查并转换数据
|
|
var trades = tradeUtils.prepareTrades([this.trade], pageVue.getVolType(), false);
|
|
if (!trades) return;
|
|
var self = this;
|
|
main.post("/pricing/AjaxGetInitialMargin", { trade: trades[0] })
|
|
.done(function (resp) {
|
|
self.trade.InitialMargin = parseFloat(resp.obj) + self.trade.TradePrice * (self.trade.BuySell == "买入" ? -1 : 1);
|
|
//非双向追保交易员收预付金,预付金不能小于0;交易员买入,付期权费收预付金,预付金不能小于0;交易员卖出,收期权费付预付金,预付金不能大于0
|
|
if (pageVue.ClientUsedForCalc && !self.viewState.TwoSideMargin && self.trade.InitialMargin < 0) {
|
|
self.trade.InitialMargin = 0;
|
|
}
|
|
else if (self.trade.BuySell == "卖出" && self.trade.InitialMargin > 0) {
|
|
self.trade.InitialMargin = 0;
|
|
}
|
|
else if (self.trade.BuySell == "买入" && self.trade.InitialMargin < 0) {
|
|
self.trade.InitialMargin = 0;
|
|
}
|
|
self.trade.InitialMargin = pricingFormat.tradePrice(self.trade.InitialMargin);
|
|
self.viewState.InitialMargin = "system";
|
|
self.resetSummary(resetSummaryFlags.initialMargin);
|
|
self.sumTotal();
|
|
});
|
|
},
|
|
resetSummary() { },
|
|
//获取系统波动率
|
|
getTradeOpenVolatility(flag) {
|
|
var self = this;
|
|
var closeVol = this.trade.TradeCloseVolatility;
|
|
tradeUtils.getTradeVol(this.trade, flag === 'user' ? {
|
|
BidVar: this.trade.Var,
|
|
AskVar: this.trade.Var,
|
|
BaseVol: isNaN(closeVol) ? '' : closeVol
|
|
} : {}, this.viewState.variety.id).done(function (resp) {
|
|
var vol = resp.obj.vol;
|
|
if (isNaN(vol)) {
|
|
let msg = resp.msg || resp.message;
|
|
return main.message("未获取到波动率: " + msg);
|
|
}
|
|
self.viewState.VolState = flag === 'user' ? "user" : "system";
|
|
var formatVol = consNumberFormat.volValueFmt(vol);
|
|
if (pageVue.SkewMapVol) {
|
|
self.trade.Var = resp.obj.var;
|
|
self.trade.TradeOpenVolatility = formatVol;
|
|
self.trade.TradeCloseVolatility = consNumberFormat.volValueFmt(resp.obj.baseVol);
|
|
} else if (pageVue.IsTradeVol) {
|
|
self.trade.TradeOpenVolatility = formatVol;
|
|
self.trade.Vol = formatVol;
|
|
}
|
|
else {
|
|
self.trade.Vol = formatVol;
|
|
}
|
|
});
|
|
},
|
|
//获取系统Mid波动率
|
|
getTradeMidVolatility(flag) {
|
|
var self = this;
|
|
var closeVol = this.trade.TradeCloseVolatility;
|
|
tradeUtils.getTradeVol(this.trade, flag === 'user' ? {
|
|
BidVar: this.trade.Var,
|
|
AskVar: this.trade.Var,
|
|
BaseVol: isNaN(closeVol) ? '' : closeVol
|
|
} : {}, this.viewState.variety.id).done(function (resp) {
|
|
var Midvol = resp.obj.Midvol;
|
|
if (isNaN(Midvol)) {
|
|
let msg = resp.msg || resp.message;
|
|
return main.message("未获取到Mid波动率: " + msg);
|
|
}
|
|
self.viewState.MidVolState = flag === 'user' ? "user" : "system";
|
|
var formatVol = consNumberFormat.volValueFmt(Midvol);
|
|
self.trade.MidVol = formatVol;
|
|
});
|
|
},
|
|
//获取隐含波动率
|
|
getTradeImpliedVol(flag) {
|
|
var self = this;
|
|
if (!this.trade.OptionType) {
|
|
return main.message("请填写看涨看跌");
|
|
}
|
|
if (!this.trade.TradeDate) {
|
|
return main.message("请填写成交日期");
|
|
}
|
|
if (!this.trade.Strike) {
|
|
return main.message("请填写执行价格");
|
|
}
|
|
if (!this.trade.ExerciseDate) {
|
|
return main.message("请填写到期日期");
|
|
}
|
|
if (!this.trade.SpotPrice) {
|
|
return main.message("请填写标的期初价格");
|
|
}
|
|
if (!this.trade.TradeSinglePrice) {
|
|
return main.message("请填写权利金");
|
|
}
|
|
main.post("/pricing/GetImpliedVol", this.trade).done(function (res) {
|
|
if (isNaN(res.obj)) {
|
|
return main.message("未获取到波动率,请检查输入的数值");
|
|
}
|
|
self.viewState.VolState = "user";
|
|
if (pageVue.IsTradeVol) {
|
|
self.trade.TradeOpenVolatility = consNumberFormat.volValueFmt(res.obj);
|
|
}
|
|
else {
|
|
self.trade.Vol = consNumberFormat.volValueFmt(res.obj);;
|
|
}
|
|
});
|
|
},
|
|
//获取目标波动率
|
|
getTradeCloseVolatility() {
|
|
var self = this;
|
|
if (pageVue.SkewMapVol) {
|
|
self.viewState.CloseVolState = "system";
|
|
self.trade.TradeCloseVolatility = "";
|
|
return;
|
|
}
|
|
let over = pageVue.TradeCloseVolatilityCfg === 'Mid' ? { VolType: '交易' } : null;
|
|
tradeUtils.getTradeVol(this.trade, over, this.viewState.variety.id).done(function (resp) {
|
|
var vol = resp.obj.vol;
|
|
if (isNaN(vol)) {
|
|
return main.message("未获取到波动率");
|
|
}
|
|
self.viewState.CloseVolState = "system";
|
|
self.trade.TradeCloseVolatility = consNumberFormat.volValueFmt(vol);
|
|
});
|
|
},
|
|
//变更分红率
|
|
changeDividendRate() {
|
|
this.viewState.DividendRate = 'user';
|
|
this.synchTrade();
|
|
},
|
|
//获取分红率
|
|
getDividendRate() {
|
|
if (!this.trade.TradeDate) {
|
|
return main.alert("缺少交易日期");
|
|
}
|
|
var self = this;
|
|
var postData = { underlyingCode: this.trade.UnderlyingCode, tradeDate: this.trade.TradeDate };
|
|
main.post("/pricing/AjaxGetDividendRate", postData).done(function (resp) {
|
|
self.trade.DividendRate = resp.obj;
|
|
self.viewState.DividendRate = "system";
|
|
});
|
|
},
|
|
//变更无风险利率
|
|
changeNoRiskRate() {
|
|
this.viewState.NoRiskRate = 'user';
|
|
this.synchTrade();
|
|
},
|
|
//获取无风险利率
|
|
getNoRiskRate() {
|
|
var self = this;
|
|
if (this.trade.TradeType === "现金流交易" && this.trade.DepositType == 1) {
|
|
return;
|
|
}
|
|
!pageVue.IsCheck && main.post("/pricing/AjaxGetNoRiskRate", { startDate: this.trade.TradeDate, endDate: this.trade.ExerciseDate }).done(function (resp) {
|
|
self.trade.NoRiskRate = resp.obj.toFixed(4);
|
|
self.viewState.NoRiskRate = "system";
|
|
});
|
|
},
|
|
calcSnowballAnnualPremium() {
|
|
let trade = this.trade;
|
|
let trades = tradeUtils.prepareTrades([trade], pageVue.getVolType(), false);
|
|
if (!trades) return;
|
|
var self = this;
|
|
main.post("/pricing/AjaxCalcSnowballAnnualPremium", { trade: trades[0] }).done(function (resp) {
|
|
trade.AnnualizedPremiumRate = resp.obj;
|
|
});
|
|
},
|
|
calcKORebate() {
|
|
let trade = this.trade;
|
|
let trades = tradeUtils.prepareTrades([trade], pageVue.getVolType(), false);
|
|
if (!trades) return;
|
|
var self = this;
|
|
main.post("/pricing/AjaxCalcSnowballKORebate", { trade: trades[0] }).done(function (resp) {
|
|
trade.KORebate = resp.obj;
|
|
self.changeRebate();
|
|
});
|
|
},
|
|
calcPhoenixCouponRate() {
|
|
let trade = this.trade;
|
|
let trades = tradeUtils.prepareTrades([trade], pageVue.getVolType(), false);
|
|
if (!trades) return;
|
|
var self = this;
|
|
main.post("/pricing/AjaxCalcPhoenixCouponRate", { trade: trades[0] }).done(function (resp) {
|
|
trade.Coupon = resp.obj;
|
|
self.changeRebate();
|
|
});
|
|
},
|
|
//设置观察日
|
|
setObservationDates(observationId) {
|
|
let isKO = observationId === '#KO';
|
|
let isKS = observationId === '#KS';
|
|
let isST = observationId === '#ST';
|
|
observationId = this.trade.CalcId + observationId;
|
|
var startTime = _.trim(this.trade.TradeDate);
|
|
var endTime = _.trim(this.trade.ExerciseDate);
|
|
if (!startTime) {
|
|
return main.alert("请输入交易日期");
|
|
}
|
|
if (!endTime) {
|
|
return main.alert("请输入到期日期");
|
|
}
|
|
var obs = isKO ? this.viewState.KOObservation : (isKS ? (this.viewState.KOObservationSettle.hasValue ? this.viewState.KOObservationSettle : this.viewState.KOObservation) : this.viewState.Observation);
|
|
var query = {
|
|
observationNum: obs.ObservationNum,
|
|
observationUnit: obs.ObservationUnit,
|
|
observationHolidayType: obs.ObservationHolidayType,
|
|
startTime: startTime, endTime: endTime,
|
|
alignEnd: _.toString(obs.ObservationAlignEnd) !== "false",
|
|
btnId: observationId,
|
|
couponDayInterval: obs.CouponDayInterval,
|
|
tradeType: this.trade.TradeType,
|
|
ObservationFrequency: obs.ObservationFrequency
|
|
};
|
|
|
|
query.Comments = isKO ? this.trade.MetaDic["ObservationRemark3"] : (isKS ? this.trade.MetaDic["ObservationRemark2"] : this.trade.MetaDic["ObservationRemark1"]);
|
|
|
|
if (isKO && !this.viewState.hasObservationDates) {
|
|
query.Comments = this.trade.MetaDic["ObservationRemark2"];
|
|
}
|
|
|
|
var whith = 500;
|
|
if (isKO) {
|
|
query.title1 = "障碍价格";
|
|
query.title2 = "票息";
|
|
query.isTitle1Percent = this.trade.IsMoneynessOption === "是";
|
|
query.isTitle2Percent = true;
|
|
query.defaultTitle1Value = this.trade.KOBarrier;
|
|
query.defaultTitle2Value = this.trade.TradeType === "凤凰期权" ? this.trade.Coupon : this.trade.KORebate;
|
|
if (this.trade.TradeType === "累计期权") {
|
|
if (this.trade.PayoffType === '固定') {
|
|
query.title2 = "";
|
|
query.defaultTitle2Value = this.trade.Coupon;
|
|
query.showDate2 = true;
|
|
query.date2Title = "结算日期";
|
|
query.showDate2OffsetControl = true;
|
|
query.date2OffsetControlTitle = "结算日期间隔"
|
|
whith += 20;
|
|
} else {
|
|
query.title2 = ""
|
|
}
|
|
}
|
|
else if (this.trade.TradeType === "雪球期权") {
|
|
query.dateTitle = "敲出观察日(T)";
|
|
if (_.toString(this.trade.KOPayoffType) === "0") {
|
|
query.showDate2 = true;
|
|
}
|
|
query.date2Title = "票息支付日";
|
|
query.showDate2OffsetControl = true;
|
|
query.date2OffsetControlTitle = "票息支付日间隔"
|
|
whith += 20;
|
|
}
|
|
}
|
|
|
|
if (isST) {
|
|
query.title1 = "固定价格";
|
|
query.title2 = "是否了结";
|
|
query.title3 = "了结价格";
|
|
query.isTitle2TrueFalse = true;
|
|
query.isTitle1Percent = this.trade.IsMoneynessOption === "是";
|
|
query.isTitle2Percent = false;
|
|
query.isTitle3Percent = this.trade.IsMoneynessOption === "是";
|
|
}
|
|
|
|
query = $.param(query);
|
|
sessionStorage.setItem(observationId + "_observationdates", obs.ObservationDates);
|
|
layer.open({
|
|
type: 2, title: "设置自定义观察日", shadeClose: false, shade: 0.4,
|
|
area: [whith + 'px', '560px'], content: "/trade/tradeObservationDates?" + query
|
|
});
|
|
},
|
|
getObservationRate(observation) {
|
|
if (!observation || !observation.hasValue) return '';
|
|
var observationRate = "", newUnit = "";
|
|
switch (observation.ObservationUnit) {
|
|
case "D":
|
|
newUnit = "天"; break;
|
|
case "W":
|
|
newUnit = "周"; break;
|
|
case "M":
|
|
newUnit = "月"; break;
|
|
case "Y":
|
|
newUnit = "年"; break;
|
|
}
|
|
observationRate = observation.ObservationNum + newUnit;
|
|
if (newUnit !== "") {
|
|
observationRate = "每" + observationRate;
|
|
}
|
|
return observationRate;
|
|
},
|
|
setStructureList() {
|
|
var query = {
|
|
structure: this.trade.MetaDic["structures"],
|
|
CalcId: this.trade.CalcId
|
|
};
|
|
query = $.param(query);
|
|
layer.open({
|
|
type: 2, title: "设置结构要素", shadeClose: false, shade: 0.4,
|
|
area: ['900px', '560px'], content: "/trade/StructureList?" + query
|
|
});
|
|
},
|
|
setcashOrPhysical() {
|
|
var cashOrPhysical = this.viewState.CashOrPhysical;
|
|
this.trade.MetaDic["cashOrPhysical"] = cashOrPhysical;
|
|
},
|
|
//变更奇异期权字段状态
|
|
changeFieldState() {
|
|
this.fieldState.resetFieldState(this.trade, false);
|
|
},
|
|
//设置观察频率
|
|
setObservation(observationNum, observationUnit, observationHolidayType, observationDates, alignEnd, calcId, couponDayInterval, Comments, ObservationFrequency) {
|
|
if (!calcId) return;
|
|
let isKO = calcId.endsWith('#KO');
|
|
let isComments = Comments != "";
|
|
let observation = {
|
|
ObservationNum: observationNum,
|
|
ObservationUnit: observationUnit,
|
|
ObservationHolidayType: observationHolidayType,
|
|
ObservationAlignEnd: alignEnd,
|
|
CouponDayInterval: couponDayInterval,
|
|
ObservationFrequency: ObservationFrequency
|
|
};
|
|
|
|
var self = this;
|
|
_.each(observation, (val, key) => self.trade[key] = val);
|
|
|
|
observation.hasValue = true;
|
|
observation.ObservationDates = observationDates;
|
|
|
|
if (isKO) {
|
|
self.viewState.KOObservation = observation;
|
|
var dates = observationDates.split(';').filter(o => o);
|
|
if (dates.length === 4) {
|
|
self.trade.KOObservationDates = dates[0] + ";" + dates[2] + ";" + dates[3] + ";";
|
|
} else if (self.trade.TradeType === "累计期权" && self.trade.PayoffType === "固定") {
|
|
self.trade.KOObservationSettleDates = dates[1];
|
|
self.trade.KOObservationDates = dates[0] + ";" + dates[2] + ";";
|
|
observation.ObservationDates = observationDates + ";0";
|
|
} else {
|
|
self.trade.KOObservationDates = observationDates;
|
|
}
|
|
if (!this.viewState.hasObservationDates) {
|
|
self.viewState.KOObservationSettle = _.clone(observation);
|
|
if (dates.length === 4) {
|
|
self.trade.KOObservationSettleDates = dates[1];
|
|
} else {
|
|
self.trade.KOObservationSettleDates = dates[0];
|
|
}
|
|
if (isComments || this.trade.MetaDic["ObservationRemark2"] != undefined) this.trade.MetaDic["ObservationRemark2"] = Comments;
|
|
} else {
|
|
if (isComments || this.trade.MetaDic["ObservationRemark3"] != undefined) this.trade.MetaDic["ObservationRemark3"] = Comments;
|
|
}
|
|
|
|
self.trade.MetaDic["敲出观察周期"] = observationNum + observationUnit + "|" + ObservationFrequency;
|
|
}
|
|
else if (calcId.endsWith('#KS')) {
|
|
self.viewState.KOObservationSettle = _.clone(observation);
|
|
self.trade.KOObservationSettleDates = observationDates;
|
|
this.viewState.hasObservationDates = true;
|
|
if (isComments || this.trade.MetaDic["ObservationRemark2"] != undefined) this.trade.MetaDic["ObservationRemark2"] = Comments;
|
|
}
|
|
else if (calcId.endsWith('#ST')) {
|
|
self.viewState.Observation = observation;
|
|
this.trade.MetaDic["observationDate"] = observationDates;
|
|
if (isComments || this.trade.MetaDic["ObservationRemark1"] != undefined) this.trade.MetaDic["ObservationRemark1"] = Comments;
|
|
}
|
|
else {
|
|
self.trade.MetaDic["敲入观察周期"] = observationNum + observationUnit + "|" + ObservationFrequency;
|
|
|
|
self.viewState.Observation = observation;
|
|
self.trade.ObservationDates = observationDates;
|
|
if (isComments || this.trade.MetaDic["ObservationRemark1"] != undefined) this.trade.MetaDic["ObservationRemark1"] = Comments;
|
|
}
|
|
},
|
|
setStructure(value, calcId) {
|
|
this.trade.MetaDic["structures"] = value;
|
|
},
|
|
//变更票息结算方式
|
|
changeCouponPayType() {
|
|
this.trade.CouponPayType = parseInt(this.trade.CouponPayType) || 0;
|
|
this.trade.IncludeCouponAfterKI = true;
|
|
},
|
|
//变更交易场所
|
|
changeTradingPlace() {
|
|
this.trade.MetaDic["交易场所"] = this.viewState.Extend.TradingPlace;
|
|
},
|
|
//变更清算机构
|
|
changeClearingAgency() {
|
|
this.trade.MetaDic["清算机构"] = this.viewState.Extend.ClearingAgency;
|
|
},
|
|
changeMainProtocolCode() {
|
|
this.getSupProtocolCode();
|
|
},
|
|
getMainProtocolCode() {
|
|
$("#MainProtocolCode option").remove();
|
|
main.post("/Client/getMainProtocolCodes", { clientId: $("#ClientId").val() }, { async: false }).done(function (resp) {
|
|
var obj = $("#MainProtocolCode");
|
|
_.forEach(resp, (v) => {
|
|
obj.append("<option >" + v.Text + "</option>");
|
|
})
|
|
});
|
|
this.getSupProtocolCode()
|
|
},
|
|
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("<option >" + v + "</option>");
|
|
})
|
|
});
|
|
}
|
|
},
|
|
changeMarginTemplate() {
|
|
tradeUtils.changeMarginTemplate(this.trade, this.tradeMarginTemplates);
|
|
},
|
|
//变更亚式期权行权价类型
|
|
changeStrikeType() {
|
|
if (this.trade.StrikeType === 'Segmented') {
|
|
this.trade.PayoffType = 'EnhancedArithmeticAverage';
|
|
}
|
|
if (this.trade.StrikeType != "Floating" && this.trade.PayoffType == 'EnhancedArithmeticAverage' && $("#EnhancedPriceTitle")) {
|
|
$("#EnhancedPriceTitle").show();
|
|
if (parseFloat(this.trade.EnhancedPrice) === 0) {
|
|
this.trade.EnhancedPrice = this.trade.Strike;
|
|
}
|
|
} else if ($("#EnhancedPriceTitle")) {
|
|
$("#EnhancedPriceTitle").hide();
|
|
}
|
|
},
|
|
//变更亚式期权均价计算类型
|
|
changePayOffType() {
|
|
if (this.trade.StrikeType != "Floating" && this.trade.PayoffType == 'EnhancedArithmeticAverage') {
|
|
if ($("#EnhancedPriceTitle")) {
|
|
$("#EnhancedPriceTitle").show();
|
|
}
|
|
if (parseFloat(this.trade.EnhancedPrice) === 0) {
|
|
this.trade.EnhancedPrice = this.trade.Strike;
|
|
}
|
|
} else if ($("#EnhancedPriceTitle")) {
|
|
$("#EnhancedPriceTitle").hide();
|
|
}
|
|
},
|
|
//亚式结算方式
|
|
consAsiaSettleModes() {
|
|
if (this.trade.PayoffType == 'EnhancedArithmeticAverage' && !this.trade.SettleMode) {
|
|
this.trade.SettleMode = 'AtHit';
|
|
}
|
|
return consRebateTypes;
|
|
},
|
|
changeKOBarrier() {
|
|
if (this.viewState.KOObservation.ObservationDates) {
|
|
var arr = this.trade.KOObservationDates.split(';').filter(o => o);
|
|
if (arr.length >= 3 || this.trade.TradeType === '累计期权') {
|
|
let fmt = this.trade.IsMoneynessOption === "是" ? consNumberFormat.umpriceP : pricingFormat.umprice;
|
|
var barrierStr = _.toString(this.trade.KOBarrier) ? fmt(this.trade.KOBarrier) : '';
|
|
var index = arr.length >= 3 ? arr.length - 2 : 1;
|
|
var barrierArr = arr[index].split(',');
|
|
barrierArr.forEach(function (v, i) {
|
|
barrierArr[i] = barrierStr;
|
|
});
|
|
arr[index] = barrierArr.join(',');
|
|
this.trade.KOObservationDates = arr.join(';');
|
|
if (this.trade.TradeType === "雪球期权") {
|
|
arr = [].concat(arr[0], this.trade.KOObservationSettleDates, arr[1], arr[2]);
|
|
} else if (this.trade.TradeType === "累计期权" && arr.length >= 3) {
|
|
arr = [].concat(arr[0], this.trade.KOObservationSettleDates, arr[1], arr[2]);
|
|
}
|
|
this.viewState.KOObservation.ObservationDates = arr.join(';');
|
|
main.message("敲出障碍价格已同步");
|
|
}
|
|
}
|
|
},
|
|
changeRebate() {
|
|
if (this.viewState.KOObservation.ObservationDates) {
|
|
var arr = this.trade.KOObservationDates.split(';').filter(o => o);
|
|
if (arr.length >= 3) {
|
|
var rebateStr = pricingFormat.premiumRate(this.trade.TradeType === "凤凰期权" ? this.trade.Coupon : this.trade.KORebate) + ""
|
|
var index = arr.length - 1;
|
|
var rebateArr = arr[index].split(',');
|
|
rebateArr.forEach(function (v, i) {
|
|
rebateArr[i] = rebateStr;
|
|
});
|
|
arr[index] = rebateArr.join(',');
|
|
this.trade.KOObservationDates = arr.join(';');
|
|
if (this.trade.TradeType === "雪球期权") {
|
|
arr = [].concat(arr[0], this.trade.KOObservationSettleDates, arr[1], arr[2]);
|
|
}
|
|
this.viewState.KOObservation.ObservationDates = arr.join(';');
|
|
main.message("票息已同步");
|
|
}
|
|
}
|
|
},
|
|
changePayoffType() {
|
|
this.trade.AccumuType = '';
|
|
if (this.trade.TradeType === '累计期权') {
|
|
if (this.trade.SettlementMode === '现金期末' && this.trade.PayoffType === '固定') {
|
|
this.trade.PayoffType = '浮动';
|
|
}
|
|
}
|
|
|
|
if (this.trade.TradeType === '二元期权') {
|
|
if (this.trade.ExerciseMode === 'European' || this.trade.PayoffType === 'DoubleNoTouch' || this.trade.PayoffType === 'UpNoTouch' || this.trade.PayoffType === 'DownNoTouch') {
|
|
this.trade.RebateAnnualizedAtKO = false;
|
|
}
|
|
if (this.trade.PayoffType === 'AssetOrNothing') {
|
|
this.trade.CashOrNothingAmountRate = "";
|
|
this.trade.CashOrNothingAmount = "";
|
|
}
|
|
}
|
|
|
|
this.fieldState.resetFieldState(this.trade);
|
|
}
|
|
},
|
|
computed: {
|
|
maxTradeDate() {
|
|
let exerciseDate = this.trade.ExerciseDate;
|
|
return exerciseDate;//&& exerciseDate <= pageVue.SysDate ? exerciseDate : pageVue.SysDate;
|
|
},
|
|
structure() {
|
|
return "";
|
|
},
|
|
observationRate() {
|
|
return this.getObservationRate(this.viewState.Observation);
|
|
},
|
|
koObservationRate() {
|
|
return this.getObservationRate(this.viewState.KOObservation);
|
|
},
|
|
koObservationSettleRate() {
|
|
return this.getObservationRate(this.viewState.KOObservationSettle);
|
|
},
|
|
inputFormatStrike() {
|
|
let fmt = {};
|
|
if (this.trade.IsMoneynessOption === '是') {
|
|
fmt.append = '%';
|
|
fmt.negative = false;
|
|
fmt.precision = pricingFormat.umpriceP.precision;
|
|
} else {
|
|
fmt.append = '';
|
|
fmt.negative = true;
|
|
fmt.precision = pricingFormat.umprice.precision;
|
|
}
|
|
return fmt;
|
|
},
|
|
inputFormatSinglePriceOnly() {
|
|
let underlying = this.viewState.underlying;
|
|
let quoteUnit = underlying && underlying.QuoteUnitString
|
|
? "元/" + (this.viewState.synthetic ? "份" : underlying.QuoteUnitString) : '';
|
|
return { precision: pricingFormat.tradeSinglePrice.precision, negative: true, append: quoteUnit };
|
|
},
|
|
inputFormatSinglePrincipalOnly() {
|
|
let underlying = this.viewState.underlying;
|
|
let quoteUnit = underlying && underlying.QuoteUnitString
|
|
? "元/" + (this.viewState.synthetic ? "份" : underlying.QuoteUnitString) : '';
|
|
return { precision: pricingFormat.tradeSinglePrice.precision, negative: true, append: quoteUnit };
|
|
},
|
|
inputFormatTradeAmount() {
|
|
let fmt = { precision: 10, append: '' };
|
|
let variety = this.viewState.variety;
|
|
if (variety) {
|
|
if (variety.InstrumentType === 'Stock' || !variety.QuoteUnit) {
|
|
fmt.append = '股';
|
|
} else {
|
|
var QuoteUnit = variety.QuoteUnit;
|
|
var unit = QuoteUnit.substring(QuoteUnit.lastIndexOf('/') + 1).trim();
|
|
fmt.append = unit === "500千克" ? "吨" : unit || '';
|
|
}
|
|
}
|
|
return fmt;
|
|
},
|
|
inputFormatTradeAmountV() {
|
|
let fmt = { precision: pricingFormat.notional.precision, append: '' };
|
|
let variety = this.viewState.variety;
|
|
if (variety) {
|
|
if (variety.InstrumentType === 'Stock' || !variety.QuoteUnit) {
|
|
fmt.append = '股';
|
|
} else {
|
|
var QuoteUnit = variety.QuoteUnit;
|
|
var unit = QuoteUnit.substring(QuoteUnit.lastIndexOf('/') + 1).trim();
|
|
fmt.append = unit === "500千克" ? "吨" : unit || '';
|
|
}
|
|
}
|
|
return fmt;
|
|
},
|
|
inputFormatTradePrice() {
|
|
return { precision: 2, negative: true, grouping: true, append: '' };
|
|
},
|
|
inputFormatNotional() {
|
|
let fmt = { precision: 10, append: '' };
|
|
let variety = this.viewState.variety;
|
|
if (variety) {
|
|
if (variety.InstrumentType === 'Stock' || !variety.QuoteUnit) {
|
|
fmt.append = '股';
|
|
} else {
|
|
fmt.append = '份';
|
|
}
|
|
}
|
|
return fmt;
|
|
},
|
|
showExerciseMode() {
|
|
switch (this.trade.TradeType) {
|
|
case '彩虹期权': case '合成价差期权': case '双鲨期权':
|
|
this.trade.ExerciseMode = 'European'; return 1;
|
|
case '凤凰期权': case '雪球期权': case '累计期权':
|
|
case '自定义交易': case '现金流交易': return 0;
|
|
case '障碍期权':
|
|
if ((this.trade.BarrierType || '').startsWith('双')) {
|
|
this.trade.ExerciseMode = 'European'; return 1;
|
|
}
|
|
break;
|
|
}
|
|
return this.trade.ExerciseMode === 'European' ? 11 : 101;
|
|
},
|
|
showOptionType() {
|
|
switch (this.trade.TradeType) {
|
|
case '现金流交易': this.trade.OptionType = ""; return false;
|
|
case '区间累积期权': case '双鲨期权':
|
|
case '气囊结构': case '收益增强结构': return false;
|
|
case '二元期权': return this.trade.ExerciseMode === 'European';
|
|
default: return true;
|
|
}
|
|
},
|
|
//亚式行权价类型
|
|
consAsiaStrikeType() {
|
|
let isAmerican = this.trade.ExerciseMode === 'American';
|
|
if (isAmerican && this.trade.StrikeType === 'Floating') {
|
|
this.trade.StrikeType = 'Fixed';
|
|
}
|
|
return isAmerican ? [{ value: 'Fixed', text: '固定行权价' }]
|
|
: [{ value: 'Fixed', text: '固定行权价' }, { value: 'Floating', text: '浮动行权价' }, { value: 'Segmented', text: '分段式' }];
|
|
},
|
|
//二元补偿支付类型
|
|
binaryRebateTypes() {
|
|
if (this.trade.ExerciseMode === 'American' && this.trade.PayoffType === 'DoubleNoTouch') {
|
|
this.trade.RebateType = 'AtEnd';
|
|
return [{ value: 'AtEnd', text: '递延' }];
|
|
}
|
|
return consRebateTypes;
|
|
},
|
|
//是否需要标的
|
|
needUnderlying() {
|
|
return this.trade.TradeType !== '现金流交易';
|
|
},
|
|
annualDayDiff() {
|
|
let diff = this.AnnualizeFactor.ttmDays - this.AnnualizeFactor.refDays;
|
|
return diff < 0 ? diff.toString() : "+" + diff;
|
|
},
|
|
annualDayDiff2() {
|
|
let diff = this.AnnualizeFactor2.ttmDays - this.AnnualizeFactor2.refDays;
|
|
return diff < 0 ? diff.toString() : "+" + diff;
|
|
},
|
|
//雪球敲出赔付类别
|
|
snowballKOPayoffTypeEnums() {
|
|
if (this.trade.OptionType === "看涨") {
|
|
return [{ value: '0', text: '票息补偿' }, { value: '1', text: '敲出转看涨' }, { value: '2', text: '敲出转牛市价差' }];
|
|
} else {
|
|
return [{ value: '0', text: '票息补偿' }, { value: '1', text: '敲出转看跌' }, { value: '2', text: '敲出转熊市价差' }];
|
|
}
|
|
},
|
|
//雪球敲入到期支付类别
|
|
snowballKIPayoffTypeEnums() {
|
|
let kiPayoffType = this.trade.KIPayoffType + "";
|
|
let isInitialKnockedIn = this.trade.IsInitialKnockedIn;
|
|
if (this.trade.OptionType === "看涨") {
|
|
switch (kiPayoffType) {
|
|
case "0":
|
|
if (isInitialKnockedIn) {
|
|
this.trade.KIPayoffType = "1";
|
|
}
|
|
break;
|
|
case "1": case "2": break;
|
|
case "3":
|
|
this.trade.KIPayoffType = "1";
|
|
break;
|
|
case "4":
|
|
this.trade.KIPayoffType = "2";
|
|
break;
|
|
default:
|
|
this.trade.KIPayoffType = "0";
|
|
this.changeFieldState();
|
|
break;
|
|
}
|
|
if (isInitialKnockedIn) {
|
|
return [{ value: '1', text: '敲入转看跌' }, { value: '2', text: '敲入转熊市价差' }];
|
|
} else {
|
|
return [{ value: '0', text: '无' }, { value: '1', text: '敲入转看跌' }, { value: '2', text: '敲入转熊市价差' }];
|
|
}
|
|
} else {
|
|
switch (kiPayoffType) {
|
|
case "0":
|
|
if (isInitialKnockedIn) {
|
|
this.trade.KIPayoffType = "3";
|
|
}
|
|
break;
|
|
case "3": case "4": break;
|
|
case "1":
|
|
this.trade.KIPayoffType = "3";
|
|
break;
|
|
case "2":
|
|
this.trade.KIPayoffType = "4";
|
|
break;
|
|
default:
|
|
this.trade.KIPayoffType = "0";
|
|
this.changeFieldState();
|
|
break;
|
|
}
|
|
if (isInitialKnockedIn) {
|
|
return [{ value: '3', text: '敲入转看涨' }, { value: '4', text: '敲入转牛市价差' }];
|
|
} else {
|
|
return [{ value: '0', text: '无' }, { value: '3', text: '敲入转看涨' }, { value: '4', text: '敲入转牛市价差' }];
|
|
}
|
|
}
|
|
},
|
|
//凤凰敲入到期支付类别
|
|
autocallKIPayoffTypeEnums() {
|
|
let kiPayoffType = this.trade.KIPayoffType + "";
|
|
if (this.trade.OptionType === "看涨") {
|
|
switch (kiPayoffType) {
|
|
case "1": case "2": break;
|
|
case "0": case "3":
|
|
this.trade.KIPayoffType = "1"; break;
|
|
case "4":
|
|
this.trade.KIPayoffType = "2"; break;
|
|
default:
|
|
this.trade.KIPayoffType = "1";
|
|
this.changeFieldState();
|
|
break;
|
|
}
|
|
return [{ value: '1', text: '敲入转看跌' }, { value: '2', text: '敲入转熊市价差' }];
|
|
} else {
|
|
switch (kiPayoffType) {
|
|
case "3": case "4": break;
|
|
case "0": case "1":
|
|
this.trade.KIPayoffType = "3"; break;
|
|
case "2":
|
|
this.trade.KIPayoffType = "4"; break;
|
|
default:
|
|
this.trade.KIPayoffType = "3";
|
|
this.changeFieldState();
|
|
break;
|
|
}
|
|
return [{ value: '3', text: '敲入转看涨' }, { value: '4', text: '敲入转牛市价差' }];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const tradePricing = {};
|
|
|
|
//tradeCalc,逻辑参见'交易数据互算'
|
|
(function (global) {
|
|
|
|
var _Precision = null;
|
|
|
|
const consCalcReason = Object.freeze({
|
|
None: 0, SpotPrice: 1, Notional: 2, TradeAmount: 3,
|
|
StockEqvNotional: 4, StockEqvNotionalReal: 5,
|
|
TradePrice: 6, PremiumRate: 7, TradeSinglePrice: 8, Pv: 9,
|
|
SpotPrice2: 11, TradeAmountV: 12, OriginalPrincipalSum: 13
|
|
});
|
|
|
|
function setPrecision(otcformat) {
|
|
_Precision = {
|
|
umprice: otcformat.umprice.precision,
|
|
notional: otcformat.notional.precision,
|
|
stockEqvNotional: otcformat.stockEqvNotional.precision,
|
|
tradePrice: otcformat.tradePrice.precision,
|
|
premiumRate: otcformat.premiumRate.precision,
|
|
tradeSinglePrice: otcformat.tradeSinglePrice.precision
|
|
};
|
|
}
|
|
|
|
function tradeCalc(trade, reason, countRatio) {
|
|
if (!trade) return;
|
|
|
|
if (!_Precision) {
|
|
if (!otcformat) throw 'missing otcformat';
|
|
setPrecision(otcformat.trading);
|
|
}
|
|
|
|
let isAutoCall = trade.TradeType === '雪球期权' || trade.TradeType === '凤凰期权';
|
|
|
|
let data = {
|
|
reason: reason,
|
|
countRatio: Math.abs(countRatio) || 1,
|
|
spotPrice: Math.abs(trade.SpotPrice) || 0,
|
|
principalRateWrite: trade.PrincipalRateWrite ? Math.abs(trade.PrincipalRateWrite) || 0 : 0,
|
|
singlePrincipalWrite: trade.SinglePrincipalWrite ? Math.abs(trade.SinglePrincipalWrite) || 0 : 0,
|
|
participationRate: trade.ParticipationRate ? Math.abs(trade.ParticipationRate) || 0 : 1,
|
|
annualizeFactor: isAutoCall
|
|
? (trade.AnnualizeFactor2 ? Math.abs(trade.AnnualizeFactor2) || 0 : 1)
|
|
: (trade.AnnualizeFactor ? Math.abs(trade.AnnualizeFactor) || 0 : 1),
|
|
getAnnRate() {
|
|
return isAutoCall ? this.participationRate : this.participationRate * this.annualizeFactor;
|
|
},
|
|
//通过名义本金+期权费率计算权利金总额
|
|
getTradePriceByPremiumRate() {
|
|
return tradeHelper.GetTradePriceByPremiumRate(trade.PremiumRate, trade.StockEqvNotional, this.participationRate
|
|
, trade.OriginalPrincipalSum, isAutoCall ? 1 : this.annualizeFactor, trade.BuySell, trade.TradeType, true);
|
|
},
|
|
//通过名义本金+期权单价计算权利金总额
|
|
getTradePriceBySinglePrice() {
|
|
return tradeHelper.GetTradePriceBySinglePrice(trade.TradeSinglePrice, trade.StockEqvNotional, trade.Notional
|
|
, trade.OriginalPrincipalSum, isAutoCall ? 1 : this.annualizeFactor, trade.BuySell, trade.TradeType, true);
|
|
},
|
|
getNotionalEqv() {
|
|
return isAutoCall ? trade.StockEqvNotional * this.participationRate : trade.StockEqvNotionalReal;
|
|
},
|
|
//通过有效交易份额算交易数量
|
|
getTradeAmount(notional) {
|
|
return _.round(notional / this.countRatio, 10);
|
|
},
|
|
//通过有效交易份额算交易数量
|
|
getTradeAmountV(notional) {
|
|
let annRate = this.getAnnRate();
|
|
return _.round((annRate ? notional / annRate : notional) / this.countRatio, _Precision.notional);
|
|
}
|
|
};
|
|
|
|
let instance = { data: data };
|
|
|
|
//期初标的价格(只受自身影响)
|
|
instance.SpotPrice = function () {
|
|
switch (data.reason) {
|
|
case consCalcReason.SpotPrice:
|
|
trade.SpotPrice = _.round(trade.SpotPrice, _Precision.umprice) || 0;
|
|
data.spotPrice = Math.abs(trade.SpotPrice);
|
|
break;
|
|
case consCalcReason.SpotPrice2:
|
|
data.reason = consCalcReason.SpotPrice;
|
|
data.spotPrice = _.round(trade.SpotPrice, _Precision.umprice) || 0;
|
|
data.spotPrice = Math.abs(data.spotPrice);
|
|
break;
|
|
}
|
|
};
|
|
|
|
//交易份额/数量(受名义本金、期初价格影响)
|
|
//特别说明:凤凰雪球的实际份额是非年化的,其他是年化折算后的
|
|
instance.Notional = function () {
|
|
switch (data.reason) {
|
|
case consCalcReason.Notional:
|
|
trade.Notional = _.round(trade.Notional, _Precision.notional);
|
|
trade.OriginalNotional = trade.Notional;
|
|
trade.TradeAmount = data.getTradeAmount(trade.Notional);
|
|
trade.TradeAmountV = data.getTradeAmountV(trade.Notional);
|
|
break;
|
|
case consCalcReason.TradeAmount:
|
|
trade.TradeAmount = _.round(trade.TradeAmount, 10);
|
|
trade.Notional = _.round(trade.TradeAmount * data.countRatio, 10);
|
|
trade.OriginalNotional = trade.Notional;
|
|
trade.TradeAmountV = data.getTradeAmountV(trade.Notional);
|
|
break;
|
|
case consCalcReason.TradeAmountV:
|
|
trade.TradeAmountV = _.round(trade.TradeAmountV, _Precision.notional);
|
|
trade.TradeAmount = _.round(trade.TradeAmountV * data.getAnnRate(), 10);
|
|
trade.Notional = _.round(trade.TradeAmount * data.countRatio, 10);
|
|
trade.OriginalNotional = trade.Notional;
|
|
break;
|
|
case consCalcReason.SpotPrice:
|
|
//数量成交方式下数量是固定的,期初标的价格会导致名义本金变动
|
|
//名义本金成交方式下名义本金是固定的,期权标的价格的变动会导致交易份额变动
|
|
if (trade.IsUsePremiumRate) {
|
|
let notional = data.spotPrice ? data.getNotionalEqv() / data.spotPrice : 0;
|
|
trade.Notional = _.round(notional, _Precision.notional);
|
|
trade.OriginalNotional = trade.Notional;
|
|
trade.TradeAmount = data.getTradeAmount(trade.Notional);
|
|
trade.TradeAmountV = data.getTradeAmountV(trade.Notional);
|
|
}
|
|
break;
|
|
case consCalcReason.StockEqvNotional:
|
|
{
|
|
let notionalV = data.spotPrice ? trade.StockEqvNotional / data.spotPrice : 0;
|
|
trade.Notional = _.round(notionalV * data.getAnnRate(), _Precision.notional);
|
|
trade.OriginalNotional = trade.Notional;
|
|
trade.TradeAmount = data.getTradeAmount(trade.Notional);
|
|
trade.TradeAmountV = data.getTradeAmount(notionalV);//特殊使用
|
|
}
|
|
break;
|
|
case consCalcReason.StockEqvNotionalReal:
|
|
{
|
|
let eqv = trade.StockEqvNotionalReal;
|
|
//凤凰雪球的交易份额非年化而有效名义本金年化所以需要膨胀
|
|
data.isAutoCall && data.annualizeFactor && (eqv /= data.annualizeFactor);
|
|
let notional = data.spotPrice ? eqv / data.spotPrice : 0;
|
|
trade.Notional = _.round(notional, _Precision.notional);
|
|
trade.OriginalNotional = trade.Notional;
|
|
trade.TradeAmount = data.getTradeAmount(trade.Notional);
|
|
trade.TradeAmountV = data.getTradeAmountV(trade.Notional);
|
|
}
|
|
break;
|
|
}
|
|
};
|
|
|
|
//名义本金(受期初价格、数量/份额影响)
|
|
//非名义本金变动原因,名义本金以有效名义本金为准
|
|
instance.StockEqvNotional = function () {
|
|
let eqv = null, eqvReal = null;
|
|
switch (data.reason) {
|
|
case consCalcReason.Notional:
|
|
if (isAutoCall) {
|
|
eqv = data.participationRate ? trade.Notional * data.spotPrice / data.participationRate : 0;
|
|
} else {
|
|
eqvReal = trade.Notional * data.spotPrice;
|
|
}
|
|
break;
|
|
case consCalcReason.TradeAmount:
|
|
if (isAutoCall) {
|
|
eqv = data.participationRate ? trade.TradeAmount * data.countRatio * data.spotPrice / data.participationRate : 0;
|
|
} else {
|
|
eqvReal = trade.TradeAmount * data.countRatio * data.spotPrice;
|
|
}
|
|
break;
|
|
case consCalcReason.TradeAmountV:
|
|
eqv = trade.TradeAmountV * data.countRatio * data.spotPrice;
|
|
break;
|
|
case consCalcReason.StockEqvNotional:
|
|
eqv = trade.StockEqvNotional;
|
|
break;
|
|
case consCalcReason.StockEqvNotionalReal:
|
|
eqvReal = trade.StockEqvNotionalReal;
|
|
break;
|
|
case consCalcReason.SpotPrice:
|
|
//数量成交方式下数量是固定的,期初标的价格会导致名义本金变动
|
|
//名义本金成交方式下名义本金是固定的,期权标的价格的变动会导致交易份额变动
|
|
if (!trade.IsUsePremiumRate) {
|
|
if (isAutoCall) {
|
|
eqv = data.participationRate ? trade.Notional * data.spotPrice / data.participationRate : 0;
|
|
} else {
|
|
eqvReal = trade.Notional * data.spotPrice;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (eqv !== null) {
|
|
trade.StockEqvNotional = _.round(eqv, _Precision.stockEqvNotional);
|
|
eqvReal = trade.StockEqvNotional * data.participationRate * data.annualizeFactor;
|
|
trade.StockEqvNotionalReal = _.round(eqvReal, 10);
|
|
}
|
|
else if (eqvReal !== null) {
|
|
trade.StockEqvNotionalReal = _.round(eqvReal, 10);
|
|
let annParticipation = data.participationRate * data.annualizeFactor;
|
|
eqv = annParticipation ? trade.StockEqvNotionalReal / annParticipation : 0;
|
|
trade.StockEqvNotional = _.round(eqv, _Precision.stockEqvNotional);
|
|
}
|
|
};
|
|
|
|
//权利金费用(受期初价格、名义本金影响)
|
|
instance.TradePrice = function () {
|
|
switch (data.reason) {
|
|
case consCalcReason.Notional:
|
|
case consCalcReason.TradeAmount:
|
|
case consCalcReason.TradeAmountV:
|
|
case consCalcReason.StockEqvNotional:
|
|
case consCalcReason.OriginalPrincipalSum:
|
|
case consCalcReason.StockEqvNotionalReal:
|
|
{
|
|
let tradePrice = trade.IsUsePremiumRate ? data.getTradePriceByPremiumRate() : data.getTradePriceBySinglePrice();
|
|
return trade.TradePrice = _.round(tradePrice, _Precision.tradePrice);
|
|
}
|
|
case consCalcReason.TradePrice:
|
|
{
|
|
trade.TradePrice = _.round(trade.TradePrice, _Precision.tradePrice);
|
|
let singlePrice = 0;
|
|
let premiumRate = 0;
|
|
if (trade.TradePrice > 0) {
|
|
let tradePrice2 = (Math.abs(trade.TradePrice) || 0) - trade.OriginalPrincipalSum;
|
|
if (tradePrice2 > 0) {
|
|
singlePrice = trade.Notional ? tradePrice2 / trade.Notional : 0;
|
|
let eqv = data.getNotionalEqv();
|
|
premiumRate = eqv ? tradePrice2 / eqv : 0;
|
|
}
|
|
}
|
|
else {
|
|
let tradePrice2 = (trade.TradePrice || 0) - trade.OriginalPrincipalSum * (trade.BuySell === "卖出" ? -1 : 1);
|
|
singlePrice = trade.Notional ? tradePrice2 / trade.Notional : 0;
|
|
let eqv = data.getNotionalEqv();
|
|
premiumRate = eqv ? tradePrice2 / eqv : 0;
|
|
}
|
|
trade.TradeSinglePrice = _.round(singlePrice, _Precision.tradeSinglePrice);
|
|
trade.PremiumRate = _.round(premiumRate, _Precision.premiumRate);
|
|
}
|
|
return;
|
|
case consCalcReason.Pv:
|
|
{
|
|
let singlePrice = 0;
|
|
let premiumRate = 0;
|
|
let tradePrice2 = (trade.TradePrice) || 0;
|
|
singlePrice = trade.Notional ? tradePrice2 / trade.Notional : 0;
|
|
premiumRate = data.spotPrice ? singlePrice / data.spotPrice : 0;
|
|
trade.TradeSinglePrice = _.round(singlePrice, _Precision.tradeSinglePrice);
|
|
trade.PremiumRate = _.round(premiumRate, _Precision.premiumRate);
|
|
trade.TradePrice = _.round(tradePrice2 + trade.OriginalPrincipalSum, _Precision.tradePrice);
|
|
}
|
|
return;
|
|
case consCalcReason.SpotPrice:
|
|
if (trade.IsUsePremiumRate) {
|
|
let singlePrice = trade.PremiumRate * data.spotPrice;
|
|
trade.TradeSinglePrice = _.round(singlePrice, _Precision.tradeSinglePrice);
|
|
let tradePrice = data.getTradePriceByPremiumRate();
|
|
trade.TradePrice = _.round(tradePrice, _Precision.tradePrice);
|
|
}
|
|
else {
|
|
let premiumRate = data.spotPrice ? trade.TradeSinglePrice / data.spotPrice : 0;
|
|
trade.PremiumRate = _.round(premiumRate, _Precision.premiumRate);
|
|
let tradePrice = data.getTradePriceBySinglePrice();
|
|
trade.TradePrice = _.round(tradePrice, _Precision.tradePrice);
|
|
}
|
|
return;
|
|
case consCalcReason.PremiumRate:
|
|
{
|
|
trade.PremiumRate = _.round(trade.PremiumRate, _Precision.premiumRate);
|
|
let singlePrice = trade.PremiumRate * data.spotPrice;
|
|
trade.TradeSinglePrice = _.round(singlePrice, _Precision.tradeSinglePrice);
|
|
let tradePrice = data.getTradePriceByPremiumRate();
|
|
trade.TradePrice = _.round(tradePrice, _Precision.tradePrice);
|
|
}
|
|
return;
|
|
case consCalcReason.TradeSinglePrice:
|
|
{
|
|
trade.TradeSinglePrice = _.round(trade.TradeSinglePrice, _Precision.tradeSinglePrice);
|
|
let premiumRate = data.spotPrice ? trade.TradeSinglePrice / data.spotPrice : 0;
|
|
trade.PremiumRate = _.round(premiumRate, _Precision.premiumRate);
|
|
let tradePrice = trade.IsUsePremiumRate ? data.getTradePriceByPremiumRate() : data.getTradePriceBySinglePrice();
|
|
trade.TradePrice = _.round(tradePrice, _Precision.tradePrice);
|
|
}
|
|
return;
|
|
}
|
|
};
|
|
|
|
instance.OriginalPrincipalSum = function () {
|
|
switch (data.reason) {
|
|
case consCalcReason.TradeAmountV:
|
|
trade.OriginalPrincipalSum = _.round(trade.TradeAmount * data.singlePrincipalWrite * countRatio, _Precision.tradePrice);
|
|
trade.PrincipalRateWrite = trade.OriginalPrincipalSum / (trade.StockEqvNotional * (isAutoCall ? 1 : data.annualizeFactor));
|
|
break;
|
|
case consCalcReason.StockEqvNotional:
|
|
trade.OriginalPrincipalSum = _.round(trade.StockEqvNotional * data.principalRateWrite * (isAutoCall ? 1 : data.annualizeFactor), _Precision.tradePrice);
|
|
trade.SinglePrincipalWrite = trade.TradeAmount === 0 ? 0 : trade.OriginalPrincipalSum / trade.TradeAmount;
|
|
break;
|
|
}
|
|
};
|
|
|
|
|
|
instance.CalcFns = (function () {
|
|
|
|
switch (reason) {
|
|
case consCalcReason.SpotPrice:
|
|
case consCalcReason.SpotPrice2:
|
|
return [this.SpotPrice].concat(trade.IsUsePremiumRate ? [this.Notional] : [this.StockEqvNotional, this.OriginalPrincipalSum, this.TradePrice]);
|
|
case consCalcReason.Notional:
|
|
case consCalcReason.TradeAmount:
|
|
case consCalcReason.TradeAmountV:
|
|
return [this.Notional, this.StockEqvNotional, this.OriginalPrincipalSum, this.TradePrice];
|
|
case consCalcReason.StockEqvNotional:
|
|
case consCalcReason.StockEqvNotionalReal:
|
|
return [this.StockEqvNotional, this.Notional, this.OriginalPrincipalSum, this.TradePrice];
|
|
case consCalcReason.TradePrice:
|
|
case consCalcReason.Pv:
|
|
case consCalcReason.PremiumRate:
|
|
case consCalcReason.OriginalPrincipalSum:
|
|
case consCalcReason.TradeSinglePrice:
|
|
return [this.TradePrice];
|
|
default: return [];
|
|
}
|
|
|
|
}.call(instance));
|
|
|
|
instance.Execute = function () {
|
|
this.CalcFns.forEach(x => x());
|
|
};
|
|
|
|
return instance;
|
|
}
|
|
|
|
tradePricing.calcReason = consCalcReason;
|
|
|
|
//reason:consCalcReason
|
|
tradePricing.tradeCalc = function (trade, reason, countRatio) {
|
|
new tradeCalc(trade, reason, countRatio).Execute();
|
|
};
|
|
|
|
tradePricing.tradeCalc.setPrecision = function (otcformat) {
|
|
otcformat && setPrecision(otcformat);
|
|
};
|
|
|
|
}(window.tradePricing)); |