using Qdp.Foundation.Implementations; using Qdp.Pricing.Base.Enums; using Qdp.Pricing.Base.Implementations; using Qdp.Pricing.Library.Equity.Engines.Analytical; using System.Text.RegularExpressions; using YLErp.BLL.MarginCalculation; using YLErp.Configuration; using YLErp.DBModels; using YLErp.DBModels.Consts; using YLErp.Enums; using YLErp.Modules.CalculationModule; using YLErp.Modules.DataProviderModule; using YLErp.Modules.PricingModule; using YLErp.Modules.PricingModule.Models; using YLErp.Modules.StructureModule; using YLErp.Modules.SystemModule; using YLErp.Modules.TagModule; using YLErp.Modules.TradeModule; using YLErp.Modules.TradeModule.OrderModule; using YLErp.Modules.UnderlyingModule; using YLErp.Modules.VolatilityModule; using YLErp.Office; using YLErp.QdpModule; namespace YLErp.Web.Controllers { public class PricingController : BaseController { /// /// 组合报价 /// [MyAuthorize("报价管理-结构化交易定价")] public ActionResult Structure() { 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; var map = new StructureService(CurUser) .QueryStructureMap(StructureRangeEnum.EXTEND_INFO); ViewBag.ExtendInfoMap = new Dictionary>(); ViewBag.ExtendInfoMap[""] = new List(); foreach (var item in map) { ViewBag.ExtendInfoMap[item.Key] = item.Value; } using (var tagService = new TagService(CurUser)) { ViewBag.TagList = tagService.GetTagListByType(TagTypeEnum.Trade); } return View(model); } /// /// 组合报价导入 /// public ActionResult StructureImport(string EncryptId) { var tradeId = DecryptInt(EncryptId); var otcTrade = new TradeDetailsQueryService(CurUser).GetOtcTradeFull(tradeId); if (otcTrade == null) { return ShowError("没有找到交易数据"); } if (!otcTrade.IsOption() && otcTrade.TradeType != "结构化产品") { return ShowError("期权定价只支持期权交易"); } ResetOtcOptionTrade(otcTrade); otcTrade.ValueDate = valuedateBLL.ValueDate; if (PS.Config.IsTradeVol && PS.Config.Company == CompanyEnum.厦门象屿) { var daycountMode = PS.Config.ErpElement.SmoothingDaycountMode == Configuration.Enums.SmoothingDaycountMode.CalendarDay ? DayCountMode.CalendarDay : DayCountMode.TradingDay; //新增交易当天的持仓波动率需要划掉一天,修改后的持仓波动率不需要再划一天 var vol = AnalyticalOptionTradeVolInterp.tradeVolLinearInterp( valuationDate: new Qdp.Foundation.Implementations.Date(otcTrade.ValueDate), tradeOpenVol: otcTrade.TradeOpenVolatility ?? 0, tradeCloseVol: otcTrade.TradeCloseVolatility ?? 0, startDate: new Qdp.Foundation.Implementations.Date(otcTrade.StartDate.Value), maturityDate: new Qdp.Foundation.Implementations.Date(otcTrade.ExerciseDate.Value), numOfSmoothingDays: otcTrade.NumOfSmoothingDays ?? 0, dayCountMode: daycountMode, calendar: CalendarImpl.Get("chn"), includeStartDate: false); otcTrade.TradeOpenVolatility = otcTrade.TradeCloseVolatility = vol; } var model = new Models.PricingModel(CurUser, UserBLL.IsTradeOfCurrentLogin(CurUser.UserId)) { IsImport = true, Trade = otcTrade }; //获取自定义结构信息 var structureTypes = new StructureService(CurUser) .QueryStructureMap(StructureRangeEnum.BALCK_TRADE); var structureTypeMap = new Dictionary>() { { "气囊结构",new List() } }; var dict = new Dictionary>(); foreach (var item in structureTypes) { structureTypeMap[item.Key] = item.Value; } ViewBag.StructureTypeMap = structureTypeMap; var map = new StructureService(CurUser) .QueryStructureMap(StructureRangeEnum.EXTEND_INFO); ViewBag.ExtendInfoMap = new Dictionary>(); ViewBag.ExtendInfoMap[""] = new List(); foreach (var item in map) { ViewBag.ExtendInfoMap[item.Key] = item.Value; } return View(nameof(Structure), model); } #region----定价模板---- /// /// 加载定价模板 /// public JsonResult AjaxGetTemplateList() { var datas = new SysUserConfigService(CurUser).GetConfigInfos(); return JsonSuccessData(datas); } /// /// 加载定价模板 /// public JsonResult AjaxGetTemplate(string name, string TemplateType) { var t_Type = SysUserConfigType.PricingTemplateV2; if (TemplateType == "公共模板") { t_Type = SysUserConfigType.CommonTemplate; } var configData = new SysUserConfigService(CurUser).GetConfigData(t_Type, name); if (string.IsNullOrWhiteSpace(configData)) { return JsonError("模板数据不存在"); } return JsonSuccessData(configData); } /// /// 删除定价模板 /// public JsonResult AjaxRemoveTemplate(int sysUserConfigId) { if (sysUserConfigId == 0) { return JsonError("请求参数为空"); } new SysUserConfigService(CurUser).RemoveData(sysUserConfigId, SysUserConfigType.PricingTemplateV2, Server.CacheProvider); return JsonSuccess("成功删除"); } /// /// 保存定价模板 /// /// /// /// 是否可以覆盖 /// 公共模板 /// public JsonResult AjaxSaveTemplate(string name, string dataJson, bool _override = true, bool _CommonTemplate = false) { if (string.IsNullOrWhiteSpace(name)) { return JsonError("错误参数:name"); } try { new SysUserConfigService(CurUser).SaveData(SysUserConfigType.PricingTemplateV2, name, dataJson, Server.CacheProvider, enableOverride: _override, enableCommtemplate: _CommonTemplate); } catch (ServiceException ex) { if (ex.Message.Contains("覆盖")) { return JsonError("模板已经存在,请填写其它名称或着选择覆盖已存在的模板"); } } return JsonSuccess("保存成功"); } /// /// 管理模板列表 /// public ActionResult TemplateList() { return View(); } #endregion /// /// 根据报价或定价的参数,获取一个波动率的值,只在Normal模式下使用。 /// SkewMap模式使用GetBaseVolValue /// public JsonResult AjaxGetVol(SingleVolReq req) { var userGroup = UserBLL.GetUserGroup(UserId); if (PS.Config.ErpElement.SkewMapVolConstruction) { try { if (req.IsMoneynessOption == "是") { req.Strike = req.SpotPrice * req.Strike; } var volType = req.VolType; req.Vols = VolatilityHelper.GetVol(req.TradeDate, "交易", req.UnderlyingCode, userGroup); if (req.Vols == null || string.IsNullOrEmpty(req.Vols.VolSurfaceMode)) { throw new InvalidOperationException($"找不到波动率曲面{req.UnderlyingCode}"); } if (req.BaseVol == null || req.BaseVol <= 0) { req.BaseVol = SkewMapVolHelper.GetSkewMapBaseVolForTrade(req.TradeDate, req.UnderlyingCode, req.Vols, req.UnderlyingTypeId, req.ExerciseDate); } int? varValue = null; switch (volType) { case "报价Ask": varValue = req.AskVar; break; case "报价Bid": varValue = req.BidVar; break; } if (!varValue.HasValue) { var vol = VolatilityHelper.GetVol(req.TradeDate, "交易", req.UnderlyingCode, CurUser.UserGroup); if (vol == null) { return JsonError("找不到波动率:" + req.UnderlyingCode); } switch (volType) { case "报价Ask": req.AskVar = varValue = (int)(vol.GetAskVar() ?? 0); break; case "报价Bid": req.BidVar = varValue = (int)(vol.GetBidVar() ?? 0); break; } } req.VolType = volType; var TargetVol = SingleVolService.GetSingleVol(req, UserId).ToString(); return JsonSuccessData(new { var = varValue, vol = TargetVol, baseVol = req.BaseVol }); } catch (ArgumentException ex) { if (Regex.IsMatch(ex.Message, @"列.\d+.不属于表")) { return JsonError("Var值超出SkewMap可选范围"); } return JsonError(ex.Message); } catch (Exception ex) { return JsonError(ex.Message); } } else { if (ConsUserGroup.HasGroup && string.IsNullOrWhiteSpace(userGroup)) { return JsonSuccessData(new { vol = 0 }); } var vol = SingleVolService.GetSingleVol(req, UserId); if (PS.Config.Is润和) { req.VolType = "交易"; var Midvol = SingleVolService.GetSingleVol(req, UserId); return JsonSuccessData(new { vol = vol, Midvol = Midvol }); } return JsonSuccessData(new { vol }); } } /// /// 获取一个标的信息 /// public JsonResult AjaxGetUnderlying(UnderlyingGetRequest req) { underlying_manager underlying = null; if (!string.IsNullOrWhiteSpace(req.UnderlyingCode)) { underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode); } else { var query = DataCacheProvider.GetUnderlyingDataSource().AsQueryable() .Where(n => n.LaunchState == "1" && n.UnderlyingState != "Matured"); if (!string.IsNullOrWhiteSpace(req.InstrumentType)) { underlying = query.FirstOrDefault(n => n.UnderlyingInstrumentType == req.InstrumentType); } else if (req.VarietyId > 0) { underlying = query.FirstOrDefault(n => n.UnderlyingTypeId == req.VarietyId); } } if (underlying != null) { // 获取不超过到期日的实行日 // 1个月,2周,1周,到日期这样的规则向前计算 if (!req.TradeDate.HasValue) { req.TradeDate = valuedateBLL.ValueDate; } var MaturityDate = underlying.MaturityDate ?? DateTime.Today; if (underlying.CalcTypeIsStock() || underlying.IsCommoditySpot()) { MaturityDate = DateTime.Today.AddYears(3); } underlying.ExerciseDate = QdpCalendarHelper.GetUnderlyingExerciseDate(MaturityDate, req.TradeDate.Value); SyntheticPriceModel synthetic = null; if (underlying.CommodityCode == "组合标的") { //返回组合标的中组成标的的现价 synthetic = new SyntheticUnderlyingPriceService(CurUser).GetPriceModel(underlying.UnderlyingCode); underlying.Price = synthetic.Price; } var variety = DataCacheProvider.GetVariety(underlying.UnderlyingCode); underlying.CountRatio = variety != null ? variety.CountRatio : 1; return JsonSuccessData(new { underlying, synthetic }); } return JsonError("标的信息缺失"); } /// /// 获取组合标的价格模型 /// public JsonResult AjaxGetSyntheticPriceModel(string underlyingCode, DateTime? tradeDate = null) { var synthetic = new SyntheticUnderlyingPriceService(CurUser).GetPriceModel(underlyingCode); if (synthetic != null && tradeDate != null && tradeDate != valuedateBLL.ValueDate && EodPriceQueryService.TryGetEodPrice(tradeDate.Value, underlyingCode, out var eodPrice)) { synthetic.Price = eodPrice.ClosePrice; } return JsonSuccessData(synthetic); } /// /// 获取系统设定的无风险利率 /// public JsonResult AjaxGetNoRiskRate(DateTime? startDate, DateTime? endDate) { var riskFreeRate = valuedateBLL.GetRiskFreeRateFromCurve(startDate, endDate); return JsonSuccessData(riskFreeRate / 100); } /// /// 获取系统设定的股票分红率 /// public JsonResult AjaxGetDividendRate(string underlyingCode, DateTime tradeDate, string tradetype, string optiontype) { if (string.IsNullOrEmpty(underlyingCode)) { return JsonSuccessData(0); } var list = yldb.dividendrate_record.Where(x => x.TradeType.Contains(tradetype) && tradeDate >= x.ValueDate && (x.OptionType == optiontype || x.OptionType == "全部")).ToList(); var record = list.Where(x => x.UnderlyingCode.Split(',').Any(code => code == underlyingCode)).OrderByDescending(x => x.OptDate).OrderByDescending(x => x.ValueDate); if (record.Any()) { return JsonSuccessData(record.FirstOrDefault()?.DividendRate ?? 0); } tradeDate = tradeDate.Date; var query = from n in yldb.underlying_manager join m in yldb.UnderlyingDividend.Where(a => a.ValueDate >= tradeDate) on n.id equals m.UnderlyingId into ms from m in ms.DefaultIfEmpty() where n.UnderlyingCode == underlyingCode select m == null ? n.DividendRate : m.DividendRate; return JsonSuccessData(query.FirstOrDefault() ?? 0); } public JsonResult AjaxGetExchangeOptionPrice(string optionCode) { var provider = new ExchangeOptionPriceProvider(); var price = provider.GetPrice(optionCode); return JsonSuccessData(price); } /// /// 获取标的价格 /// public JsonResult AjaxGetUnderlyingPrice(string underlyingCode, DateTime? tradeDate = null) { double price = 0; double netPrice = 0; var udm = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode); SyntheticPriceModel synthetic = null; if (udm != null) { if (udm.CommodityCode == "组合标的") { using var syntheService = new SyntheticUnderlyingPriceService(CurUser); if (tradeDate != null) { synthetic = syntheService.GetPriceModel(underlyingCode, Convert.ToDateTime(tradeDate)); if (synthetic != null) { price = synthetic.Price; } } else { synthetic = syntheService.GetPriceModel(underlyingCode); price = synthetic.Price; } } else { price = udm.Price ?? 0; netPrice = price; if (udm.IsBond()) { if (EodPriceQueryService.TryGetBondEodPrice(valuedateBLL.ValueDate, underlyingCode, out var eodPrice)) { price = eodPrice.ClosePrice; netPrice = eodPrice.SettlePrice; } else { price = price * Convert.ToDouble(ConsGlobal.bondPriceMultiple); netPrice = price; } } } if (tradeDate != null && tradeDate != valuedateBLL.ValueDate) { var date = Convert.ToDateTime(tradeDate); if (udm.IsBond()) { if (EodPriceQueryService.TryGetBondEodPrice(date, underlyingCode, out var eodPrice)) { price = eodPrice.ClosePrice; netPrice = eodPrice.SettlePrice; } } else { if (EodPriceQueryService.TryGetEodPrice(date, underlyingCode, out var eodPrice)) { price = eodPrice.ClosePrice; netPrice = eodPrice.SettlePrice; } } } } return JsonSuccessData(new { price, synthetic, netPrice }); } //期权定价计算(带预付金计算) public JsonResult AjaxCalcPrices(IEnumerable trades, bool calcMargin, bool calcAutocallGreeks = false) { if (trades is null || !trades.Any()) { return JsonSuccessData(Enumerable.Empty()); } var service = new PriceCalcService(CurUser); var resultList = service.CalcOptionPrice(trades, calcMargin , CalcScenarioEnum.Pricing, td => (td.TradeType != "凤凰期权" && td.TradeType != "雪球期权") || calcAutocallGreeks , calcVersion: Request.Form["version"]); return JsonSuccessData(resultList); } //期权定价计算(带预付金计算) public JsonResult AjaxCalcPrice(OtcOptionTradeFull trade) { if (trade is null) { return JsonError("错误:请求参数为空"); } var result = new PriceCalcService(CurUser).CalcOptionPrice(trade, false, CalcScenarioEnum.Pricing, false); result.calcResult.Pv = result.calcResult.Pv - (trade.StockEqvNotional * (trade.PrepaymentRatio ?? 0) * (trade.BuySell == "卖出" ? -1 : 1)); result.calcResult.RoundedPv = result.calcResult.RoundedPv - (trade.StockEqvNotional * (trade.PrepaymentRatio ?? 0) * (trade.BuySell == "卖出" ? -1 : 1)); return JsonSuccessData(new { result.calcResult, result.Day1Pnl }); } public JsonResult AjaxCalcTrades(CalcTradesRequest request) { var result = new PriceCalcService(CurUser).CalcTrades(request); return JsonSuccessData(result); } public JsonResult AjaxCalcSwapTrades(CalcTradesRequest request) { var result = new PriceCalcService(CurUser).CalcSwapTrades(request); return JsonSuccessData(result); } //保本雪球计算年化期权费率 public JsonResult AjaxCalcSnowballAnnualPremium(OtcOptionTradeFull trade) { if (trade is null) { return JsonError("错误:请求参数为空"); } var result = new PriceCalcService(CurUser).CalcSnowballAnnualPremium(trade); return JsonSuccessData(result); } /// /// 根据权利金反算雪球票息 /// public JsonResult AjaxCalcSnowballKORebate(OtcOptionTradeFull trade) { if (trade is null) { return JsonError("错误:请求参数为空"); } var result = new PriceCalcService(CurUser).CalcSnowballKORebate(trade); return JsonSuccessData(result); } /// /// 根据权利金反算凤凰票息 /// /// /// public JsonResult AjaxCalcPhoenixCouponRate(OtcOptionTradeFull trade) { if (trade is null) { return JsonError("错误:请求参数为空"); } var result = new PriceCalcService(CurUser).CalcPhoenixCouponRate(trade); return JsonSuccessData(result); } //获取初始预付金 public JsonResult AjaxGetInitialMargin(OtcOptionTradeFull trade) { if (trade is null) { throw new ArgumentNullException(nameof(trade)); } try { var tdConv = TradeConverter.ConvertOptionTrade(trade); var realTradeId = tdConv.id; tdConv.id = 0; tdConv.OptId = UserId; tdConv.VolType = "报价Bid"; tdConv.TradeCloseVolatility = null;//不设置为null会影响计算结果 if (!tdConv.TTMDays.HasValue) { var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(tdConv.UnderlyingCode); tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(tdConv.TradeDate.Value, tdConv.ExerciseDate.Value, underlying?.UnderlyingTypeId ?? 0, PS.Config.ErpElement.PrecisionOfMinuteInQuote); } var initialMargin = MarginDefault.GetInitialMargin(tdConv, realTradeId, true); return JsonSuccessData(initialMargin); } catch (Exception ex) { LogFactory.GetLogger("AjaxGetInitialMargin").Error(ex); return JsonError("获取初始预付金失败:" + ex.Message); } } //保存交易 public JsonResult AjaxSaveTrades(IEnumerable trades) { if (trades is null || !trades.Any()) { return JsonError("没有交易数据"); } if (PS.Config.Is润和) { foreach (var x in trades) { x.MetaDic["MidVol"] = x.MidVol.ToString(); x.MetaDic["Day1Pnl"] = x.Day1Pnl.ToString(); } } var dbTrades = new OtcTradeSaveService(CurUser).SaveOptionTradesFromPricing(trades); var items = dbTrades.SelectMany(n => { return n.TradeType == "结构化交易" ? n.SubTrades.Select(m => new { m.EncryptId }) : new[] { new { n.EncryptId } }; }); return JsonSuccess("录入交易成功", items); } //保存分组交易 public JsonResult AjaxSaveGroupTrade(trade trade, IEnumerable subTrades) { var dbTrade = new OtcTradeSaveService(CurUser).SaveGroupTradeFromPricing(trade, subTrades); return JsonSuccess("录入交易成功", dbTrade); } //计算组合的到期收益曲线 public JsonResult GetTradesPayoffLine(IEnumerable trades) { var service = new OptionTradeAnalysisService(CurUser); return Json(service.GetTradesPayoffLine(trades)); } //计算组合的Pv曲线 public JsonResult GetTradesPvLine(IEnumerable trades) { var service = new OptionTradeAnalysisService(CurUser); return Json(service.GetTradesPvLine(trades)); } /// /// 计算某笔交易的Pv曲线 /// public JsonResult GetTradesPvLine2(string enTradeId) { var intid = DecryptInt(enTradeId); var service = new OptionTradeAnalysisService(CurUser); return Json(service.GetTradesPvLine2(intid)); } //同时返回到期收益、Pv、以及在一半ttm时的Pv,三条曲线 public JsonResult GetTradesLifePvLine(IEnumerable trades) { var service = new OptionTradeAnalysisService(CurUser); return Json(service.GetTradePvLifeLine(trades)); } /// /// 计算组合Greeks随时间变化的曲线 /// /// /// public JsonResult GetTradesGreeksForLifetime(IEnumerable trades) { var service = new OptionTradeAnalysisService(CurUser); return Json(service.GetTradesGreeksForLifetime(trades)); } /// /// 根据交易编号获取otctradefull /// public JsonResult AjaxGetOtcTradeFull(string tradeNumber) { var otcTrade = new TradeDetailsQueryService(CurUser).GetOtcTradeFull(tradeNumber); ResetOtcOptionTrade(otcTrade); return JsonSuccessData(otcTrade); } /// /// 获取不超过到期日的执行日 /// 1个月,2周,1周,到日期这样的规则向前计算 /// public JsonResult AjaxGetExerciseDate(DateTime? underlyingMaturityDate, DateTime? tradeDate = null) { if (!tradeDate.HasValue) { tradeDate = valuedateBLL.ValueDate; } var ExerciseDate = QdpCalendarHelper.GetUnderlyingExerciseDate(underlyingMaturityDate ?? DateTime.Today.AddYears(1), tradeDate.Value); return JsonSuccessData(new { ExerciseDate }); } /// /// 获取隐含波动率 /// public JsonResult GetImpliedVol(OtcOptionTradeFull trade) { if (trade == null) { return JsonError("所传数据不能为空"); } if (string.IsNullOrEmpty(trade.UnderlyingCode)) { return JsonError("请选择标的代码"); } if (string.IsNullOrWhiteSpace(trade.TradeType)) { return JsonError("请填写结构类型"); } if (trade.TradeType != "香草期权") { return JsonError("暂时只支持香草期权类型"); } if (string.IsNullOrWhiteSpace(trade.OptionType)) { return JsonError("请填写看涨看跌"); } if (!trade.TradeDate.HasValue) { return JsonError("请填写交易日期"); } if (!trade.ExerciseDate.HasValue) { return JsonError("请填写到期日期"); } if (!trade.Strike.HasValue) { return JsonError("请填写行权价"); } if (!trade.IsMoneynessOptionData && !trade.SpotPrice.HasValue) { return JsonError("请填写期初标的价格"); } if (!trade.TradeSinglePrice.HasValue) { return JsonError("请填写权利金"); } if (trade.ExerciseDate <= trade.TradeDate) { return JsonError("到期日期不能小于成交日期"); } var sysDate = valuedateBLL.ValueDate; var valDate = trade.ValueDate ?? trade.TradeDate ?? valuedateBLL.ValueDate; trade.VolType = "交易"; var impliedVol = VolatilityHelper.GetImpliedVol(valDate, trade, trade.TTMDays, trade.UnderlyingPrice ?? trade.SpotPrice ?? 0, sysDate > valDate); return JsonSuccessData(Math.Abs(impliedVol)); } /// /// /// private void ResetOtcOptionTrade(OtcOptionTradeFull otcTrade) { if (otcTrade is null) { return; } otcTrade.id = 0; otcTrade.TradeNumber = string.Empty; otcTrade.Notional = otcTrade.OriginalNotional ?? 0; otcTrade.StockEqvNotional = otcTrade.OriginalStockEqvNotional ?? 0; var um = DataCacheProvider.GetUnderlyingDataSource().GetData(otcTrade.UnderlyingCode); otcTrade.TradeAmount = otcTrade.Notional / (um?.CountRatio ?? 1); otcTrade.ParentTradeId = 0; otcTrade.IsGroup = 0; otcTrade.UnWindDate = null; otcTrade.FinalPrice = null; otcTrade.UnWindNotional = null; otcTrade.HasPartialUnWind = null; otcTrade.KnockInOutDate = null; otcTrade.KnockInOutStatus = null; otcTrade.CheckStatus = null; otcTrade.ProcessOrderId = 0; otcTrade.ProcessOptDate = null; otcTrade.ProcessStatus = null; otcTrade.DividendDate = new DateTime(2000, 1, 1); otcTrade.StructureType = string.Empty; otcTrade.StructureIntroduction = string.Empty; otcTrade.TraderId = CurUser.UserId; otcTrade.TraderName = CurUser.UserName; otcTrade.StockEqvNotional = otcTrade.StockEqvNotional.OtcFormatValue(OtcFormatFlag.StockEqvNotional); otcTrade.StockEqvNotionalReal = otcTrade.StockEqvNotionalReal.OtcFormatValue(OtcFormatFlag.StockEqvNotional); otcTrade.CouponIncludeStartDate ??= false; otcTrade.CouponUsePaymentDate ??= false; otcTrade.IsApproval = false; otcTrade.CountRatio = DataCacheProvider.GetUnderlyingDataSource().GetData(otcTrade.UnderlyingCode)?.CountRatio ?? 1; } public ActionResult HistoricalBacktest(bool isLayer = false) { var datas = new SysUserConfigService(CurUser).GetConfigInfos() .GroupBy(O => O.ConfigType) .ToDictionary( K => K.Key == SysUserConfigType.CommonTemplate ? "公共定价模板" : "个人定价模板", V => V.Select(O => new { O.EncryptId, O.ConfigName })); ViewBag.isLayer = isLayer; ViewBag.pageObj = new { dataSourceList = datas, dataSourceTypeList = datas.Keys, defaultType = datas.Keys.FirstOrDefault() ?? "", calcRange = new[] { "估值日期", "标的名称", "时间序列-标的价格" }, calcIndex = new[] { "delta", "deltaCash", "deltaInLots", "gamma", "vega", "rho", "theta" }, }; return View(); } public JsonResult ConfigInfos(string encryptId) { var id = DecryptInt(encryptId); var obj = new SysUserConfigService(CurUser).GetConfigInfos(id); return JsonSuccess("ok", obj); } public JsonResult UploadPrice(IFormFile file) { try { using var stream = file.OpenReadStream(); var priceList = HistoricalBacktestService.AnalysisPrice(stream); if (priceList.Any(O => O.TimeSeries == default)) { throw new ServiceException("文件中存在错误的日期格式"); } if (priceList.Any(O => string.IsNullOrWhiteSpace(O.UnderlyingCode))) { throw new ServiceException("标的代码不应为空"); } if (priceList.Any(O => double.IsNaN(O.Price))) { throw new ServiceException("价格不应为空"); } var codes = priceList.Select(O => O.UnderlyingCode).ToHashSet(); var list = new List(); foreach (var item in codes) { if (DataCacheProvider.GetUnderlyingDataSource().GetData(item) == null) { list.Add(item); } } if (list.Count > 0) { return JsonError($"导入失败:标的 {string.Join(",", list)} 不存在"); } Server.CacheProvider.Remove("backtest_" + UserId); Server.CacheProvider.Set("backtest_" + UserId, priceList, DateTime.Now.AddHours(6)); return JsonSuccess("导入成功"); } catch (ServiceException) { throw; } catch (Exception ex) { LogFactory.GetLogger("HistoricalBacktest").Error(ex); return JsonError("导入失败"); } } public FileResult OutputHistoricalBacktest(HistoricalBacktestReq req) { var priceInfos = Server.CacheProvider.Get("backtest_" + UserId) as List; if (priceInfos == null) { throw new ServiceException("请先导入要计算的维度序列"); } var id = DecryptInt(req.encryptId); var result = new HistoricalBacktestService(CurUser).Execute(id, priceInfos); var modelDict = new Dictionary { ["Sheet1"] = new { InfoList = result } }; var sourcePath = OtcAppContext.MapPath("~/App_Docs/导出模板/"); var settleDocName = "历史回测导出模板.xlsx"; var sourceFileName = Path.Combine(sourcePath, settleDocName); var buffer = new ExcelTemplateGenerator().SetTemplateFile(sourceFileName).SetTemplateData(modelDict).Output(); return File(buffer, "application/ms-excel", $"历史回测-{DateTime.Now:yyyy-MM-dd}.xlsx"); } public JsonResult calcHistoricalBacktest(HistoricalBacktestReq req) { var priceInfos = Server.CacheProvider.Get("backtest_" + UserId) as List; if (priceInfos == null) { return JsonError("请先导入要计算的维度序列"); } var id = DecryptInt(req.encryptId); var result = new HistoricalBacktestService(CurUser).Execute(id, priceInfos); return JsonSuccess("计算成功", result); } } }