using BaseOUDAL; using YLErp.Abstract.DataProviders; using YLErp.BLL; using YLErp.Helpers; using YLErp.Model; using YLErp.Modules.CalculationModule; using YLErp.Modules.DataProviderModule; using YLErp.Modules.EodModule.SettlementModule; using YLErp.Office; using YLErp.QdpModule; namespace YLErp.Modules.EodModule.QueryModule { /// /// 东证日终存续交易导出服务 /// public class DongZhengEodTradePositionExportService : YLBaseService { public DongZhengEodTradePositionExportService(OptUserInfo optUser) : base(optUser) { } public void ExportDongZhengZipFile(DateTime valueDate) { if (QdpCalendarHelper.GetNonHoliday(valueDate) != valueDate) { throw new ServiceException($"所选日期'{valueDate:yyyy-MM-dd}'不是交易日!"); } if (!DbContext.eodStatus.Any(n => n.ValueDate == valueDate && n.Status == "已收盘")) { throw new ServiceException($"所选日期'{valueDate:yyyy-MM-dd}'未收盘!"); } List eods = GetEodPosition(valueDate); List clients = GetClients(valueDate); var dateStr = valueDate.ToString("yyyyMMdd"); var fileName = $"RH_Otc_Option_List.{dateStr}"; var zipFileName = $"{fileName}.zip"; var targetPath = Path.Combine("F:"); if (!Directory.Exists(targetPath)) { targetPath = Path.Combine("D:\\list\\wait", dateStr); if (!Directory.Exists(targetPath)) { Directory.CreateDirectory(targetPath); } } else { targetPath = Path.Combine("F:\\list\\wait", dateStr); if (!Directory.Exists(targetPath)) { Directory.CreateDirectory(targetPath); } } var marketZipFile = Path.Combine(targetPath, zipFileName); if (File.Exists(marketZipFile)) { File.Delete(marketZipFile); } var excelFileName = $"{fileName}.xlsx"; var targetFileName = Path.Combine(targetPath, excelFileName); var path = Path.Combine(targetPath, targetFileName); if (File.Exists(path)) { File.Delete(path); } var modelDict = new Dictionary(); if (eods != null) { modelDict.Add("存续交易", eods); } if (clients != null) { modelDict.Add("客户信息", clients); } var sourcePath = OtcAppContext.MapPath("~/App_Docs/导出模板"); var sourceFileName = Path.Combine(sourcePath, "东证RH_Otc_Option_List.xlsx"); var pdffile = ExcelTemplate.GeneratePDFFromExeclTemplate(sourcePath, sourceFileName, modelDict, targetPath, targetFileName, shouldDeleteSheet: true, needToPdf: false); ZipHelper.zipOnlyFile(pdffile, marketZipFile, "Dzrh@2022"); File.Delete(targetFileName); } private List GetEodPosition(DateTime valueDate) { using (var basedb = new ErpBaseContext()) { EodPriceProvider eodPrice = new EodPriceProvider(valueDate, isDiviendPrice: false); eodPrice.Initialize(); double riskFreeRate = (valuedateBLL.SystemDate.RiskFreeRate / 100).Normalize(); IEodVolProvider eodVolProvider = EodVolProviderFactory.GetEodVolProvider(valueDate, "持仓"); List otcTrades = OtcTrades(valueDate); var eodTradeQuery = DbContext.eod_trade.Where(et => et.ValueDate == valueDate && et.TradeId > 0 && et.TradeType != "结构化交易" && et.ClientId > 0 && ConsTrade.NeedMarginTradeStatusList.Contains(et.TradeStatus)); var query = from trade in eodTradeQuery join tradeOrigin in DbContext.trade on trade.TradeId equals tradeOrigin.id join po in DbContext.eod_trade_position.Where(x => x.ValueDate == valueDate) on trade.TradeId equals po.TradeId into position from po in position.DefaultIfEmpty() where (tradeOrigin.TradeType != "结构化交易" && tradeOrigin.IsGroup != 1 || tradeOrigin.IsGroup == 1 && tradeOrigin.TradeType == "结构化交易") && (tradeOrigin.IsGroup != 2 || tradeOrigin.IsGroup == 2 && tradeOrigin.TradeType == "收益互换") && tradeOrigin.ValidState != ConsGlobal.InValid select new eod_position_dz { TradeId = tradeOrigin.id, TradeType = tradeOrigin.IsGroup == 1 ? tradeOrigin.StructureType : tradeOrigin.TradeType, ClientId = tradeOrigin.ClientId, TradeNumber = tradeOrigin.TradeNumber, TradeDate = tradeOrigin.TradeDate, ExerciseDate = tradeOrigin.ExerciseDate, PrincipalRate = tradeOrigin.PrincipalRate ?? 0, BasisUnderlyingCode = tradeOrigin.BasisUnderlyingCode, BasisGap = tradeOrigin.BasisGap ?? 0, Lots = tradeOrigin.Lots ?? 0, ParticipationRate = tradeOrigin.ParticipationRate ?? 0, NoRiskRate = tradeOrigin.NoRiskRate ?? 0, UnderlyingCode = trade.UnderlyingCode, UnderlyingPrice = null, ValueDate = valueDate, TradeJson = trade.TradeJson, PvDouble = 0, PnlDouble = 0, InstrumentType = tradeOrigin.UnderlyingInstrumentType, IsGroup = tradeOrigin.IsGroup, SettlementType = tradeOrigin.SettlementType, Margin = po == null ? 0 : po.Margin, Pv = po == null ? 0 : PS.Config.IsPVRounded ? po.RoundedPv : po.Pv, }; var queryList = query.ToList(); var underlyingDataSource = DataCacheProvider.GetUnderlyingDataSource(); var varietyDataSource = DataCacheProvider.GetVarietyDataSource(); var clients = DataCacheProvider.GetClientDataSource(); var varietyTypeList = (from i in basedb.DictionaryItems join d in basedb.Dictionaries on i.DictId equals d.Id where d.Name == "品种类型" select i).ToList(); var assetTypeList = (from i in basedb.DictionaryItems join d in basedb.Dictionaries on i.DictId equals d.Id where d.Name == "资产类型" select i).ToList(); GetUpDownLimitPrices(otcTrades, eodPrice, out IPriceProvider upLimitPrices, out IPriceProvider downLimitPrices); foreach (var x in queryList) { var um = underlyingDataSource.GetData(x.UnderlyingCode); if (um != null) { if (um.IsSynthetic()) { var sy = underlyingDataSource.GetSyntheticUnderlying(x.UnderlyingCode); if (sy != null) { x.SyntheticUnderlyingTipsInfo = sy.UnderlyingTipsInfo; } } var va = varietyDataSource.GetData(um.UnderlyingTypeId); if (va != null) { x.underingName = um.MarketCode != null ? va.VarietyCode + "." + um.MarketCode : va.VarietyCode; x.varietyType = formatDictItem(varietyTypeList, va.AssetType); x.assType = formatDictItem(assetTypeList, va.AssetType, "O"); } } if (x.TradeType == "收益互换") { x.toolType = "SW"; } else if (x.TradeType == "远期" || x.TradeType == "掉期") { x.toolType = "FW"; } else { x.toolType = "OP"; } if (eodPrice.TryGetPrice(x.UnderlyingCode, x.SettlementType, out var price)) { x.UnderlyingPrice = price; } var client = clients.GetData(x.ClientId ?? 0); if (client != null) { x.ClientName = client.Name; x.clientNumber = client.Number; x.ClientType = client.CustomerNature2 != null && !client.CustomerNature2.Contains("产业客户") ? "其他客户" : "产业客户"; //增加内部客户属性 进行赋值 x.IsInsided = client.IsInsided; } x.dic = x.trade.MetaDic; if (x.TradeType == "现金流交易") { x.trade.OriginalNotional = null; } x.buyType = x.BuySell == "买入" ? "买" : "卖"; x.isEnd = "否"; x.tradeType2 = x.OptionType == "看涨" ? "C" : x.OptionType == "看跌" ? "P" : "0"; var td = otcTrades.FirstOrDefault(t => t.id == x.TradeId); if (td.TradeType != "自定义交易") { var upValue = CalcLiveOtcTradeValueR(valueDate, td, upLimitPrices, eodVolProvider, riskFreeRate); var downValue = CalcLiveOtcTradeValueR(valueDate, td, downLimitPrices, eodVolProvider, riskFreeRate); x.MaximumLoss = Math.Min(PS.Config.IsPVRounded ? upValue.RoundedPv : upValue.Pv, PS.Config.IsPVRounded ? downValue.RoundedPv : downValue.Pv) + x.TradePrice; } } //去除内部客户交易 queryList = queryList.Where(l => l.IsInsided != 1).ToList(); return queryList; } } private List GetClients(DateTime valueDate, IEnumerable clienIds = null) { using (var baseDb = new ClientDBContext()) { var clientQuery = from client in baseDb.client.Where(n => n.ProcessStatus == "已开户" && n.IsInsided != 1) select new client_dz { id = client.id, ProtocolSignDate = client.ProtocolSignDate, LicenseCode = client.LicenseCode, Number = client.Number, Name = client.Name, ClientType = client.CustomerNature2 }; var creditList = DbContext.credit.Where(t => t.ProcessStatus == "已审批" && (!t.CreditDeadLine.HasValue || t.CreditDeadLine >= valueDate) && (!t.CreditStartDate.HasValue || t.CreditStartDate <= valueDate)); if (clienIds != null) { clientQuery = clientQuery.Where(t => clienIds.Contains(t.id)); } var list = clientQuery.ToList(); foreach (var item in list) { item.ClientType = item.ClientType != null && !item.ClientType.Contains("产业客户") ? "其他客户" : "产业客户"; var credit = creditList?.FirstOrDefault(x => x.ClientId == item.id); item.isCredit = credit != null ? "是" : "否"; item.CreditNumber = credit != null ? credit.Credit : null; } return list; } } private static string formatDictItem(List dictItemMap, string name, string defaultValue = "") { if (name == null || dictItemMap == null) { return defaultValue; } var obj = dictItemMap.FirstOrDefault(O => O.Name == name); return obj == null ? defaultValue : obj.ShortName; } private TradeValueResult CalcLiveOtcTradeValueR(DateTime valueDate, trade td, IPriceProvider eodPrice, IEodVolProvider volProvider, double riskFreeRate) { var underlyings = new underlying_manager[] { new underlying_manager { UnderlyingCode = td.UnderlyingCode } }; var spotPrice = eodPrice.GetPrice(td.UnderlyingCode); switch (td.TradeType) { case ConsGlobal.TradeType.Forward: { if (PS.Config.ErpElement.ForwardTradePriceModel == Configuration.Enums.ForwardTradePriceModel.STANDARD && !string.IsNullOrWhiteSpace(td.BasisUnderlyingCode)) { var BasiseodPrice = eodPrice.GetPrice(td.BasisUnderlyingCode); spotPrice -= BasiseodPrice; } return ForwardradeCalcService.CalcValue(td, spotPrice); } case ConsGlobal.TradeType.PayoffSwap: return PayoffSwapCalcService.CalcValue(td, valueDate, eodPrice, true); default: { var req = new OptionValueCalcRequest(riskFreeRate) { correlations = null,//不计算彩虹等多标的期权暂时不需要 engineName = null, preciseTimeMode = false, //日终一定是false isEodCalc = true, pricingRequest = QdpPricingRequest.BASIC_GREEKS, spotPrices = new[] { spotPrice }, calcScenario = Enums.CalcScenarioEnum.EodSettlement, }; if (td.TradeType != ConsGlobal.TradeType.CashFlow) { var vol = volProvider.GetVol(td, spotPrice) ?? ConsGlobal.DefaultVol; req.vols = new[] { vol }; } return OptionCalculatorV2.GetOptionValueResult(valueDate, td, req, out underlyings); } } } /// /// 获取涨跌20%停价格字典 /// private void GetUpDownLimitPrices(List trade, EodPriceProvider eodPrice, out IPriceProvider upLimitPrices, out IPriceProvider downLimitPrices) { HashSet _underlyingCodeSet; _underlyingCodeSet = new HashSet(StringComparer.OrdinalIgnoreCase); void setUnderlyingCode(trade td) { if (td?.UnderlyingCode == null) { return; } _underlyingCodeSet.Add(td.UnderlyingCode); switch (td.TradeType) { case "彩虹期权": if (td.trade_rainbow_option != null) { _underlyingCodeSet.Add(td.trade_rainbow_option.UnderlyingAssetCode2); } break; case "价差期权": if (td.trade_spread_option != null) { var codes = td.trade_spread_option.UnderlyingAssetCodes(); foreach (var code in codes) { _underlyingCodeSet.Add(code); } } break; case "结构化交易": if (td.SubTrades != null) { foreach (var std in td.SubTrades) { setUnderlyingCode(std); } } break; } } foreach (var td in trade) { setUnderlyingCode(td); } var upLimitPrices2 = new ManualPriceProvider(); var downLimitPrices2 = new ManualPriceProvider(); //根据涨跌幅限制以及当日结算价计算涨停价以及跌停价 foreach (var code in _underlyingCodeSet) { if (eodPrice.TryGetPrice(code, SettlementTypeEnum.ClosePrice, out var price)) { var limit = price * 0.2; var upprice = price + limit; var downprice = price - limit; upLimitPrices2.SetPrice(code, upprice); downLimitPrices2.SetPrice(code, downprice); } } upLimitPrices = upLimitPrices2; downLimitPrices = downLimitPrices2; } TradeCashDataProvider _tradeCashProvider; private List OtcTrades(DateTime valueDate) { //如果前一天是假日,要显示包含假日的交易 var preday = BLL.valuedateBLL.GetNonHolidayDefore(valueDate.AddDays(-1)); var predicate = PredicateBuilder.Create(t => t.TradeDate <= valueDate && t.ValidState != ConsGlobal.InValid && t.ClientId > 0 && t.TradeType != "结构化交易" && t.ExerciseDate > preday && (!ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus) || t.UnWindDate > preday)); var list = new EodSettleDataQueryService(UserInfo) .GetOtcTrades(valueDate, predicate, out _tradeCashProvider); var hisDataProvider = new TradeHisDataProvider(valueDate).Initialize(); foreach (var item in list) { var noRiskRate = hisDataProvider.GetNoRiskRate(item.id); var dividendRate = hisDataProvider.GetDividendRate(item.id); //如果修改过无风险利率或分红率,且和eodTrade中不匹配,则移除该交易来自EodTrade的标记; if (((noRiskRate ?? 0) > 0 && noRiskRate.GetValueOrDefault() != item.NoRiskRate.GetValueOrDefault()) || ((dividendRate ?? 0) > 0 && dividendRate.GetValueOrDefault() != item.DividendRate.GetValueOrDefault())) { item.MetaDic.Remove("from_eod_trade"); } item.NoRiskRate = noRiskRate ?? item.NoRiskRate ?? BLL.valuedateBLL.RiskFreeRate; item.DividendRate = dividendRate ?? item.DividendRate; if (item.DividendRate == null) { item.DividendRate = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode)?.DividendRate ?? item.NoRiskRate; } } return list; } } public class eod_position_dz : eod_position { public string clientNumber { set; get; } public string ClientType { get; set; } public string buyType { get; set; } public string isEnd { get; set; } public string tradeType2 { get; set; } public string varietyType { get; set; } public string assType { get; set; } public string toolType { get; set; } public string underingName { get; set; } /// /// 最大亏损 /// public double? MaximumLoss { set; get; } /// /// 内部客户 /// public int? IsInsided { get; set; } } public class client_dz { public int id { get; set; } public DateTime? ProtocolSignDate { get; set; } public string LicenseCode { get; set; } public string Number { set; get; } public string Name { set; get; } public string ClientType { get; set; } public string isCredit { set; get; } public double? CreditNumber { set; get; } } }