预警线:
diff --git a/YLErpWeb/wwwroot/Scripts/app/marginTemplate/marginTemplateV2DefaultEdit.js b/YLErpWeb/wwwroot/Scripts/app/marginTemplate/marginTemplateV2DefaultEdit.js
index 15c6c0f5..52bc0117 100644
--- a/YLErpWeb/wwwroot/Scripts/app/marginTemplate/marginTemplateV2DefaultEdit.js
+++ b/YLErpWeb/wwwroot/Scripts/app/marginTemplate/marginTemplateV2DefaultEdit.js
@@ -35,10 +35,6 @@ const TermTierEnabledUnderlyingMask = 16;
//基金及基金专户标志位(ETF 子类行的 UnderlyingType,不加新枚举位)
const FundTypeMask = 32768;
-//允许配置期限档的 ETF 子类(SpanConfig.EtfKind 值,取值=数据字典"ETF 子类"),
-//与后端 ConsMarginTerm.TermTierEnabledEtfKinds 保持一致;其余子类与"不区分"的基金行不分档
-const TermTierEnabledEtfKinds = ['可转债 ETF', '科创债 ETF'];
-
//利率债区块固定展开的4个期限档([BondTerm值, 展示文案]),与 docx 原型 4.3.1 一致
const SpanBondTerms = [['<5y', '≤5y'], ['5y-10y', '(5y-10y]'], ['10y-30y', '(10y-30y]'], ['>30y', '>30y']];
@@ -62,6 +58,10 @@ const vue = new Vue({
etfSubtypeItems: page.etfSubtypeItems || []
},
created: function () {
+ //留档进入页面时的生效状态:v-model 改写的就是 page.marginTemplate 同一对象(data 浅拷贝的是引用),
+ //原始值须在用户交互前记下,供保存时判断"生效中规则被改为否"弹失效确认;
+ //select 的 v-model 取值是字符串,统一 String 化比较
+ this.originalIsValid = String(this.marginTemplate.IsValid);
//区间追保结构:加载时按 资产类型+期限 排序一次,再重建资产类型区块(利率债区块补齐固定4档),
//编辑过程中不再重排,避免 chosen/选择器失序
if (this.marginTemplate.RuleType == MarginRuleTypeEnum.区间追保结构 && this.marginTemplate.Details) {
@@ -106,8 +106,8 @@ const vue = new Vue({
}
return rows;
},
- //区间追保结构录入区块:每个资产类型区块一个;纯利率债(ut=16)与分档 ETF 子类
- //(ut=基金 + EtfKind∈可转债/科创债 ETF)区块固定展开4个期限档段(每段一条 detail),其他区块单段(BondTerm 为空)。
+ //区间追保结构录入区块:每个资产类型区块一个;纯利率债(ut=16)区块固定展开4个期限档段(每段一条 detail),
+ //其他区块(含全部 ETF 子类,2026-08-27 裁定 ETF 无期限概念不再分档)单段(BondTerm 为空)。
//段的 detail 缺失时模板侧 v-if 兜底不渲染该段
ruleRangeBlocks() {
if (this.marginTemplate.RuleType != MarginRuleTypeEnum.区间追保结构) return [];
@@ -120,10 +120,8 @@ const vue = new Vue({
var ut = head.detail.UnderlyingType || 0;
var kind = (head.detail.SpanConfig && head.detail.SpanConfig.EtfKind) || '';
var isTBond = ut === 16;
- var isTieredEtf = ut === FundTypeMask && TermTierEnabledEtfKinds.indexOf(kind) >= 0;
- var isTiered = isTBond || isTieredEtf;
var sections = [];
- if (isTiered) {
+ if (isTBond) {
for (var t = 0; t < SpanBondTerms.length; t++) {
var found = null;
for (var j = head.index; j < head.index + head.rowspan; j++) {
@@ -135,7 +133,7 @@ const vue = new Vue({
} else {
sections.push({ key: 'single', termLabel: '', detail: head.detail });
}
- blocks.push({ key: head.detail._bk || ('i' + head.index), no: blocks.length + 1, headIndex: head.index, ut: ut, etfKind: kind, isTBond: isTBond, isTieredEtf: isTieredEtf, isFundBlock: ut === FundTypeMask, sections: sections });
+ blocks.push({ key: head.detail._bk || ('i' + head.index), no: blocks.length + 1, headIndex: head.index, ut: ut, etfKind: kind, isTBond: isTBond, isFundBlock: ut === FundTypeMask, sections: sections });
}
return blocks;
}
@@ -256,6 +254,18 @@ const vue = new Vue({
})
},
saveMarginTemplateV2(isForClient) {
+ //失效确认:编辑中把"生效中"规则改为否时先弹窗提示降级影响,确认后才真正提交;
+ //新增规则尚未生效、生效状态未翻转,不弹。自定义规则编辑页无"是否生效"开关,不经过此逻辑
+ if (!this.isAdd && this.originalIsValid === 'true' && String(this.marginTemplate.IsValid) === 'false') {
+ var that = this;
+ main.confirm("该规则失效后,适用范围内的持仓将降级匹配下一优先级规则(客户→全局),确认继续?", function () {
+ that.doSaveMarginTemplateV2(isForClient);
+ });
+ return;
+ }
+ this.doSaveMarginTemplateV2(isForClient);
+ },
+ doSaveMarginTemplateV2(isForClient) {
if (!this.marginTemplate.Name) {
main.message("模板名称不能为空");
return;
@@ -421,64 +431,35 @@ const vue = new Vue({
d.SpanConfig.EtfKind = etfKind || null;
return d;
},
- //ETF 子类选择器变更(仅纯基金区块渲染):选可转债/科创债 ETF → 区块展开固定4档(缺档补行、异常档行删除);
- //选其他子类/不区分 → 收敛为单行(BondTerm 置空,多余档位行删除,首行承载 EtfKind)
+ //ETF 子类选择器变更(仅纯基金区块渲染):子类仅用于行区分(取数侧子类行优先),
+ //ETF 无期限概念不分档——任何子类切换后区块始终收敛为单行(BondTerm 置空,多余档位行删除,首行承载 EtfKind;
+ //存量 4 档子类数据在这里被收敛时仅保留首行录入内容)
onEtfKindChange(blk) {
var head = blk.sections[0].detail;
var kind = (head.SpanConfig && head.SpanConfig.EtfKind) || '';
- var tiered = TermTierEnabledEtfKinds.indexOf(kind) >= 0;
var details = this.marginTemplate.Details;
var bk = head._bk;
var rows = details.filter(function (d) { return d._bk === bk; });
- if (tiered) {
- var byTerm = {};
- rows.forEach(function (r) {
- var bt = (r.SpanConfig && r.SpanConfig.BondTerm) || '';
- if (byTerm[bt] === undefined) byTerm[bt] = r;
- });
- var that = this;
- var merged = [];
- //首行未被4个合法档位命中时(单段行 BondTerm 为空)复用为首个档位行,保留已录入内容;
- //否则首行会以"EtfKind 已选、BondTerm 为空"的残留行留在明细里,保存时被服务端"分期限档类型必须配置期限档"拦截
- var headMatched = SpanBondTerms.some(function (t) { return byTerm[t[0]] === head; });
- SpanBondTerms.forEach(function (t, ti) {
- var r = byTerm[t[0]] || (ti === 0 && !headMatched ? head : null);
- if (!r) r = that.newSpanDetail(FundTypeMask, t[0], kind);
- r.UnderlyingType = FundTypeMask;
- r.SpanConfig.EtfKind = kind;
- r.SpanConfig.BondTerm = t[0];
- Vue.set(r, '_bk', bk);
- merged.push(r);
+ rows.forEach(function (r) {
+ if (r !== head) {
var idx = details.indexOf(r);
- if (idx >= 0 && r !== head) details.splice(idx, 1);
- });
- //异常档/重复行全部移除后按档序回插到区块首行位置
- var headIdx = details.indexOf(head);
- if (headIdx < 0) headIdx = details.length;
- details.splice.apply(details, [headIdx + 1, 0].concat(merged.filter(function (r) { return r !== head; })));
- } else {
- rows.forEach(function (r) {
- if (r !== head) {
- var idx = details.indexOf(r);
- if (idx >= 0) details.splice(idx, 1);
- }
- });
- head.UnderlyingType = FundTypeMask;
- head.SpanConfig.BondTerm = '';
- head.SpanConfig.EtfKind = kind || null;
- }
+ if (idx >= 0) details.splice(idx, 1);
+ }
+ });
+ head.UnderlyingType = FundTypeMask;
+ head.SpanConfig.BondTerm = '';
+ head.SpanConfig.EtfKind = kind || null;
this.refreshChosen();
},
- //分档行判定与分档键:纯利率债 → 'tbond';基金 + 分档 ETF 子类 → 'etf:子类'(同一子类的连续行归一个区块)
+ //分档行判定与分档键:纯利率债 → 'tbond'(连续行归一个区块按固定4档补齐);ETF 子类不分档,基金行一律独立成块
spanTierKey(d) {
var ut = d.UnderlyingType || 0;
- var kind = (d.SpanConfig && d.SpanConfig.EtfKind) || '';
if (ut === TermTierEnabledUnderlyingMask) return 'tbond';
- if (ut === FundTypeMask && TermTierEnabledEtfKinds.indexOf(kind) >= 0) return 'etf:' + kind;
return null;
},
- //存量回显重建资产类型区块(分配区块键 _bk):连续的纯利率债行、基金+分档ETF子类行各自归为区块并按固定4档补齐(缺档补空行);
- //同档重复/异常档的分档行、非分档行(含基金+不分档子类、基金通配行)均各自独立成块展示,不丢数据
+ //存量回显重建资产类型区块(分配区块键 _bk):连续的纯利率债行归为区块并按固定4档补齐(缺档补空行);
+ //同档重复/异常档的分档行、其余全部行(含基金+子类行、基金通配行)均各自独立成块展示,不丢数据——
+ //存量 ETF 子类 4 档行会以多个单行区块出现,保存时由服务端"ETF 子类不分期限档"校验拦截提示清理
rebuildSpanBlocks(details) {
var termValues = SpanBondTerms.map(function (t) { return t[0]; });
var result = [];
@@ -500,13 +481,10 @@ const vue = new Vue({
if (termValues.indexOf(bt) >= 0 && !byTerm[bt]) byTerm[bt] = r; else anomalies.push(r);
});
var bk = 'bk_' + (++spanBlockSeq);
- var utv = tk === 'tbond' ? TermTierEnabledUnderlyingMask : FundTypeMask;
- var kind = tk === 'tbond' ? '' : tk.substring(4);
termValues.forEach(function (t) {
var r = byTerm[t];
- if (!r) r = that.newSpanDetail(utv, t, kind);
- r.UnderlyingType = utv;
- if (kind) r.SpanConfig.EtfKind = kind;
+ if (!r) r = that.newSpanDetail(TermTierEnabledUnderlyingMask, t);
+ r.UnderlyingType = TermTierEnabledUnderlyingMask;
r.SpanConfig.BondTerm = t;
Vue.set(r, '_bk', bk);
result.push(r);
@@ -538,15 +516,19 @@ const vue = new Vue({
return map[v] !== undefined ? map[v] : v;
},
//规则15录入区块文案:按 detail 资产类型切换计价口径。
- //债券类(利率债16/信用债32/其它债券64)及其他类型(默认债券口径):期初净价/当前净价,金额=×期初全价×券面总额;
- //基金及基金专户(32768)/债券指数(536870912):参考标的期初净价/参考标的当前收盘价(空头各层统一收盘价,BUG-11 修正),
+ //比较价格口径与计算侧对齐(MarginCalculationBase.CalcSwapSpanMaintenanceMargin:IsBond→中债估值净价,其余→收盘价):
+ //纯债券类(利率债16/信用债32/其它债券64,可组合):期初净价/当前净价,金额=×期初全价×券面总额;
+ //其余全部类型(基金/债券指数/股票/股指等):参考标的期初净价/参考标的当前收盘价(多空各层统一收盘价),
//金额=×参考标的期初价格×参考标的名义份额。
spanText(detail) {
var ut = (detail && detail.UnderlyingType) || 0;
- if ((ut & 32768) > 0 || (ut & 536870912) > 0) {
+ //非空且标志位全部落在债券三类内才按债券口径;混合行(债券|非债券)按收盘价口径显示,与取数侧标的实际类型判定方向一致
+ var bondMask = 16 | 32 | 64;
+ var isBond = ut !== 0 && (ut & ~bondMask) === 0;
+ if (!isBond) {
return {
priceInit: '参考标的期初净价',
- priceCur: '参考标的当前净价',
+ priceCur: '参考标的当前收盘价',
priceCurShort1: '参考标的当前收盘价',
priceCurShort: '参考标的当前收盘价',
amountBase: '参考标的期初价格 × 参考标的名义份额'
diff --git a/YLErpWeb/wwwroot/Scripts/app/marginTemplate/marginTemplateV2Edit.js b/YLErpWeb/wwwroot/Scripts/app/marginTemplate/marginTemplateV2Edit.js
index 676fa7dc..1a04d355 100644
--- a/YLErpWeb/wwwroot/Scripts/app/marginTemplate/marginTemplateV2Edit.js
+++ b/YLErpWeb/wwwroot/Scripts/app/marginTemplate/marginTemplateV2Edit.js
@@ -35,10 +35,6 @@ const TermTierEnabledUnderlyingMask = 16;
//基金及基金专户标志位(ETF 子类行的 UnderlyingType,不加新枚举位)
const FundTypeMask = 32768;
-//允许配置期限档的 ETF 子类(SpanConfig.EtfKind 值,取值=数据字典"ETF 子类"),
-//与后端 ConsMarginTerm.TermTierEnabledEtfKinds 保持一致;其余子类与"不区分"的基金行不分档
-const TermTierEnabledEtfKinds = ['可转债 ETF', '科创债 ETF'];
-
//利率债区块固定展开的4个期限档([BondTerm值, 展示文案]),与 docx 原型 4.3.1 一致
const SpanBondTerms = [['<5y', '≤5y'], ['5y-10y', '(5y-10y]'], ['10y-30y', '(10y-30y]'], ['>30y', '>30y']];
@@ -106,8 +102,8 @@ const vue = new Vue({
}
return rows;
},
- //区间追保结构录入区块:每个资产类型区块一个;纯利率债(ut=16)与分档 ETF 子类
- //(ut=基金 + EtfKind∈可转债/科创债 ETF)区块固定展开4个期限档段(每段一条 detail),其他区块单段(BondTerm 为空)。
+ //区间追保结构录入区块:每个资产类型区块一个;纯利率债(ut=16)区块固定展开4个期限档段(每段一条 detail),
+ //其他区块(含全部 ETF 子类,2026-08-27 裁定 ETF 无期限概念不再分档)单段(BondTerm 为空)。
//段的 detail 缺失时模板侧 v-if 兜底不渲染该段
ruleRangeBlocks() {
if (this.marginTemplate.RuleType != MarginRuleTypeEnum.区间追保结构) return [];
@@ -120,10 +116,8 @@ const vue = new Vue({
var ut = head.detail.UnderlyingType || 0;
var kind = (head.detail.SpanConfig && head.detail.SpanConfig.EtfKind) || '';
var isTBond = ut === 16;
- var isTieredEtf = ut === FundTypeMask && TermTierEnabledEtfKinds.indexOf(kind) >= 0;
- var isTiered = isTBond || isTieredEtf;
var sections = [];
- if (isTiered) {
+ if (isTBond) {
for (var t = 0; t < SpanBondTerms.length; t++) {
var found = null;
for (var j = head.index; j < head.index + head.rowspan; j++) {
@@ -135,7 +129,7 @@ const vue = new Vue({
} else {
sections.push({ key: 'single', termLabel: '', detail: head.detail });
}
- blocks.push({ key: head.detail._bk || ('i' + head.index), no: blocks.length + 1, headIndex: head.index, ut: ut, etfKind: kind, isTBond: isTBond, isTieredEtf: isTieredEtf, isFundBlock: ut === FundTypeMask, sections: sections });
+ blocks.push({ key: head.detail._bk || ('i' + head.index), no: blocks.length + 1, headIndex: head.index, ut: ut, etfKind: kind, isTBond: isTBond, isFundBlock: ut === FundTypeMask, sections: sections });
}
return blocks;
}
@@ -432,64 +426,35 @@ const vue = new Vue({
d.SpanConfig.EtfKind = etfKind || null;
return d;
},
- //ETF 子类选择器变更(仅纯基金区块渲染):选可转债/科创债 ETF → 区块展开固定4档(缺档补行、异常档行删除);
- //选其他子类/不区分 → 收敛为单行(BondTerm 置空,多余档位行删除,首行承载 EtfKind)
+ //ETF 子类选择器变更(仅纯基金区块渲染):子类仅用于行区分(取数侧子类行优先),
+ //ETF 无期限概念不分档——任何子类切换后区块始终收敛为单行(BondTerm 置空,多余档位行删除,首行承载 EtfKind;
+ //存量 4 档子类数据在这里被收敛时仅保留首行录入内容)
onEtfKindChange(blk) {
var head = blk.sections[0].detail;
var kind = (head.SpanConfig && head.SpanConfig.EtfKind) || '';
- var tiered = TermTierEnabledEtfKinds.indexOf(kind) >= 0;
var details = this.marginTemplate.Details;
var bk = head._bk;
var rows = details.filter(function (d) { return d._bk === bk; });
- if (tiered) {
- var byTerm = {};
- rows.forEach(function (r) {
- var bt = (r.SpanConfig && r.SpanConfig.BondTerm) || '';
- if (byTerm[bt] === undefined) byTerm[bt] = r;
- });
- var that = this;
- var merged = [];
- //首行未被4个合法档位命中时(单段行 BondTerm 为空)复用为首个档位行,保留已录入内容;
- //否则首行会以"EtfKind 已选、BondTerm 为空"的残留行留在明细里,保存时被服务端"分期限档类型必须配置期限档"拦截
- var headMatched = SpanBondTerms.some(function (t) { return byTerm[t[0]] === head; });
- SpanBondTerms.forEach(function (t, ti) {
- var r = byTerm[t[0]] || (ti === 0 && !headMatched ? head : null);
- if (!r) r = that.newSpanDetail(FundTypeMask, t[0], kind);
- r.UnderlyingType = FundTypeMask;
- r.SpanConfig.EtfKind = kind;
- r.SpanConfig.BondTerm = t[0];
- Vue.set(r, '_bk', bk);
- merged.push(r);
+ rows.forEach(function (r) {
+ if (r !== head) {
var idx = details.indexOf(r);
- if (idx >= 0 && r !== head) details.splice(idx, 1);
- });
- //异常档/重复行全部移除后按档序回插到区块首行位置
- var headIdx = details.indexOf(head);
- if (headIdx < 0) headIdx = details.length;
- details.splice.apply(details, [headIdx + 1, 0].concat(merged.filter(function (r) { return r !== head; })));
- } else {
- rows.forEach(function (r) {
- if (r !== head) {
- var idx = details.indexOf(r);
- if (idx >= 0) details.splice(idx, 1);
- }
- });
- head.UnderlyingType = FundTypeMask;
- head.SpanConfig.BondTerm = '';
- head.SpanConfig.EtfKind = kind || null;
- }
+ if (idx >= 0) details.splice(idx, 1);
+ }
+ });
+ head.UnderlyingType = FundTypeMask;
+ head.SpanConfig.BondTerm = '';
+ head.SpanConfig.EtfKind = kind || null;
this.refreshChosen();
},
- //分档行判定与分档键:纯利率债 → 'tbond';基金 + 分档 ETF 子类 → 'etf:子类'(同一子类的连续行归一个区块)
+ //分档行判定与分档键:纯利率债 → 'tbond'(连续行归一个区块按固定4档补齐);ETF 子类不分档,基金行一律独立成块
spanTierKey(d) {
var ut = d.UnderlyingType || 0;
- var kind = (d.SpanConfig && d.SpanConfig.EtfKind) || '';
if (ut === TermTierEnabledUnderlyingMask) return 'tbond';
- if (ut === FundTypeMask && TermTierEnabledEtfKinds.indexOf(kind) >= 0) return 'etf:' + kind;
return null;
},
- //存量回显重建资产类型区块(分配区块键 _bk):连续的纯利率债行、基金+分档ETF子类行各自归为区块并按固定4档补齐(缺档补空行);
- //同档重复/异常档的分档行、非分档行(含基金+不分档子类、基金通配行)均各自独立成块展示,不丢数据
+ //存量回显重建资产类型区块(分配区块键 _bk):连续的纯利率债行归为区块并按固定4档补齐(缺档补空行);
+ //同档重复/异常档的分档行、其余全部行(含基金+子类行、基金通配行)均各自独立成块展示,不丢数据——
+ //存量 ETF 子类 4 档行会以多个单行区块出现,保存时由服务端"ETF 子类不分期限档"校验拦截提示清理
rebuildSpanBlocks(details) {
var termValues = SpanBondTerms.map(function (t) { return t[0]; });
var result = [];
@@ -511,13 +476,10 @@ const vue = new Vue({
if (termValues.indexOf(bt) >= 0 && !byTerm[bt]) byTerm[bt] = r; else anomalies.push(r);
});
var bk = 'bk_' + (++spanBlockSeq);
- var utv = tk === 'tbond' ? TermTierEnabledUnderlyingMask : FundTypeMask;
- var kind = tk === 'tbond' ? '' : tk.substring(4);
termValues.forEach(function (t) {
var r = byTerm[t];
- if (!r) r = that.newSpanDetail(utv, t, kind);
- r.UnderlyingType = utv;
- if (kind) r.SpanConfig.EtfKind = kind;
+ if (!r) r = that.newSpanDetail(TermTierEnabledUnderlyingMask, t);
+ r.UnderlyingType = TermTierEnabledUnderlyingMask;
r.SpanConfig.BondTerm = t;
Vue.set(r, '_bk', bk);
result.push(r);
@@ -549,15 +511,19 @@ const vue = new Vue({
return map[v] !== undefined ? map[v] : v;
},
//规则15录入区块文案:按 detail 资产类型切换计价口径。
- //债券类(利率债16/信用债32/其它债券64)及其他类型(默认债券口径):期初净价/当前净价,金额=×期初全价×券面总额;
- //基金及基金专户(32768)/债券指数(536870912):参考标的期初净价/参考标的当前收盘价(空头各层统一收盘价,BUG-11 修正),
+ //比较价格口径与计算侧对齐(MarginCalculationBase.CalcSwapSpanMaintenanceMargin:IsBond→中债估值净价,其余→收盘价):
+ //纯债券类(利率债16/信用债32/其它债券64,可组合):期初净价/当前净价,金额=×期初全价×券面总额;
+ //其余全部类型(基金/债券指数/股票/股指等):参考标的期初净价/参考标的当前收盘价(多空各层统一收盘价),
//金额=×参考标的期初价格×参考标的名义份额。
spanText(detail) {
var ut = (detail && detail.UnderlyingType) || 0;
- if ((ut & 32768) > 0 || (ut & 536870912) > 0) {
+ //非空且标志位全部落在债券三类内才按债券口径;混合行(债券|非债券)按收盘价口径显示,与取数侧标的实际类型判定方向一致
+ var bondMask = 16 | 32 | 64;
+ var isBond = ut !== 0 && (ut & ~bondMask) === 0;
+ if (!isBond) {
return {
priceInit: '参考标的期初净价',
- priceCur: '参考标的当前净价',
+ priceCur: '参考标的当前收盘价',
priceCurShort1: '参考标的当前收盘价',
priceCurShort: '参考标的当前收盘价',
amountBase: '参考标的期初价格 × 参考标的名义份额'
From cd615a612a839f3c3db12b9ac1f76e8f533a01ce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=94=A6=E9=BA=9F=20=E7=8E=8B?=
Date: Thu, 27 Aug 2026 20:41:53 +0800
Subject: [PATCH 03/19] =?UTF-8?q?BugFix=20=E9=A2=84=E4=BB=98=E9=87=91?=
=?UTF-8?q?=E5=8F=96=E9=94=99=E4=BA=86=E8=B5=84=E4=BA=A7=E7=B1=BB=E5=9E=8B?=
=?UTF-8?q?=E8=A7=84=E5=88=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...arginTemplateV2InstrumentFirstMatchTest.cs | 144 ++++++++++++++++++
.../MarginTemplateV2RateHelper.cs | 46 +++---
2 files changed, 168 insertions(+), 22 deletions(-)
create mode 100644 UnitTestProject/Modules/CalcModules/MarginTemplateV2InstrumentFirstMatchTest.cs
diff --git a/UnitTestProject/Modules/CalcModules/MarginTemplateV2InstrumentFirstMatchTest.cs b/UnitTestProject/Modules/CalcModules/MarginTemplateV2InstrumentFirstMatchTest.cs
new file mode 100644
index 00000000..6cf60d56
--- /dev/null
+++ b/UnitTestProject/Modules/CalcModules/MarginTemplateV2InstrumentFirstMatchTest.cs
@@ -0,0 +1,144 @@
+using YLErp.BLL;
+using YLErp.DBModels;
+using YLErp.Enums;
+using YLErp.Modules.MarginModule;
+
+namespace YLErp.Modules.CalcModules
+{
+ ///
+ /// GetRateByTemplate 资产类型先行匹配回归(连 dev 库,2026-08-27 顺序裁定:先品种后期限):
+ /// 期限档仅利率债允许配置,品种匹配在期限之前——防止非利率债标的被利率债期限档行截胡
+ /// (交易2567 实证:信用债标的按 "<5y" 精确匹配到利率债行,信用债行 BondTerm 空永远不参与)。
+ /// 标的代码用库里不存在的代码(GetApplicableMarginTerm 无标的兜底返回 <5y),不依赖真实标的行情数据。
+ /// 测试数据全部带 "ZZZ-品种先行测试-" 名称前缀,TestInitialize/TestCleanup 双向清理。
+ ///
+ [TestClass]
+ public class MarginTemplateV2InstrumentFirstMatchTest
+ {
+ private const string Marker = "ZZZ-品种先行测试-";
+ private DateTime EffectiveDate = new DateTime(2000, 1, 1);
+
+ private YLContext db;
+
+ [TestInitialize]
+ public void Init()
+ {
+ db = new YLContext();
+ Cleanup();
+ }
+
+ [TestCleanup]
+ public void CleanupFixture()
+ {
+ Cleanup();
+ db.Dispose();
+ }
+
+ private void Cleanup()
+ {
+ var templateIds = db.margin_template_v2.Where(x => x.Name.StartsWith(Marker)).Select(x => x.id).ToList();
+ if (templateIds.Count > 0)
+ {
+ db.margin_template_detail.RemoveRange(db.margin_template_detail.Where(x => templateIds.Contains(x.MarginTemplateId)));
+ db.margin_template_v2.RemoveRange(db.margin_template_v2.Where(x => templateIds.Contains(x.id)));
+ db.SaveChanges();
+ }
+ }
+
+ private margin_template_v2 AddTieredTemplate()
+ {
+ var t = new margin_template_v2
+ {
+ Name = Marker + "分档",
+ IsDefault = false,
+ IsForClient = false,
+ IsValid = true,
+ TradeTypes = "收益互换",
+ RuleType = (int)MarginRuleTypeEnum.区间追保结构,
+ UnderlyingSeperateType = (int)UnderlyingSeperateTypeEnum.CustomInstrumentType,
+ ValueDate = EffectiveDate
+ };
+ db.margin_template_v2.Add(t);
+ db.SaveChanges();
+ return t;
+ }
+
+ private void AddDetail(int templateId, UnderlyingTypeEnum underlyingType, string bondTermJson, double initRate, double maintainRate)
+ {
+ db.margin_template_detail.Add(new margin_template_detail
+ {
+ MarginTemplateId = templateId,
+ ValueDate = EffectiveDate,
+ UnderlyingType = underlyingType,
+ SpanConfigJson = bondTermJson,
+ MarginRatio1 = initRate,
+ MarginRatio2 = maintainRate
+ });
+ }
+
+ ///
+ /// 信用债标的不被利率债期限档行截胡:term 恒为 "<5y"(标的不存在兜底),
+ /// 旧序会精确命中利率债 <5y 行;新序品种先行应命中信用债行(BondTerm 空)。
+ ///
+ [TestMethod]
+ public void TI_001_信用债标的_命中信用债行_不被利率债期限档截胡()
+ {
+ var tpl = AddTieredTemplate();
+ AddDetail(tpl.id, UnderlyingTypeEnum.TBonds, "{\"BondTerm\":\"<5y\"}", 0.11, 0.12);
+ AddDetail(tpl.id, UnderlyingTypeEnum.CreditBonds, null, 0.05, 0.06);
+ db.SaveChanges();
+
+ var rate = MarginTemplateV2RateHelper.GetRateByTemplate(tpl, "ZZZ-NOT-EXIST-CD.IB", "CreditBonds", DateTime.Today, db);
+ Assert.IsNotNull(rate, "品种先行后信用债行(BondTerm 空)应经期限兜底命中");
+ Assert.AreEqual(0.05m, rate.InitRate.Value, "应取信用债行的初始预付金率,而非利率债 <5y 行的 0.11");
+ Assert.AreEqual(0.06m, rate.MaintainRate.Value, "应取信用债行的维持预付金率,而非利率债 <5y 行的 0.12");
+ }
+
+ ///
+ /// 利率债标的行为不变:品种命中利率债行后,期限精确档 "<5y" 命中对应期限行(压过 5y-10y 行)。
+ ///
+ [TestMethod]
+ public void TI_002_利率债标的_品种内期限精确档仍生效()
+ {
+ var tpl = AddTieredTemplate();
+ AddDetail(tpl.id, UnderlyingTypeEnum.TBonds, "{\"BondTerm\":\"<5y\"}", 0.11, 0.12);
+ AddDetail(tpl.id, UnderlyingTypeEnum.TBonds, "{\"BondTerm\":\"5y-10y\"}", 0.13, 0.14);
+ db.SaveChanges();
+
+ var rate = MarginTemplateV2RateHelper.GetRateByTemplate(tpl, "ZZZ-NOT-EXIST-TB.IB", "TBonds", DateTime.Today, db);
+ Assert.IsNotNull(rate);
+ Assert.AreEqual(0.11m, rate.InitRate.Value, "期限兜底 <5y 时应精确命中 <5y 档行");
+ Assert.AreEqual(0.12m, rate.MaintainRate.Value);
+ }
+
+ ///
+ /// 模板未配标的品种时的既有兜底不变:品种行与通配行均无 → 不缩小行集,回落期限匹配(与旧序一致)。
+ ///
+ [TestMethod]
+ public void TI_003_模板未配品种_回落期限匹配_行为不变()
+ {
+ var tpl = AddTieredTemplate();
+ AddDetail(tpl.id, UnderlyingTypeEnum.TBonds, "{\"BondTerm\":\"<5y\"}", 0.11, 0.12);
+ db.SaveChanges();
+
+ var rate = MarginTemplateV2RateHelper.GetRateByTemplate(tpl, "ZZZ-NOT-EXIST-CF.IB", "CommodityFutures", DateTime.Today, db);
+ Assert.IsNotNull(rate, "品种落空应回落到期限匹配(旧行为兜底),不应返回 null");
+ Assert.AreEqual(0.11m, rate.InitRate.Value);
+ }
+
+ ///
+ /// 品种行与期限行均无法匹配时返回 null:非利率债标的不再"借用"利率债期限档行,
+ /// 由调用方按无预付金要求兜底(引擎不产出 trade_span)。
+ ///
+ [TestMethod]
+ public void TI_004_品种与期限均无匹配行_返回null()
+ {
+ var tpl = AddTieredTemplate();
+ AddDetail(tpl.id, UnderlyingTypeEnum.TBonds, "{\"BondTerm\":\"5y-10y\"}", 0.13, 0.14);
+ db.SaveChanges();
+
+ var rate = MarginTemplateV2RateHelper.GetRateByTemplate(tpl, "ZZZ-NOT-EXIST-CD.IB", "CreditBonds", DateTime.Today, db);
+ Assert.IsNull(rate, "信用债标的不应命中利率债 5y-10y 期限行");
+ }
+ }
+}
diff --git a/YLErpDAL/Modules/MarginModule/MarginTemplateV2RateHelper.cs b/YLErpDAL/Modules/MarginModule/MarginTemplateV2RateHelper.cs
index 99e47346..25999088 100644
--- a/YLErpDAL/Modules/MarginModule/MarginTemplateV2RateHelper.cs
+++ b/YLErpDAL/Modules/MarginModule/MarginTemplateV2RateHelper.cs
@@ -129,19 +129,39 @@ namespace YLErp.Modules.MarginModule
var latestValueDate = detailQuery.Max(x => x.ValueDate);
var details = detailQuery.Where(x => x.ValueDate == latestValueDate).ToList();
- //4.利率债/分档ETF 期限档匹配:精确档 → "全部"(BondTerm 为空)兜底
+ //4.资产类型先行(2026-08-27 顺序裁定:先品种后期限):按资产类型分档的模板先按标的品种缩小行集——
+ //品种行 → 通配行(None/All)→ 均无则不缩小(回落到与旧序一致的期限匹配,模板未配该品种的既有兜底不变)。
+ //期限档仅利率债允许配置(ConsMarginTerm),品种匹配必须在期限之前:期限精确匹配对任何标的恒有 term
+ //(GetApplicableMarginTerm 兜底 <5y),非利率债标的会被利率债期限档行截胡、本品种行(BondTerm 空)永远不参与
+ //(2026-08-27 交易2567 实证:信用债标的按 "<5y" 命中利率债行多收追保)
+ var candidates = details;
+ if (template.UnderlyingSeperateType == (int)UnderlyingSeperateTypeEnum.CustomInstrumentType
+ && Enum.TryParse(underlyingInstrumentType, out var instrumentFlag))
+ {
+ var byInstrument = details.Where(x => (x.UnderlyingType & instrumentFlag) > 0).ToList();
+ if (!byInstrument.Any())
+ {
+ byInstrument = details.Where(x => x.UnderlyingType == UnderlyingTypeEnum.None || x.UnderlyingType == UnderlyingTypeEnum.All).ToList();
+ }
+ if (byInstrument.Any())
+ {
+ candidates = byInstrument;
+ }
+ }
+
+ //5.期限档匹配(利率债四档):精确档 → "全部"(BondTerm 为空)兜底
var term = UnderlyingHelper.GetApplicableMarginTerm(underlyingCode, valueDate);
- var matched = details.Where(x => x.SpanConfig != null && x.SpanConfig.BondTerm == term).ToList();
+ var matched = candidates.Where(x => x.SpanConfig != null && x.SpanConfig.BondTerm == term).ToList();
if (!matched.Any())
{
- matched = details.Where(x => x.SpanConfig == null || string.IsNullOrEmpty(x.SpanConfig.BondTerm)).ToList();
+ matched = candidates.Where(x => x.SpanConfig == null || string.IsNullOrEmpty(x.SpanConfig.BondTerm)).ToList();
}
if (!matched.Any())
{
return null;
}
- //5.ETF 子类行优先(子类区分度高于期限):标的有 EtfSubType(基金类)时优先取 EtfKind=子类 的行——
+ //6.ETF 子类行优先(子类区分度高于期限):标的有 EtfSubType(基金类)时优先取 EtfKind=子类 的行——
//期限档匹配未命中子类行时再单独尝试"子类 + BondTerm 空"(子类不分档通配);无子类行维持原 matched(基金通配兜底)
var underlyingCategory = GetUnderlyingCategory(underlyingCode, underlyingInstrumentType);
if (underlyingCategory != null)
@@ -149,24 +169,6 @@ namespace YLErp.Modules.MarginModule
matched = PreferCategoryRows(matched, details, underlyingCategory);
}
- if (template.UnderlyingSeperateType == (int)UnderlyingSeperateTypeEnum.CustomInstrumentType
- && Enum.TryParse(underlyingInstrumentType, out var instrumentFlag))
- {
- var byInstrument = matched.Where(x => (x.UnderlyingType & instrumentFlag) > 0).ToList();
- if (byInstrument.Any())
- {
- matched = byInstrument;
- }
- else
- {
- var wildcard = matched.Where(x => x.UnderlyingType == UnderlyingTypeEnum.None || x.UnderlyingType == UnderlyingTypeEnum.All).ToList();
- if (wildcard.Any())
- {
- matched = wildcard;
- }
- }
- }
-
var detail = matched.First();
return new MarginRateResult
{
From 65cb3ff95c82109d919ce4f2d2c86884f8b370af Mon Sep 17 00:00:00 2001
From: hjhan
Date: Fri, 28 Aug 2026 06:54:31 +0800
Subject: [PATCH 04/19] =?UTF-8?q?docs:=20=E6=97=A7=E4=BF=9D=E8=AF=81?=
=?UTF-8?q?=E9=87=91=E9=93=BE=E8=B7=AF=E5=AD=98=E4=BA=A1=E5=88=86=E6=9E=90?=
=?UTF-8?q?=E4=B8=8E=E4=B8=8B=E7=BA=BF=E9=87=8D=E6=9E=84=E6=96=B9=E6=A1=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
预付金引擎收口(4adfbabe)后对山证拷贝遗留的保证金链路做全量死活判定:
bond-oms HTTP接口/CalcDMAMargin全链死、V1模板链部分可删、client_marginrate
半死、远期结算链确定死、DMA仅剩2处真实分支;附分阶段下线方案与需确认清单。
---
.../旧保证金链路存亡分析与下线重构方案.md | 125 ++++++++++++++++++
1 file changed, 125 insertions(+)
create mode 100644 项目文档/旧保证金链路存亡分析与下线重构方案.md
diff --git a/项目文档/旧保证金链路存亡分析与下线重构方案.md b/项目文档/旧保证金链路存亡分析与下线重构方案.md
new file mode 100644
index 00000000..ed948c57
--- /dev/null
+++ b/项目文档/旧保证金链路存亡分析与下线重构方案.md
@@ -0,0 +1,125 @@
+# 旧保证金链路存亡分析与下线重构方案
+
+- 日期:2026-08-28
+- 分析基线:`glms/feature/1.4.2` @ `cd615a61`
+- 背景:2026-08-26 `4adfbabe`(配套 `134e07c6`/`1969e292`)将互换预付金 EOD 计算收口为本端 .NET 引擎(`EodWorstClientPayableCalc` + `MarginTemplateV2RateHelper`,读 `margin_template_v2`),不再经 bond-oms Java 按旧 `marginrate` 数据算盯市。本文回答:**山证 v2.3.0 拷贝带过来的旧保证金相关代码(DMA 与否),在新引擎上线后还剩多少活口、如何分阶段下线。**
+
+## 一、结论总览
+
+| # | 链路 | 判定 | 一句话依据 |
+|---|------|------|-----------|
+| 1 | bond-oms HTTP 保证金接口(`/marginAlgorithm/realTimeMarginCalc` ← `CalcDMAMargin`) | **死,可删** | 全仓(含 YLWinSer/前端/配置)零调用方;`git log -S` 全历史自拷贝日起从未被调用 |
+| 2 | `margin_template` V1 模板管理链 | **部分可删** | 计算链已全部 V2 化;但 5 个 Razor 页面的模板下拉仍喂 V1 表 |
+| 3 | `client_marginrate` 旧预付金率链(MarginRateSwap) | **半死** | EOD 盯市消费方已随收口消失;剩 2 条活读链(流水导入落快照、录入页取率回显) |
+| 4 | `MarginRate`(不带 Swap)+ 远期保证金链 | **远期结算确定死;其余需业务确认** | `EodForwardMarginSettlement` 2025-04-16 已从 EOD 调度摘除,零调用方 |
+| 5 | DMA 分类(`Client.SwapTradeType`) | **活,但仅 2 处真实分支** | 其余全是透传展示与命名误导;`CalcDMAMargin` 死代码不影响判定 |
+
+## 二、分链详解
+
+### 2.1 bond-oms HTTP 保证金接口(死,可删)
+
+接口本体在 bond-oms Java 服务(`BondOmsInterface.BaseUrl = trs_hub_api`),本仓是唯一已知调用方,而调用方本身是死的:
+
+| 层 | 位置 | 状态 |
+|---|---|---|
+| 调用点 | `YLErpDAL/BLL/EodSettlement/RealtimePnlCalc.cs:722` `CalcDMAMargin()` | 全仓无任何调用方(全文件类型 grep) |
+| 方法内 URL | `RealtimePnlCalc.cs:725` 硬编码 | 随方法一起死 |
+| 配置键 | `YLWinSer/RealTimeCalcPositionService/appsettings.json:48` `CalculateDMAMarginUrl` | 无任何代码读取(URL 是硬编码的,配置键是摆设) |
+| DTO | `YLErpDAL/Model/CalculateMarginRequest.cs` | 仅被 `CalcDMAMargin` 使用,可连带删 |
+
+排除项:YLWinSer 常驻任务(ClientPosiTask/Worker/ClientNoDMABalanceTask)不调它;YLErpWeb 无 `marginAlgorithm` 路由/代理,前端(含 Vue3 仓)架构上不可达;无反射/字符串调度。
+
+**历史证据**:`git log -S "CalcDMAMargin()" --all` 仅两条 —— `f9d8a256`(山证拷贝,方法进仓)与 `4adfbabe`(只加注释)。即该方法从进仓第一天起就从未被调用,山证原版的 DMA 任务未随拷贝进来(本仓只有 `ClientNoDMABalanceTask`)。
+
+**注意**:`EodCheckMonitoredTrade.cs:186` 注释(4adfbabe 加)把 `CalcDMAMargin` 描述为"迁移方案阶段三待切项"——与事实不符(无流量可切),删码时必须同步修正,否则迁移计划继续被带偏。
+
+Java 端点能否删需在 bond-oms 仓自查内部调度,本仓证据只能证明"本仓侧调用链已死"。
+
+### 2.2 margin_template V1 模板链(部分可删)
+
+**计算链已全部 V2 化,无 V1 回退**:
+- 核心预付金:`MarginCalculationBase.cs:392,420` → `MarginTemplateV2RateHelper`(纯 V2 三级解析)
+- 追保/span:`SwapAdditionalMarginService.cs:115`、`SwapSpanBalanceQueryService.cs:91` 均走 V2 helper
+- 交易保存:`SwapTradeService.cs:162-209` 只查 `margin_template_v2` 并写 `trade_margin_template`(存 V2 id);`TradeSaveService.cs:654-697`、`TradeQueryService.cs:324`、`SwapMarginTemplateConfigService.cs:17` 同
+- V1 管理页入口已死:`Menus.txt` 仅剩指向 V2;`margin_templateController.cs:9` 引用的 FunctionRight 权限已不存在
+
+**V1 表仅剩 2 类活读点**:
+1. `tradeController.cs:8861/8873`(`GetMarginTemplates`/`GetMarginTemplateItems`,直接 `db.margin_template.ToList()`),被 5 个在用页面 Razor 服务端调用做模板名下拉:
+ `Views/SwapTrade2/TradeEdit.cshtml:17,33`、`Views/SwapTrade/tradeEdit.cshtml:15-16`、`Views/trade/TradeEditV2.cshtml:20-21`(远期编辑)、`Views/Pricing/Structure_DZ.cshtml:22-23`、`Views/Pricing/structure.cshtml`
+2. `ForwardTradeImportService.cs:318-352,872` 远期导入消费 V1 比率(远期业务本身存亡见 2.4)
+
+**不可删(易误伤)**:`client_margin_template` 已 V2 化,是 V2 引擎第二级数据源(`MarginTemplateV2RateHelper.cs:256,341`、`ClientBalanceUtility.cs:631`);`margin_template_detail`、`trade_margin_template` 是 V2 的表,与 V1 同名前缀但归属 V2。
+
+### 2.3 client_marginrate 旧预付金率链(半死)
+
+无独立 margin_rate_swap 表,整条链落在 `client_marginrate` 表(`Framework/YLErp.Core/DBModels/client_marginrate.cs:8`)。
+
+- **写入/维护链**:菜单入口已于 `ab907122`(2026-08-12,EQD-6947)注释下线(Menus.txt:81-82"由预付金模板V2替代");`MarginRateSwapController` 的 CRUD/导入 action 仍可直接 URL 访问,FunctionRight 权限残留。
+- **活读链(仅剩 2 条)**:
+ 1. 流水导入:`SwapTradeFlowImportService.cs:351-354` 按 客户+品种+日期 取 `client_marginrate` → `:374` `InitialMargin = marginRate * StockEqvNotional` → `:393` 落 `trade_swap.GetMarginRate` 快照(TradeSwapService 8 处调用)
+ 2. 录入取率回显:`SwapTradeController.cs:1152` `GetInitMarginRate` → `swapTradeEdit.js`(互换录入页实时回显)
+- **死读点**:`RealTimeClientBanlanceService.cs:1469` 加载后从未使用;`ClientBalanceUtility.cs:1517`、`ConfirmationGenerateContext.cs:1913`、`ITradeDocGeneratorContext.cs:443` 全仓零调用;`SwapTradeValidator` 只看 `trade_swap.GetMarginRate` 快照,`:24` 的调用已注释。
+- **与新引擎零交叉**:已逐一确认 `EodWorstClientPayableCalc`、`MarginTemplateV2RateHelper`、`RealtimePnlCalc`、`SwapSpanBalanceCalc`、`SwapAdditionalMarginService` 均不读 `client_marginrate`;无 SQL/Dapper/存储过程读点;YLWinSer 无相关任务。
+
+### 2.4 MarginRate(不带 Swap)+ 远期链
+
+- **`EodForwardMarginSettlement` 确定死**:`8e61f23e`(2025-04-16)已将其从 EOD 调度摘除,现全仓零调用方;`eod_forward_margin` 表唯一写入点随之失活,读方(`EodPositionSettleService.cs:870,967`、`RealtimePnlCalc.cs:1009`、`TradeForwardUnwindService.cs:96`)全部空转。`SettlementConfig.CalcForwradMargin` 零消费。
+- **Forward 模块整体**:拷贝后零提交改动;录入页/导入/11 个 Views/API 均在,菜单在 DB `sys_menu`(仓内无法证明挂没挂);`tradeController.cs:6295` 仅渤海/广发商贸分支才查远期。**需业务确认**(菜单是否还挂、trade 表有无远期存量)。
+- **`MarginRate` CRUD/导入**:UI 自闭环;表读方仅 `SwapTradeFlowImportService.cs:337`(且被 `PS.Config.Company==中金` 门控,本部署非中金)与 `VarietyDalService.cs:426`(防删校验);`GetMarginRate`(`MarginRateService.cs:210`)零调用。
+- **`MarginParamProvider` 不可删**:`MarginCalcHelper.cs:30`(活引擎 `RunMarginCalculation` 内)与 `TradeDelaySettlementService.cs:72,163` 在用其涨跌幅/波动率;其预付金率读法(`:149-160`)才是死的。
+
+### 2.5 DMA 概念:字段活,概念基本只剩命名
+
+字段 = `Client.SwapTradeType`(`Client.cs:1149`,1=DMA/MDA,0=非DMA)。**真实分支仅 2 处**:
+1. `QuotaMonitorService.cs:5591` `CheckFund`:`SwapTradeType==0` 非 DMA 不做资金校验直接过(经 `RunQuotaTrial` ← `tradeController.cs:8502` 限额试算,活)
+2. `YLErpWeb/App/KafkaTask/ClientBalanceTask.cs:62`:DMA 客户 `lastBalanceDate=valuedate`(起算日改为当日),活
+
+其余全部是透传/展示/命名:`ClientSettleBalance.ClientTypeStr`、`ClientBalanceUtility`、`RealTimeClientBanlanceService`、监控/报表 js、客户编辑下拉等;`SwapFlowCombookingHub.cs:79` "DMA合成持仓" 只是 region 名(DMA 过滤已注释,与 DMA 无关,Hub 本身有前端连接方,活);`ClientNoDMABalanceTask` 的 "NoDMA" 纯任务名,`GetBalances()` 取全部客户无 DMA 过滤。
+
+## 三、下线重构方案(分阶段)
+
+### 阶段 0:立即可删(零调用方,已实证)
+
+| 删除项 | 前置条件 |
+|---|---|
+| `RealtimePnlCalc.CalcDMAMargin()` + `CalculateMarginRequest.cs` + appsettings `CalculateDMAMarginUrl` 键 | 无 |
+| `EodCheckMonitoredTrade.cs:186` 注释修正(去掉"阶段三待切项"误导) | 随上条同提交 |
+| `EodForwardMarginSettlement.cs` + `SettlementConfig.CalcForwradMargin` | 无(可选:同步清 `eod_forward_margin` 读方空转代码) |
+
+### 阶段 1:小改造后可删(V1 模板链)
+
+1. `tradeController.GetMarginTemplates/GetMarginTemplateItems` 改为从 `margin_template_v2` 取数(或确认 5 个页面的 V1 下拉已无业务意义直接去掉下拉)。
+2. 改造完成后删:`margin_templateController.cs`、`Views/margin_template/*`、`MarginTemplateService.cs`、`margin_templateReq.cs`、`DBModels/margin_template.cs`、`YLContext.cs` 中对应 DbSet。
+3. 远期导入的 V1 比率消费随阶段 2 远期业务结论一并处理。
+
+### 阶段 2:需先迁移读链(client_marginrate / 远期)
+
+1. 流水导入落快照(`SwapTradeFlowImportService.cs:351-393`)与录入取率回显(`GetInitMarginRate`)两条链迁 V2 引擎取率。
+2. 迁完后整体下线 `MarginRateSwapController` + `MarginRateSwapService` + `client_marginrate` 表链,并清理 FunctionRight 残留权限。
+3. 远期业务经业务确认后:无存量则 Forward 模块 + `MarginRate` 链整体清理;有存量则查询/平仓/对账页暂留、仅清结算死链。
+
+### 需确认清单(删除前必须逐项闭环)
+
+| 项 | 确认方式 | 风险 |
+|---|---|---|
+| Vue3 前端仓是否调用 `/trade/GetMarginTemplates`、ForwardTrade、MarginRateSwap 系列 action | 前端仓 grep(本仓不可见) | action 被直连调用 |
+| trade 表有无远期存量数据、`sys_menu` 是否还挂远期菜单 | DB 查询 | 存量交易无法查询/平仓 |
+| `client_margin_template` 历史行 `MarginTemplateId` 是否残留 V1 id | DB 查询 | join 不上会静默落到第三级默认 |
+| `client_marginrate` 表是否有 V2 之外的人工维护依赖(运营流程) | 业务确认 | 导入链迁 V2 后仍有人改旧表 |
+| bond-oms 内部是否有 `/marginAlgorithm/realTimeMarginCalc` 的其他触发方 | bond-oms 仓确认 | Java 端点下线 |
+
+### 不可删清单(防误伤)
+
+`client_margin_template` / `margin_template_detail` / `trade_margin_template`(V2 数据源)、`MarginParamProvider`(涨跌幅/波动率在用)、`Client.SwapTradeType` 及其 2 处分支、`ClientBalanceTask`、`SwapFlowCombookingHub`。
+
+### 顺手项(命名去毒,不动逻辑)
+
+- `ClientNoDMABalanceTask` 任务名、`SwapFlowCombookingHub` "DMA合成持仓" region 名、`ClientBalanceForTrsResponse.ClientType` 注释口径,均与实际行为不符,可在触碰时改名。
+
+## 四、验证纪律(执行删除时)
+
+沿用 2026-08 死代码清理的教训(见 `多租户死代码清理执行计划.md`):
+1. 每个删除项独立小提交,删前全文件类型 grep(不止 *.cs,含 js/cshtml/xml/json/配置),删后 Release + Debug 双构建;
+2. `git rm` 目录混批后必须重新 find 核对(笔误会静默回滚整批);
+3. 涉及 DBModel/列的删除,列残留留给 DBA,不在应用层迁移;
+4. action 删除必须先过"前端仓确认"关卡。
From c007037d36d330af1c7d7ca2cada880dfe1f6475 Mon Sep 17 00:00:00 2001
From: hjhan
Date: Fri, 28 Aug 2026 08:24:50 +0800
Subject: [PATCH 05/19] =?UTF-8?q?cleanup:=20=E5=88=A0=E9=99=A4=20PricingCo?=
=?UTF-8?q?ntroller=20=E4=B8=AD=E6=97=A0=E4=BA=BA=E4=BD=BF=E7=94=A8?=
=?UTF-8?q?=E7=9A=84=20Structure=5FDZ=20=E6=AD=BB=20Action=20=E5=8F=8A=20S?=
=?UTF-8?q?tructureImport=20=E6=B6=A6=E5=92=8C=E6=AD=BB=E5=88=86=E6=94=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
依据 outputs/zszq-trs-pricing-structure-dz-死代码分析报告.md 第一轮零风险清理。
国联分支下 Is润和 恒为 false,Structure_DZ 无任何菜单/代码入口。
单独 revert 本 commit 即可整体找回。
---
YLErpWeb/Controllers/PricingController.cs | 53 -----------------------
1 file changed, 53 deletions(-)
diff --git a/YLErpWeb/Controllers/PricingController.cs b/YLErpWeb/Controllers/PricingController.cs
index b0af74eb..c65dc87d 100644
--- a/YLErpWeb/Controllers/PricingController.cs
+++ b/YLErpWeb/Controllers/PricingController.cs
@@ -85,54 +85,6 @@ namespace YLErp.Web.Controllers
return View(model);
}
- ///
- /// 组合报价
- ///
- [MyAuthorize("报价管理-结构化交易定价")]
- public ActionResult Structure_DZ()
- {
- var otcTrade = new OtcOptionTradeFull()
- {
- TraderId = CurUser.UserId,
- TraderName = CurUser.UserName,
- BuySell = "卖出",
- VolType = "交易",
- TradeType = "香草期权",
- OptionType = "看涨",
- ExerciseMode = "European",
- TradeDate = valuedateBLL.ValueDate,
- UnderlyingInstrumentType = AppHelper.OtcConfig.StockFirst ? "Stock" : "CommodityFutures",
- SettlementType = (int)SettlementTypeEnum.ClosePrice,
- NoRiskRate = valuedateBLL.SystemDate.RiskFreeRate / 100,
- ParticipationRate = 1,
- AnnualizeFactor = 1,
- MarginTemplateName = "系统默认",
- CouponIncludeStartDate = false,
- CouponUsePaymentDate = false
- };
-
- var model = new Models.PricingModel(CurUser, UserBLL.IsTradeOfCurrentLogin(CurUser.UserId)) { Trade = otcTrade };
-
- if (model.NumOfSmoothingDaysCfg == "ONE")
- {
- model.Trade.NumOfSmoothingDays = 1;
- }
- //获取自定义结构信息
- var structureTypes =
- new StructureService(CurUser)
- .QueryStructureMap(StructureRangeEnum.BALCK_TRADE);
- var structureTypeMap = new Dictionary>() {
- { "气囊结构",new List() }
- };
- foreach (var item in structureTypes)
- {
- structureTypeMap[item.Key] = item.Value;
- }
- ViewBag.StructureTypeMap = structureTypeMap;
-
- return View(model);
- }
-
///
/// 组合报价导入
///
@@ -198,11 +150,6 @@ namespace YLErp.Web.Controllers
ViewBag.ExtendInfoMap[item.Key] = item.Value;
}
- if (PS.Config.Is润和)
- {
- return View(nameof(Structure_DZ), model);
- }
-
return View(nameof(Structure), model);
}
From 91680a8f2ff3051cefa0c230b8ccc1b69393d561 Mon Sep 17 00:00:00 2001
From: hjhan
Date: Fri, 28 Aug 2026 08:25:35 +0800
Subject: [PATCH 06/19] =?UTF-8?q?cleanup:=20=E5=88=A0=E9=99=A4=20=5Fdz=20?=
=?UTF-8?q?=E9=93=BE=E6=AD=BB=E8=A7=86=E5=9B=BE=20Structure=5FDZ.cshtml=20?=
=?UTF-8?q?=E4=B8=8E=20=5FPricingItemTpl=5Fdz.cshtml?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
依据死代码分析报告第一轮零风险清理。
Structure_DZ.cshtml 仅被已删除的 Structure_DZ() Action 使用;
_PricingItemTpl_dz.cshtml 仅被 Structure_DZ.cshtml PartialAsync 引用。
单独 revert 本 commit 即可整体找回。
---
YLErpWeb/Views/Pricing/Structure_DZ.cshtml | 910 ------------------
.../Views/Pricing/_PricingItemTpl_dz.cshtml | 905 -----------------
2 files changed, 1815 deletions(-)
delete mode 100644 YLErpWeb/Views/Pricing/Structure_DZ.cshtml
delete mode 100644 YLErpWeb/Views/Pricing/_PricingItemTpl_dz.cshtml
diff --git a/YLErpWeb/Views/Pricing/Structure_DZ.cshtml b/YLErpWeb/Views/Pricing/Structure_DZ.cshtml
deleted file mode 100644
index af6dcf67..00000000
--- a/YLErpWeb/Views/Pricing/Structure_DZ.cshtml
+++ /dev/null
@@ -1,910 +0,0 @@
-@using Microsoft.AspNetCore.Html
-@using YLErp.QdpModule.Constants
-@model PricingModel
-@{
- ViewBag.Title = "期权定价";
-
- if (Model.IsImport)
- {
- Layout = "~/Views/Shared/_InfoLayout.cshtml";
- }
- else
- {
- Layout = "~/Views/Shared/_MainLayout.cshtml";
- }
- var assetunits = JsDataModel.GetAssetUnits(CurUser);
- var traders = JsDataModel.GetTraders(assetunits);
- var pageObj = new
- {
- assetunits = assetunits,
- trade = new trade() { Strike = 0 },
- traders = JsDataModel.GetTraders(assetunits),
- tradeMarginTemplateItems = new tradeController().GetMarginTemplateItems(),
- tradeMarginTemplates = new tradeController().GetMarginTemplates(),
- engineNames = new[] { "abc", "xyz" },
- structureTypes = ViewBag.StructureTypeMap?.Keys,
- PropertyMap = ViewBag.StructureTypeMap,
- IsPVIncludePrincipal = PS.Config.ErpElement.IsPVIncludePrincipal
- };
- var pageData = new
- {
- showCCR = PS.Config.Company == CompanyEnum.国海,
- is厦门象屿 = PS.Config.Company == CompanyEnum.厦门象屿,
- };
-}
-@section CSS{
-
- @switch (PS.Config.Company)
- {
- case CompanyEnum.光大光子:
-
- break;
- case CompanyEnum.国泰君安:
-
- break;
- }
-
-}
-@section JS{
-
-
-
-
-
-
-
-
-
-
- @if (PS.Config.Company == CompanyEnum.伴兴)
- {
-
- }
- else if (PS.Config.Company == CompanyEnum.茂川资本)
- {
-
- }
- else if (PS.Config.Company == CompanyEnum.弘业)
- {
-
- }
-
-
-
-}
-
-@await Html.PartialAsync("_CouponDayCount")
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @if (CurUser.交易管理_交易新增)
- {
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
- @if (CurUser.交易管理_分组设置)
- {
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- | 对冲手数 |
- @*组合成交金额 | *@
- 组合预付金 |
- Day1Pnl |
- PV |
- Delta |
- GammaCash |
- Theta |
- Vega |
- Rho |
-
-
-
-
- |
- {{DeltaHands}}
- @if (Model.HedgingOrder && Model.IsTrader)
- {
-
- }
- |
- @*{{summary.TotalTradePrice| FixNumber}} | *@
- {{summary.TotalMargin| FixNumber}} |
- {{summary.TotalDay1Pnl| FixNumber}} |
- {{summary.Pv| FixNumber}} |
- {{summary.Delta| FixNumber}} |
- {{summary.GammaCash| FixNumber}} |
- {{summary.Theta| FixNumber}} |
- {{summary.Vega| FixNumber}} |
- {{summary.Rho| FixNumber}} |
-
-
-
-
-
-
-
-
-
-
-
-
@(Model.CompanyName) 付 0.000
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@await Html.PartialAsync("_PricingItemTpl_dz")
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@(Model.CompanyName) 付 0.000
-
-
-
-
-
-
-
-
-
-
-
-
-
-
![]()
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- | 编号 |
- 错误信息 |
-
-
-
-
- | 1 |
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@await Html.PartialAsync("_SyntheticPrice")
-@await Html.PartialAsync("/Views/trade/_part/SalesCommission.cshtml", new SalesCommissionModel() { Disabled = false, ViewType = "期权" })
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/YLErpWeb/Views/Pricing/_PricingItemTpl_dz.cshtml b/YLErpWeb/Views/Pricing/_PricingItemTpl_dz.cshtml
deleted file mode 100644
index f51a95fd..00000000
--- a/YLErpWeb/Views/Pricing/_PricingItemTpl_dz.cshtml
+++ /dev/null
@@ -1,905 +0,0 @@
-@*定价模板*@
-@model PricingModel
-@{ Layout = null;}
-
-
-
-
交易序号
-
-
- 客户名称
-
-
-
-
-
-
-
期权类型
-
-
-
-
-
- 现金流
-
-
-
- 标的类型
-
-
-
-
-
-
-
-
- 标的品种
-
-
-
-
-
-
-
-
标的代码
-
-
-
-
-
-
-
成交数量
-
-
-
-
-
-
-
- 有效成交数量
-
-
-
-
-
-
- 名义本金
-
-
-
-
-
- 有效名义本金
-
-
-
-
-
-
-
- 行权方式
-
-
-
-
-
-
-
-
交易日期
-
-
-
-
-
-
到期日期
-
-
-
-
-
-
- 结算日期
-
-
-
-
-
-
-
期初标的价格
-
-
-
期初标的价格
-
-
- 标的价格
-
-
- 标的价格
-
-
-
执行价格
-
-
执行价格
-
-
- 看涨看跌
-
-
-
-
-
-
-
交易方向
-
-
-
-
-
权利金
-
-
-
-
-
- 参与率
-
-
-
-
-
- 保底收益
-
-
-
-
-
-
-
-
-
-
- 保底收益总额
-
-
-
-
成交金额
-
-
-
-
-
-
- @if (Model.ShowInitialMargin)
- {
- //组合交易初始预付金不需要展示,只需要展示组合预付金
-
- 初始预付金
-
-
-
- }
-
-
- 组合价格
-
-
-
-
-
-
-
- @if (Model.ShowInitialMargin)
- {
-
- 组合预付金
-
-
-
-
- }
-
-
-
-
-
- 二元类型
-
-
-
- 补偿金额
-
-
-
-
-
-
- 高障碍价格
-
-
-
- 高障碍补偿金额
-
-
- 观察方式
-
-
-
- 补偿支付
-
-
-
-
- 补偿按敲出日年化
-
-
-
- 补偿计息规则
-
-
-
-
-
-
- 障碍类型
-
-
-
- 障碍价格
-
-
-
- 高障碍价格
-
-
-
- 补偿金额
-
-
-
-
- 高障碍补偿金额
-
-
-
-
- 障碍偏移
-
-
-
- 观察方式
-
-
-
- 补偿支付
-
-
-
- 补偿按敲出日年化
-
-
-
- 补偿计息规则
-
-
-
-
-
-
- 障碍价格
-
-
-
- 高障碍价格
-
-
-
- 高行权价
-
-
-
- 补偿金额
-
-
-
-
- 高障碍补偿金额
-
-
-
-
- 高参与率
-
-
-
- 低参与率
-
-
-
- 观察方式
-
-
-
-
- 补偿支付
-
-
-
-
-
-
- 均价起算日
-
-
-
- 均价计算
-
-
-
- 增强价格
-
-
-
- 行权价类型
-
-
-
- 杠杆率
-
-
-
-
-
-
-
- 敲出障碍价格
-
- 敲出赔付类别
-
-
-
- 票息年化
-
-
-
- 票息率
-
- 票息日历规则
-
-
-
- 票息包含首日
-
-
-
- 使用支付日计息
-
-
-
- 年化期权费率
-
- 敲出行权价1
-
-
-
- 敲出行权价2
-
-
-
- 敲出支付方式
-
-
-
- 敲出观察频率
-
-
- 敲入观察频率
-
- 敲入障碍价格
-
- 敲入到期支付类别
-
-
-
- 敲入行权价
-
-
-
- 封底/封顶行权价
-
-
-
- 红利票息
-
-
-
-
-
-
- 票息年化
-
-
-
- 票息率
-
- 票息障碍价格
-
- 票息日历规则
-
-
-
- 票息包含首日
-
-
-
- 票息结算方式
-
-
-
- 敲出障碍价格
-
- 敲出观察频率
-
- 敲入观察频率
-
- 敲入障碍价格
-
- 敲入到期是否支付票息
-
- 敲入到期支付类别
-
-
-
- 敲入行权价
-
-
-
- 封顶/封底行权价
-
-
-
-
-
-
- 区间下限
-
-
-
- 区间上限
-
-
-
- 区间收益
-
-
-
-
-
-
- 障碍价格
-
-
-
- 观察方式
-
-
-
- 敲入参与率
-
-
-
- 收益封顶
-
-
-
- 收益封顶价格
-
-
-
-
-
-
- 年化增强收益
-
-
-
-
-
-
- 利率
-
-
-
- 资金类型
-
-
-
- 利率类型
-
-
-
- 计算日历规则
-
-
-
- 预付返还比例
-
-
-
-
-
-
- 观察频率
-
-
-
-
-
成交波动率
-
-
-
-
Mid Vol
-
-
-
-
Day1 Pnl
-
-
-
-
-
-
- 收益结算
-
-
-
-
-
-
-
-
- 无风险利率
-
-
-
-
- 分红率
-
-
-
-
-
-
-
TTM(Days)
-
-
-
-
-
-
-
- 结算类型
-
-
-
- 结构要素
-
- 观察日
-
-
-
-
-
工作日
-
-
-
-
-
-
交易日
-
-
-
-
-
-
公共假日
-
-
-
-
-
-
-
-
- 是否年化
-
-
-
-
-
-
- 年化系数
-
-
-
-
-
- 是否年化
-
-
-
-
-
-
- 年化系数
-
-
-
-
-
-
-
-
- 定价模型
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- | PV |
- Delta |
-
-
- | {{calcResult.Pv}} |
- {{calcResult.Delta}} |
-
-
- | GammaCash |
- Vega |
-
-
- | {{calcResult.GammaCash}} |
- {{calcResult.Vega}} |
-
-
- | Theta |
- Rho |
-
-
- | {{calcResult.Theta}} |
- {{calcResult.Rho}} |
-
-
-
-
-
-
-
-
\ No newline at end of file
From 678ea1f52acd0b614365456b1024a6db7df73fb2 Mon Sep 17 00:00:00 2001
From: hjhan
Date: Fri, 28 Aug 2026 08:25:50 +0800
Subject: [PATCH 07/19] =?UTF-8?q?cleanup:=20=E5=88=A0=E9=99=A4=20structure?=
=?UTF-8?q?=5Fdz.js=20/=20tradePricing=5Fdz.js=20=E5=B9=B6=E5=8E=BB?=
=?UTF-8?q?=E6=8E=89=20TradeEditV2=20=E6=B6=A6=E5=92=8C=E8=84=9A=E6=9C=AC?=
=?UTF-8?q?=E5=88=86=E6=94=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
依据死代码分析报告第一轮零风险清理。
两个 _dz JS 仅被已删除的 _dz 视图链引用;
TradeEditV2.cshtml 国联下恒走 else 分支,简化后渲染结果不变。
单独 revert 本 commit 即可整体找回。
---
YLErpWeb/Views/trade/TradeEditV2.cshtml | 9 +-
.../Scripts/app/pricing/structure_dz.js | 2663 -----------------
.../Scripts/app/pricing/tradePricing_dz.js | 2472 ---------------
3 files changed, 1 insertion(+), 5143 deletions(-)
delete mode 100644 YLErpWeb/wwwroot/Scripts/app/pricing/structure_dz.js
delete mode 100644 YLErpWeb/wwwroot/Scripts/app/pricing/tradePricing_dz.js
diff --git a/YLErpWeb/Views/trade/TradeEditV2.cshtml b/YLErpWeb/Views/trade/TradeEditV2.cshtml
index f0ecffcf..5fad2564 100644
--- a/YLErpWeb/Views/trade/TradeEditV2.cshtml
+++ b/YLErpWeb/Views/trade/TradeEditV2.cshtml
@@ -93,14 +93,7 @@
- @if (PS.Config.Is润和)
- {
-
- }
- else
- {
-
- }
+
diff --git a/YLErpWeb/wwwroot/Scripts/app/pricing/structure_dz.js b/YLErpWeb/wwwroot/Scripts/app/pricing/structure_dz.js
deleted file mode 100644
index 6fdc2192..00000000
--- a/YLErpWeb/wwwroot/Scripts/app/pricing/structure_dz.js
+++ /dev/null
@@ -1,2663 +0,0 @@
-//客户选择组件
-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: '' +
- '{{underlying.Code}}{{showName}}
'
- };
-};
-
-//预付金模板
-const vueMarginTemplateName = function () {
- return {
- props: ['value'],
- data() {
- return { autoCtrl: null };
- },
- mounted() {
- var self = this;
- this.autoctrl = FastVue.autocomplete(this.$el, {
- valueField: 'Value',
- searchField: ['Value'],
- lookup: ylotc.tradeMarginTemplateItems,
- onSelect(data) {
- if (self.value !== data.Value) {
- self.value = data.Value;
- self.$emit('input', self.value);
- }
- }
- });
- value = self.value || "系统默认";
- this.autoctrl.setData({ Text: value, Value: value })
- },
- watch: {
- value(newVal, oldVal) {
- if (newVal !== oldVal) {
- let data = ylotc.tradeMarginTemplateItems.find(x => x.Value === newVal);
- this.autoctrl.setData(data || { Text: "系统默认", Value: "系统默认" })
- }
- }
- },
- destroyed() {
- FastVue.autocomplete.dispose(this.$el);
- },
- template: ''
- };
-}
-
-//计算结果字段
-const consCalcFields = Object.freeze(['TotalTradePrice', 'TotalDay1Pnl', 'TotalMargin', 'Pv', 'Delta', 'GammaCash', 'Vega', 'Theta', 'Rho']);
-
-var _trades, _tradeVues, _salesCommissionCtrl;
-//交易保存
-const tradeSaver = (function () {
- const needClient = !pageVue.ClientUsedForCalc;
-
- function showTradeView(items) {
- let item = items.shift();
- if (!item) return;
- let url = "/trade/tradeview?abstract=1&enid=" + item.EncryptId;
- main.open("查看交易", url, {
- end: function () {
- showTradeView(items);
- }
- });
- }
-
- //保存
- function _save() {
- if (!_trades) return;
- let AssetId = parseInt($('#AssetId').data('id'));
- let TraderId = parseInt($('#TraderId').data('id'));
- let ClientId = parseInt($('#ClientId').data('id'));
- if (!AssetId) {
- return main.alert("未设置簿记账户");
- }
- if (!TraderId) {
- return main.alert("未设置交易员");
- }
- if (needClient && !ClientId) {
- return main.alert("未设置客户");
- }
- let TradeNumber = $('#TradeNumber').val();
- let AssetBookName = $('#AssetId').val();
- let TraderName = $('#TraderId').val();
- let ClientName = $('#ClientId').val();
- let TradingPlace = $("#TradingPlace").val();
- let ClearingAgency = $("#ClearingAgency").val();
- let MainProtocolCode = $("#MainProtocolCode").val();
- let SupProtocolCode = $("#SupProtocolCode").val();
- let OpponentRole = $("#OpponentRole").val();
- let InitialAdvance = parseFloat($("#InitialAdvance").val()) || 0;
- let PeriodAdvance = parseFloat($("#PeriodAdvance").val()) || 0;
- let FontEarning = parseFloat($("#FontEarning").val()) || 0;
- let ConfirmedLine = parseFloat($("#ConfirmedLine").val()) || 0;
- let ConfirmedFloor = parseFloat($("#ConfirmedFloor").val()) || 0;
-
- let IsCentralClearing = $("#IsCentralClearing").val();
- let CentralClearingPaltform = $("#CentralClearingPaltform").val();
- let TradingPaltform = $("#TradingPaltform").val();
- let QuoteCurrency = $("#QuoteCurrency").val();
-
- let salesCommission = _salesCommissionCtrl.getValue();
- _trades.forEach(x => {
- x.AssetId = AssetId;
- x.AssetBookName = AssetBookName;
- x.TraderId = TraderId;
- x.TraderName = TraderName;
- x.TradeNumber = TradeNumber;
- x.SalesCommission = salesCommission;
- if (needClient) {
- x.ClientId = ClientId;
- x.ClientName = ClientName;
- }
- if (x.TradePremium) {
- x.MetaDic["交易溢价"] = x.TradePremium;
- }
- x.MetaDic["交易场所"] = TradingPlace;
- x.MetaDic["清算机构"] = ClearingAgency;
- x.MetaDic["主协议编号"] = MainProtocolCode;
- x.MetaDic["补充协议编号"] = SupProtocolCode;
-
- x.MetaDic["中央对手方清算"] = IsCentralClearing;
- x.MetaDic["中央清算平台"] = CentralClearingPaltform;
- x.MetaDic["交易平台"] = TradingPaltform;
-
- if (pageData.showCCR) {
- //if (x.MarginType == 0) {
- x.MetaDic["ccr_k"] = x.ccr_k || "0";
- //}
- x.MetaDic["ignoreRiskExposure"] = x.ignoreRiskExposure || "0";
- }
- x.TradeType === '现金流交易' && tradeUtils.resetCashFlow(x);
- x.OpponentRole = OpponentRole;
- x.QuoteCurrency = QuoteCurrency;
- x.InitialAdvance = InitialAdvance;
- x.PeriodAdvance = PeriodAdvance;
- x.FontEarning = FontEarning;
- x.ConfirmedLine = ConfirmedLine;
- x.ConfirmedFloor = ConfirmedFloor;
- x.IsUsePremiumRate = (x.IsUsePremiumRate == "" || x.IsUsePremiumRate == null) ? false : x.IsUsePremiumRate;
- });
- main.post("/pricing/AjaxSaveTrades", { trades: _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));
- }
- });
- }
-
- //保存
- function _saveTrades(tradeVues) {
- _tradeVues = tradeVues;
- if (!Array.isArray(_tradeVues)) {
- throw "参数错误:tradeVues";
- }
- if (!_tradeVues.length) {
- return main.alert("请至少勾选一条交易");
- }
-
- _trades = _.flatMap(_tradeVues, x => x.datas).map(x => {
- x.viewState.synthetic && (x.trade.MetaDic["组合标的"] = JSON.stringify(x.viewState.synthetic));
- return x.trade;
- });
- _trades = tradeUtils.prepareTrades(_trades, pageVue.getVolType(), true);
- if (_trades) {
- $('#TradeNumber').val('').parent().toggle(_tradeVues.length === 1);
- $('#modalTradeSave').modal('show');
- _setSalesman();
- if (pageVue.SecuritiesEnvironment && !pageVue.ClientUsedForCalc) {
- var clientId = parseInt($('#ClientId').data('id')) || _trades[0].ClientId;
- _getMainProtocolCode(clientId);
- }
- return true;
- }
- return false;
- }
-
- function _setAutocomplete(elId, datas, initData, onSelectFn, width) {
- initData = initData || datas[0] || {};
- FastVue.autocomplete(document.getElementById(elId), {
- valueField: 'Name', searchField: ['Name', 'PinYin'], lookup: datas, width: width,
- onSelect(data) {
- $('#' + elId).data('id', data.id);
- if (elId === "ClientId") {
- if (!pageVue.ClientUsedForCalc) {
- $("#IsCentralClearing").val(data.IsCentralClearing);
- $("#CentralClearingPaltform").val(data.CentralClearingPaltform);
- $("#TradingPaltform").val(data.TradingPaltform);
- }
- _setSalesman();
- }
- onSelectFn && onSelectFn(data);
- },
- zIndex: 300000
- }).setData(initData);
- $('#' + elId).data('id', initData.id);
- }
- //保存组合交易
- function _saveGroupTrades(tradeVues) {
- _tradeVues = tradeVues;
- if (!Array.isArray(_tradeVues)) {
- throw "参数错误:tradeVues";
- }
- if (!_tradeVues.length) {
- return main.alert("请至少勾选一条交易");
- }
-
- _trades = _.flatMap(_tradeVues, x => x.datas).map(x => {
- x.viewState.synthetic && (x.trade.MetaDic["组合标的"] = JSON.stringify(x.viewState.synthetic));
- return x.trade;
- });
-
- var viewStates = _.flatMap(_tradeVues, x => x.datas).map(x => x.viewState).filter(x => x.variety && x.variety.Code);
-
- //验证分组交易是否是同标的同期初价格
- var underlyingId = 0;
- var hasDiffUnderlying = false;
- var spotPrice = 0;
- var hasDiffPrice = false;
- var tradeDate = null;
- var exerciseDate = null;
- var hasDiffTradeDate = false;
- var buySell = groupVue.trade.BuySell;
- var tradePrice = 0;
- _trades.forEach(x => {
- if (x.TradeType != "现金流交易") {
- if (underlyingId && underlyingId != x.UnderlyingId) {
- hasDiffUnderlying = true;
- }
- else {
- underlyingId = x.UnderlyingId;
- }
-
- if (spotPrice && parseFloat(spotPrice) != parseFloat(x.SpotPrice)) {
- hasDiffPrice = true;
- }
- else {
- spotPrice = x.SpotPrice;
- }
-
- if (!buySell) {
- buySell = x.BuySell;
- }
- }
-
- tradePrice += x.TradePrice * (x.BuySell == "卖出" ? 1 : -1);
-
- if (tradeDate != null && tradeDate != x.TradeDate) {
- hasDiffTradeDate = true;
- }
- else {
- tradeDate = x.TradeDate;
- }
-
- if (exerciseDate == null || exerciseDate < x.ExerciseDate) {
- exerciseDate = x.ExerciseDate;
- }
- });
- if (hasDiffUnderlying) {
- main.alert("分组交易必须要保持相同的标的");
- return false;
- }
- if (hasDiffPrice) {
- main.alert("分组交易必须要保持相同的期初价格");
- return false;
- }
- if (hasDiffTradeDate) {
- main.alert("分组交易必须要保持相同的交易日期");
- return false;
- }
-
- _trades = tradeUtils.prepareTrades(_trades, pageVue.getVolType(), true);
- if (_trades) {
- $('#TradeNumber').val('').parent().toggle(_tradeVues.length === 1);
- groupVue.viewState = viewStates[0];
- groupVue.trade.TradeDate = tradeDate;
- groupVue.trade.ExerciseDate = exerciseDate;
- groupVue.trade.BuySell = buySell;
- if (groupVue.trade.IsUsePremiumRate == null) {
- groupVue.trade.IsUsePremiumRate = true
- }
- if (groupVue.trade.IsMoneynessOption == null) {
- groupVue.trade.IsMoneynessOption = "是";
- }
- groupVue.trade.IsTradePricePayType = true;
- groupVue.trade.TradePrice = tradePrice * (buySell == "卖出" ? 1 : -1);
- groupVue.changeIsUsePremiumRate();
- groupVue.trade.SpotPrice = spotPrice;
- if (!groupVue.trade.StructureType) {
- groupVue.trade.StructureType = "气囊结构";
- groupVue.trade.OptionType = "看涨";
- groupVue.trade.trade_airbag.Barrier = 0.8;
- groupVue.trade.Strike = 1;
- groupVue.trade.trade_airbag.NotKIParticipationRate = 1;
- groupVue.trade.trade_airbag.KIParticipationRate = 1;
- }
-
-
- let stockEqvNotionalArrs = _trades.map((value, index, array) => {
- return value.StockEqvNotional
- });
- groupVue.trade.StockEqvNotional = Math.max(...stockEqvNotionalArrs);
- groupVue.changeStockEqvNotional();
- $('#modalGroupTradeSave').modal('show');
- _setSalesman();
- return true;
- }
- return false;
- }
-
- return {
- saveTrades: _saveTrades,
- saveGroupTrades: _saveGroupTrades,
- init() {
- _setAutocomplete('AssetId', ylotc.assetunits);
- if (pageVue.CanSelectTrader) {
- let trader = pageVue.Trade.TraderName ? ylotc.traders.find(x => x.Name === pageVue.Trade.TraderName) : null;
- if (!trader) {
- trader = { Name: "" }
- }
- _setAutocomplete('TraderId', ylotc.traders, { Name: trader.Name });
- }
- if (!pageVue.ClientUsedForCalc) {
- var width = 300;
- 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
- }
- }
- _setAutocomplete('ClientId', ylotc.clients, null, (pageVue.SecuritiesEnvironment ? _getMainProtocolCode : null), width);
- }
-
- //组合标的控件
- synthenticPriceCtrl.init({
- getSynthetic(input) {
- return consSyntheticMap[input.dataset.calcid];
- },
- setSynthetic(input, synthetic) {
- var calcid = input.dataset.calcid;
- _.each(pricingVue.getTradeVues(), x => !x.updateSynthetic(calcid, synthetic));
- }
- });
-
- //销售提成控件
- _salesCommissionCtrl = new SalesCommissionCtrl(document.getElementById('salesCommissionCtrl'), pageVue.SalesCommission);
-
- _setSalesman();
-
- $('#btnSave').on('click', _save);
-
- $('#MainProtocolCode').on('change', _changeMainProtocolCode);
-
- $('#modalTradeSave').on('show.bs.modal', function () {
- $('#TradeNumber').val('');
- _salesCommissionCtrl.reset();
- });
- }
- };
-}());
-
-//截图
-const screenshoter = (function () {
-
- var _layerIndex;
-
- function execute(tradeVue) {
-
- if (tradeVue == null) {
- return;
- }
-
- if (!tradeVue.floating) {
- floatVue.show(tradeVue, true);
- }
-
- var opts = { bgcolor: '#fff', width: tradeVue.datas.length * 200 + 140 };
-
- var $fs = $('#for-screenshot');
- $fs.parent().children().addClass('for-screenshot');
- $fs.closest('.modal').css('margin-top', -999999);
-
- domtoimage.toPng($fs[0], opts).then(function (dataUrl) {
- $('#screenshot-img').attr('src', dataUrl);
- _layerIndex = layer.open({
- type: 1, closeBtn: 0, title: false, scrollbar: false, shadeClose: true,
- area: ['auto', '50%'], content: $('#screenshot'), offset: '100px',
- success: function (layero, index) {
- setTimeout(function () {
- let height = $('#screenshot-img').height();
- height > 100 && layero.children('.layui-layer-content').css('height', height + 100);
- }, 100);
- }
- });
- try {
- //chrome 定价页面生成截图时自动复制
- const blobInput = convertBase64ToBlob(dataUrl.replace("data:image/png;base64,", ""), 'image/png');
- const clipboardItemInput = new ClipboardItem({ 'image/png': blobInput });
- navigator.clipboard.write([clipboardItemInput]);
- }
- catch (e) { }
- }).catch(function (error) {
- main.alert('截图发生错误:' + error);
- }).finally(function () {
- $fs.closest('.modal').css('margin-top', 0);
- $fs.parent().children().removeClass('for-screenshot');
- if (!tradeVue.floating) {
- $('#floatModal').modal('hide');
- }
- });
- }
-
- function closeShow() {
- layer.close(_layerIndex);
- }
-
- //保存截图
- function download() {
- domtoimage.toBlob(document.getElementById('screenshot-img'))
- .then(function (blob) {
- moment().format();
- window.saveAs(blob, '报价' + moment().format('YYYYMMDDHHmmss') + '.png');
- });
- }
-
- return {
- execute: execute,
- download: download,
- closeShow: closeShow
- };
-}());
-
-function convertBase64ToBlob(base64, type) {
- var bytes = window.atob(base64);
- var ab = new ArrayBuffer(bytes.length);
- var ia = new Uint8Array(ab);
- for (var i = 0; i < bytes.length; i++) {
- ia[i] = bytes.charCodeAt(i);
- }
- return new Blob([ab], { type: type });
-}
-
-//#singleprice
-const singlePricer = (function () {
-
- const _sumDatas = [];
-
- //datas:[{Pv,Notional}]
- function _calcSinglePrice(datas) {
- var result = _.reduce(datas, (acc, data) => {
- if (Array.isArray(data)) {
- _.each(data, item => {
- acc.totalPv += parseFloat(item.Pv) || 0;
- var notional = parseFloat(item.Notional) || 0;
- if (acc.minNotional > notional) {
- acc.minNotional = notional;
- }
- });
- } else if (data) {
- acc.totalPv += parseFloat(data.Pv) || 0;
- var notional = parseFloat(data.Notional) || 0;
- if (acc.minNotional > notional) {
- acc.minNotional = notional;
- }
- }
- return acc;
- }, { totalPv: 0, minNotional: Number.MAX_SAFE_INTEGER });
- var singlePrice = Math.abs(result.minNotional) < 1e-5 ? "0.00"
- : pricingFormat.tradeSinglePrice(Math.abs(result.totalPv) / result.minNotional);
- return pageVue.CompanyName + (result.totalPv >= 0 ? "付" : "收") + singlePrice;
- }
-
- //重设统计单价
- function _reset() {
- var singlePrice = _calcSinglePrice(_sumDatas);
- $('#singleprice').text(singlePrice);
- }
-
- const _debounceReset = _.debounce(_reset, 300);
-
- return {
- //data:[{Pv,Notional}]
- reset(index, data) {
- _sumDatas[index] = !data || Array.isArray(data) ? data : [data];
- _debounceReset();
- },
- //datas:[{Pv,Notional}]
- calcSinglePrice: _calcSinglePrice
- };
-}());
-
-//pageVue状态
-(function () {
-
- const viewState = { VolType: '交易', structureType: '' };
-
- _.extend(pageVue, {
- viewState: viewState,
- getVolType() { return viewState.VolType; },
- setVolType(value) { viewState.VolType = value; },
- getStructureType() { return viewState.structureType; },
- setStructureType(value) { viewState.structureType = value; }
- });
-
- //冻结修改
- _.each([pageVue], x => Object.freeze(x));
-}());
-
-//一个简单的事件总线
-const EventBus = (new function () {
-
- var _bus;
-
- function getBus() {
- return _bus || (_bus = document.createElement('div'));
- }
-
- //增加事件监听
- this.addEventListener = function (event, callback) {
- getBus().addEventListener(event, callback);
- };
-
- //移除事件监听
- this.removeEventListener = function (event, callback) {
- getBus().removeEventListener(event, callback);
- };
-
- //激发事件
- this.triggerEvent = function (event, detail = {}) {
- getBus().dispatchEvent(new CustomEvent(event, { detail }));
- };
-
- //仅供调试用
- this.getEventListeners = function () {
- return getEventListeners(getBus());
- };
-}())
-
-//顶部组件
-const topVue = (function () {
- return new Vue({
- el: '#pricing-top',
- data: {
- viewState: pageVue.viewState,
- simpleMode: pageVue.SimpleMode,
- calcMargin: pageVue.CalcMargin && pageVue.ShowInitialMargin,
- calcAutocallGreeks: pageVue.CalcAutocallGreeks,
- },
- mounted() {
-
- },
- methods: {
- changeVolType() {
- pricingVue.changeVolType();
- },
- addStructure(type, typeCn) {
- pageVue.setStructureType(typeCn);
- var url = "/trade/structureoptionV2?name=" + type;
- main.open("添加策略", url, { area: ['500px', '800px'] });
- },
- normOtcTrade(td) {
- td.PremiumPayDate = '';
- td.Notional = pricingFormat.notional(td.Notional);
- td.TradeAmount = pricingFormat.notional(td.TradeAmount);
- td.TradePrice = pricingFormat.tradePrice(td.TradePrice);
- td.Day1Pnl = pricingFormat.tradePrice(td.Day1Pnl);
- td.StockEqvNotional = pricingFormat.StockEqvNotional(td.StockEqvNotional);
- td.StockEqvNotionalReal = pricingFormat.StockEqvNotional(td.StockEqvNotionalReal);
- tradeUtils.normalizeTrade(td);
- return td;
- },
- importTrade() {
- let tradeNumber = $('#importTradeNumber').val().trim();
- if (!tradeNumber) return;
- var self = this;
- main.post('/pricing/AjaxGetOtcTradeFull?tradeNumber=' + encodeURIComponent(tradeNumber)).done(function (resp) {
- if (resp.obj) {
- pricingVue.addTrade(self.normOtcTrade(resp.obj));
- } else {
- main.alert('没有找到交易数据');
- }
- }).always(function () {
-
- });
- },
- toggleSimpleMode() {
- this.simpleMode = !this.simpleMode;
- EventBus.triggerEvent('simpleMode.changed');
- main.post("/sysuserConfig/AjaxSaveOtcWebConfig", { name: 'Pricing_SimpleMode', value: this.simpleMode }, false);
- },
- toggleCalcMargin() {
- this.calcMargin = !this.calcMargin;
- main.post("/sysuserConfig/AjaxSaveOtcWebConfig", { name: 'Pricing_CalcMargin', value: this.calcMargin }, false);
- },
- toggleCalcAutocallGreeks() {
- this.calcAutocallGreeks = !this.calcAutocallGreeks;
- main.post("/sysuserConfig/AjaxSaveOtcWebConfig", { name: 'Pricing_CalcAutocallGreeks', value: this.calcAutocallGreeks }, false);
- }
- }
- })
-}());
-
-//统计组件
-const summaryVue = (function () {
-
- const _summaryDatas = [];
-
- const summary = _.reduce(consCalcFields, (acc, cur) => {
- acc[cur] = 0;
- return acc;
- }, {});
-
- //重设统计合计
- function _resetSummary() {
- var datas = _.flatMap(_summaryDatas, x => x && x.length > 0 ? x : []);
- var UnderlyingCode = '';
- var isSameCode = _.every(datas, x => {
- return UnderlyingCode ? x.UnderlyingCode === UnderlyingCode : UnderlyingCode = x.UnderlyingCode;
- });
- if (isSameCode) {
- _.each(summary, (val, key) => {
- summary[key] = 0;
- });
- _.each(datas, data =>
- _.each(consCalcFields, field => summary[field] += parseFloat(data[field]) || 0)
- );
- } else {
- _.each(summary, (val, key) => {
- summary[key] = 'NaN';
- });
- summary.Pv = summary.TotalMargin = summary.TotalTradePrice = summary.TotalDay1Pnl = summary.Rho = 0;
- _.each(datas, data => {
- summary.Pv += parseFloat(data.Pv) || 0;
- summary.Rho += parseFloat(data.Rho) || 0;
- summary.TotalMargin += parseFloat(data.TotalMargin) || 0;
- summary.TotalTradePrice += parseFloat(data.TotalTradePrice) || 0;
- summary.TotalDay1Pnl += parseFloat(data.TotalDay1Pnl) || 0;
- });
- }
- if (!pageVue.TwoSideMargin) {
- //如果为负数,则显示0
- summary.TotalMargin < 0 && (summary.TotalMargin = 0);
- }
- }
-
- const _debounceReset = _.debounce(_resetSummary, 300);
-
- const _vue = new Vue({
- el: '#pricing-summary',
- data: {
- summary: summary
- },
- computed: {
- DeltaHands() {
- var delta = this.summary.DeltaInLots;
- return typeof delta !== 'number' ? delta
- : (delta > 0 ? "卖" : "买") + pricingFormat.greek(Math.abs(delta)) + '手';
- }
- },
- filter: {
- greekFmt(val) {
- return typeof val !== 'number' ? val : pricingFormat.greek(val);
- }
- }
- });
-
- return {
- getVue() {
- return _vue;
- },
- //data:consCalcFields
- reset(index, data) {
- _summaryDatas[index] = !data || Array.isArray(data) ? data : [data];
- _debounceReset();
- }
- };
-}());
-
-//视图呈现器
-const renders = (function () {
-
- function _getHtml($tpl, removeClass) {
- var html = $tpl.html();
- var $temp = $('').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(),
- },
-});
\ No newline at end of file
diff --git a/YLErpWeb/wwwroot/Scripts/app/pricing/tradePricing_dz.js b/YLErpWeb/wwwroot/Scripts/app/pricing/tradePricing_dz.js
deleted file mode 100644
index cfaa75c8..00000000
--- a/YLErpWeb/wwwroot/Scripts/app/pricing/tradePricing_dz.js
+++ /dev/null
@@ -1,2472 +0,0 @@
-//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 => '
| ' + x + ' |
');
- }
- var errmsg = x.errors.join('
');
- return `| ${x.index} | ${errmsg} |
`;
- });
- $('#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("");
- })
- });
- 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("");
- })
- });
- }
- },
- 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));
\ No newline at end of file
From 902731e2fb2c9426afa0308c4f2f9b8bf0ed646f Mon Sep 17 00:00:00 2001
From: hjhan
Date: Fri, 28 Aug 2026 08:45:15 +0800
Subject: [PATCH 08/19] =?UTF-8?q?cleanup:=20=E5=88=A0=E9=99=A4=E4=BB=8E?=
=?UTF-8?q?=E6=9C=AA=E6=8E=A5=E7=BA=BF=E7=9A=84=20structureoptionV2.js=20?=
=?UTF-8?q?=E5=AD=A4=E5=84=BF=E8=84=9A=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
2024-05-09 从山证v2.3.0拷贝带入后从未修改、从未被任何视图/bundle/动态加载引用
(同名视图 structureoptionV2.cshtml 是活视图,但其加载的是 structureoption.js)。
全仓库大小写不敏感搜索引用数为 0。单独 revert 本 commit 即可找回。
---
.../Scripts/app/pricing/structureoptionV2.js | 585 ------------------
1 file changed, 585 deletions(-)
delete mode 100644 YLErpWeb/wwwroot/Scripts/app/pricing/structureoptionV2.js
diff --git a/YLErpWeb/wwwroot/Scripts/app/pricing/structureoptionV2.js b/YLErpWeb/wwwroot/Scripts/app/pricing/structureoptionV2.js
deleted file mode 100644
index f76378ae..00000000
--- a/YLErpWeb/wwwroot/Scripts/app/pricing/structureoptionV2.js
+++ /dev/null
@@ -1,585 +0,0 @@
-const consVarieties = ylotc.varieties;
-const pricingFormat = otcformat.trading;
-const consUnderlyingFlag = (function () {
- let unSelFlag = tradeHelper.UnderlyingSelectFlag;
- return unSelFlag.UseForTrading | unSelFlag.IncludeMatured | unSelFlag.UsePinYinFilter;
-}());
-
-var autoVariety, autoUnderlying;
-
-function __init(vue) {
-
- //标的品种
- autoVariety = FastVue.autocomplete(document.getElementById('VarietyId'), {
- lookup: consVarieties, nameField: 'Name', valueField: 'id', searchField: ['Name', 'Code', 'PinYin'],
- onSelect: function (data) { vue.changeVariety(data, 'select'); }
- });
-
- //标的资产
- autoUnderlying = tradeHelper.UnderlyingAutoComplete('UnderlyingCode').setFlag(consUnderlyingFlag);
- autoUnderlying.onSelect(vue.changeunderlying);
-
- //组合标的控件
- synthenticPriceCtrl.init({
- getSynthetic(input) {
- return vue.viewState.synthetic;
- },
- setSynthetic(input, synthetic) {
- vue.viewState.synthetic = synthetic;
- vue.Model.SpotPrice = synthetic.Price;
- }
- });
-
- //标的控件初始化
- if (vue.Model.UnderlyingCode) {
- autoUnderlying.selectByCode(vue.Model.UnderlyingCode);
- }
- else {
- autoUnderlying.selectFirst();
- }
-
-}
-
-var vue = new Vue({
- el: "#listdiv",
- data: {
- Model: Model,
- IsMoneynessOption: false
- },
- mounted: function () {
- var thisObj = this;
- //设置默认值补丁
- thisObj.Model.ExerciseMode = "European";
- thisObj.Model.TradeDate = moment(thisObj.Model.TradeDate).format("YYYY-MM-DD");
- thisObj.Model.UnderlyingInstrumentType = "CommodityFutures";
- $("#selUnderlyingInstrumentType").val("CommodityFutures");
- __init(thisObj)
- thisObj.setUnderlyingInstrumentType();
- },
- computed: {
- inputFormatStrike: function () {
- let fmt = {};
- if (this.Model.UnderlyingInstrumentType === 'Stock') {
- fmt.append = '%';
- fmt.negative = false;
- fmt.precision = pricingFormat.premiumRateP.precision;
- } else {
- fmt.append = '';
- fmt.negative = true;
- fmt.precision = pricingFormat.umprice.precision;
- }
- return fmt;
- }
- },
- filters: {
- ShowUnit: function (m) {
- return m && m.Variety ? m.Variety.QuoteUnitSingle : "";
- }
- },
- methods: {
- ExerciseDateChange: function (data) {
- var thisObj = this;
- thisObj.Model.ExerciseDate = data;
- },
- ExerciseDateChange2: function (data) {
- var thisObj = this;
- thisObj.Model.ExerciseDate2 = data;
- },
- TradeDateChange: function (data) {
- var thisObj = this;
- thisObj.Model.TradeDate = data;
- },
- changeUnderlyingPrice: function () {
- this.Model.UnderlyingPriceType = "User";
- if (this.Model.UnderlyingInstrumentType !== "Stock") {
- this.Model.StockEqvNotional = otcformat.trading.StockEqvNotional(this.Model.Notional * this.Model.Price);
- this.Model.Price = otcformat.trading.umprice(this.Model.Price);
- }
- else {
- this.Model.Notional = otcformat.trading.notional(this.Model.StockEqvNotional / this.Model.Price);
- var CountRatio = this.Model.Variety.CountRatio || 1;
- this.Model.TradeAmount = otcformat.trading.notional(this.Model.Notional / CountRatio);
- }
- },
- refreshUnderlyingPrice: function () {
- var thisObj = this;
- main.post("/underlying_manager/underlyingGetById", { id: thisObj.Model.UnderlyingId }).done(function (res) {
- thisObj.Model.Price = otcformat.trading.umprice(res.underlying_manager.Price);
- if (thisObj.Model.UnderlyingInstrumentType !== "Stock") {
- thisObj.Model.StockEqvNotional = otcformat.trading.StockEqvNotional(thisObj.Model.Notional * thisObj.Model.Price);
- }
- else {
- thisObj.Model.Notional = otcformat.trading.notional(thisObj.Model.StockEqvNotional / thisObj.Model.Price);
- var CountRatio = thisObj.Model.Variety.CountRatio || 1;
- thisObj.Model.TradeAmount = otcformat.trading.notional(thisObj.Model.Notional / CountRatio);
- }
- thisObj.$forceUpdate();
- });
- },
- setUnderlyingInstrumentType: function () {
- var Underlying = $("#selUnderlyingInstrumentType").val();
- autoUnderlying.selectFirst({ InstrumentTypes: Underlying });
- },
- changeunderlying: function (data) {
- var thisObj = this;
- thisObj.Model.VarietyId = data.VarietyId;
- thisObj.Model.UnderlyingId = data.id;
- thisObj.Model.UnderlyingCode = data.Code;
- thisObj.Model.Variety = data;
- thisObj.Model.Price = data.Price;
- if (!data.Code) return;
- thisObj.Model.UnderlyingInstrumentType = data.InstrumentType;
- main.post("/Pricing/AjaxGetExerciseDate", { underlyingMaturityDate: data.MaturityDate, tradeDate: thisObj.Model.TradeDate }).done(function (res) {
- thisObj.Model.ExerciseDate = new moment(res.obj.ExerciseDate).format("YYYY-MM-DD");
- });
- $.each(thisObj.Model.trades, function (i, d) {
- d.MaturityDate = data.MaturityDate;
- d.UnderlyingPrice = data.Price;
- });
- autoUnderlying.setVarietyId(data.VarietyId);
- thisObj.changeVariety(data.VarietyId ? ylotc.varieties.find(x => x.id === data.VarietyId) : null);
- thisObj.Model.Price = otcformat.trading.umprice(data.Price);
- main.setTradeDatePicker("", "#ModelTradeDate", thisObj.Model.TradeDate, thisObj.TradeDateChange);
- main.setTradeDatePicker("", "#ModelExerciseDate", thisObj.Model.ExerciseDate, thisObj.ExerciseDateChange);
- main.setTradeDatePicker("", "#ModelExerciseDate2", thisObj.Model.ExerciseDate, thisObj.ExerciseDateChange2);
- thisObj.IsMoneynessOption = thisObj.Model.UnderlyingInstrumentType === "Stock";
- if (thisObj.IsMoneynessOption) {
- thisObj.Model.StockEqvNotional = 1e6;
- thisObj.changeStockEqvNotional();
- } else {
- thisObj.changeAmount();
- }
- },
- setStrike: function (strikeType) {
- if (this.Model.Name === "Condor") {
- if (this.Model.Strike2 && this.Model.Strike3) {
- if (this.Model.Strike2 >= this.Model.Strike3) {
- if (strikeType === "Strike2") {
- main.message("执行价格2需要小于执行价格3");
- this.Model.Strike2 = "";
- } else if (strikeType === "Strike3") {
- main.message("执行价格3需要大于执行价格2");
- this.Model.Strike3 = "";
- }
- return;
- }
- var diff = this.Model.Strike3 - this.Model.Strike2;
- this.Model.Strike = this.Model.Strike2 - diff;
- this.Model.Strike4 = parseFloat(this.Model.Strike3) + diff;
- }
- }
- this.setStrike1();
- },
- setStrike1: function () {
- this.Model.Strike = otcformat.trading.umprice(this.Model.Strike);
- this.Model.Strike2 = otcformat.trading.umprice(this.Model.Strike2);
- this.Model.Strike3 = otcformat.trading.umprice(this.Model.Strike3);
- this.Model.Strike4 = otcformat.trading.umprice(this.Model.Strike4);
- },
- submitstructure: function () {
- var thisObj = this;
- if (thisObj.checkTrades()) {
- thisObj.setModelTrades();
- window.parent.vue.setOption(thisObj.Model.trades);
- window.parent.layer.closeAll();
- }
- },
- close: function () {
- window.parent.layer.closeAll();
- },
- checkTrades: function () {
- var thisObj = this;
- var pass = true;
- var checkModel = { Strike12: true, Strike12Empty: true, CheckExerciseDate: true };
-
- switch (thisObj.Model.Name) {
- case "Butterfly":
- checkModel.Strike12 = false;
- checkModel.Strike12Empty = false;
- thisObj.Model.MidStrike = thisObj.Model.Price;
- if (!thisObj.Model.StrikeGap || !thisObj.Model.MidStrike) {
- main.message("执行价格间距必填!"); pass = false;
- }
- if (parseFloat(thisObj.Model.MidStrike) <= parseFloat(thisObj.Model.StrikeGap)) {
- main.message("标的价格必须大于行权价间隔!"); pass = false; //中间行权价 即 标的价格
- }
- break;
- case "Condor":
- checkModel.Strike12 = false;
- checkModel.Strike12Empty = false;
- //验证行权价是否相同,从小到大排序,中间两个行权价允许相同,其他行权价不能相同
- if (!thisObj.Model.Strike3 || !thisObj.Model.Strike4) {
- main.message("行权价3,4必需输入!"); pass = false;
- }
- var strikes = [thisObj.Model.Strike, thisObj.Model.Strike2, thisObj.Model.Strike3, thisObj.Model.Strike4];
- strikes.sort();
- if (strikes[0] == strikes[1] || strikes[3] == strikes[2]) {
- main.message("中间两个行权价允许相同,其他行权价不能相同!"); pass = false;
- }
- break;
- //case "Preplicating Underlying":
- case "Straddle":
- //跨式组合的 执行价格 永远和标的价格一致
- if (thisObj.Model.UnderlyingInstrumentType === "Stock") {
- thisObj.Model.Strike = 100;
- }
- else {
- thisObj.Model.Strike = thisObj.Model.Price;
- }
- checkModel.Strike12 = false;
- checkModel.Strike12Empty = false;
- if (!thisObj.Model.Strike) {
- main.message("行权价必需输入!"); pass = false;
- }
- break;
- case "Ratio Spread":
-
- break;
- case "Calender Spread"://ExerciseDate2
- checkModel.Strike12 = false;
- checkModel.Strike12Empty = false;
- if (!thisObj.Model.ExerciseDate || !thisObj.Model.ExerciseDate2) {
- main.message("两个到期日必需输入!"); pass = false;
- }
- if ((new Date(thisObj.Model.ExerciseDate)).getTime() <= (new Date(thisObj.Model.ExerciseDate2)).getTime()) {
- main.message("到期日期2须早于到期日期1!"); pass = false;
- }
- if (!thisObj.Model.Strike) { main.message("请输入行权价!"); pass = false; }
- break;
- case "Collar":
- checkModel.Strike12 = false;
- checkModel.Strike12Empty = false;
- if (!thisObj.Model.Strike || !thisObj.Model.Strike2 || !thisObj.Model.Strike3) {
- main.message("执行价格1,2,3必需输入!"); pass = false;
- }
- if ((thisObj.Model.Strike == thisObj.Model.Strike2) || thisObj.Model.Strike == thisObj.Model.Strike3 || thisObj.Model.Strike3 == thisObj.Model.Strike2) {
- main.message("执行价格1,2,3两两不能相等!"); pass = false;
- }
- break;
- }
- if (!thisObj.Model.TradeAmount) {
- main.message("请输入交易数量!"); pass = false;
- }
- if (thisObj.Model.TradeAmount < 0) {
- main.message("交易数量不能小于0!"); pass = false;
- }
- if (checkModel.CheckExerciseDate) {
- if (!main.isDate(thisObj.Model.ExerciseDate)) {
- main.message("请输入正确的到期日!"); pass = false;
- }
- }
- if (checkModel.Strike12Empty && (!thisObj.Model.Strike || !thisObj.Model.Strike2)) { // Butterfly Condor 不必
- main.message("执行价格1,2必需输入!"); pass = false;
- }
- return pass;
- },
- setModelTrades: function () {
- var thisObj = this;
- $.each(thisObj.Model.trades, function (i, d) {
- d.VarietyId = thisObj.Model.VarietyId;
- d.UnderlyingId = thisObj.Model.UnderlyingId;
- d.UnderlyingCode = thisObj.Model.UnderlyingCode;
- d.UnderlyingInstrumentType = thisObj.Model.UnderlyingInstrumentType;
- d.Notional = thisObj.Model.Notional;
- d.TradeAmount = otcformat.trading.notional(thisObj.Model.TradeAmount);
- d.StockEqvNotional = thisObj.Model.StockEqvNotional;
- d.ExerciseMode = thisObj.Model.ExerciseMode;
- d.SettlementType = thisObj.Model.SettlementType;
- if (thisObj.Model.OptionType)
- d.OptionType = thisObj.Model.OptionType;
- d.ExerciseDate = thisObj.Model.ExerciseDate;
- d.TradeDate = thisObj.Model.TradeDate;
- d.UnderlyingPrice = d.SpotPrice = thisObj.Model.Price;
- d.NoRiskRateType = "System";
- });
-
- switch (thisObj.Model.Name) {
- case "Bull Spread":
- if (thisObj.Model.Strike < thisObj.Model.Strike2) {
- thisObj.Model.trades[0].BuySell = "买入";
- thisObj.Model.trades[1].BuySell = "卖出";
- } else {
- thisObj.Model.trades[0].BuySell = "卖出";
- thisObj.Model.trades[1].BuySell = "买入";
- }
- thisObj.Model.trades[0].Strike = thisObj.Model.Strike;
- thisObj.Model.trades[1].Strike = thisObj.Model.Strike2;
- break;
- case "Bear Spread":
- if (thisObj.Model.Strike < thisObj.Model.Strike2) {
- thisObj.Model.trades[0].BuySell = "卖出";
- thisObj.Model.trades[1].BuySell = "买入";
- } else {
- thisObj.Model.trades[0].BuySell = "买入";
- thisObj.Model.trades[1].BuySell = "卖出";
- }
-
- thisObj.Model.trades[0].Strike = thisObj.Model.Strike;
- thisObj.Model.trades[1].Strike = thisObj.Model.Strike2;
- thisObj.Model.Strike = thisObj.Model.Price;
- break;
- case "Straddle":
- thisObj.Model.trades[0].BuySell = "买入";
- thisObj.Model.trades[1].BuySell = "买入";
- if (thisObj.Model.UnderlyingInstrumentType === "Stock") {
- thisObj.Model.Strike = 100;
- thisObj.Model.trades[0].Strike = 100;
- thisObj.Model.trades[1].Strike = 100;
- }
- else {
- thisObj.Model.Strike = thisObj.Model.Price;
- thisObj.Model.trades[0].Strike = thisObj.Model.Strike;
- thisObj.Model.trades[1].Strike = thisObj.Model.Strike;
- }
-
- break;
- case "Preplicating Underlying":
- //交易方向“买入” 0看涨,1看跌
- //两条leg 分别是: 买入执行价格高的看涨和卖出执行价格低看跌;
- //交易方向为“卖出”:
- //两条leg分别是:买入执行价格低的看跌和卖出执行价格高的看涨
- //debugger;
- thisObj.Model.trades[0].Strike = thisObj.Model.Strike;
- thisObj.Model.trades[1].Strike = thisObj.Model.Strike2;
-
- if (thisObj.Model.BuySell === "买入") {
- if (thisObj.Model.trades[0].Strike > thisObj.Model.trades[1].Strike) {
- thisObj.Model.trades[0].BuySell = "买入";
- thisObj.Model.trades[0].OptionType = "看涨";
-
- thisObj.Model.trades[1].BuySell = "卖出";
- thisObj.Model.trades[1].OptionType = "看跌";
- }
- else {
- thisObj.Model.trades[1].BuySell = "买入";//买入执行价格高的看涨
- thisObj.Model.trades[1].OptionType = "看涨";
-
- thisObj.Model.trades[0].BuySell = "卖出"; //看跌??
- thisObj.Model.trades[0].OptionType = "看跌";
- }
-
- } else {
- if (thisObj.Model.trades[0].Strike > thisObj.Model.trades[1].Strike) {
- thisObj.Model.trades[0].BuySell = "买入"; //相对客户说??
- thisObj.Model.trades[0].OptionType = "看涨";
-
- thisObj.Model.trades[1].BuySell = "卖出";
- thisObj.Model.trades[1].OptionType = "看跌";
- } else {
- thisObj.Model.trades[1].BuySell = "买入";
- thisObj.Model.trades[1].OptionType = "看涨";
-
- thisObj.Model.trades[0].BuySell = "卖出";
- thisObj.Model.trades[0].OptionType = "看跌";
- }
- }
- thisObj.Model.Strike = thisObj.Model.Price;
- break;
- case "Strangle":
- thisObj.Model.trades[0].BuySell = "买入";
- thisObj.Model.trades[1].BuySell = "买入";
- //thisObj.Model.Strike = thisObj.Model.Price;
- thisObj.Model.trades[0].Strike = thisObj.Model.Strike;
- thisObj.Model.trades[1].Strike = thisObj.Model.Strike2;
- if (thisObj.Model.Strike >= thisObj.Model.Strike2) {
- thisObj.Model.trades[0].OptionType = "看涨";
- thisObj.Model.trades[1].OptionType = "看跌";
- } else {
- thisObj.Model.trades[0].OptionType = "看跌";
- thisObj.Model.trades[1].OptionType = "看涨";
- }
- break;
- case "Butterfly":
- thisObj.Model.trades[0].BuySell = "买入";
- thisObj.Model.trades[1].BuySell = "卖出";
- thisObj.Model.trades[2].BuySell = "买入";
- thisObj.Model.trades[0].Notional = thisObj.Model.Notional;
- var CountRatio = thisObj.Model.Variety.CountRatio || 1;
- thisObj.Model.trades[0].TradeAmount = otcformat.trading.notional(thisObj.Model.trades[0].Notional / CountRatio);
- thisObj.Model.trades[1].Notional = thisObj.Model.Notional * 2;
- thisObj.Model.trades[1].TradeAmount = otcformat.trading.notional(thisObj.Model.trades[1].Notional / CountRatio);
- thisObj.Model.trades[2].Notional = thisObj.Model.Notional;
- thisObj.Model.trades[2].TradeAmount = otcformat.trading.notional(thisObj.Model.trades[2].Notional / CountRatio);
- //中间行权价-行权价间隔,中间行权价,中间行权价+行权价间隔
- thisObj.Model.trades[0].Strike = thisObj.Model.MidStrike - thisObj.Model.StrikeGap;
- thisObj.Model.trades[1].Strike = thisObj.Model.MidStrike;
- thisObj.Model.trades[2].Strike = parseFloat(thisObj.Model.MidStrike) + parseFloat(thisObj.Model.StrikeGap);
- break;
- case "Condor":
- thisObj.Model.trades[0].Strike = thisObj.Model.Strike;
- thisObj.Model.trades[1].Strike = thisObj.Model.Strike2;
- thisObj.Model.trades[2].Strike = thisObj.Model.Strike3;
- thisObj.Model.trades[3].Strike = thisObj.Model.Strike4;
-
- thisObj.Model.trades[0].OptionType = thisObj.Model.OptionType;
- thisObj.Model.trades[1].OptionType = thisObj.Model.OptionType;
- thisObj.Model.trades[2].OptionType = thisObj.Model.OptionType;
- thisObj.Model.trades[3].OptionType = thisObj.Model.OptionType;
-
-
- thisObj.Model.trades.sort(thisObj.strikeSort);
- thisObj.Model.trades[0].BuySell = "买入";
- thisObj.Model.trades[1].BuySell = "卖出";
- thisObj.Model.trades[2].BuySell = "卖出";
- thisObj.Model.trades[3].BuySell = "买入";
- break;
- case "Ratio Spread":
- if (thisObj.Model.OptionType === "看涨") {
- if (thisObj.Model.Strike < thisObj.Model.Strike2) {
- thisObj.Model.trades[0].BuySell = "买入";
- thisObj.Model.trades[1].BuySell = "卖出";
- } else {
- thisObj.Model.trades[0].BuySell = "卖出";
- thisObj.Model.trades[1].BuySell = "买入";
- }
- } else if (thisObj.Model.OptionType === "看跌") {
- if (thisObj.Model.Strike < thisObj.Model.Strike2) {
- thisObj.Model.trades[0].BuySell = "卖出";
- thisObj.Model.trades[1].BuySell = "买入";
- } else {
- thisObj.Model.trades[0].BuySell = "买入";
- thisObj.Model.trades[1].BuySell = "卖出";
- }
- }
- thisObj.Model.trades[0].Notional = thisObj.Model.Notional;
- var CountRatio = thisObj.Model.Variety.CountRatio || 1;
- thisObj.Model.trades[0].TradeAmount = otcformat.trading.notional(thisObj.Model.trades[0].Notional / CountRatio);
- thisObj.Model.trades[1].Notional = thisObj.Model.Notional2;
- thisObj.Model.trades[1].TradeAmount = otcformat.trading.notional(thisObj.Model.trades[1].Notional / CountRatio);
- thisObj.Model.trades[0].Strike = thisObj.Model.Strike;
- thisObj.Model.trades[1].Strike = thisObj.Model.Strike2;
- break;
- case "Calender Spread":
- if (thisObj.Model.ExerciseDate < thisObj.Model.ExerciseDate2) {
- thisObj.Model.trades[0].BuySell = "卖出";
- thisObj.Model.trades[1].BuySell = "买入";
- } else {
- thisObj.Model.trades[0].BuySell = "买入";
- thisObj.Model.trades[1].BuySell = "卖出";
- }
- thisObj.Model.trades[0].ExerciseDate = thisObj.Model.ExerciseDate;
- thisObj.Model.trades[1].ExerciseDate = thisObj.Model.ExerciseDate2;
- thisObj.Model.trades[0].Strike = thisObj.Model.Strike;
- thisObj.Model.trades[1].Strike = thisObj.Model.Strike;
- break;
- case "Box Spread":
- thisObj.Model.trades[0].BuySell = "买入";
- thisObj.Model.trades[1].BuySell = "卖出";
- thisObj.Model.trades[2].BuySell = "买入";
- thisObj.Model.trades[3].BuySell = "卖出";
-
- thisObj.Model.trades[0].OptionType = "看涨";
- thisObj.Model.trades[1].OptionType = "看跌";
- thisObj.Model.trades[2].OptionType = "看跌";
- thisObj.Model.trades[3].OptionType = "看涨";
- if (thisObj.Model.Strike < thisObj.Model.Strike2) {
- thisObj.Model.trades[0].Strike = thisObj.Model.Strike;
- thisObj.Model.trades[1].Strike = thisObj.Model.Strike;
- thisObj.Model.trades[2].Strike = thisObj.Model.Strike2;
- thisObj.Model.trades[3].Strike = thisObj.Model.Strike2;
- } else {
- thisObj.Model.trades[0].Strike = thisObj.Model.Strike2;
- thisObj.Model.trades[1].Strike = thisObj.Model.Strike2;
- thisObj.Model.trades[2].Strike = thisObj.Model.Strike;
- thisObj.Model.trades[3].Strike = thisObj.Model.Strike;
- }
- break;
- case "Risk Reversal":
- thisObj.Model.trades[0].Strike = Math.max(thisObj.Model.Strike, thisObj.Model.Strike2);
- thisObj.Model.trades[1].Strike = Math.min(thisObj.Model.Strike, thisObj.Model.Strike2);
- break;
- case "Collar":
- thisObj.Model.trades[0].OptionType = "看跌";
- thisObj.Model.trades[1].OptionType = "看跌";
- thisObj.Model.trades[2].OptionType = "看涨";
- if (thisObj.Model.SeagullType === "Bullish") {
- thisObj.Model.trades[1].OptionType = "看涨";
- }
-
- var strikes = [parseFloat(thisObj.Model.Strike), parseFloat(thisObj.Model.Strike2), parseFloat(thisObj.Model.Strike3)];
- strikes.sort((x1, x2) => x1 - x2)
- thisObj.Model.trades[0].Strike = strikes[0];
- thisObj.Model.trades[1].Strike = strikes[1];
- thisObj.Model.trades[2].Strike = strikes[2];
- break;
- }
-
- //设置初始化信息
- $.each(thisObj.Model.trades, function (i, d) {
- if (d.UnderlyingInstrumentType === "Stock") {
- d.IsMoneynessOption = "是";
- d.TradeSinglePriceType = "%";
- d.IsUsePremiumRate = true;
- }
- d.Strike = otcformat.trading.umprice(d.Strike);
- });
- thisObj.buySellCalc();
- },
- strikeSort: function (tr1, tr2) {
- return tr1.Strike - tr2.Strike;
- },
- buySellCalc: function () {
- var thisObj = this;
- if (thisObj.Model.BuySell === "卖出") {
- //相反
- $.each(thisObj.Model.trades, function (i, d) {
- d.BuySell = d.BuySell === "卖出" ? "买入" : "卖出";
- });
- }
- },
- addStructureOption: function () {
- //增加交易策略
- var thisObj = this;
- main.open("/trade/GetStructureOption", thisObj.Model.Option).done(function (res) { });
- },
- changeAmount: function () { //交易数量= 份额 / 比率
- var thisObj = this;
- thisObj.Model.TradeAmount = otcformat.trading.notional(thisObj.Model.TradeAmount);
- thisObj.Model.Notional = thisObj.Model.TradeAmount * (thisObj.Model.Variety.CountRatio || 1);
- thisObj.Model.StockEqvNotional = otcformat.trading.StockEqvNotional(thisObj.Model.Notional * thisObj.Model.Price);
- },
- changeVariety: function (variety, flag) {
- if (flag === 'select') {
- if (this.Model.VarietyId === variety.id) return;
- autoUnderlying.setVarietyId(variety.id);
- autoUnderlying.selectFirst(variety.id);
- } else {
- autoVariety.setData(variety);
- }
- },
- changeStockEqvNotional: function () {
- var thisObj = this;
- thisObj.Model.Notional = otcformat.trading.notional(thisObj.Model.StockEqvNotional / thisObj.Model.Price);
- if (thisObj.Model.Notional) {
- thisObj.Model.TradeAmount = otcformat.trading.notional(thisObj.Model.Notional / (thisObj.Model.Variety.CountRatio || 1));
- } else {
- thisObj.Model.TradeAmount = "";
- }
- },
- //绝对值执行价格
- showAbsStrike() {
- this.IsMoneynessOption = false;
- this.moneynessSwitch(this.trade);
- },
- //百分比执行价格
- showPercentStrike() {
- this.IsMoneynessOption = true;
- this.moneynessSwitch(this.trade);
- },
- moneynessSwitch(trade) {
- let spotPrice = parseFloat(this.Model.Price) || 0;
- let isSpotZero = Math.abs(spotPrice) < 1e-4;
- let strikes = ["Strike", "Strike2", "Strike3", "Strike4"];
- for (var key of strikes) {
- if (this.IsMoneynessOption) {
- this.Model[key] = pricingFormat.premiumRate(isSpotZero ? 1 : this.Model[key] / spotPrice);
- } else {
- this.Model[key] = pricingFormat.umprice(this.Model[key] * spotPrice);
- }
- }
- }
- },
- components: {
- 'vue-number-input': FastVue.vueNumberInput()
- }
-});
\ No newline at end of file
From 3be4b48ca276c38cb47e1f6c052a9e17964764c4 Mon Sep 17 00:00:00 2001
From: hjhan
Date: Fri, 28 Aug 2026 08:45:21 +0800
Subject: [PATCH 09/19] =?UTF-8?q?cleanup:=20=E5=88=A0=E9=99=A4=20roleEdit.?=
=?UTF-8?q?js=20/=20errorInfo.js=20/=20demo/autocomplete.js=20=E4=B8=89?=
=?UTF-8?q?=E4=B8=AA=E5=AD=A4=E5=84=BF=E8=84=9A=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
三者均为 2024-05-09 从山证v2.3.0拷贝带入后从未修改、全仓库零引用:
- roleEdit.js: RoleEdit.cshtml 无任何 script 引用
- errorInfo.js: 操作 #infoTable/pageObj.SacInfoList,无视图加载
- autocomplete.js: demo 示例残留
单独 revert 本 commit 即可整体找回。
---
.../wwwroot/Scripts/app/demo/autocomplete.js | 9 --
.../Scripts/app/superviseReport/errorInfo.js | 24 ----
.../wwwroot/Scripts/app/system/roleEdit.js | 105 ------------------
3 files changed, 138 deletions(-)
delete mode 100644 YLErpWeb/wwwroot/Scripts/app/demo/autocomplete.js
delete mode 100644 YLErpWeb/wwwroot/Scripts/app/superviseReport/errorInfo.js
delete mode 100644 YLErpWeb/wwwroot/Scripts/app/system/roleEdit.js
diff --git a/YLErpWeb/wwwroot/Scripts/app/demo/autocomplete.js b/YLErpWeb/wwwroot/Scripts/app/demo/autocomplete.js
deleted file mode 100644
index 474896a8..00000000
--- a/YLErpWeb/wwwroot/Scripts/app/demo/autocomplete.js
+++ /dev/null
@@ -1,9 +0,0 @@
-$(function () {
- var autoClientCtrl = FastVue.autocomplete(document.getElementById('ClientId'), {
- lookup: ylotc.clients, nameField: 'Name', valueField: 'id', searchField: ['Name', 'Code', 'PinYin'],
- onSelect: function (data) {
- console.log(data);
- console.log('#ClientId Value:' + $('#ClientId').val());
- }
- });
-});
\ No newline at end of file
diff --git a/YLErpWeb/wwwroot/Scripts/app/superviseReport/errorInfo.js b/YLErpWeb/wwwroot/Scripts/app/superviseReport/errorInfo.js
deleted file mode 100644
index 856353d9..00000000
--- a/YLErpWeb/wwwroot/Scripts/app/superviseReport/errorInfo.js
+++ /dev/null
@@ -1,24 +0,0 @@
-$(function () {
- var table = $("#infoTable")[0];
- pageObj.SacInfoList.forEach(Obj => {
- formatHtml(Obj, table, 1);
- })
-})
-function formatHtml(obj, table, rowIndex) {
- if (obj.FieldName !== "Root" && obj.FieldName !== "Header" && obj.FieldName !== "Body") {
- var html = '{1} | {2} | {3} | '.template(obj.FieldName, obj.FieldDescribe, obj.FieldValue, obj.ShowMessage);
- rowIndex = addRow(table, rowIndex, html);
- }
- if (obj.SubInfos && obj.SubInfos.length > 0) {
- obj.SubInfos.forEach(function (item) {
- rowIndex = formatHtml(item, table, rowIndex);
- });
- }
- return rowIndex;
-}
-function addRow(table, rowIndex, htmlStr) {
- var row = table.insertRow(rowIndex);
- row.innerHTML = htmlStr;
- rowIndex += 1;
- return rowIndex;
-}
\ No newline at end of file
diff --git a/YLErpWeb/wwwroot/Scripts/app/system/roleEdit.js b/YLErpWeb/wwwroot/Scripts/app/system/roleEdit.js
deleted file mode 100644
index 9b8eb60b..00000000
--- a/YLErpWeb/wwwroot/Scripts/app/system/roleEdit.js
+++ /dev/null
@@ -1,105 +0,0 @@
-//roleFunctionEdit.cshtml
-
-function autocheck(a) {
- $("input[id={0}]".template(a.id)).prop("checked", a.checked);
- linkage(a);
-}
-
-function linkage(a) {
- var obj = $(a);
- var parentName = obj.attr('data-parent');
- var status = $(a).is(":checked");
- var typeName = obj.attr('data-type');
- var fname = obj.attr('lang');
- if (parentName === "-") {
- $(obj).next().css('display', 'none');
- $('input[type="checkbox"][data-parent="{0}"][data-type="{1}"]'.template(fname, typeName)).prop("checked", status);
- return;
- }
- var arr = $("input[type='checkbox'][data-parent='{0}'][data-type='{1}']".template(parentName, typeName));
- var parSelector = 'input[type="checkbox"][lang="{0}"][data-type="{1}"]'.template(parentName, typeName);
- $(parSelector).next().css('display', 'none');
- var continueState = false;
- $(arr).each(function (i, obj) {
- if ($(obj).is(":checked") != status) {
- $(parSelector).prop("checked", true);
- $(parSelector).next().css('display', 'inline-block');
- continueState = true;
- return false;
- }
- });
- if (!continueState) {
- $(parSelector).prop("checked", status);
- }
-}
-
-function GoBack() {
- if (document.all) { //ie
- if (window.history.length > 0) {
- window.history.back();
- return;
- }
- } else {
- if (window.history.length > 1) {
- window.history.back();
- } else {
- window.opener = null;
- window.close();
- }
- }
- window.close();
-}
-
-function showOrHide(res) {
- if ($(res).text() === "-") {
- $(res).parent().next().next().hide();
- $(res).html("+");
- } else {
- $(res).parent().next().next().show();
- $(res).html("-");
- }
-}
-function modulePermissions() {
- $(".tab-1").parent().addClass("active");
- $(".tab-link").parent().removeClass("active");
-
- $(".tab-content").removeClass("tab-none");
- $(".tab-content2").addClass("tab-none");
- $(".tab-content2").removeClass("tab-block");
-}
-function operationPeemissions() {
- $(".tab-link").parent().addClass("active");
- $(".tab-1").parent().removeClass("active");
- $(".tab-content").addClass("tab-none");
- $(".tab-content2").addClass("tab-block");
-
-}
-$(".spanleft").parent().addClass("spanleft-w");
-
-$(document).ready(function () {
- var parentArr = $("input[type='checkbox'][data-parent='-']");
- $(parentArr).each(function (i, obj) {
- var dataType = $(obj).attr("data-type")
- var allCount = $('input[type="checkbox"][data-parent="{0}"][data-type="{1}"]'.template(obj.lang, dataType)).length;
- var selectCount = $('input[type="checkbox"][data-parent="{0}"][data-type="{1}"]:checked'.template(obj.lang, dataType)).length;
- if (allCount != selectCount && selectCount > 0) {
- $(obj).next().css('display', 'inline-block');
- }
- });
- modulePermissions();
- main.form({
- el: '#roleFunctionEditForm',
- submit: {
- url: '/system/roleFunctionEditFormJson',
- after(res) {
- window.location.href = "/system/RoleView?id=" + res.obj.Id;
- try {
- window.parent && window.parent.SearchClick();
- }
- catch (e) {
- //
- }
- }
- }
- });
-});
\ No newline at end of file
From 0222c4abe4eb716e18f0963c60f333f87fe8e285 Mon Sep 17 00:00:00 2001
From: hjhan
Date: Fri, 28 Aug 2026 09:01:48 +0800
Subject: [PATCH 10/19] =?UTF-8?q?fix:=20=E9=A2=84=E4=BB=98=E9=87=91?=
=?UTF-8?q?=E6=A8=A1=E6=9D=BF=E4=B8=89=E9=A1=B5=E9=9D=A2=E3=80=8C=E5=88=AA?=
=?UTF-8?q?=E9=99=A4=E3=80=8D=E7=B9=81=E4=BD=93=E5=AD=97=E7=BB=9F=E4=B8=80?=
=?UTF-8?q?=E4=B8=BA=E7=AE=80=E4=BD=93=E3=80=8C=E5=88=A0=E9=99=A4=E3=80=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../margin_template_v2/margin_template_v2ClientEdit.cshtml | 4 ++--
.../margin_template_v2/margin_template_v2DefaultEdit.cshtml | 4 ++--
.../Views/margin_template_v2/margin_template_v2Edit.cshtml | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml b/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml
index 47cf0db2..5f6b6131 100644
--- a/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml
+++ b/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml
@@ -214,7 +214,7 @@
@*期限档由区块结构固定承载(纯利率债区块固定4档、其他区块为空=全部),只读展示不再下拉选择*@
{{bondTermLabel(row.detail)}} |
- |
+ |
@@ -507,7 +507,7 @@
-
+
diff --git a/YLErpWeb/Views/margin_template_v2/margin_template_v2DefaultEdit.cshtml b/YLErpWeb/Views/margin_template_v2/margin_template_v2DefaultEdit.cshtml
index 2d33cd4b..a89b98d3 100644
--- a/YLErpWeb/Views/margin_template_v2/margin_template_v2DefaultEdit.cshtml
+++ b/YLErpWeb/Views/margin_template_v2/margin_template_v2DefaultEdit.cshtml
@@ -215,7 +215,7 @@
@*期限档由区块结构固定承载(纯利率债区块固定4档、其他区块为空=全部),只读展示不再下拉选择*@
{{bondTermLabel(row.detail)}} |
-
|
+
|
@@ -487,7 +487,7 @@
-
+
diff --git a/YLErpWeb/Views/margin_template_v2/margin_template_v2Edit.cshtml b/YLErpWeb/Views/margin_template_v2/margin_template_v2Edit.cshtml
index f554999c..cd205bc1 100644
--- a/YLErpWeb/Views/margin_template_v2/margin_template_v2Edit.cshtml
+++ b/YLErpWeb/Views/margin_template_v2/margin_template_v2Edit.cshtml
@@ -180,7 +180,7 @@
@*期限档由区块结构固定承载(纯利率债区块固定4档、其他区块为空=全部),只读展示不再下拉选择*@
{{bondTermLabel(row.detail)}} |
-
|
+
|
From 0e0a6d0962c856593464fc055d0c01e2c1c65c30 Mon Sep 17 00:00:00 2001
From: hjhan
Date: Fri, 28 Aug 2026 09:03:52 +0800
Subject: [PATCH 11/19] =?UTF-8?q?style:=20=E9=A2=84=E4=BB=98=E9=87=91?=
=?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=B7=A6=E6=A0=8F=E6=8E=A7=E4=BB=B6=E7=BB=9F?=
=?UTF-8?q?=E4=B8=80=E5=AE=BD=E5=BA=A6=EF=BC=8C=E5=A4=9A=E9=80=89=E6=A0=87?=
=?UTF-8?q?=E7=AD=BE=E5=AE=BD=E5=BA=A6=E8=87=AA=E9=80=82=E5=BA=94?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- chosen 已选项去掉固定 130px 宽:短标签右侧留白、搜索框被挤到下一行撑高控件
- 左栏加 tpl-base 标识,input/select 统一 240px、说明 textarea 跟随列宽,
去掉三页面各写一个内联宽度(320/460px 混用)
---
.../margin_template_v2ClientEdit.cshtml | 6 +++---
.../margin_template_v2DefaultEdit.cshtml | 6 +++---
.../margin_template_v2Edit.cshtml | 6 +++---
.../Style/Css/margin_template_v2Edit.css | 17 ++++++++++++++---
4 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml b/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml
index 5f6b6131..36313a1f 100644
--- a/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml
+++ b/YLErpWeb/Views/margin_template_v2/margin_template_v2ClientEdit.cshtml
@@ -34,7 +34,7 @@