using YLErp.DBModels.Enums; namespace YLErp.Modules.EodModule.SettlementModule { /// /// 远期预付金收盘结算 /// class EodForwardMarginSettlement : EodSettleServiceBaseV2 { public const string Step = "远期预付金结算"; public EodForwardMarginSettlement(EodSettlementContextV2 context) : base(context) { } /// /// 远期预付金收盘结算 /// public void Execute() { var settleDate = _context.SettleDate; //计息天数--如果大于1时判断中间的日期是否遗漏了收盘 var interestDays = (settleDate - _context.PreSettleDate).Days; //-------------------------------------------- // 删除旧数据 //-------------------------------------------- if (_context.ClienIds!=null&& _context.ClienIds.Any()) { var forwardTradeId = _context.OtcTrades.Where(t => t.TradeType == "远期").Where(l => _context.ClienIds.Contains(l.ClientId)).Select(l => l.id); var forwardTradeIdStr = string.Join(",", forwardTradeId); if (!string.IsNullOrEmpty(forwardTradeIdStr)) { DbContext.BulkDelete($" TradeId in({forwardTradeIdStr}) and ValueDate='{settleDate:yyyy-MM-dd}'"); } } else { DbContext.BulkDelete($"ValueDate='{settleDate:yyyy-MM-dd}'"); } //-------------------------------------------- // 基础数据查询 -- 构建查询时需要考虑历史收盘 // 最后一天为特殊场景,需要考虑到和上一交易日中间有假期的情况 //-------------------------------------------- var tradeInfos = _context.OtcTrades.Where(t => t.TradeType == "远期").Select(GetTradeInfo).ToArray(); _context.CheckCanceled(); if (!tradeInfos.Any()) { return; } var eodLastDic = DbContext.eod_forward_margin.Where(a => a.ValueDate == _context.PreSettleDate).ToDictionary(n => n.TradeId); foreach (var td in tradeInfos) { eodLastDic.TryGetValue(td.TradeId, out var lastMargin); if (lastMargin == null && _context.PreSettleDate >= td.TradeDate) { _context.RaiseError(Step, $"交易'{td.TradeNumber}'缺少上一交易日({_context.PreSettleDate.OtcFormatDate()})结算数据"); } var margin = CalcMargin(td, lastMargin, interestDays); DbContext.eod_forward_margin.Add(margin); } DbContext.SaveChanges(_context.CancellationToken); } private eod_forward_margin CalcMargin(TradeInfo td, eod_forward_margin lastMargin, int interestDays) { var margin = new eod_forward_margin { AnnualRate = td.AnnualMarginRate, Notional = td.Notional, TradeId = td.TradeId, ValueDate = _context.SettleDate, MarginRate = 0, SettlePrice = 0, MarginSum = 0, CloseMarginSum = 0, InterestDays = 0, MarginCost = 0 }; double? settlePrice = null; switch (td.MarginType) { case MarginTypeEnum.NONE: margin.MarginRate = 0; break; case MarginTypeEnum.FIXED: margin.MarginRate = td.PositionMarginRate; settlePrice = td.SpotPrice; break; case MarginTypeEnum.FLOAT: margin.MarginRate = td.MarginRate; break; default: var um = UnderlyingDataProvider.GetUnderlying(td.UnderlyingCode); if (um == null) { _context.RaiseError(Step, $"交易'{td.TradeNumber}'未找到标的数据:{td.UnderlyingCode}"); } margin.MarginRate = um.MarginRate ?? 0; break; case MarginTypeEnum.FLOATP: margin.MarginRate = td.PositionMarginRate; break; } if (settlePrice == null) { if (_context.GetEodPriceProvider().TryGetPrice(td.UnderlyingCode, SettlementTypeEnum.ClosePrice, out var price)) { settlePrice = price; } else { _context.RaiseError(Step, $"交易'{td.TradeNumber}'未找到标的结算价:{td.UnderlyingCode}"); } } margin.SettlePrice = settlePrice.Value; if (lastMargin != null) { margin.MarginSum = lastMargin.MarginSum; margin.CloseMarginSum = lastMargin.CloseMarginSum; //首先结算假期日的预付金成本 if (interestDays > 1) { margin.InterestDays = interestDays - 1; //使用上一交易日结算数据重算(因为上一交易日也可能会有假期日的计息) margin.MarginCost = lastMargin.Notional * settlePrice.Value * lastMargin.MarginRate * lastMargin.AnnualRate * margin.InterestDays / 365; } } //当前交易日持仓预付金成本结算 if (td.IsCompleted) { margin.Notional = 0; } else { margin.InterestDays += 1; if (lastMargin != null && lastMargin.Notional > td.Notional) { //算法:昨日预付金累计+(昨日持仓预付金累计=昨日预付金累计-昨日平仓预付金累计)*(今日平仓数量=昨日持仓份额-今日持仓份额)/昨日持仓份额 margin.CloseMarginSum += (lastMargin.MarginSum - lastMargin.CloseMarginSum) * (lastMargin.Notional - td.Notional) / lastMargin.Notional; } margin.MarginCost += margin.Notional * settlePrice.Value * margin.MarginRate * margin.AnnualRate / 365; } margin.MarginSum += margin.MarginCost; return margin; } class TradeInfo { public int TradeId { get; set; } public string TradeNumber { get; set; } public DateTime TradeDate { get; set; } public double Notional { get; set; } public MarginTypeEnum MarginType { get; set; } public double AnnualMarginRate { get; set; } public string UnderlyingCode { get; set; } public double SpotPrice { get; set; } public double MarginRate { get; set; } public double PositionMarginRate { get; set; } public bool IsCompleted { get; set; } public override string ToString() { return $"{UnderlyingCode}--{TradeId}--{TradeNumber}"; } } private TradeInfo GetTradeInfo(trade td) { if (td.TradeDate == null) { _context.RaiseError(Step, "缺少交易日期:" + td.TradeNumber); return null; } if (td.trade_forward == null) { _context.RaiseError(Step, "缺少远期交易数据:" + td.TradeNumber); return null; } return new TradeInfo { TradeId = td.id, TradeNumber = td.TradeNumber, TradeDate = td.TradeDate.Value, MarginType = td.MarginType, Notional = td.Notional, AnnualMarginRate = td.trade_forward.AnnualMarginRate, UnderlyingCode = td.UnderlyingCode, SpotPrice = td.SpotPrice ?? 0, MarginRate = td.MarginRate, PositionMarginRate = td.PositionMarginRate, IsCompleted = ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus) }; } } }