using Microsoft.EntityFrameworkCore.Metadata; using Newtonsoft.Json; using Org.BouncyCastle.Ocsp; using Qdp.Foundation.Implementations; using Qdp.Pricing.Library.Base.Utilities; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using YLErp.BLL; using YLErp.BLL.Eod; using YLErp.DBModels; using YLErp.Helpers; using YLErp.Model; using YLErp.Modules.CalculationModule; using YLErp.Modules.EodModule.QueryModule; using static YLErp.ConsGlobal; namespace YLErp.Modules.EodModule.SettlementModule { public class EodClientBalanceCalcBS : YLBaseService { public EodClientBalanceCalcBS(OptUserInfo userInfo) : base(userInfo) { } public static ConcurrentStack thQueue = new ConcurrentStack(); public void ClientBalanceByDay(DateTime settleDate, List clients) { var preBalanceDate = BLL.valuedateBLL.GetNonHolidayDefore(settleDate.AddDays(-1)); var clientDb = DbContextFactory.GetClientDbContext(OptUser); if (clients == null) { clients = clientDb.client.Where(t => t.ProcessStatus != "未提交").ToList(); } //循环客户信息计算客户资金信息 IQueryable eodpnlList = DbContext.eod_trade_position.Where(t => t.ValueDate == settleDate && t.TradeId > 0);//排除场内期权 var newClientBalanceDaily = new List(200); var newValuedate = settleDate.AddDays(1); var lastBalanceDateAddOne = preBalanceDate.AddDays(1); //如果前一天是假日,要显示包含假日的交易 var preday = settleDate.AddDays(-1); //获取上一个交易日的下一天(例如收盘日是周一,上一个交易日就是周五,他的下一天就是周六,获取的是周六) var nonHolidayAddOne = valuedateBLL.GetNonHolidayDefore(preday).AddDays(1); var entryexitPredicate = PredicateBuilder.Create(t => t.ClientId != null && t.ValidState != "InValid" && t.HappenDate >= lastBalanceDateAddOne && t.HappenDate < newValuedate && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认)); var tcActions = new string[] { ClientCashInCashOut.系统操作_平仓费, ClientCashInCashOut.系统操作_行权费, ClientCashInCashOut.系统操作_互换, ClientCashInCashOut.系统操作_票息, ClientCashInCashOut.人工操作_其他 }; var tradeCashs = DbContext.trade_cash.Where(tc => (tc.ValueDate >= nonHolidayAddOne && tc.ValueDate <= settleDate && tc.HappenedDate == null || tc.HappenedDate >= nonHolidayAddOne && tc.HappenedDate <= settleDate) && tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted && tcActions.Contains(tc.Action)); var entryexits = (from cash in DbContext.ClientCashInCashOut.Where(entryexitPredicate) join trade in DbContext.trade on cash.TradeId equals trade.id into trade from td in trade.DefaultIfEmpty() select cash); var preDailyList = DbContext.ClientBalanceDailyBS.Where(t => t.BalanceDate == preBalanceDate); var etQuery = DbContext.eod_trade.Where(x=>x.ValueDate== settleDate).AsEnumerable(); var tradeQuerys = etQuery.Select(s=>s.trade); foreach (var client in clients) { var entryexitList = entryexits.Where(x => x.ClientId == client.id).ToList(); var preDailys = preDailyList.Where(x => x.ClientId == client.id).ToList(); var _eodpnlList = eodpnlList.Where(x => x.ClientId == client.id).ToList(); var _tradeQuerys = tradeQuerys.Where(x => x.ClientId == client.id).ToList(); var tradeIds = _tradeQuerys.Select(s=>s.id).ToList(); var _tradeCashs = tradeCashs.Where(x=> tradeIds.Contains(x.TradeId)).ToList(); var dailys = ProcessClientBalance(client, _tradeQuerys, _eodpnlList, preDailys, entryexitList, _tradeCashs, settleDate); newClientBalanceDaily.AddRange(dailys); } #region DB删除历史数据 增加当日数据 //删除 当日导入的old日数据 ClientBalanceDaily ct1; DbContext.BulkDelete($"{nameof(ct1.BalanceDate)}='{settleDate.ToSqlDate()}'"); DbContext.ClientBalanceDailyBS.AddRange(newClientBalanceDaily); DbContext.SaveChanges(); #endregion } //计算客户资金 private List ProcessClientBalance(Client client, List trades, List eodpnlList, List preDailys, List entryexitList, List tradeCashs, DateTime settleDate) { #region 变量初始化 ClientBalanceDailyBS clientbalancedailyB = null; ClientBalanceDailyBS clientbalancedailyS = null; ClientBalanceDailyBS preClientBalanceDailyB = preDailys.FirstOrDefault(x => x.IsBuy); ClientBalanceDailyBS preClientBalanceDailyS = preDailys.FirstOrDefault(x => !x.IsBuy); #endregion #region 客户昨日现金 clientbalancedailyBS对象初始化 //获取用户买权最后结算日可用资金 if (preClientBalanceDailyB != null) { clientbalancedailyB = preClientBalanceDailyB.Clone(); clientbalancedailyB.id = 0; clientbalancedailyB.LastDayRemainFund = preClientBalanceDailyB.ToDayRemainFund;//期初=上日期末 clientbalancedailyB.OutFund = 0; clientbalancedailyB.Coupon = 0; clientbalancedailyB.InFund = 0; clientbalancedailyB.OptionPremium = 0; clientbalancedailyB.OutFund = 0; clientbalancedailyB.PosiPremium = 0; clientbalancedailyB.PositionPnl=0; clientbalancedailyB.OptionPremium = 0; clientbalancedailyB.Pv = 0; clientbalancedailyB.RoundedPositionPnl = 0; clientbalancedailyB.RoundedPv = 0; clientbalancedailyB.WinLoss = 0; clientbalancedailyB.BalanceDate = settleDate; clientbalancedailyB.OptId = UserId; clientbalancedailyB.OptDate = DateTime.Now; } else { clientbalancedailyB = new ClientBalanceDailyBS() { ClientId = client.id, ClientName = client.Name, ClientNumber = client.Number, BalanceDate = settleDate, OptId = UserId, OptDate = DateTime.Now, IsBuy = true }; } //获取用户卖权最后结算日可用资金 if (preClientBalanceDailyS != null) { clientbalancedailyS = preClientBalanceDailyS.Clone(); clientbalancedailyS.id = 0; clientbalancedailyS.LastDayRemainFund = preClientBalanceDailyS.ToDayRemainFund;//期初=上日期末 clientbalancedailyS.OutFund = 0; clientbalancedailyS.Coupon = 0; clientbalancedailyS.InFund = 0; clientbalancedailyS.OptionPremium = 0; clientbalancedailyS.OutFund = 0; clientbalancedailyS.PosiPremium = 0; clientbalancedailyS.PositionPnl = 0; clientbalancedailyS.OptionPremium = 0; clientbalancedailyS.Pv = 0; clientbalancedailyS.RoundedPositionPnl = 0; clientbalancedailyS.RoundedPv = 0; clientbalancedailyS.WinLoss = 0; clientbalancedailyS.BalanceDate = settleDate; clientbalancedailyS.OptId = UserId; clientbalancedailyS.OptDate = DateTime.Now; } else { clientbalancedailyS = new ClientBalanceDailyBS() { ClientId = client.id, ClientName = client.Name, ClientNumber = client.Number, BalanceDate = settleDate, OptId = UserId, OptDate = DateTime.Now, }; } #endregion #region 出金 入金 实现盈亏资金计算 //获取客户所有出入金列表 if (entryexitList != null) { foreach (var clientEntryexit in entryexitList) { var tradeCash = tradeCashs.FirstOrDefault(x => x.id == clientEntryexit.TradeCashId); var money = Math.Round(clientEntryexit.Money ?? 0, 2); var cashAmount = Math.Round(tradeCash?.Amount ?? 0, 2); if (money == 0 && clientEntryexit.IsGroup == 1) { money = -cashAmount; } if (null == clientEntryexit.Direction) { throw new Exception("客户:" + client.Name + "有一条出入记录存在出入金方向存在问题!"); } //之前单币种环境改为多币种环境后历史数据为""和配置的币种匹配不上 if (string.IsNullOrWhiteSpace(clientEntryexit.CurrencyCode)) { clientEntryexit.CurrencyCode = "CNY"; } if (clientEntryexit.Direction.Equals("入金")) { clientbalancedailyB.InFund += money; clientbalancedailyB.InFundSum += money; clientbalancedailyB.ToDayRemainFund += money; } else if (clientEntryexit.Direction.Equals("出金")) { clientbalancedailyB.OutFund += money; clientbalancedailyB.OutFundSum += money; clientbalancedailyB.ToDayRemainFund -= money; } else if (clientEntryexit.Direction.Equals("其他收入") || clientEntryexit.Direction.Equals("其他支出")) { clientbalancedailyB.ToDayRemainFund += money; } else { var td = trades.FirstOrDefault(x => x.id == clientEntryexit.TradeId); if (td == null) { continue; } bool isBuy = td.BuySell == "卖出";//客户角度 if (ClientCashInCashOut.系统操作_期权费.Equals(clientEntryexit.Action)) { if (isBuy) { clientbalancedailyB.OptionPremium += money; clientbalancedailyB.OptionPremiumSum += money; } else { clientbalancedailyS.OptionPremium += money; clientbalancedailyS.OptionPremiumSum += money; } } else if (ClientCashInCashOut.系统操作_票息.Equals(clientEntryexit.Action)) { if (isBuy) { clientbalancedailyB.Coupon += money; clientbalancedailyB.CouponSum += money; } else { clientbalancedailyS.Coupon += money; clientbalancedailyS.CouponSum += money; } } if (isBuy) { clientbalancedailyB.ToDayRemainFund += money; } else { clientbalancedailyS.ToDayRemainFund += money; } } if (ClientCashInCashOut.已确认.Equals(clientEntryexit.State)) { clientEntryexit.SettleDate = settleDate; clientEntryexit.State = ClientCashInCashOut.已结算; } } } if (tradeCashs.Any()) { foreach (var tc in tradeCashs) { var td = trades.FirstOrDefault(x => x.id == tc.TradeId); if (td == null) { continue; } var cost = 0d; if (td.TradeType != "远期") { cost = TradeCalcHelper.GetSign(td.BuySell) * (td.TradePrice * tc.UnwindPercentRate??0); } else { cost = -(td.TradePrice * tc.UnwindPercentRate??0);//远期开仓总费用占比 } var winloss= cost - tc.Amount; bool isBuy = td.BuySell == "卖出";//客户角度 if (isBuy) { clientbalancedailyB.WinLoss += winloss; clientbalancedailyB.WinLossSum += winloss; } else { clientbalancedailyS.WinLoss += winloss; clientbalancedailyS.WinLossSum += winloss; } } } #endregion #region 持仓市值 持仓Pnl //持仓交易 var positionTrades = trades.Where(t => t.TradeStatus.Equals(ConsTrade.确认成交)).ToList(); //var tradeIds = clientTrades.Where(t => t.ClientId == client.id).Select(t => t.id).ToList(); //获取所有持仓交易ids var positionTradeIds = positionTrades == null || positionTrades.Count == 0 ? new List() : positionTrades.Select(t => t.id).ToList(); //获取客户持仓交易的pv以及持仓盈亏 var eodpnlQuery = eodpnlList.Where(t => positionTradeIds.Contains(t.TradeId)).ToArray(); if (eodpnlQuery.Any()) { foreach (var x in eodpnlQuery) { if (x.BuySell == "买入") { clientbalancedailyS.Pv -= x.Pv; clientbalancedailyS.PvSum -= x.Pv; clientbalancedailyS.RoundedPv -= x.RoundedPv; clientbalancedailyS.RoundedPvSum -= x.RoundedPv; clientbalancedailyS.PositionPnl -= x.PositionPnL; clientbalancedailyS.RoundedPositionPnl -= x.RoundedPositionPnL; clientbalancedailyS.RoundedPositionPnlSum -= x.RoundedPositionPnL; } else { clientbalancedailyB.Pv -= x.Pv; clientbalancedailyB.PvSum -= x.Pv; clientbalancedailyB.RoundedPv -= x.RoundedPv; clientbalancedailyB.RoundedPvSum -= x.RoundedPv; clientbalancedailyB.PositionPnl -= x.PositionPnL; clientbalancedailyB.RoundedPositionPnl -= x.RoundedPositionPnL; clientbalancedailyB.RoundedPositionPnlSum -= x.RoundedPositionPnL; } } } #endregion #region 精度处理 clientbalancedailyB.AmountRound(2); clientbalancedailyS.AmountRound(2); #endregion List clientBalanceDailyBs = new List(); clientBalanceDailyBs.Add(clientbalancedailyB); clientBalanceDailyBs.Add(clientbalancedailyS); return clientBalanceDailyBs; } /// /// 客户重头开始计算 /// public void ClientBalanceReCalc(DateTime? startDate,DateTime? endDate) { thQueue = new ConcurrentStack(); var logger = LogFactory.GetLogger(); var firstDate = DbContext.eodStatus.Where(t => t.Status == "已收盘" && (t.VolTypeFlag & VolTypeFlagEnum.PositionVol) == VolTypeFlagEnum.PositionVol) .Min(t => (DateTime?)t.ValueDate); if (!firstDate.HasValue) { throw new Exception("未找到持仓收盘记录"); } var settleDate = startDate.HasValue? startDate .Value: firstDate.Value; var valueDate = valuedateBLL.ValueDate; var lastBalanceDate = EodOperationBase.GetLastSettlementDate(valueDate); if (endDate.HasValue && endDate< lastBalanceDate) { lastBalanceDate = endDate.Value; } var clientDb = DbContextFactory.GetClientDbContext(OptUser); var clients = clientDb.client.Where(t => t.ProcessStatus != "未提交").ToList(); logger.Info($"客户资金买卖权计算开始,开始日期:{settleDate:yyyy-MM-dd},结束日期:{lastBalanceDate:yyyy-MM-dd}"); while (settleDate <= lastBalanceDate) { logger.Info($"客户资金买卖权计算开始,计算日期:{settleDate:yyyy-MM-dd}"); thQueue.Push(new EodClientBalanceReCalcModel() { SettleDate = settleDate.ToString("yyyy-MM-dd"), Msg = "正在计算" }); try { ClientBalanceByDay(settleDate, clients); logger.Info($"客户资金买卖权计算结束,计算日期:{settleDate:yyyy-MM-dd}"); thQueue.Push(new EodClientBalanceReCalcModel() { SettleDate = settleDate.ToString("yyyy-MM-dd"), Msg = "计算结束" }); settleDate = BLL.valuedateBLL.GetNonHoliday(settleDate.AddDays(1)); } catch (Exception ex) { thQueue.Push(new EodClientBalanceReCalcModel() { SettleDate = settleDate.ToString("yyyy-MM-dd"), Msg = $"计算结束异常:{ex.Message}" }); logger.Error($"客户资金买卖权计算异常,计算日期:{settleDate:yyyy-MM-dd}", ex); break; } } thQueue.Push(new EodClientBalanceReCalcModel() { SettleDate = "", Msg = "全部计算结束" }); logger.Info($"客户资金买卖权计算结束"); } } }