using YLErp.Abstract.DataProviders; using YLErp.BLL; using YLErp.BLL.Calculation; using YLErp.Modules.DataProviderModule; using YLErp.QdpModule; namespace YLErp.Modules.CalculationModule { public class CCRService : YLBaseService { public CCRService(OptUserInfo userInfo) : base(userInfo) { } public CCRService(YLBaseService baseService) : base(baseService) { } /// /// 是否实时计算 /// 实时计算时传参不一样 /// public bool IsRealtime { get; set; } private DateTime? _settlementDate = null; /// /// 结算日期 /// public DateTime SettlementDate { get { return _settlementDate ?? SystemValueDate; } set { _settlementDate = value; } } /// /// 要忽略计算的交易类型 /// private readonly List ignoreTradeTypes = new List() { "结构化交易", "自定义交易" }; /// /// 执行ccr计算 /// 原公式:CCR=MTM+k*(ADD-ON);最新的更改为:CCR=MTM+(ADD-ON) /// MTM:持仓浮动盈亏 /// ADD-ON:假定未来盈亏 /// /// 要计算的交易;一般为某个客户的所有持仓交易 /// CCR算法必要参数 /// CCR算法必要参数 /// Key:交易Id:Value:CCR值 public Dictionary CalculationCCR(List trades, double j, double N) { var result = new Dictionary(); var add_onVal = CalculationADD_ON(trades, (int)(j * 244), (int)N, out var mtmVal); if (add_onVal.Count != mtmVal.Count) { throw new Exception($"计算出错,结果数量不匹配:\r\nadd_on:{add_onVal.ToJson()}\r\nmtm:{mtmVal.ToJson()}"); } foreach (var item in add_onVal) { result[item.Key] = mtmVal[item.Key] + item.Value; } return result; } /// /// 计算ADD_ON /// 算法由国海提供 /// /// 要计算的交易;一般为某个客户的所有持仓交易 /// 计算要覆盖的交易日数量;一般为244的倍数 /// 计算时滚动价格的间隔;为国海算法中的N /// 当前持仓交易持仓部分累计浮动盈亏; /// Key:交易Id:Value:CCR值 private Dictionary CalculationADD_ON(List trades, int dayCount, int interval, out Dictionary mtmVal) { if (trades is null) { throw new ArgumentNullException(nameof(trades)); } var calcTrades = trades.Where(O => !ignoreTradeTypes.Contains(O.TradeType)).ToList(); if (calcTrades.Count == 0) { throw new ServiceException("不存在可计算的交易"); } var underlyingCodes = calcTrades.Select(O => O.UnderlyingCode); var dateList = QdpCalendarHelper.AllBizDays(QdpCalendarHelper.BizDayShift(SettlementDate, -dayCount), SettlementDate); dateList.Add(SettlementDate); var settlementType = ConsGlobal.SettlePriceMode.GetSettlementType(valuedateBLL.SystemDate.EodSettlePriceMode); var latestPriceProvider = IsRealtime ? (IPriceProvider)DataCacheProvider.GetUnderlyingDataSource() : new EodPriceProvider(SettlementDate).GetPriceProvider(settlementType); var latestRiskResult = CalculatorHelper.CalculateRisksForTrades(SettlementDate, calcTrades, Enums.CalcScenarioEnum.EodSettlement, latestPriceProvider, QdpPricingRequest.PV_ONLY, settlementType: settlementType, isUseTradeVol: PS.Config.IsTradeVol, preciseTimeMode: false, canUseManual: true); var priceProviderDict = new Dictionary(); var latestPvMap = new Dictionary(); // 日期维度 合计值 交易编号 浮动盈亏 var pvList = new List>>(); //var pricessss = new List(); for (int i = 0; i < dateList.Count; i++) { TradeRiskResult riskResult = null; if (dateList[i].Date.Equals(SettlementDate.Date)) { riskResult = latestRiskResult; } else { var priceProvider = new EodPriceProvider(dateList[i]); priceProviderDict[dateList[i]] = priceProvider; if (i < interval) { continue; } var calcPriceProvider = new ManualPriceProvider(); //pricessss.Add(""); foreach (var code in underlyingCodes) { var currentPrice = priceProviderDict[dateList[i]].GetPrice(code, SettlementTypeEnum.ClosePrice); var lastPrice = priceProviderDict[dateList[i - interval]].GetPrice(code, SettlementTypeEnum.ClosePrice); var price = latestPriceProvider.GetPrice(code) * (currentPrice / lastPrice).Normalize(); //pricessss[i - 1] += $"{dateList[i].ToString("yyyy-MM-dd")}\t{code}\t{currentPrice}\t{lastPrice}\t{price}\t"; calcPriceProvider.SetPrice(code, price); } riskResult = CalculatorHelper.CalculateRisksForTrades(SystemValueDate, calcTrades, IsRealtime ? Enums.CalcScenarioEnum.Pricing : Enums.CalcScenarioEnum.EodSettlement, calcPriceProvider, QdpPricingRequest.PV_ONLY, settlementType: settlementType, isUseTradeVol: PS.Config.IsTradeVol, preciseTimeMode: false); } //ADD-ON合计规则 var pv = riskResult.Results.ToDictionary( K => K.Trade.id, V => { if (!(V.Trade.MetaDic.TryGetValue(YLErp.DBModels.Consts.ConsTradeMetaKey.CCR_K, out var strK) && double.TryParse(strK, out var k))) { k = 1; } if (!latestPvMap.ContainsKey(V.Trade.id)) { latestPvMap[V.Trade.id] = latestRiskResult.Results.Where(B => B.Trade.id == V.Trade.id).FirstOrDefault()?.ValueResult.Pv ?? 0; } return k * (V.ValueResult.Pv - latestPvMap[V.Trade.id]); }); //System.Diagnostics.Debug.WriteLine(((double)i / dateList.Count).ToString("0.##%")); pvList.Add(new KeyValuePair>(pv.Values.Sum(), pv)); } //LogFactory.GetLogger().Info($"计算次数:{pvList.Count}\r\n{pvList.Select(O => O.Value).ToJson()}"); mtmVal = latestRiskResult.Results .ToDictionary( K => K.Trade.id, V => V.ValueResult.Pv); return pvList.OrderByDescending(O => O.Key).FirstOrDefault().Value; } } }