diff --git a/UnitTestProject/BasicTests/GLMSGreeksHandleServiceTest.cs b/UnitTestProject/BasicTests/GLMSGreeksHandleServiceTest.cs new file mode 100644 index 00000000..f3b570b7 --- /dev/null +++ b/UnitTestProject/BasicTests/GLMSGreeksHandleServiceTest.cs @@ -0,0 +1,51 @@ +using YLErp.Modules.CalculationModule; +using YLErp.Modules.EodModule; + +namespace YLErp.BasicTests +{ + [TestClass] + public class GLMSGreeksHandleServiceTest + { + [TestMethod] + public void Handle_NonRateAsset_CalculatesVega1bpBeforeRateFilter() + { + var service = new GLMSGreeksHandleService(); + var dto = new EodPositionRisksDTO + { + Vega = 2500d + }; + var underlying = new underlying_manager + { + UnderlyingInstrumentType = ConsGlobal.InstrumentType.Stock + }; + + service.Handle(dto, underlying); + + Assert.IsNotNull(dto.Vega_1bp); + Assert.AreEqual(0.25d, dto.Vega_1bp.Value, 1e-12); + Assert.IsNull(dto.Vega_r); + Assert.IsNull(dto.Vega_r_1bp); + } + + [TestMethod] + public void Handle_TradeValueResult_NonRateAsset_CalculatesVega1bpBeforeRateFilter() + { + var service = new GLMSGreeksHandleService(); + var result = new TradeValueResult + { + Vega = 2500d + }; + var underlying = new underlying_manager + { + UnderlyingInstrumentType = ConsGlobal.InstrumentType.Stock + }; + + service.Handle(new trade(), result, underlying); + + Assert.IsNotNull(result.Vega_1bp); + Assert.AreEqual(0.25d, result.Vega_1bp.Value, 1e-12); + Assert.IsNull(result.Vega_r); + Assert.IsNull(result.Vega_r_1bp); + } + } +} diff --git a/YLErpDAL/Model/EodPositionRisks.cs b/YLErpDAL/Model/EodPositionRisks.cs index 3669e15c..0bf11bcf 100644 --- a/YLErpDAL/Model/EodPositionRisks.cs +++ b/YLErpDAL/Model/EodPositionRisks.cs @@ -230,6 +230,8 @@ namespace YLErp.Model public double? Vega_r_1bp { get; set; } + public double? Vega_1bp { get; set; } + /// diff --git a/YLErpDAL/Modules/CalcPriceShowConfigModule/CalcPriceShowConfigService.cs b/YLErpDAL/Modules/CalcPriceShowConfigModule/CalcPriceShowConfigService.cs index c021ef8a..c0c613dd 100644 --- a/YLErpDAL/Modules/CalcPriceShowConfigModule/CalcPriceShowConfigService.cs +++ b/YLErpDAL/Modules/CalcPriceShowConfigModule/CalcPriceShowConfigService.cs @@ -256,6 +256,7 @@ namespace YLErp.Modules.CalcPriceShowConfigModule new CalcQuotaDto{Name="Gamma_r(1bp)",Tip=""}, new CalcQuotaDto{Name="Vega_r",Tip=""}, new CalcQuotaDto{Name="Vega_r(1bp)",Tip=""}, + new CalcQuotaDto{Name="Vega(1bp)",Tip=""}, }; } diff --git a/YLErpDAL/Modules/CalculationModule/Models/TradeValueResult.cs b/YLErpDAL/Modules/CalculationModule/Models/TradeValueResult.cs index d7df7d83..64702052 100644 --- a/YLErpDAL/Modules/CalculationModule/Models/TradeValueResult.cs +++ b/YLErpDAL/Modules/CalculationModule/Models/TradeValueResult.cs @@ -515,6 +515,8 @@ namespace YLErp.Modules.CalculationModule public double? Vega_r { get; set; } public double? Vega_r_1bp { get; set; } + + public double? Vega_1bp { get; set; } } public class TradeValueResultExtend diff --git a/YLErpDAL/Modules/EodModule/EodPositionRisksQueryService.cs b/YLErpDAL/Modules/EodModule/EodPositionRisksQueryService.cs index e9a3286e..6dfaaf49 100644 --- a/YLErpDAL/Modules/EodModule/EodPositionRisksQueryService.cs +++ b/YLErpDAL/Modules/EodModule/EodPositionRisksQueryService.cs @@ -2032,6 +2032,7 @@ namespace YLErp.Modules.EodModule dic.Add("Gamma_r_1bp", OtcFormatExtensions.OtcFormat(item.Gamma_r_1bp, OtcFormatFlag.greek)); dic.Add("Vega_r", OtcFormatExtensions.OtcFormat(item.Vega_r, OtcFormatFlag.greek)); dic.Add("Vega_r_1bp", OtcFormatExtensions.OtcFormat(item.Vega_r_1bp, OtcFormatFlag.greek)); + dic.Add("Vega_1bp", OtcFormatExtensions.OtcFormat(item.Vega_1bp, OtcFormatFlag.greek)); } @@ -2143,6 +2144,7 @@ namespace YLErp.Modules.EodModule dic.Add("Gamma_r_1bp", ""); dic.Add("Vega_r", ""); dic.Add("Vega_r_1bp", ""); + dic.Add("Vega_1bp", ""); } results.Add(dic); } diff --git a/YLErpDAL/Modules/EodModule/GLMSGreeksHandleService.cs b/YLErpDAL/Modules/EodModule/GLMSGreeksHandleService.cs index 5ccfdf94..4d5e7b3b 100644 --- a/YLErpDAL/Modules/EodModule/GLMSGreeksHandleService.cs +++ b/YLErpDAL/Modules/EodModule/GLMSGreeksHandleService.cs @@ -164,6 +164,7 @@ namespace YLErp.Modules.EodModule dto.Dv01 = calcDto.Dv01; dto.Gamma_r_1bp = calcDto.Gamma_r_1bp; dto.Vega_r_1bp = calcDto.Vega_r_1bp; + dto.Vega_1bp = calcDto.Vega_1bp; } @@ -183,6 +184,7 @@ namespace YLErp.Modules.EodModule calRes.Dv01 = calcDto.Dv01; calRes.Gamma_r_1bp = calcDto.Gamma_r_1bp; calRes.Vega_r_1bp = calcDto.Vega_r_1bp; + calRes.Vega_1bp = calcDto.Vega_1bp; } @@ -267,6 +269,9 @@ namespace YLErp.Modules.EodModule private void handle(GreeksCalcDto dto, underlying_manager um) { + // 标准 Vega 的 1bp 变体:全资产计算,不受利率类过滤限制 + dto.Vega_1bp = dto.Vega * 0.0001; + if (!calcInstrumentTypes.Contains(um.UnderlyingInstrumentType)) { return; @@ -353,6 +358,8 @@ namespace YLErp.Modules.EodModule public double? Vega_r_1bp { get; set; } + public double? Vega_1bp { get; set; } + } public class ChinaBondIndexQuoteQueryDto diff --git a/YLErpWeb/Views/Pricing/_PricingItemTpl.cshtml b/YLErpWeb/Views/Pricing/_PricingItemTpl.cshtml index 6d565236..b9308c4c 100644 --- a/YLErpWeb/Views/Pricing/_PricingItemTpl.cshtml +++ b/YLErpWeb/Views/Pricing/_PricingItemTpl.cshtml @@ -1195,59 +1195,66 @@ else if("Gamma*".Equals( quta.Name)) { - Gamma* + Gamma* {{calcResult.GammaContainsKnockOut}} } else if ("Delta_r".Equals(quta.Name)) { - Delta_r + Delta_r {{calcResult.Delta_r}} } else if ("Delta_r(1bp)".Equals(quta.Name)) { - Delta_r(1bp) + Delta_r(1bp) {{calcResult.Delta_r_1bp}} } else if ("Dv01".Equals(quta.Name)) { - Dv01 + Dv01 {{calcResult.Dv01}} } else if ("Gamma_r".Equals(quta.Name)) { - Gamma_r + Gamma_r {{calcResult.Gamma_r}} } else if ("Gamma_r(1bp)".Equals(quta.Name)) { - Gamma_r(1bp) + Gamma_r(1bp) {{calcResult.Gamma_r_1bp}} } else if ("Vega_r".Equals(quta.Name)) { - Vega_r + Vega_r {{calcResult.Vega_r}} } else if ("Vega_r(1bp)".Equals(quta.Name)) { - Vega_r(1bp) + Vega_r(1bp) {{calcResult.Vega_r_1bp}} } + else if ("Vega(1bp)".Equals(quta.Name)) + { + + Vega(1bp) + {{calcResult.Vega_1bp}} + + } } } diff --git a/YLErpWeb/wwwroot/Scripts/app/calcpriceshow/templeteset.js b/YLErpWeb/wwwroot/Scripts/app/calcpriceshow/templeteset.js index bc4563d7..e3db62e5 100644 --- a/YLErpWeb/wwwroot/Scripts/app/calcpriceshow/templeteset.js +++ b/YLErpWeb/wwwroot/Scripts/app/calcpriceshow/templeteset.js @@ -1,4 +1,4 @@ -$(function () { +$(function () { var __jq = { PostData: typeof PostData !== 'undefined' ? PostData : {}, @@ -259,6 +259,13 @@ function ResetEditPanel(model) { calcQuotaNameChecked.push(model.CalcQuota[i].Name); } } + // 编辑旧模板时,把模板未包含的新增指标(如 Vega(1bp))追加到勾选列表末尾,默认不勾选,保证与新增模板口径一致 + var defaultQuota = pageObj && pageObj.DefaultQuota ? pageObj.DefaultQuota.CalcQuota : []; + for (var j = 0; j < defaultQuota.length; j++) { + if (!quotaOrder.includes(defaultQuota[j].Name)) { + quotaOrder.push(defaultQuota[j].Name); + } + } InitQuotaCheckList(quotaOrder); if (calcQuotaNameChecked != null && calcQuotaNameChecked.length > 0) { diff --git a/YLErpWeb/wwwroot/Scripts/app/eod/EodPositionRisks.js b/YLErpWeb/wwwroot/Scripts/app/eod/EodPositionRisks.js index c2daddde..ffdb70f9 100644 --- a/YLErpWeb/wwwroot/Scripts/app/eod/EodPositionRisks.js +++ b/YLErpWeb/wwwroot/Scripts/app/eod/EodPositionRisks.js @@ -559,6 +559,15 @@ function getColModelGrid() { sortIndex: i++, formatter: greeksFormat, sortable: false + }, { + name: 'Vega_1bp', + label: 'Vega(1bp)', + index: 'Vega_1bp', + width: 100, + align: 'center', + sortIndex: i++, + formatter: greeksFormat, + sortable: false },); } @@ -664,7 +673,7 @@ function TradeAmountFormat(cellValue, options, rowObject) { } function greeksFormat(cellValue, options, rowObject) { - if (cellValue) { + if (cellValue != null) { return otcformat.trading.greek(cellValue); } return ""; diff --git a/YLErpWeb/wwwroot/Scripts/app/pricing/structure.js b/YLErpWeb/wwwroot/Scripts/app/pricing/structure.js index efb5a424..0699c182 100644 --- a/YLErpWeb/wwwroot/Scripts/app/pricing/structure.js +++ b/YLErpWeb/wwwroot/Scripts/app/pricing/structure.js @@ -254,7 +254,7 @@ const vueMarginTemplateName = function () { } //计算结果字段 -const consCalcFields = Object.freeze(['TotalMargin', 'Pv', 'Delta', 'Gamma', 'Vega', 'Theta', 'Rho', 'PvContainsKnockOut', 'DeltaContainsKnockOut', 'DeltaInLots', 'GammaInLots', 'DeltaCash', 'GammaCash', 'VegaCash', 'GammaContainsKnockOut', 'Delta_r', 'Delta_r_1bp', 'Dv01', 'Gamma_r', 'Gamma_r_1bp', 'Vega_r', 'Vega_r_1bp']); +const consCalcFields = Object.freeze(['TotalMargin', 'Pv', 'Delta', 'Gamma', 'Vega', 'Theta', 'Rho', 'PvContainsKnockOut', 'DeltaContainsKnockOut', 'DeltaInLots', 'GammaInLots', 'DeltaCash', 'GammaCash', 'VegaCash', 'GammaContainsKnockOut', 'Delta_r', 'Delta_r_1bp', 'Dv01', 'Gamma_r', 'Gamma_r_1bp', 'Vega_r', 'Vega_r_1bp', 'Vega_1bp']); var _trades, _tradeVues, _salesCommissionCtrl; //交易保存 @@ -1959,13 +1959,14 @@ function createVue(index, baseVue, floating) { calcResult.PvContainsKnockOut = pricingFormat.tradePrice(result.PvContainsKnockOut); calcResult.DeltaContainsKnockOut = pricingFormat.greek(result.DeltaContainsKnockOut); calcResult.GammaContainsKnockOut = pricingFormat.greek(result.GammaContainsKnockOut); - calcResult.Delta_r = result.Delta_r ? pricingFormat.greek(result.Delta_r) : ""; - calcResult.Delta_r_1bp = result.Delta_r_1bp ? pricingFormat.greek(result.Delta_r_1bp) : ""; - calcResult.Dv01 = result.Dv01 ? pricingFormat.greek(result.Dv01) : ""; - calcResult.Gamma_r = result.Gamma_r ? pricingFormat.greek(result.Gamma_r) : ""; - calcResult.Gamma_r_1bp = result.Gamma_r_1bp ? pricingFormat.greek(result.Gamma_r_1bp) : ""; - calcResult.Vega_r = result.Vega_r ? pricingFormat.greek(result.Vega_r) : ""; - calcResult.Vega_r_1bp = result.Vega_r_1bp ? pricingFormat.greek(result.Vega_r_1bp) : ""; + calcResult.Delta_r = result.Delta_r != null ? pricingFormat.greek(result.Delta_r) : ""; + calcResult.Delta_r_1bp = result.Delta_r_1bp != null ? pricingFormat.greek(result.Delta_r_1bp) : ""; + calcResult.Dv01 = result.Dv01 != null ? pricingFormat.greek(result.Dv01) : ""; + calcResult.Gamma_r = result.Gamma_r != null ? pricingFormat.greek(result.Gamma_r) : ""; + calcResult.Gamma_r_1bp = result.Gamma_r_1bp != null ? pricingFormat.greek(result.Gamma_r_1bp) : ""; + calcResult.Vega_r = result.Vega_r != null ? pricingFormat.greek(result.Vega_r) : ""; + calcResult.Vega_r_1bp = result.Vega_r_1bp != null ? pricingFormat.greek(result.Vega_r_1bp) : ""; + calcResult.Vega_1bp = result.Vega_1bp != null ? pricingFormat.greek(result.Vega_1bp) : ""; calcResult.TotalMargin = 0; if (data === this.datas[this.datas.length - 1]) { calcResult.TotalMargin = pageVue.GetTotalMargin(_.map(this.datas, x => x.trade), this.structureType); diff --git a/YLErpWeb/wwwroot/Statics/manual/greeks.html b/YLErpWeb/wwwroot/Statics/manual/greeks.html index b59cf52e..fc33399d 100644 --- a/YLErpWeb/wwwroot/Statics/manual/greeks.html +++ b/YLErpWeb/wwwroot/Statics/manual/greeks.html @@ -255,6 +255,9 @@

系统中 \(𝜎_\mathrm{\bigtriangleup}\) 为 0.0001(绝对偏移,对应1bp)

+

Vega(1bp)

+

\(Vega(1bp) = Vega \times 0.0001\)(标的波动率 σ 上升 1 个基点对应的价值变动)

+

Theta

设当前期权到期时间为 \(T\), 期权理论价值为 \(V,T'=T-frac(1),\)

其中 \(𝑓𝑟𝑎𝑐(1)\) 是根据设定的日期规则计算一天的年化时间长度。例如: