using BaseOUDAL; using Newtonsoft.Json; using System.ComponentModel.DataAnnotations.Schema; using System.Reflection; using YLErp.BLL; using YLErp.Configuration; using YLErp.Helpers; using YLErp.Model.Enum; using YLErp.Modules.CalculationModule; namespace YLErp.Modules.EodModule.SettlementModule { /// /// 客户结算 /// public class EodClientBalanceCalcV2 : EodSettleServiceBaseV2 { readonly DateTime balanceDate; readonly double marginRatio, marginMaxRatio; readonly MyComparer _myComparer; public EodClientBalanceCalcV2(EodSettlementContextV2 context, bool compareMode = false) : base(context) { balanceDate = _context.SettleDate; //保证金可取上浮比率 marginRatio = _context.SystemValue.MarginRatio ?? 0.15; marginMaxRatio = marginRatio + 0.02; DbContext.Database.SetCommandTimeout(600); if (compareMode) { _myComparer = new MyComparer(); } } /// /// 客户结算 /// public void ClientBalanceCalc() { var reqClientIds = _context.Request.ClientIds; var preBalanceDate = _context.PreSettleDate; var clientDb = DbContextFactory.GetClientDbContext(OptUser); //获取所有客户信息 var clients = GetClients(clientDb, reqClientIds); //获取客户所有交易信息(所及为客户所有确认交易,以及当日平仓或者当日执行的交易) var trades = _context.OtcTrades; //获取所有用户当日Eod_Risk IQueryable eodRiskList = null; //循环客户信息计算客户资金信息 IQueryable eodpnlList = null; if (PS.Config.Is国投) { eodRiskList = DbContext.eod_trade_risk_openvol.Where(t => t.ValueDate == balanceDate); eodpnlList = DbContext.eod_trade_position_openvol.Where(t => t.ValueDate == balanceDate); } else if (PS.Config.Company == CompanyEnum.格林大华) { eodRiskList = DbContext.eod_trade_risk.Where(t => t.ValueDate == balanceDate); eodpnlList = DbContext.eod_trade_position.Where(t => t.ValueDate == balanceDate); } else { eodRiskList = DbContext.eod_trade_risk.Where(t => t.ValueDate == balanceDate); eodpnlList = DbContext.eod_trade_position.Where(t => t.ValueDate == balanceDate); } List frozenClientIdList = null, normalClientIdList = null; if (_context.IsCurrentDay) { frozenClientIdList = new List(50); normalClientIdList = new List(50); } var newClientBalanceDaily = new List(100); foreach (var client in clients) { _context.CancellationToken.ThrowIfCancellationRequested(); var daily = ProcessClientBalance(client, _context.OtcTrades, eodpnlList, eodRiskList); newClientBalanceDaily.Add(daily); if (_context.IsCurrentDay) { if (daily.State == "冻结") { if (client.PendingMarginCallPayment != 1) { frozenClientIdList.Add(daily.ClientId); } } else if (daily.State == "正常") { if (client.PendingMarginCallPayment != 0) { normalClientIdList.Add(daily.ClientId); } } } if (_myComparer != null) { var dataOld = DbContext.ClientBalanceDaily.FirstOrDefault(n => n.BalanceDate == balanceDate && n.ClientId == client.id); _myComparer.Compare(client, daily, dataOld); } } if (_myComparer != null) { return; } #region DB删除历史数据 增加当日数据 //删除 当日导入的old日数据 ClientBalanceDaily ct1; if (reqClientIds == null) { DbContext.BulkDelete($"{nameof(ct1.BalanceDate)}='{balanceDate.ToSqlDate()}'"); } else { DbContext.BulkDelete( $"{nameof(ct1.BalanceDate)}='{balanceDate.ToSqlDate()}' AND {nameof(ct1.ClientId)} in @clientIds" , new { clientIds = reqClientIds }); } DbContext.ClientBalanceDaily.AddRange(newClientBalanceDaily); DbContext.SaveChanges(); #endregion //更新客户表是否追保 if (_context.IsCurrentDay) { var sql = ""; if (frozenClientIdList.Count > 0) { sql = string.Format("update client set PendingMarginCallPayment=1 where id in ({0});" , string.Join(",", frozenClientIdList)); } if (normalClientIdList.Count > 0) { sql += string.Format("update client set PendingMarginCallPayment=0 where id in ({0});" , string.Join(",", normalClientIdList)); } if (!string.IsNullOrWhiteSpace(sql)) { clientDb.Database.ExecuteSqlRaw(sql); } } } //计算客户资金 private ClientBalanceDaily ProcessClientBalance(ClientDto client, IEnumerable todayTrades, IQueryable eodpnlList, IQueryable eodRiskList) { var preDaily = client.PreDaily; var eodPriceProvider = _context.GetEodPriceProvider().GetPriceProvider(); var fundObject = new FundObject(); var currencyCodes = _context.CurrencyCodes; var currencyProvider = _context.EodCurrencyProvider; var underlyDataSource = DataCacheProvider.GetUnderlyingDataSource(); var varietyDataSource = DataCacheProvider.GetVarietyDataSource(); foreach (var currencyCode in currencyCodes) { fundObject.InFund.Add(currencyCode, 0); fundObject.OutFund.Add(currencyCode, 0); fundObject.NetFund.Add(currencyCode, 0); fundObject.InFundOther.Add(currencyCode, 0); fundObject.OutFundOther.Add(currencyCode, 0); fundObject.OtherFund.Add(currencyCode, 0); fundObject.TodayRemainFund.Add(currencyCode, 0); fundObject.LastDayRemainFund.Add(currencyCode, 0); } #region 变量初始化 //入金 var inFund = 0.0; //出金 var outFund = 0.0; //其他收入 var inFundOther = 0.0; //其他支出 var outFundOther = 0.0; //当日浮动盈亏 var CurrPnl = 0.0; //期权空头浮动盈利=∑max(期权空头持仓*(期权合约成本价-期权合约现价), 0) 从客户角度看的 var ClientSellPositionPnl = 0.0; //今日可用资金 var CurrAvailAmount = 0.0; //今日可用资金 var LastDayAvailAmount = 0.0; //当日资金变动 var CurrChangeAmount = 0.0; //资信等级 //credit_rating creditRating = null; //授信额度 var lineOfCredit = 0.0; //用户持仓价值 double? clientPv; double? roundedClientPv; //用户卖出部分持仓价值 double? clientSellPv; //客户持仓交易预付金总和,客户买入为正,客户卖出为负 double? PrepaymentAmount; //期权费收支 var OptionPremium = 0.0; //期权费收支--互换 var OptionPremiumSwap = 0.0; //结算收支(平仓行权) var SettlementBalance = 0.0; //平仓收支 var UnwindBalance = 0.0; //行权收支 var ExerciseBalance = 0.0; //实现盈亏 var WinLoss = 0.0; //持仓期权费净额(客户角度卖出为负,买入为正) var PositionPremiumNetCash = 0.0; //权利金冻结(当日该客户所有持仓的卖出期权权利金) var SellTradePrice = 0.0; // 期初持仓交易净额 var LastDayPositionPremiumNetCash = 0.0; //潜在行权盈余 var PotentialSurpluses = 0.0; //权利金应付应收 总额 //var FrozenAndCopeWithExpirePremium = 0.0; //净资金流入,其他资金,票息,互换收益,保证金余额,冻结权利金(未到期支付),冻结资金 double NetFund = 0.0, OtherFund = 0.0, Coupon = 0.0, SwapBalance = 0.0, MarginBalance, FrozenPremium, FrozenBalance = 0.0; //应付到期权利金,应付存续权利金,应收存续权力金,了结权利金 double CopeWithExpirePremium, CopeWithLastPremium, ReceivablePremium, EndPremium = 0.0; //未了结名义成交金额 var TotalNominal = 0.0; //应付保证金,可提取保证金 double PayableMargin = 0d, AdvisableMargin = 0d, DeltaMargin = 0d, SwapPayableMargin = 0.0; //双向保证金 var TwoSideMargin = 0.0; var OtherSideMargin = 0.0; var MySideMargin = 0.0; //额外追保 var AdditionalMargin = 0.0; //追保金额 var Margin = 0.0; //昨日抵押品价值 var LastGuaranteesTotalAmount = 0.0; //持仓名义本金规模 var AvailableStockEqvNotional = 0.0; //潜在风险暴露 var PFE = 0d; // ClientBalanceDaily clientbalancedaily = null; #endregion #region 客户授信 资信等级 客户昨日现金 clientbalancedaily对象初始化 //获取客户授信 lineOfCredit = client.CreditSum; clientbalancedaily = new ClientBalanceDaily() { ClientId = client.id, ClientName = client.Name, ClientNumber = client.Number, BalanceDate = balanceDate }; //获取用户最后结算日可用资金 if (preDaily != null) { if (!string.IsNullOrEmpty(preDaily.FundJson)) { preDaily.FundObject = JsonConvert.DeserializeObject(preDaily.FundJson); if (preDaily.FundObject.InFund.Count() == 1 && preDaily.FundObject.InFund.ContainsKey(string.Empty) && currencyCodes.FirstOrDefault() != string.Empty) { if (string.IsNullOrEmpty(client.SettlementCurrency)) { throw new Exception("客户:" + client.Name + "未配置结算币种"); } preDaily.FundObject.InFund.Add(client.SettlementCurrency, preDaily.InFund ?? 0); preDaily.FundObject.InFundSum.Add(client.SettlementCurrency, preDaily.InFundSum ?? 0); preDaily.FundObject.OutFund.Add(client.SettlementCurrency, preDaily.OutFund ?? 0); preDaily.FundObject.OutFundSum.Add(client.SettlementCurrency, preDaily.OutFundSum ?? 0); preDaily.FundObject.NetFund.Add(client.SettlementCurrency, preDaily.NetFund ?? 0); preDaily.FundObject.NetFundSum.Add(client.SettlementCurrency, preDaily.NetFundSum ?? 0); preDaily.FundObject.OtherFund.Add(client.SettlementCurrency, preDaily.OtherFund ?? 0); preDaily.FundObject.OtherFundSum.Add(client.SettlementCurrency, preDaily.OtherFundSum ?? 0); preDaily.FundObject.TodayRemainFund.Add(client.SettlementCurrency, preDaily.ToDayRemainFund ?? 0); } foreach (var currencyCode in currencyCodes) { if (preDaily.FundObject.TodayRemainFund.ContainsKey(currencyCode)) { fundObject.LastDayRemainFund[currencyCode] = preDaily.FundObject.TodayRemainFund[currencyCode]; } } } else { preDaily.FundObject = new FundObject() { }; if (currencyCodes.Count() == 1 && currencyCodes.FirstOrDefault() == string.Empty) { preDaily.FundObject = new FundObject(); preDaily.FundObject.InFund.Add(string.Empty, preDaily.InFund ?? 0); preDaily.FundObject.InFundSum.Add(string.Empty, preDaily.InFundSum ?? 0); preDaily.FundObject.OutFund.Add(string.Empty, preDaily.OutFund ?? 0); preDaily.FundObject.OutFundSum.Add(string.Empty, preDaily.OutFundSum ?? 0); preDaily.FundObject.NetFund.Add(string.Empty, preDaily.NetFund ?? 0); preDaily.FundObject.NetFundSum.Add(string.Empty, preDaily.NetFundSum ?? 0); preDaily.FundObject.OtherFund.Add(string.Empty, preDaily.OtherFund ?? 0); preDaily.FundObject.OtherFundSum.Add(string.Empty, preDaily.OtherFundSum ?? 0); fundObject.LastDayRemainFund[string.Empty] = preDaily.ToDayRemainFund ?? 0; } else { preDaily.FundObject.InFund.Add(client.SettlementCurrency, preDaily.InFund ?? 0); preDaily.FundObject.InFundSum.Add(client.SettlementCurrency, preDaily.InFundSum ?? 0); preDaily.FundObject.OutFund.Add(client.SettlementCurrency, preDaily.OutFund ?? 0); preDaily.FundObject.OutFundSum.Add(client.SettlementCurrency, preDaily.OutFundSum ?? 0); preDaily.FundObject.NetFund.Add(client.SettlementCurrency, preDaily.NetFund ?? 0); preDaily.FundObject.NetFundSum.Add(client.SettlementCurrency, preDaily.NetFundSum ?? 0); preDaily.FundObject.OtherFund.Add(client.SettlementCurrency, preDaily.OtherFund ?? 0); preDaily.FundObject.OtherFundSum.Add(client.SettlementCurrency, preDaily.OtherFundSum ?? 0); fundObject.LastDayRemainFund[client.SettlementCurrency] = preDaily.ToDayRemainFund ?? 0; } } LastDayPositionPremiumNetCash = preDaily.PositionPremiumNetCash ?? 0.0; LastGuaranteesTotalAmount = preDaily.TodayRemianFundProduct ?? 0.0; } AvailableStockEqvNotional = client.CreditStockEqvNotionalSum - (todayTrades.Where(O => ConsTrade.PositionTradeStatusList.Contains(O.TradeStatus) && O.ClientId == client.id).Sum(O => (double?)O.StockEqvNotional) ?? 0.0); #endregion #region 出金 入金 当日可用资金计算 //获取客户所有出入金列表 if (client.EntryexitList != null) { var tradeCashGroupIds = client.EntryexitList.Where(y => y.IsGroup == 1).Select(y => y.TradeCashId).ToArray(); var tradeCashGroups = DbContext.trade_cash.Where(x => tradeCashGroupIds.Contains(x.id)); foreach (var clientEntryexit in client.EntryexitList) { if (null == clientEntryexit.Direction) { throw new Exception("客户:" + client.Name + "有一条出入记录存在出入金方向存在问题!"); } if (clientEntryexit.CurrencyCode == null) { clientEntryexit.CurrencyCode = ""; } //之前单币种环境改为多币种环境后历史数据为""和配置的币种匹配不上 if (currencyCodes.Count() >= 1 && string.IsNullOrWhiteSpace(clientEntryexit.CurrencyCode)) { if (currencyCodes.Contains("CNY")) { clientEntryexit.CurrencyCode = "CNY"; } else if (currencyCodes.Contains("RMB")) { clientEntryexit.CurrencyCode = "RMB"; } } if (clientEntryexit.Direction.Equals("入金")) { if (fundObject.InFund.ContainsKey(clientEntryexit.CurrencyCode)) { fundObject.InFund[clientEntryexit.CurrencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.InFund.Add(clientEntryexit.CurrencyCode, clientEntryexit.Money ?? 0); } if (fundObject.NetFund.ContainsKey(clientEntryexit.CurrencyCode)) { fundObject.NetFund[clientEntryexit.CurrencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.NetFund.Add(clientEntryexit.CurrencyCode, clientEntryexit.Money ?? 0); } } else if (clientEntryexit.Direction.Equals("出金")) { if (fundObject.OutFund.ContainsKey(clientEntryexit.CurrencyCode)) { fundObject.OutFund[clientEntryexit.CurrencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.OutFund.Add(clientEntryexit.CurrencyCode, clientEntryexit.Money ?? 0); } if (fundObject.NetFund.ContainsKey(clientEntryexit.CurrencyCode)) { fundObject.NetFund[clientEntryexit.CurrencyCode] -= clientEntryexit.Money ?? 0; } else { fundObject.NetFund.Add(clientEntryexit.CurrencyCode, -clientEntryexit.Money ?? 0); } } else if (clientEntryexit.Direction.Equals("其他收入")) { if (fundObject.InFundOther.ContainsKey(clientEntryexit.CurrencyCode)) { fundObject.InFundOther[clientEntryexit.CurrencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.InFundOther.Add(clientEntryexit.CurrencyCode, clientEntryexit.Money ?? 0); } if (fundObject.OtherFund.ContainsKey(clientEntryexit.CurrencyCode)) { fundObject.OtherFund[clientEntryexit.CurrencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.OtherFund.Add(clientEntryexit.CurrencyCode, clientEntryexit.Money ?? 0); } } else if (clientEntryexit.Direction.Equals("其他支出")) { if (fundObject.OutFundOther.ContainsKey(clientEntryexit.CurrencyCode)) { fundObject.OutFundOther[clientEntryexit.CurrencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.OutFundOther.Add(clientEntryexit.CurrencyCode, clientEntryexit.Money ?? 0); } if (fundObject.OtherFund.ContainsKey(clientEntryexit.CurrencyCode)) { fundObject.OtherFund[clientEntryexit.CurrencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.OtherFund.Add(clientEntryexit.CurrencyCode, clientEntryexit.Money ?? 0); } } else { if (ClientCashInCashOut.系统操作_期权费.Equals(clientEntryexit.Action)) { //CurrPnl += clientEntryexit.Money ?? 0.0; CurrChangeAmount += clientEntryexit.Money ?? 0.0; OptionPremium += clientEntryexit.Money ?? 0.0; } //黑箱了结资金按照主交易来进行归类 else if ((ClientCashInCashOut.系统操作_行权费.Equals(clientEntryexit.Action) || ClientCashInCashOut.系统操作_平仓费.Equals(clientEntryexit.Action)) && clientEntryexit.IsGroup != 2) { var money = clientEntryexit.Money ?? 0; //如果是黑箱主交易了结资金(主交易Money没有赋值),取对应tradeCash数据 if (money == 0 && clientEntryexit.IsGroup == 1) { var tradeCash = tradeCashGroups.FirstOrDefault(x => x.id == clientEntryexit.TradeCashId); money = -(tradeCash?.Amount ?? 0); } //CurrPnl += money; CurrChangeAmount += money; SettlementBalance += money; if (ClientCashInCashOut.系统操作_平仓费.Equals(clientEntryexit.Action)) { UnwindBalance += money; } else { ExerciseBalance += money; } } else if (ClientCashInCashOut.系统操作_票息.Equals(clientEntryexit.Action)) { var money = clientEntryexit.Money ?? 0; //如果是黑箱主交易了结资金(主交易Money没有赋值),取对应tradeCash数据 if (money == 0 && clientEntryexit.IsGroup == 1) { var tradeCash = tradeCashGroups.FirstOrDefault(x => x.id == clientEntryexit.TradeCashId); money = -(tradeCash?.Amount ?? 0); } CurrChangeAmount += money; Coupon += money; } } if (ClientCashInCashOut.已确认.Equals(clientEntryexit.State)) { clientEntryexit.SettleDate = balanceDate; clientEntryexit.State = ClientCashInCashOut.已结算; } } } //获取客户所有出入金列表 if (client.EntryexitSwapList != null) { foreach (var clientEntryexit in client.EntryexitSwapList) { if (null == clientEntryexit.Direction) { throw new Exception("客户:" + client.Name + "有一条出入记录存在出入金方向存在问题!"); } if (clientEntryexit.CurrencyCode == null) { clientEntryexit.CurrencyCode = ""; } //之前单币种环境改为多币种环境后历史数据为""和配置的币种匹配不上 if (currencyCodes.Count() >= 1 && string.IsNullOrWhiteSpace(clientEntryexit.CurrencyCode)) { if (currencyCodes.Contains("CNY")) { clientEntryexit.CurrencyCode = "CNY"; } else if (currencyCodes.Contains("RMB")) { clientEntryexit.CurrencyCode = "RMB"; } } if (ClientCashInCashOut.系统操作_期权费.Equals(clientEntryexit.Action)) { CurrChangeAmount += clientEntryexit.Money ?? 0.0; OptionPremiumSwap += clientEntryexit.Money ?? 0.0; } else if (ClientCashInCashOut.系统操作_平仓费.Equals(clientEntryexit.Action)) { CurrChangeAmount += clientEntryexit.Money ?? 0.0; SwapBalance += clientEntryexit.Money ?? 0.0; } else if (ClientCashInCashOut.系统操作_互换.Equals(clientEntryexit.Action)) { CurrChangeAmount += clientEntryexit.Money ?? 0.0; SwapBalance += clientEntryexit.Money ?? 0.0; } if (ClientCashInCashOut.已确认.Equals(clientEntryexit.State)) { clientEntryexit.SettleDate = balanceDate; clientEntryexit.State = ClientCashInCashOut.已结算; } } } foreach (var item in currencyCodes) { if (fundObject.TodayRemainFund.ContainsKey(item)) { fundObject.TodayRemainFund[item] = fundObject.LastDayRemainFund[item] + fundObject.NetFund[item] + fundObject.OtherFund[item]; } else { fundObject.TodayRemainFund.Add(item, fundObject.LastDayRemainFund[item] + fundObject.NetFund[item] + fundObject.OtherFund[item]); } if ((client.SettlementCurrency ?? "CNY") == item || item == "") { fundObject.TodayRemainFund[item] += CurrChangeAmount; } var rateObj = new eod_currency_rate { Rate = 1, SellRate = 1, BuyRate = 1, ForeignCurrency = item, LocalCurrency = client.SettlementCurrency, }; if (PS.Config.ErpElement.SupportMultiCurrency) { currencyProvider.TryGetCurrencyRate(item, client.SettlementCurrency, out rateObj); if (rateObj == null) { _context.RaiseError("结算客户资金", $"[{client.Name}]找不到汇率:{item}{client.SettlementCurrency}"); } } CurrAvailAmount += fundObject.TodayRemainFund[item] * rateObj.Rate; LastDayAvailAmount += fundObject.LastDayRemainFund[item] * rateObj.Rate; inFund += fundObject.InFund[item] * rateObj.Rate; outFund += fundObject.OutFund[item] * rateObj.Rate; NetFund += fundObject.NetFund[item] * rateObj.Rate; inFundOther += fundObject.InFundOther[item] * rateObj.Rate; outFundOther += fundObject.OutFundOther[item] * rateObj.Rate; OtherFund += fundObject.OtherFund[item] * rateObj.Rate; } #endregion #region 抵押品 //抵押品资金价值 var GuaranteesTotalAmount = 0.0; if (client.ClientProductList != null) { foreach (var t in client.ClientProductList) { if (!t.UnderlyingId.HasValue || string.IsNullOrEmpty(t.UnderlyingCode)) { throw new Exception($"抵押品,出入金单号[{t.ProductNumber}]标的ID[{t.UnderlyingId}]未找到对应标的基本信息!"); } if (eodPriceProvider.TryGetPrice(t.UnderlyingCode, out var price)) { GuaranteesTotalAmount += (t.ProductAmount ?? 0) * (t.Rate ?? 0.0) * price; } else { throw new Exception($"抵押品,出入金单号[{t.ProductNumber}]标的[{t.UnderlyingCode}]在{balanceDate:yyyy-MM-dd}收盘价不存在!"); } } } #endregion #region 当日冻结权利金 当日应收权利金 潜在行权盈余 持仓市值 持仓Pnl //持仓交易 var positionTrades = todayTrades.Where(t => t.TradeStatus.Equals(ConsTrade.确认成交) && t.ClientId == client.id).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(); //获取客户冻结资金 var clientFrozenFund = client.FrozenFunds; //当前持仓并且未到支付日的卖出交易(客户为买入,渠道为卖出)则为冻结的权利金 //FrozenPremium = positionTrade == null || positionTrade.Count == 0 ? 0.0 : (positionTrade.Where(t => t.PremiumPayDate > balanceDate && t.BuySell == "卖出").Sum(t => t.TradePrice ?? 0.0)); FrozenPremium = clientFrozenFund.FrozenPayableOptionMoney; //应收存续权利金 //ReceivablePremium = positionTrade == null || positionTrade.Count == 0 ? 0.0 : (positionTrade.Where(t => t.PremiumPayDate > balanceDate && t.BuySell == "买入").Sum(t => t.TradePrice ?? 0.0)); ReceivablePremium = clientFrozenFund.FrozenReceivableOptionMoney; //冻结保证金(绝对值) var FrozenMarginMoney = clientFrozenFund.FrozenMarginMoney; //冻结资金=冻结权利金+到期停牌股票名义金额×6.5%*30/365(默认收取一个月资金利息,多追少补) //todo 此处需要增加股票停复牌信息,同时检查到期交易不能默认执行到期 //FrozenBalance = FrozenPremium; //应付到期权利金 CopeWithExpirePremium = OptionPremium + OptionPremiumSwap; //应付存续权利金 CopeWithLastPremium = FrozenPremium; //获取客户持仓交易的pv以及浮动盈亏 var eodpnlQuery = eodpnlList.Where(t => positionTradeIds.Contains(t.TradeId)).ToArray(); //未了结名义成交金额 todo //持仓的买入 以及卖出是否都统计 TotalNominal = positionTrades == null || positionTrades.Count == 0 ? 0 : positionTrades.Sum(t => t.UnderlyingInstrumentType == "Stock" ? t.StockEqvNotional : (t.Notional * (t.SpotPrice ?? 0.0))); //计算潜在客户行权盈余 max{名义成交金额*(标的当日结算价-标的初始价格)/行权价格, 0} foreach (var t in positionTrades) { if (t.TradeType != "现金流交易") { var spotPrice = t.SpotPrice ?? t.SpotPrice ?? 0.0; var nominal = t.UnderlyingInstrumentType == "Stock" ? t.StockEqvNotional : (t.Notional * spotPrice); if (!eodPriceProvider.TryGetPrice(t.UnderlyingCode, out var nowPrice)) { throw new Exception($"找不到收盘价({t.UnderlyingCode}),无法结算!"); } var Strike = t.IsMoneynessOptionData ? ((t.Strike ?? 0) * t.SpotPrice ?? 0) : t.Strike ?? 0; //如果期初价格未0则已份额计算潜在行权盈余 if (spotPrice == 0) { PotentialSurpluses += Math.Max(Strike == 0 ? 0 : (t.Notional * (nowPrice - Strike) * ("Call".Equals(t.CallPut) ? 1 : -1)), 0) * TradeCalcHelper.GetSign(t.BuySell) * -1; } else { PotentialSurpluses += Math.Max(Strike == 0 ? 0 : (nominal * (nowPrice - Strike) / spotPrice * ("Call".Equals(t.CallPut) ? 1 : -1)), 0) * TradeCalcHelper.GetSign(t.BuySell) * -1; } } if (t.BuySell == "买入") { var um = underlyDataSource.GetData(t.UnderlyingCode); //deltaS/S var rate = um?.DeltaS_S ?? 0; if (!rate.IsNormalize()) { var variety = varietyDataSource.GetData(um.CommodityCode); if (ConsGlobal.InstrumentType.IsStockIndex(um?.UnderlyingInstrumentType) || ConsGlobal.InstrumentType.IsStockIF(um?.UnderlyingInstrumentType)) { rate = variety.UpLimitValue; } else { rate = variety.Margin ?? 0; } } var risk = eodRiskList.Where(O => O.TradeId == t.id).FirstOrDefault(); var pfe1 = t.StockEqvNotional == 0 ? (t.Notional * t.SpotPrice ?? 0) : t.StockEqvNotional; var pfe2 = risk != null ? Math.Abs(risk.DeltaCash) * rate + 0.5 * 100 * rate * rate * Math.Abs(Math.Min(risk.GammaCash, 0)) : 0; PFE += Math.Min(pfe1, pfe2); } } //todo:收益互换没有OriginalNotional PositionPremiumNetCash = positionTrades.Sum(t => -TradeCalcHelper.GetSign(t.BuySell) * ((t.TradePrice ?? 0) * (t.OriginalNotional != null && t.OriginalNotional != 0 ? t.Notional / t.OriginalNotional.Value : 1))); //现金流交易没有份额的概念 SellTradePrice = positionTrades.Where(x => x.BuySell == "买入").Sum( t => t.OriginalNotional.HasValue && t.OriginalNotional > 0 ? (t.TradePrice ?? 0) * t.Notional / t.OriginalNotional.Value : (t.TradePrice ?? 0)); //客户持仓交易预付金总和,客户买入为正,客户卖出为负 PrepaymentAmount = positionTrades.Sum(x => x.StockEqvNotional * (x.trade_snowball?.PrepaymentRatio ?? 0) * (x.BuySell == "卖出" ? 1 : -1)); if (client.TodayTradeCashList != null && client.TodayTradeCashList.Any()) { WinLoss = client.TodayTradeCashList.Sum(tc => { var cost = 0d; if (tc.TradeType != "远期") { cost = TradeCalcHelper.GetSign(tc.BuySell) * tc.TradePrice * tc.TcUnwindPercentRateSum; } else { cost = -tc.TradePrice * tc.TcUnwindPercentRateSum;//远期开仓总费用占比 } return cost - tc.TcAmountSum.OtcFormatValue(OtcFormatFlag.tradePrice); }); //了结开仓费用 EndPremium = client.TodayTradeCashList.Sum(tc => { var cost = 0d; if (tc.TradeType != "远期") { cost = TradeCalcHelper.GetSign(tc.BuySell) * tc.TradePrice * tc.TcUnwindPercentRateSum; } else { cost = -tc.TradePrice * tc.TcUnwindPercentRateSum;//远期开仓总费用占比 } return cost; }); } var eodPnlSum = new EodPnlSum(); if (eodpnlQuery.Any()) { //todo: eodpnlQuery 确认成交的持仓数据 LastPvSum,TotalPnlSum 当天了结的没有计算 foreach (var x in eodpnlQuery) { eodPnlSum.LastPvSum -= x.LastPv; eodPnlSum.PvSum -= x.Pv; if (x.BuySell == "买入") { eodPnlSum.SellPvSum -= x.Pv; } eodPnlSum.RoundedPvSum -= x.RoundedPv; eodPnlSum.TotalPnlSum -= x.TotalPnL; eodPnlSum.PositionPnlSum -= x.PositionPnL; eodPnlSum.RoundedPositionPnlSum -= x.RoundedPositionPnL; //期权空头浮动盈利=∑max(期权空头持仓*(期权合约成本价-期权合约现价), 0) 从客户角度看的, 结构化交易需要将两条腿的空头Pnl合计 if (x.TradeType != "结构化交易" && x.ParentTradeId == 0 && x.BuySell == "买入") { ClientSellPositionPnl += Math.Max(-x.PositionPnL, 0); } } if (_context.SystemValue.PotentialSurplusCalcMode == valuedate.PotentialSurplusCalcMode_Pv) { PotentialSurpluses = eodPnlSum.PvSum; } } if (eodpnlList.Where(t => t.ClientId == client.id).Any()) { eodPnlSum.DailyPnLSum = eodpnlList.Where(t => t.ClientId == client.id).Sum(q => q.DailyPnL) * (-1);//当日浮动盈亏 } //合计持仓浮动盈亏 CurrPnl = eodPnlSum.DailyPnLSum; clientPv = eodPnlSum.PvSum; roundedClientPv = eodPnlSum.RoundedPvSum; clientSellPv = eodPnlSum.SellPvSum; #endregion #region 应缴保证金 保证金余额 可取保证金 追保额度 //获取tradespan 追保金额 = (当日资金余额 - 维持保证金) + 授信额度 var clientSpan = client.ClientSpan; if (clientSpan != null) { PayableMargin = clientSpan.WorstCastClientPayable ?? 0d; DeltaMargin = clientSpan.DeltaMargin ?? 0d; SwapPayableMargin = clientSpan.SwapWorstCastClientPayable ?? 0d; TwoSideMargin = clientSpan.TwoSideMargin ?? 0d; OtherSideMargin = clientSpan.OtherSideMargin ?? 0d; MySideMargin = clientSpan.MySideMargin ?? 0d; AdditionalMargin = (clientSpan.AdditionalWorstCastClientPayable) ?? 0d; } //冻结资金 FrozenBalance = ReceivablePremium - FrozenPremium - (clientFrozenFund.OutFunds + clientFrozenFund.RedeemFunds) - FrozenMarginMoney; //保证金余额 MarginBalance = CurrAvailAmount + GuaranteesTotalAmount + FrozenBalance; if (PS.Config.Is申万) { //期权空头浮动盈亏 MarginBalance -= ClientSellPositionPnl; //空头持仓市值 MarginBalance -= Math.Abs((clientSellPv ?? 0)); } else { //判断潜在行权盈余是否计入保证金余额 if ((valuedateBLL.SystemDate.IsPotentialSurplusUseMargin ?? 0) == 1) { MarginBalance += PotentialSurpluses; } } //可取保证金为保证金余额 - 应缴保证金(上浮2%) AdvisableMargin = Math.Max(MarginBalance + PayableMargin * marginMaxRatio / marginRatio, 0); //计算追保金额 维持保证金带方向 所以计算追保金额是 为相加 var IsTradeCredit = (client.IsTradeCredit ?? 0) == 1; //是否期权费授信 if (IsTradeCredit) { //期权费授信可以支付权利金 则保证金余额未0时 资金从 Margin = Math.Min(MarginBalance + lineOfCredit + PayableMargin, 0); } else { //期权费授信不可以支付权利金时 Margin = Math.Min(Math.Max(MarginBalance, 0) + lineOfCredit + PayableMargin, 0) + Math.Min(MarginBalance, 0); } #endregion #region 国君收益互换--导入持仓 if (client.eodPositionSwapMannual != null && client.eodPositionSwapMannual.Any()) { double swapMargin = 0.0; double swappv = 0.0; double swappnl = 0.0; foreach (var x in client.eodPositionSwapMannual) { var underly = underlyDataSource.GetData(x.UnderlyingCode); var variety = varietyDataSource.GetData(underly.UnderlyingTypeId); currencyProvider.TryGetCurrencyRate(variety.QuoteCurrency, client.SettlementCurrency, out var rateObj); //保证金导入是客户方向,其他值是交易员方向 swapMargin += (x.Margin) * rateObj.Rate; swappv += (-x.PositionPnl + x.TotalFee) * rateObj.Rate; swappnl += (-x.PositionPnl * rateObj.Rate); } PayableMargin -= swapMargin; clientPv = (clientPv ?? 0) + swappv; roundedClientPv = (roundedClientPv ?? 0) + swappv; eodPnlSum.PositionPnlSum += swappnl; eodPnlSum.RoundedPositionPnlSum += swappnl; } #endregion #region ClientBalacedaily ClientBalace对象赋值 //if (client.BoundSide == BoundSideEnum.南向 && clientSpan != null) //{ // var credit = DbContext.credit.FirstOrDefault(x => x.CreditStartDate <= balanceDate && x.CreditDeadLine >= balanceDate && x.ClientId == client.id && x.ProcessStatus == "已审批"); // var PFECredit = credit != null ? credit.PFECredit : 0; // clientSpan.PFEUsed = Math.Max(Math.Min((clientSpan.PFEUsed ?? 0) - CurrAvailAmount, PFECredit ?? 0), 0); //} clientbalancedaily.ToDayRemainFund = CurrAvailAmount; clientbalancedaily.LastDayRemainFund = LastDayAvailAmount; clientbalancedaily.PositionPremiumNetCash = PositionPremiumNetCash; clientbalancedaily.SellTradePrice = SellTradePrice; clientbalancedaily.LastDayPositionPremiumNetCash = LastDayPositionPremiumNetCash; clientbalancedaily.WinLoss = WinLoss; clientbalancedaily.InFund = inFund; clientbalancedaily.OutFund = outFund; clientbalancedaily.InFundOther = inFundOther; clientbalancedaily.OutFundOther = outFundOther; clientbalancedaily.BalanceDate = balanceDate; clientbalancedaily.DayGainLoss = CurrPnl; clientbalancedaily.PositionPnl = eodPnlSum.PositionPnlSum; clientbalancedaily.RoundedPositionPnl = eodPnlSum.RoundedPositionPnlSum; clientbalancedaily.ClientSellPositionPnl = ClientSellPositionPnl; clientbalancedaily.TotalPnl = eodPnlSum.TotalPnlSum; clientbalancedaily.DailyPnl = eodPnlSum.DailyPnLSum; clientbalancedaily.OptionPremium = OptionPremium; clientbalancedaily.OptionPremiumSwap = OptionPremiumSwap; clientbalancedaily.SettlementBalance = SettlementBalance; clientbalancedaily.UnwindBalance = UnwindBalance; clientbalancedaily.Coupon = Coupon; clientbalancedaily.SwapBalance = SwapBalance; clientbalancedaily.ExerciseBalance = ExerciseBalance; clientbalancedaily.AdvisableMargin = AdvisableMargin; clientbalancedaily.TotalNominal = TotalNominal; clientbalancedaily.Pv = clientPv ?? 0; clientbalancedaily.RoundedPv = roundedClientPv ?? 0; clientbalancedaily.SellPv = clientSellPv ?? 0; clientbalancedaily.PrepaymentAmount = PrepaymentAmount; clientbalancedaily.CopeWithLastPremium = CopeWithLastPremium; clientbalancedaily.CopeWithExpirePremium = CopeWithExpirePremium; clientbalancedaily.EndPremium = EndPremium; clientbalancedaily.FrozenBalance = FrozenBalance; clientbalancedaily.FrozenPremium = FrozenPremium; clientbalancedaily.FrozenOutFund = clientFrozenFund.OutFunds; clientbalancedaily.FrozenRedeemFunds = clientFrozenFund.RedeemFunds; clientbalancedaily.MarginBalance = MarginBalance; clientbalancedaily.NetFund = NetFund; clientbalancedaily.OtherFund = OtherFund; clientbalancedaily.IsTradeCredit = IsTradeCredit; clientbalancedaily.Credit = lineOfCredit; clientbalancedaily.CashDeposit = PayableMargin; clientbalancedaily.PayableMargin = PayableMargin; clientbalancedaily.DeltaMargin = DeltaMargin; clientbalancedaily.SwapPayableMargin = SwapPayableMargin; clientbalancedaily.TwoSideMargin = TwoSideMargin; clientbalancedaily.OtherSideMargin = OtherSideMargin; clientbalancedaily.MySideMargin = MySideMargin; clientbalancedaily.IsPayableMarginManual = clientSpan?.ModifiedFlag; clientbalancedaily.FrozenMarginMoney = FrozenMarginMoney; clientbalancedaily.Margin = Margin; clientbalancedaily.TodayRemianFundProduct = GuaranteesTotalAmount; clientbalancedaily.CashInCashOutProductChange = GuaranteesTotalAmount - LastGuaranteesTotalAmount; clientbalancedaily.PotentialSurpluses = PotentialSurpluses; clientbalancedaily.OptId = UserId; clientbalancedaily.OptName = UserName; clientbalancedaily.OptDate = DateTime.Now; clientbalancedaily.AdditionalMargin = AdditionalMargin; clientbalancedaily.AvailableStockEqvNotional = AvailableStockEqvNotional; clientbalancedaily.PFE = PFE; clientbalancedaily.EAD = Math.Max(PFE - (clientbalancedaily.Pv ?? 0) - (clientbalancedaily.ToDayRemainFund ?? 0), 0); if (PS.Config.Is厦门象屿) { var xmxyClient = new XiaMenXiangYuClientInfo { ClientId = client.id, ClientName = client.Name, ClientNumber = client.Number, FundThreshold = client.FundThreshold }; new XiaMenXiangYuCashService(this).ExecuteV2(xmxyClient, clientbalancedaily, fundObject, out var InOutFund, _context.SettleDate, positionTrades.Count); NetFund += InOutFund; MarginBalance += InOutFund; } clientbalancedaily.FundObject = fundObject; clientbalancedaily.FundJson = JsonHelper.Serialize(fundObject); #endregion #region 客户冻结状态 //如果应付资金总额PayableFund大于0 则设置client //PayableFund => Math.Max(ClosedTradePayableFund + PositionTradePayableFund + MarginByPayableMarginCalc, 0); var ToDayRemainFund = clientbalancedaily.ToDayRemainFund ?? 0; //var PositionPremiumNetCash = clientbalancedaily.PositionPremiumNetCash ?? 0; //请知道d1、d2、d3含义的注释一下 var d1 = Math.Min(ToDayRemainFund + PositionPremiumNetCash, 0); var d2 = Math.Max(PositionPremiumNetCash - Math.Max(ToDayRemainFund + PositionPremiumNetCash, 0), 0); var d3 = -(clientbalancedaily.PayableMargin ?? 0.0) - Math.Max(ToDayRemainFund - d1 + d2, 0) - (clientbalancedaily.TodayRemianFundProduct ?? 0.0) - (clientbalancedaily.Credit ?? 0.0); if (Math.Max(-d1 + d2 + d3, 0) > 0) { clientbalancedaily.State = "冻结"; } else { //如果计算追保金额小于等于0则解冻 clientbalancedaily.State = "正常"; } #endregion #region 结算合计 clientbalancedaily.OptionPremiumSum = clientbalancedaily.OptionPremium; clientbalancedaily.OptionPremiumSwapSum = clientbalancedaily.OptionPremiumSwap; clientbalancedaily.SettlementBalanceSum = clientbalancedaily.SettlementBalance; clientbalancedaily.UnwindBalanceSum = clientbalancedaily.UnwindBalance; clientbalancedaily.ExerciseBalanceSum = clientbalancedaily.ExerciseBalance; clientbalancedaily.CouponSum = clientbalancedaily.Coupon; clientbalancedaily.SwapBalanceSum = clientbalancedaily.SwapBalance; clientbalancedaily.WinLossSum = clientbalancedaily.WinLoss; clientbalancedaily.EndPremiumSum = clientbalancedaily.EndPremium; clientbalancedaily.CashInCashOutProductChangeSum = clientbalancedaily.CashInCashOutProductChange; if (preDaily != null) { clientbalancedaily.NetFundSum = 0; clientbalancedaily.InFundSum = 0; clientbalancedaily.OutFundSum = 0; clientbalancedaily.OtherFundSum = 0; foreach (var item in currencyCodes) { var rateObj = new eod_currency_rate { Rate = 1, SellRate = 1, BuyRate = 1, ForeignCurrency = item, LocalCurrency = client.SettlementCurrency, }; if (PS.Config.ErpElement.SupportMultiCurrency) { currencyProvider.TryGetCurrencyRate(item, client.SettlementCurrency, out rateObj); if (rateObj == null) { _context.RaiseError("结算客户资金", $"[{client.Name}]找不到汇率:{item}{client.SettlementCurrency}"); } } preDaily.FundObject.NetFundSum.TryGetValue(item, out var preNetFundSum); clientbalancedaily.FundObject.NetFund.TryGetValue(item, out var netFundToday); clientbalancedaily.FundObject.NetFundSum[item] = preNetFundSum + netFundToday; clientbalancedaily.NetFundSum += clientbalancedaily.FundObject.NetFundSum[item] * rateObj.Rate; preDaily.FundObject.InFundSum.TryGetValue(item, out var preInFundSum); clientbalancedaily.FundObject.InFund.TryGetValue(item, out var inFundToday); clientbalancedaily.FundObject.InFundSum[item] = preInFundSum + inFundToday; clientbalancedaily.InFundSum += clientbalancedaily.FundObject.InFundSum[item] * rateObj.Rate; preDaily.FundObject.OutFundSum.TryGetValue(item, out var preOutFundSum); clientbalancedaily.FundObject.OutFund.TryGetValue(item, out var outFundToday); clientbalancedaily.FundObject.OutFundSum[item] = preOutFundSum + outFundToday; clientbalancedaily.OutFundSum += clientbalancedaily.FundObject.OutFundSum[item] * rateObj.Rate; preDaily.FundObject.OtherFundSum.TryGetValue(item, out var preOtherFundSum); clientbalancedaily.FundObject.OtherFund.TryGetValue(item, out var otherFundToday); clientbalancedaily.FundObject.OtherFundSum[item] = preOtherFundSum + otherFundToday; clientbalancedaily.OtherFundSum += clientbalancedaily.FundObject.OtherFundSum[item] * rateObj.Rate; } clientbalancedaily.OptionPremiumSum += preDaily.OptionPremiumSum; clientbalancedaily.OptionPremiumSwapSum += (preDaily.OptionPremiumSwapSum ?? 0); clientbalancedaily.SettlementBalanceSum += preDaily.SettlementBalanceSum; clientbalancedaily.UnwindBalanceSum += preDaily.UnwindBalanceSum; clientbalancedaily.ExerciseBalanceSum += preDaily.ExerciseBalanceSum; clientbalancedaily.CouponSum += preDaily.CouponSum; clientbalancedaily.SwapBalanceSum += preDaily.SwapBalanceSum; clientbalancedaily.WinLossSum += preDaily.WinLossSum; clientbalancedaily.EndPremiumSum += (preDaily.EndPremiumSum ?? 0); clientbalancedaily.CashInCashOutProductChangeSum += preDaily.CashInCashOutProductChangeSum; } else { clientbalancedaily.FundObject.NetFundSum = clientbalancedaily.FundObject.NetFund; clientbalancedaily.FundObject.InFundSum = clientbalancedaily.FundObject.InFund; clientbalancedaily.FundObject.OutFundSum = clientbalancedaily.FundObject.OutFund; clientbalancedaily.FundObject.OtherFundSum = clientbalancedaily.FundObject.OtherFund; clientbalancedaily.NetFundSum = clientbalancedaily.NetFund; clientbalancedaily.InFundSum = clientbalancedaily.InFund; clientbalancedaily.OutFundSum = clientbalancedaily.OutFund; clientbalancedaily.OtherFundSum = clientbalancedaily.OtherFund; } #endregion clientbalancedaily.FundJson = JsonHelper.Serialize(clientbalancedaily.FundObject); return clientbalancedaily; } //获取客户信息 private IEnumerable GetClients(ClientDBContext clientDb, IEnumerable reqClientIds) { var balanceDate = _context.SettleDate; var preBalanceDate = _context.PreSettleDate; if (reqClientIds != null && !reqClientIds.Any(n => n > 0)) { reqClientIds = null; } //获取所有客户信息 var clientPrediate = PredicateBuilder.Create(t => t.ProcessStatus != "未提交"); if (reqClientIds != null) { clientPrediate = clientPrediate.And(t => reqClientIds.Contains(t.id)); } var isHistory = balanceDate != DateTime.Today; var clientDic = clientDb.client.Where(clientPrediate).Select(n => new ClientDto { id = n.id, Name = n.Name, Number = n.Number, IsTradeCredit = n.IsTradeCredit, FundThreshold = isHistory ? 0 : n.FundThreshold, PendingMarginCallPayment = n.PendingMarginCallPayment ?? 0, SettlementCurrency = n.SettlementCurrency }).ToDictionary(n => n.id); if (isHistory) { var fundThresholdDict = clientDb.client_Axis_Fundthresholds .Where(x => x.ValueDate <= balanceDate) .AsEnumerable() .GroupBy(O => O.ClientId) .ToDictionary( K => K.Key, V => V.OrderByDescending(O => O.ValueDate).FirstOrDefault()); foreach (var item in fundThresholdDict) { if (clientDic.TryGetValue(item.Key, out var obj)) { obj.FundThreshold = item.Value.FundThreshold; } } } var allClientIds = clientDic.Keys.ToArray(); #region 获取上日客户资金结算 var preDailyPredicate = PredicateBuilder.Create(t => t.BalanceDate == preBalanceDate); if (reqClientIds != null) { preDailyPredicate = preDailyPredicate.And(t => reqClientIds.Contains(t.ClientId)); } var preDailyList = DbContext.ClientBalanceDaily.Where(preDailyPredicate).ToArray(); foreach (var preDaily in preDailyList) { if (clientDic.TryGetValue(preDaily.ClientId, out var client)) { client.PreDaily = preDaily; } } #endregion #region 获取所有客户授信 var creditPredicate = PredicateBuilder.Create(t => t.ClientId != null && t.ProcessStatus == "已审批" && (!t.CreditDeadLine.HasValue || t.CreditDeadLine >= balanceDate) && (!t.CreditStartDate.HasValue || t.CreditStartDate <= balanceDate)); if (reqClientIds != null) { creditPredicate = creditPredicate.And(t => allClientIds.Contains(t.ClientId.Value)); } var creditQuery = from c in DbContext.credit.Where(creditPredicate) group c by c.ClientId into g select new { ClientId = g.Key, CreditSum = g.Sum(t => t.Credit ?? 0), CreditStockEqvNotionalSum = g.Sum(t => t.StockEqvNotional ?? 0) }; var creditList = creditQuery.ToArray(); foreach (var credit in creditList) { if (clientDic.TryGetValue(credit.ClientId.Value, out var client)) { client.CreditSum = credit.CreditSum; client.CreditStockEqvNotionalSum = credit.CreditStockEqvNotionalSum; } } #endregion #region 出入金记录 获取上个结算日到当前结算日之间的所有已结算和已确认的出入金(包含周末的出入金) var newValuedate = balanceDate.AddDays(1); var lastBalanceDateAddOne = preBalanceDate.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.已确认)); if (reqClientIds != null) { entryexitPredicate = entryexitPredicate.And(t => allClientIds.Contains(t.ClientId.Value)); } 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() where td.TradeType != "收益互换" select cash).ToLookup(n => n.ClientId.Value); var entryexits_swap = (from cash in DbContext.ClientCashInCashOut.Where(entryexitPredicate) join trade in DbContext.trade.Where(x => x.TradeType == "收益互换") on cash.TradeId equals trade.id select cash).ToLookup(n => n.ClientId.Value); foreach (var lookup in entryexits) { if (clientDic.TryGetValue(lookup.Key, out var client)) { client.EntryexitList = lookup.ToArray(); } } foreach (var lookup in entryexits_swap) { if (clientDic.TryGetValue(lookup.Key, out var client)) { client.EntryexitSwapList = lookup.ToArray(); } } #endregion #region 获取客户所有现存(抵押状态)抵押品信息 var clientProductPredicate = PredicateBuilder.Create(product => product.HappenDate < newValuedate && (product.Status == Clientcashincashout_productStatusEnum.抵押.ToString() && product.OptStatus == ClientCashInCashOut.已确认 || product.Status == Clientcashincashout_productStatusEnum.赎回.ToString() && product.OptStatus != ClientCashInCashOut.已确认 || product.Status == Clientcashincashout_productStatusEnum.赎回.ToString() && product.OptStatus == ClientCashInCashOut.已确认 && product.BackDate >= newValuedate) ); if (reqClientIds != null) { clientProductPredicate = clientProductPredicate.And(t => allClientIds.Contains(t.ClientId)); } var clientProductQuery = from product in DbContext.clientcashincashout_product.Where(clientProductPredicate) join um in DbContext.underlying_manager on product.UnderlyingId equals um.id into um_t from um in um_t.DefaultIfEmpty() select new ClientProductDto { ProductNumber = product.Number, ClientId = product.ClientId, UnderlyingId = product.UnderlyingId, UnderlyingCode = um.UnderlyingCode, ProductAmount = product.ProductAmount, Rate = product.Rate }; var clientProductLookup = clientProductQuery.ToLookup(n => n.ClientId); foreach (var lookup in clientProductLookup) { if (clientDic.TryGetValue(lookup.Key, out var client)) { client.ClientProductList = lookup.ToArray(); } } #endregion #region 客户保证金 var clientSpanPredicate = PredicateBuilder.Create(t => t.ValueDate == balanceDate && t.SpanType == ClientSpan.SpanType_Eod); if (reqClientIds != null) { clientSpanPredicate = clientSpanPredicate.And(t => allClientIds.Contains(t.ClientId)); } var clientSpanList = DbContext.client_span.Where(clientSpanPredicate).Select(n => new ClientSpanDto { ClientId = n.ClientId, AdditionalWorstCastClientPayable = n.AdditionalWorstCastClientPayable, MySideMargin = n.MySideMargin, OtherSideMargin = n.OtherSideMargin, TwoSideMargin = n.TwoSideMargin, WorstCastClientPayable = n.WorstCastClientPayable, DeltaMargin = n.DeltaMargin, SwapWorstCastClientPayable = n.SwapWorstCastClientPayable, ModifiedFlag = n.ModifiedFlag }).ToArray(); foreach (var spanDto in clientSpanList) { if (clientDic.TryGetValue(spanDto.ClientId, out var client)) { client.ClientSpan = spanDto; } } #endregion //获取冻结资金信息 var clientFrozenFunds = new ClientFrozenFundsService(this).GetDatas(balanceDate, allClientIds); foreach (var kv in clientFrozenFunds) { if (clientDic.TryGetValue(kv.Key, out var client)) { client.FrozenFunds = kv.Value; } } //获取结算日相关的资金了结记录 var todayTcSumLookup = GetFinishedTradeCashSumList(reqClientIds).ToLookup(n => n.ClientId); foreach (var lookup in todayTcSumLookup) { if (clientDic.TryGetValue(lookup.Key, out var client)) { client.TodayTradeCashList = lookup.ToArray(); } } var EodPositionSwapMannual = DbContext.eod_trade_position_swap_mannual.Where(x => x.ValueDate == balanceDate).ToLookup(n => n.ClientId); foreach (var lookup in EodPositionSwapMannual) { if (clientDic.TryGetValue(lookup.Key, out var client)) { client.eodPositionSwapMannual = lookup.ToArray(); } } //返回数据结果 return clientDic.Values; } /// /// 获取结算日相关的资金了结记录 /// private IEnumerable GetFinishedTradeCashSumList(IEnumerable reqClientIds) { var balanceDate = _context.SettleDate; //如果前一天是假日,要显示包含假日的交易 var preday = balanceDate.AddDays(-1); //获取上一个交易日的下一天(例如收盘日是周一,上一个交易日就是周五,他的下一天就是周六,获取的是周六) var nonHolidayAddOne = valuedateBLL.GetNonHolidayDefore(preday).AddDays(1); var tcActions = new string[] { ClientCashInCashOut.系统操作_平仓费, ClientCashInCashOut.系统操作_行权费, ClientCashInCashOut.系统操作_互换, ClientCashInCashOut.系统操作_票息, ClientCashInCashOut.人工操作_其他 }; var tdPredicate = PredicateBuilder.Create(t => t.ClientId > 0 && t.ValidState != ConsGlobal.InValid && t.IsGroup != 1); if (reqClientIds != null) { tdPredicate = tdPredicate.And(t => reqClientIds.Contains(t.ClientId)); } var tcQuery = from tc in DbContext.trade_cash where (tc.ValueDate >= nonHolidayAddOne && tc.ValueDate <= balanceDate && tc.HappenedDate == null || tc.HappenedDate >= nonHolidayAddOne && tc.HappenedDate <= balanceDate) && tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted && tcActions.Contains(tc.Action) group tc by tc.TradeId into g select new { TradeId = g.Key, AmountSum = g.Sum(n => n.Amount), UnwindPercentRateSum = g.Sum(n => n.UnwindPercentRate ?? 0) }; var query = from tc in tcQuery join t in DbContext.trade.Where(tdPredicate) on tc.TradeId equals t.id select new TodayTradeCashDto { TradeId = tc.TradeId, ClientId = t.ClientId, TradeType = t.TradeType, BuySell = t.BuySell, TradePrice = t.TradePrice ?? 0, TcAmountSum = tc.AmountSum, TcUnwindPercentRateSum = tc.UnwindPercentRateSum }; return query.ToArray(); } #region----InnerClass---- class ClientSpanDto { public int ClientId { get; set; } public double? WorstCastClientPayable { get; set; } public double? DeltaMargin { get; set; } public double? SwapWorstCastClientPayable { get; set; } public double? TwoSideMargin { get; set; } public double? OtherSideMargin { get; set; } public double? MySideMargin { get; set; } public double? AdditionalWorstCastClientPayable { get; set; } /// /// 是否人工修改过 /// public bool ModifiedFlag { get; set; } } class ClientProductDto { public int ClientId { get; set; } /// /// 抵押记录编号 /// public string ProductNumber { get; set; } public int? UnderlyingId { get; set; } public string UnderlyingCode { get; set; } /// /// 质押数量 /// public double? ProductAmount { get; set; } /// /// 质押率 /// public double? Rate { get; set; } } class TodayTradeCashDto { public int TradeId { get; set; } public int ClientId { get; set; } public string TradeType { get; set; } public string BuySell { get; set; } public double TradePrice { get; set; } public double TcAmountSum { get; set; } public double TcUnwindPercentRateSum { get; set; } } class ClientDto { public int id { get; set; } public string Name { get; set; } public string Number { get; set; } public string SettlementCurrency { get; set; } public int? IsTradeCredit { get; set; } /// /// 资金限额 /// public double? FundThreshold { get; set; } /// /// 是否追保 /// public double PendingMarginCallPayment { get; set; } //-----授信----- /// /// 客户授信合计 /// public double CreditSum { get; set; } /// /// 客户授信名义本金合计 /// public double CreditStockEqvNotionalSum { get; set; } //-----其他----- /// /// 上日结算数据 /// public ClientBalanceDaily PreDaily { get; set; } /// /// 客户保证金 /// public ClientSpanDto ClientSpan { get; set; } /// /// 这个需要保持数据追踪状态以便更新数据库数据 /// public IEnumerable EntryexitList { get; set; } /// /// 这个需要保持数据追踪状态以便更新数据库数据 /// public IEnumerable EntryexitSwapList { get; set; } /// /// 抵押品信息 /// public IEnumerable ClientProductList { get; set; } /// /// 冻结资金汇总 /// public ClientFrozenFunds FrozenFunds { get; set; } /// /// 客户关联交易资金记录 /// public IEnumerable TodayTradeCashList { get; set; } //获取客户导入的所有持仓信息 -- 收益互换(国君) public IEnumerable eodPositionSwapMannual { get; set; } } class EodPnlSum { /// /// 昨日价值 /// public double LastPvSum { get; set; } /// /// 当日价值 /// public double PvSum { get; set; } /// /// /// public double SellPvSum { get; set; } /// /// 当日四舍五入价值 /// public double RoundedPvSum { get; set; } /// /// 当日浮动盈亏 /// public double DailyPnLSum { get; set; } /// /// 当日持仓盈亏 /// public double PositionPnlSum { get; set; } /// /// 当日四舍五入持仓盈亏 /// public double RoundedPositionPnlSum { get; set; } /// /// 当日累积总盈亏 /// public double TotalPnlSum { get; set; } } class MyComparer { PropertyInfo[] _props; readonly List _diffList; readonly HashSet _ignorePropNames; public MyComparer() { ClientBalanceDaily ct; _ignorePropNames = new HashSet(StringComparer.OrdinalIgnoreCase) { nameof(ct.id), nameof(ct.OptId), nameof(ct.OptDate), nameof(ct.OptName), }; _diffList = new List(20); } public void Compare(ClientDto client, ClientBalanceDaily dataNew, ClientBalanceDaily dataOld) { if (dataNew is null) { throw new ArgumentNullException(nameof(dataNew)); } if (dataOld is null) { throw new Exception("旧数据不存在"); } _diffList.Clear(); if (string.IsNullOrEmpty(dataOld.State)) { dataOld.State = "正常"; } var dd = new[] { dataNew.FrozenRedeemFunds, dataOld.FrozenRedeemFunds }; ReviseData(dataNew); ReviseData(dataOld); if (_props == null) { _props = typeof(ClientBalanceDaily).GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(p => { return !_ignorePropNames.Contains(p.Name) && p.CanWrite && p.CanRead && p.GetIndexParameters().Length < 1 && !p.GetCustomAttributes().Any(); }).ToArray(); } foreach (var p in _props) { var objNew = p.GetValue(dataNew); var objOld = p.GetValue(dataOld); var strNew = string.Empty; var strOld = string.Empty; if (p.PropertyType == typeof(double)) { strNew = ((double)objNew).ToString("F6"); strOld = ((double)objOld).ToString("F6"); } else if (p.PropertyType == typeof(double?)) { if (objNew != null) { strNew = ((double?)objNew).Value.ToString("F6"); } if (objOld != null) { strOld = ((double?)objOld).Value.ToString("F6"); } } else { if (objNew != null) { strNew = objNew.ToString(); } if (objOld != null) { strOld = objOld.ToString(); } } if (strNew != strOld) { _diffList.Add($"字段名:{p.Name},新数据:{strNew},旧数据:{strOld}"); } } if (_diffList.Any()) { var diffstr = string.Join(Environment.NewLine, _diffList.ToArray()); throw new Exception($"[{client.id},{client.Name}]新旧数据不一致:\r\n{diffstr}"); } else { Console.WriteLine($"[{client.id},{client.Name}]新旧数据一致"); } } private void ReviseData(ClientBalanceDaily data) { //因为抵押品是按照实时行情处理的所以需要去除这部分的影响 data.FrozenBalance += data.FrozenRedeemFunds; data.MarginBalance += data.FrozenRedeemFunds; if (data.AdvisableMargin > 0) { data.AdvisableMargin += data.FrozenRedeemFunds; } if (data.Margin < 0) { data.Margin += data.FrozenRedeemFunds; } data.FrozenRedeemFunds = 0; } } #endregion } } //计算渠道方合计