using BaseOUDAL; using Confluent.Kafka; using DocumentFormat.OpenXml.Drawing.Charts; using DocumentFormat.OpenXml.Office2010.Excel; using Microsoft.EntityFrameworkCore; using MoreLinq; using Newtonsoft.Json; using NPOI.SS.Formula.Functions; using Qdp.Pricing.Library.Base.Utilities; using System.Linq; using YLErp.Commons; using YLErp.DataBase; using YLErp.DBModels; using YLErp.DBModels.Enums; using YLErp.Helpers; using YLErp.Model; using YLErp.Model.Enum; using YLErp.Models; using YLErp.Modules; using YLErp.Modules.DataProviderModule; using YLErp.Modules.EodModule; using YLErp.Modules.EodModule.QueryModule; using YLErp.Modules.UnderlyingModule; namespace YLErp.BLL.Eod { /// /// 计算客户实时资金 /// public class RealTimeClientBanlanceService : YLBaseService { readonly valuedate _systemDate; //系统参数 readonly DateTime _valueDate; //系统交易日 readonly Dictionary _clientBalanceDic; Dictionary _clientFundObject; string[] _currencyCodes; EodCurrencyProvider _currencyProvider; public RealTimeClientBanlanceService(OptUserInfo optUser) : base(optUser) { _systemDate = valuedateBLL.SystemDate; _valueDate = _systemDate.ValueDate; _clientBalanceDic = new Dictionary(); _clientFundObject = new Dictionary(); _currencyCodes = DbContextFactory.GetYLDbContext().currency .Where(n => n.StartDate == null || n.StartDate.Value <= _valueDate) .Select(n => n.CurrencyCode).AsEnumerable() .Select(n => n.ToUpperInvariant()).ToArray(); if (_currencyCodes == null || !_currencyCodes.Any()) { _currencyCodes = new string[] { "" }; } _currencyProvider = new EodCurrencyProvider(_valueDate, seekPreDay: true); } /// /// 获取DMA资金 /// /// public IEnumerable GetBalances() { using var clientDb = new ClientDBContext(); var dmaClients = clientDb.client.ToList(); var clientIds = dmaClients.Select(s => s.id); var valuedate = valuedateBLL.ValueDate; //获取根据系统时间 var lastBalanceDate = EodOperationBase.GetLastSettlementDate(valuedate); return GetBanlances(clientIds, lastBalanceDate.AddDays(1), calcDate: valuedateBLL.ValueDate); } public IEnumerable GetBanlances(IEnumerable clientIds, DateTime startDate, DateTime? endDate = null, DateTime? calcDate = null) { if (null == clientIds || !clientIds.Any()) { return Enumerable.Empty(); } var set = new HashSet(); Dictionary clientRatingDic; Dictionary clientDic; calcDate = calcDate.HasValue ? calcDate.Value : _valueDate; //获取客户预付金(实时计算更新) var clientSpanQuery = from t in DbContext.client_span where t.ValueDate == calcDate && clientIds.Contains(t.ClientId) && t.SpanType == ClientSpan.SpanType_RealTime && t.WorstCastClientPayable != null select new { t.ClientId, t.WorstCastClientPayable, t.DeltaMargin, t.SwapWorstCastClientPayable, t.TwoSideMargin, t.OtherSideMargin, t.MySideMargin, t.SwapUnMargin, t.PVJsons, t.VariationMargin }; var clientSpanDic = clientSpanQuery.ToArray().Where(n => set.Add(n.ClientId)).ToDictionary(n => n.ClientId); //获取客户当时的评级 using (var db2 = DbContextFactory.GetClientDbContext(OptUser)) { set.Clear(); var clientRatingQuery = from cr in db2.Client_Rating.Where(x => !x.IsDeleted && x.ProcessStatus == "已审批") where clientIds.Contains(cr.ClientId) && cr.RatingStartDate <= calcDate && cr.RatingDeadLine >= calcDate orderby cr.RatingDeadLine descending, cr.ProcessOptDate descending select new { cr.ClientId, cr.CreditRatingStr }; clientRatingDic = clientRatingQuery.ToArray().Where(n => set.Add(n.ClientId)).ToDictionary(n => n.ClientId, m => m.CreditRatingStr); clientDic = db2.client.Where(t => clientIds.Contains(t.id)).ToDictionary(n => n.id, m => m); } //获取客户存续交易的持仓名义本金 var stockEqvNotionalDict = DbContext.trade.Where(t => t.ValidState != "InValid" && t.TradeType != "收益互换" && clientIds.Contains(t.ClientId) && ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) && t.ParentTradeId == 0) .Select(O => new { O.ClientId, O.StockEqvNotional }) .ToArray().GroupBy(O => O.ClientId) .ToDictionary(K => K.Key, V => V.Sum(O => O.StockEqvNotional)); foreach (var clientId in clientIds) { var client = DataCacheProvider.GetClientDataSource().GetData(clientId); if (client == null) { continue; } clientRatingDic.TryGetValue(clientId, out var ratingStr); clientSpanDic.TryGetValue(clientId, out var clientSpan); _clientBalanceDic[clientId] = new ClientBalanceEx { ClientId = clientId, IsTradeCredit = client?.IsTradeCredit == 1, MarginOptionType = client?.MarginOptionType, CreditRating = ratingStr, //客户应付预付金(要么为负,要么为0) //最新概念:负数代表客户应缴预付金,正数代表客户应收预付金 PayableMargin = clientSpan == null || clientSpan.WorstCastClientPayable == null ? 0 : clientSpan.WorstCastClientPayable.Value, DeltaMargin = clientSpan == null ? 0d : (clientSpan.DeltaMargin ?? 0d), //新增互换预付金 SwapPayableMargin = clientSpan == null || clientSpan.SwapWorstCastClientPayable == null ? 0 : clientSpan.SwapWorstCastClientPayable.Value, //互换预付金容忍金额 SwapUnMargin = clientSpan == null ? 0 : (clientSpan.SwapUnMargin ?? 0), TwoSideMargin = clientSpan == null ? 0 : (clientSpan.TwoSideMargin ?? 0), OtherSideMargin = clientSpan == null ? 0 : (clientSpan.OtherSideMargin ?? 0), MySideMargin = clientSpan == null ? 0 : (clientSpan.MySideMargin ?? 0), MaintenanceMargin = clientSpan == null ? 0 : (clientSpan.VariationMargin ?? 0), CreditCanApplySwap = client.creditCanApplySwap, TotalCreditStockEqvNotional = double.NaN, SettlementCurrency = client?.SettlementCurrency, MarginJson = clientSpan == null ? "" : clientSpan.PVJsons, ClientType = clientDic[clientId].SwapTradeType ?? 0, ClientName = clientDic[clientId].Name }; } clientIds = _clientBalanceDic.Keys; if (!clientIds.Any()) { return Enumerable.Empty(); } //获取客户昨日资金结算信息 var lastSettletDate = DbContext.ClientBalanceDaily.Where(t => t.BalanceDate < startDate && t.BalanceDate != null) .Max(t => t.BalanceDate) ?? DateTime.MinValue; if (lastSettletDate > DateTime.MinValue) { var banlanceQuery = from t in DbContext.ClientBalanceDaily where t.BalanceDate == lastSettletDate && clientIds.Contains(t.ClientId) select new { t.ClientId, t.ToDayRemainFund, t.TodayRemianFundProduct, t.PositionPremiumNetCash, t.RoundedPositionPnl, t.PayableMargin, t.MySideMargin, t.InFundSum, t.OutFundSum, t.vm_out_fund_sum, t.vm_in_fund_sum, t.OptionPremiumSwapSum, t.SwapBalance }; var banlanceDatas = banlanceQuery.ToArray(); foreach (var data in banlanceDatas) { if (_clientBalanceDic.TryGetValue(data.ClientId, out var c)) { //上日资金余额 c.AmountFund = data.ToDayRemainFund ?? 0; //昨日抵押品总价值 c.LastGuaranteesTotalAmount = data.TodayRemianFundProduct ?? 0; // 期初持仓交易净额 c.LastDayPositionPremiumNetCash = data.PositionPremiumNetCash ?? 0; c.InFundSum = data.InFundSum ?? 0; c.OutFundSum = data.OutFundSum ?? 0; c.VmInFundSum = data.vm_in_fund_sum ?? 0; c.VmOutFundSum = data.vm_out_fund_sum ?? 0; } } } //如果多币种 if (_currencyCodes != null && _currencyCodes.Count() > 1) { ProcessClientCashByMultipleCurrency(calcDate.Value, lastSettletDate); } else { //获取当日所有出入金记录 ProcessClientCash(lastSettletDate); } //获取客户所有现存(抵押状态)抵押品信息 ProcessClientCashProduct(); //处理客户交易 ProcessClientTrades(startDate); //获取当日所有执行的交易 交易日为当前交易日或者行权日为当前交易日 ProcessClientPosition(); ProcessClientSwap(lastSettletDate, calcDate.Value); ProcessClientFrozen(calcDate.Value, new List(), _clientBalanceDic); //if (PS.Config.IsGuoJun)//获取当日国君互换持仓 //{ // endDate = endDate.HasValue ? endDate.Value : startDate; // ProcessClientSwapPosition_GuoJun(endDate.Value, lastSettletDate); //} //获取客户授信 ProcessClientCredit(); //获取冻结资金信息 var clientFrozenFunds = new ClientFrozenFundsService(OptUserInfo.SystemUser).GetDatas(calcDate.Value, clientIds); //预付金比率 var marginRation = _systemDate.MarginRatio ?? 0.15; //最大可提取预付金上限比率 var marginMaxRation = marginRation + 0.02; var latestRecord = DbContext.intraday_trade_position.OrderByDescending(x => x.OptDate).FirstOrDefault(); //SetDebugSqlLog(); Dictionary dicIntradayTradePosition = DbContext.intraday_trade_position.Where(x => clientIds.Contains(x.ClientId)).AsEnumerable().GroupBy(p => p.ClientId).Select(p => new { p.Key, OptDate = p.Max(d => d.OptDate) }) .ToDictionary(p => p.Key, p => p.OptDate); Dictionary dicCashInOut = DbContext.ClientCashInCashOut.Where(x => clientIds.Contains((int)x.ClientId)).AsEnumerable().GroupBy(p => (int)p.ClientId).Select(p => new { p.Key, OptDate = p.Max(d => d.OptDate) }).ToDictionary(p => p.Key, p => p.OptDate); foreach (var item in _clientBalanceDic.Values) { #region 判断实时持仓数据是否最新(包含了最新交易操作的数据) var clientId = item.ClientId; item.IsLatestDate = true; #endregion item.ValueDate = calcDate; item.NetFund = item.InFund - item.OutFund; item.OtherFund = item.InFundOther + item.OutFundOther; item.InFundSum += item.InFund; item.OutFundSum += item.OutFund; item.VmInFundSum += item.VmInFund; item.VmOutFundSum += item.VmOutFund; //上日资金余额 item.LastDayRemainFund = item.AmountFund; item.LastDayRemainFundWithProduct = item.LastDayRemainFund + item.LastGuaranteesTotalAmount; //当前账号资金 item.AmountFund = item.AmountFund + item.NetFund + item.OtherFund + item.VmInFund-item.VmOutFund+item.WinLoss; item.WinLoss += item.WinLoss2; // item.CashInCashOutProductChange = item.GuaranteesTotalAmount - item.LastGuaranteesTotalAmount; item.PositionNotionalPrincipal += stockEqvNotionalDict.ContainsKey(item.ClientId) ? stockEqvNotionalDict[item.ClientId] : 0; //可用名义本金规模 item.AvailableStockEqvNotional = item.TotalCreditStockEqvNotional - item.PositionNotionalPrincipal; //预付金金额=期末结存-初始预付金金额 item.MarginBalance = item.AmountFund - item.MySideMargin; // 可用资金 = 期末结存 - 追保账户余额 - 初始保证金 item.AvailableAmount = item.MarginBalance - item.FrozenMarginMoney; // 是否追保=盯市金额小于维持保证金额 item.NeedAddMargin = item.SwapMarketAmount < item.MaintenanceMargin; // 追保金额=初始保证金金额-盯市金额 item.MarginByPayableMarginTotal = item.NeedAddMargin ? (item.MySideMargin - item.SwapMarketAmount) : 0; // 可取资金=max(期末结存+min(持仓盈亏,0)-初始保证金,0) item.DesirableFund =Math.Max( item.MarginBalance - item.FrozenMarginMoney + Math.Min(item.RoundedPositionPnl, 0),0); } return _clientBalanceDic.Values; } //获取客户授信 private void ProcessClientCredit() { var clientIds = _clientBalanceDic.Keys; var creditQuery = from t in DbContext.credit where (t.CreditDeadLine >= _valueDate || t.CreditDeadLine == null) && clientIds.Contains(t.ClientId ?? 0) && t.Credit != null && t.ProcessStatus == "已审批" && (t.CreditStartDate <= _valueDate || t.CreditStartDate == null) group t by t.ClientId into g select new { clientId = g.Key.Value, sum = g.Sum(n => n.Credit.Value), stockEqvNotional = g.Sum(O => O.StockEqvNotional) }; var datas = creditQuery.ToArray(); foreach (var data in datas) { var balance = _clientBalanceDic[data.clientId]; balance.TotalCredit = data.sum; balance.TotalCreditStockEqvNotional = data.stockEqvNotional ?? double.NaN; } } #region----处理客户交易---- //处理交易 private void ProcessClientTrades(DateTime startDate) { var clientIds = _clientBalanceDic.Keys; var tQuery = from t in DbContext.trade where t.TradeDate <= _valueDate && clientIds.Contains(t.ClientId) && t.ValidState != "InValid" && (t.TradeType != "结构化交易" || t.IsGroup == 1) && t.IsGroup != 2 && ConsTrade.TradeStatusAfterConfirmed.Contains(t.TradeStatus) select t; ProcessAllTrades(tQuery); //ProcessTodayFinishedTrades(startDate, tQuery); ProcessTodayTradesAfterConfirmed(); } //处理交易 private void ProcessAllTrades(IQueryable tQuery) { var allTradeQuery = from t in tQuery select new { t.ClientId, buy = t.BuySell == "买入" ? 1 : 0, sell = t.BuySell == "卖出" ? 1 : 0, NotionalPrincipal = t.OriginalStockEqvNotional > 0 ? t.OriginalStockEqvNotional.Value : (t.SpotPrice ?? 0) * (t.OriginalNotional ?? 0) }; var allTradeQuerySum = from t in allTradeQuery group t by t.ClientId into g select new { clientId = g.Key, //名义本金取 客户买入(交易员为卖出)的客户 SellNotionalPrincipal = g.Sum(n => n.NotionalPrincipal * n.buy), BuyNotionalPrincipal = g.Sum(n => n.NotionalPrincipal * n.sell), buyCount = g.Sum(n => n.sell), sellCount = g.Sum(n => n.buy) }; var allDatas = allTradeQuerySum.ToArray(); foreach (var data in allDatas) { var balance = _clientBalanceDic[data.clientId]; //名义本金(卖出) balance.SellNotionalPrincipal = data.SellNotionalPrincipal; //名义本金(买入) balance.BuyNotionalPrincipal = data.BuyNotionalPrincipal; //名义本金总额 balance.TotalNotionalPrincipal = data.SellNotionalPrincipal + data.BuyNotionalPrincipal; //买权交易数 balance.BuyCount = data.buyCount; //卖权交易数 balance.SellCount = data.sellCount; //交易总数 balance.TotalTradeCount = data.buyCount + data.sellCount; } } //处理当日了结交易 private void ProcessTodayFinishedTrades(DateTime startDate, IQueryable tQuery) { var tcQuery = from tc in DbContext.trade_cash where (tc.ValueDate >= startDate && tc.ValueDate <= _valueDate && tc.HappenedDate == null || tc.HappenedDate >= startDate && tc.HappenedDate <= _valueDate) && tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted && (tc.Action == ClientCashInCashOut.系统操作_平仓费 || tc.Action == ClientCashInCashOut.系统操作_行权费 || tc.Action == ClientCashInCashOut.系统操作_票息 || tc.Action == ClientCashInCashOut.系统操作_互换) && tc.Status == TradeCashStatusEnum.已执行 group tc by tc.TradeId into g select new { TradeId = g.Key, sumPercent = g.Sum(tc => tc.UnwindPercentRate ?? 0) }; var todayFinishedQuery = from t in tQuery join tc in tcQuery on t.id equals tc.TradeId select new { t.ClientId, WinLoss = t.TradeType != "远期" ? ((t.BuySell == "卖出" || t.BuySell == "多头平仓" || t.BuySell == "空头开仓" ? -1 : 1) * (t.TradePrice ?? 0) * tc.sumPercent) : (-(t.TradePrice ?? 0) * tc.sumPercent),//远期开仓费用所占比重 EndPremium = t.TradeType != "远期" ? ((t.BuySell == "卖出" || t.BuySell == "多头平仓" || t.BuySell == "空头开仓" ? -1 : 1) * (t.TradePrice ?? 0) * tc.sumPercent) : (-(t.TradePrice ?? 0) * tc.sumPercent) //远期开仓费用所占比重 }; var todayFinishedQuerySum = from t in todayFinishedQuery group t by t.ClientId into g select new { clientId = g.Key, WinLoss = g.Sum(n => n.WinLoss), EndPremium = g.Sum(n => n.EndPremium) }; var todaySumDatas = todayFinishedQuerySum.ToArray(); foreach (var data in todaySumDatas) { var balance = _clientBalanceDic[data.clientId]; //WinLoss balance.WinLoss = data.WinLoss; //WinLoss balance.EndPremiumSum = data.EndPremium; } } //处理确认状态以后的交易 private void ProcessTodayTradesAfterConfirmed() { var clientIds = _clientBalanceDic.Keys; var tQuery2 = from t in DbContext.trade where t.TradeDate == _valueDate && clientIds.Contains(t.ClientId) && t.ValidState != "InValid" && (t.TradeType != "结构化交易" || t.IsGroup == 1) && t.IsGroup != 2 && ConsTrade.TradeStatusAfterConfirmed.Contains(t.TradeStatus) group t by t.ClientId into g select new { clientId = g.Key, todayTradeCount = g.Count(), todayNotionalPrincipal = g.Sum(a => a.OriginalStockEqvNotional > 0 ? (a.OriginalStockEqvNotional ?? 0) : ((a.SpotPrice ?? 0) * (a.OriginalNotional ?? 0))) }; var todayTradesAfterConfirmed = tQuery2.ToArray(); foreach (var data in todayTradesAfterConfirmed) { var balance = _clientBalanceDic[data.clientId]; //交易笔数 balance.TradeCount = data.todayTradeCount; //当日交易名义本金 balance.TodayNotionalPrincipal = data.todayNotionalPrincipal; } } #endregion private void ProcessClientCashByMultipleCurrency(DateTime endDate, DateTime lastSettleDay) { var clientIds = _clientBalanceDic.Keys; List clientBalanceDailyList = null; List entryexitsList = null; List entryexits_swapList = null; if (clientIds != null && clientIds.Count > 0) { clientBalanceDailyList = DbContext.ClientBalanceDaily.Where(p => clientIds.Contains(p.ClientId) && p.BalanceDate == lastSettleDay).AsNoTracking().ToList(); var lastSettleDayAddOneDay = lastSettleDay.AddDays(1); var endDateAddOneDay = endDate.AddDays(1); entryexitsList = (from cash in DbContext.ClientCashInCashOut.Where(t => clientIds.Contains((int)t.ClientId) && t.ValidState != "InValid" && (t.HappenDate >= lastSettleDayAddOneDay && t.HappenDate < endDateAddOneDay && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认))) //entryexitsList = (from cash in DbContext.ClientCashInCashOut.Where(t => clientIds.Contains((int)t.ClientId) && t.ValidState != "InValid" && (t.HappenDate >= lastSettleDayAddOneDay && t.HappenDate < endDateAddOneDay && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认|| (t.Direction == "出金" && ClientCashInCashOut.outCashCals.Contains(t.State))))) join td in DbContext.trade on cash.TradeId equals td.id into trade from td in trade.DefaultIfEmpty() where td.TradeType != "收益互换" select cash).AsNoTracking().ToList(); entryexits_swapList = (from cash in DbContext.ClientCashInCashOut.Where(t => clientIds.Contains((int)t.ClientId) && t.ValidState != "InValid" && (t.HappenDate >= lastSettleDayAddOneDay && t.HappenDate < endDateAddOneDay && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认))) join trade in DbContext.trade.Where(x => x.TradeType == "收益互换") on cash.TradeId equals trade.id select cash).AsNoTracking().ToList(); } if (clientBalanceDailyList == null) { clientBalanceDailyList = new List(); } if (entryexitsList == null) { entryexitsList = new List(); } if (entryexits_swapList == null) { entryexits_swapList = new List(); } foreach (var id in clientIds) { var client = DataCacheProvider.GetClientDataSource().GetData(id); var fundObject = GetClientFundObject(client, endDate, lastSettleDayArg: lastSettleDay, clientBalanceDailyList: clientBalanceDailyList, entryexitsList: entryexitsList, entryexits_swapList: entryexits_swapList); if (fundObject != null) { _clientFundObject.Add(id, fundObject); } } } /// /// 多币种获得fundjson /// public FundObject GetClientFundObject(Client client, DateTime endDate, bool OuterCall = false, DateTime? lastSettleDayArg = null, List clientBalanceDailyList = null, List entryexitsList = null, List entryexits_swapList = null) { if (_currencyCodes == null || _currencyCodes.Count() <= 1) { //非多币种 return null; } FundObject fundObject = new FundObject(); var CurrChangeAmount = 0.0; DateTime lastSettleDay; if (lastSettleDayArg != null) { lastSettleDay = (DateTime)lastSettleDayArg; } else { lastSettleDay = EodOperationBase.GetLastSettlementDate(valuedateBLL.ValueDate); } var balance = new ClientBalanceEx(); if (!OuterCall) { balance = _clientBalanceDic[client.id]; } 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); } ClientBalanceDaily clientbalancedailyPre = null; if (clientBalanceDailyList != null) { clientbalancedailyPre = clientBalanceDailyList.FirstOrDefault(t => t.ClientId == client.id && t.BalanceDate == lastSettleDay); } else { clientbalancedailyPre = DbContext.ClientBalanceDaily.Where(t => t.BalanceDate == lastSettleDay).FirstOrDefault(t => t.ClientId == client.id); } if (clientbalancedailyPre != null) { if (!string.IsNullOrEmpty(clientbalancedailyPre.FundJson)) { clientbalancedailyPre.FundObject = JsonConvert.DeserializeObject(clientbalancedailyPre.FundJson); if (clientbalancedailyPre.FundObject.InFund.Count() == 1 && clientbalancedailyPre.FundObject.InFund.ContainsKey(string.Empty) && _currencyCodes.FirstOrDefault() != string.Empty) { if (!string.IsNullOrEmpty(client.SettlementCurrency)) { clientbalancedailyPre.FundObject.InFund.Add(client.SettlementCurrency, clientbalancedailyPre.InFund ?? 0); clientbalancedailyPre.FundObject.InFundSum.Add(client.SettlementCurrency, clientbalancedailyPre.InFundSum ?? 0); clientbalancedailyPre.FundObject.OutFund.Add(client.SettlementCurrency, clientbalancedailyPre.OutFund ?? 0); clientbalancedailyPre.FundObject.OutFundSum.Add(client.SettlementCurrency, clientbalancedailyPre.OutFundSum ?? 0); clientbalancedailyPre.FundObject.NetFund.Add(client.SettlementCurrency, clientbalancedailyPre.NetFund ?? 0); clientbalancedailyPre.FundObject.NetFundSum.Add(client.SettlementCurrency, clientbalancedailyPre.NetFundSum ?? 0); clientbalancedailyPre.FundObject.OtherFund.Add(client.SettlementCurrency, clientbalancedailyPre.OtherFund ?? 0); clientbalancedailyPre.FundObject.OtherFundSum.Add(client.SettlementCurrency, clientbalancedailyPre.OtherFundSum ?? 0); clientbalancedailyPre.FundObject.TodayRemainFund.Add(client.SettlementCurrency, clientbalancedailyPre.ToDayRemainFund ?? 0); } } foreach (var currencyCode in _currencyCodes) { if (clientbalancedailyPre.FundObject.TodayRemainFund.ContainsKey(currencyCode)) { fundObject.LastDayRemainFund[currencyCode] = clientbalancedailyPre.FundObject.TodayRemainFund[currencyCode]; } } } else { clientbalancedailyPre.FundObject = new FundObject(); if (_currencyCodes.Count() == 1 && _currencyCodes.FirstOrDefault() == string.Empty) { clientbalancedailyPre.FundObject.InFund.Add(string.Empty, clientbalancedailyPre.InFund ?? 0); clientbalancedailyPre.FundObject.InFundSum.Add(string.Empty, clientbalancedailyPre.InFundSum ?? 0); clientbalancedailyPre.FundObject.OutFund.Add(string.Empty, clientbalancedailyPre.OutFund ?? 0); clientbalancedailyPre.FundObject.OutFundSum.Add(string.Empty, clientbalancedailyPre.OutFundSum ?? 0); clientbalancedailyPre.FundObject.NetFund.Add(string.Empty, clientbalancedailyPre.NetFund ?? 0); clientbalancedailyPre.FundObject.NetFundSum.Add(string.Empty, clientbalancedailyPre.NetFundSum ?? 0); clientbalancedailyPre.FundObject.OtherFund.Add(string.Empty, clientbalancedailyPre.OtherFund ?? 0); clientbalancedailyPre.FundObject.OtherFundSum.Add(string.Empty, clientbalancedailyPre.OtherFundSum ?? 0); fundObject.LastDayRemainFund[string.Empty] = clientbalancedailyPre.ToDayRemainFund ?? 0; } else { clientbalancedailyPre.FundObject.InFund.Add(client.SettlementCurrency, clientbalancedailyPre.InFund ?? 0); clientbalancedailyPre.FundObject.InFundSum.Add(client.SettlementCurrency, clientbalancedailyPre.InFundSum ?? 0); clientbalancedailyPre.FundObject.OutFund.Add(client.SettlementCurrency, clientbalancedailyPre.OutFund ?? 0); clientbalancedailyPre.FundObject.OutFundSum.Add(client.SettlementCurrency, clientbalancedailyPre.OutFundSum ?? 0); clientbalancedailyPre.FundObject.NetFund.Add(client.SettlementCurrency, clientbalancedailyPre.NetFund ?? 0); clientbalancedailyPre.FundObject.NetFundSum.Add(client.SettlementCurrency, clientbalancedailyPre.NetFundSum ?? 0); clientbalancedailyPre.FundObject.OtherFund.Add(client.SettlementCurrency, clientbalancedailyPre.OtherFund ?? 0); clientbalancedailyPre.FundObject.OtherFundSum.Add(client.SettlementCurrency, clientbalancedailyPre.OtherFundSum ?? 0); fundObject.LastDayRemainFund[client.SettlementCurrency] = clientbalancedailyPre.ToDayRemainFund ?? 0; } } var LastDayPositionPremiumNetCash = clientbalancedailyPre.PositionPremiumNetCash ?? 0.0; var LastGuaranteesTotalAmount = clientbalancedailyPre.TodayRemianFundProduct ?? 0.0; } var endDateAddOneDay = endDate.AddDays(1); var lastSettleDayAddOneDay = lastSettleDay.AddDays(1); List clientEntryexits; if (entryexitsList != null) { clientEntryexits = entryexitsList.Where(p => p.ClientId == client.id).ToList(); } else { //var entryexits = from cash in DbContext.ClientCashInCashOut.Where(t => t.ClientId == client.id && t.ValidState != "InValid" && (t.HappenDate >= lastSettleDayAddOneDay && t.HappenDate < endDateAddOneDay && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认 || (t.Direction == "出金" && ClientCashInCashOut.outCashCals.Contains(t.State))))) var entryexits = from cash in DbContext.ClientCashInCashOut.Where(t => t.ClientId == client.id && t.ValidState != "InValid" && (t.HappenDate >= lastSettleDayAddOneDay && t.HappenDate < endDateAddOneDay && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认))) join td in DbContext.trade on cash.TradeId equals td.id into trade from td in trade.DefaultIfEmpty() where td.TradeType != "收益互换" select cash; clientEntryexits = entryexits.ToList(); } if (clientEntryexits != null) { clientEntryexits.ForEach(clientEntryexit => { var currencyCode = clientEntryexit?.CurrencyCode ?? "CNY"; 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(currencyCode)) { fundObject.InFund[currencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.InFund.Add(currencyCode, clientEntryexit.Money ?? 0); } if (fundObject.NetFund.ContainsKey(currencyCode)) { fundObject.NetFund[currencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.NetFund.Add(currencyCode, clientEntryexit.Money ?? 0); } } else if (clientEntryexit.Direction.Equals("出金")) { if (fundObject.OutFund.ContainsKey(currencyCode)) { fundObject.OutFund[currencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.OutFund.Add(currencyCode, clientEntryexit.Money ?? 0); } if (fundObject.NetFund.ContainsKey(currencyCode)) { fundObject.NetFund[currencyCode] -= clientEntryexit.Money ?? 0; } else { fundObject.NetFund.Add(currencyCode, -clientEntryexit.Money ?? 0); } } else if (clientEntryexit.Direction.Equals("其他收入")) { if (fundObject.InFundOther.ContainsKey(currencyCode)) { fundObject.InFundOther[currencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.InFundOther.Add(currencyCode, clientEntryexit.Money ?? 0); } if (fundObject.OtherFund.ContainsKey(currencyCode)) { fundObject.OtherFund[currencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.OtherFund.Add(currencyCode, clientEntryexit.Money ?? 0); } } else if (clientEntryexit.Direction.Equals("其他支出")) { if (fundObject.OutFundOther.ContainsKey(currencyCode)) { fundObject.OutFundOther[currencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.OutFundOther.Add(currencyCode, clientEntryexit.Money ?? 0); } if (fundObject.OtherFund.ContainsKey(currencyCode)) { fundObject.OtherFund[currencyCode] += clientEntryexit.Money ?? 0; } else { fundObject.OtherFund.Add(currencyCode, clientEntryexit.Money ?? 0); } } else { if (ClientCashInCashOut.系统操作_期权费.Equals(clientEntryexit.Action)) { //CurrPnl += clientEntryexit.Money ?? 0.0; CurrChangeAmount += clientEntryexit.Money ?? 0.0; balance.OptionPremium += clientEntryexit.Money ?? 0.0; } else if (ClientCashInCashOut.系统操作_行权费.Equals(clientEntryexit.Action) || ClientCashInCashOut.系统操作_平仓费.Equals(clientEntryexit.Action)) { //CurrPnl += clientEntryexit.Money ?? 0.0; CurrChangeAmount += clientEntryexit.Money ?? 0.0; balance.SettlementBalance += clientEntryexit.Money ?? 0.0; if (ClientCashInCashOut.系统操作_平仓费.Equals(clientEntryexit.Action)) { balance.UnwindBalance += clientEntryexit.Money ?? 0.0; } else { balance.ExerciseBalance += clientEntryexit.Money ?? 0.0; } } else if (ClientCashInCashOut.系统操作_票息.Equals(clientEntryexit.Action)) { CurrChangeAmount += clientEntryexit.Money ?? 0.0; balance.Coupon += clientEntryexit.Money ?? 0.0; } } }); } List clientEntryexitsSwap; if (entryexits_swapList != null) { clientEntryexitsSwap = entryexits_swapList.Where(p => p.ClientId == client.id).ToList(); } else { var entryexits_swap = from cash in DbContext.ClientCashInCashOut.Where(t => t.ClientId == client.id && t.ValidState != "InValid" && (t.HappenDate >= lastSettleDayAddOneDay && t.HappenDate < endDateAddOneDay && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认))) join trade in DbContext.trade.Where(x => x.TradeType == "收益互换") on cash.TradeId equals trade.id select cash; clientEntryexitsSwap = entryexits_swap.ToList(); } if (clientEntryexitsSwap != null) { clientEntryexitsSwap.ForEach(clientEntryexit => { if (ClientCashInCashOut.系统操作_期权费.Equals(clientEntryexit.Action) || ClientCashInCashOut.系统操作_应付预付金.Equals(clientEntryexit.Action) || ClientCashInCashOut.系统操作_预付金返息.Equals(clientEntryexit.Action)) { //CurrPnl += clientEntryexit.Money ?? 0.0; CurrChangeAmount += clientEntryexit.Money ?? 0.0; balance.OptionPremiumSwap += clientEntryexit.Money ?? 0.0; } else if (ClientCashInCashOut.系统操作_平仓费.Equals(clientEntryexit.Action)) { //CurrPnl += clientEntryexit.Money ?? 0.0; CurrChangeAmount += clientEntryexit.Money ?? 0.0; balance.SwapBalance += clientEntryexit.Money ?? 0.0; } else if (ClientCashInCashOut.系统操作_互换.Equals(clientEntryexit.Action)) { CurrChangeAmount += clientEntryexit.Money ?? 0.0; balance.SwapBalance += clientEntryexit.Money ?? 0.0; } }); } foreach (var item in _currencyCodes) { if (fundObject.TodayRemainFund.ContainsKey(item)) { fundObject.TodayRemainFund[item] = (fundObject.LastDayRemainFund.ContainsKey(item) ? fundObject.LastDayRemainFund[item] : 0) + (fundObject.NetFund.ContainsKey(item) ? fundObject.NetFund[item] : 0) + (fundObject.OtherFund.ContainsKey(item) ? fundObject.OtherFund[item] : 0); } else { fundObject.TodayRemainFund.Add(item, (fundObject.LastDayRemainFund.ContainsKey(item) ? fundObject.LastDayRemainFund[item] : 0) + (fundObject.NetFund.ContainsKey(item) ? fundObject.NetFund[item] : 0) + (fundObject.OtherFund.ContainsKey(item) ? fundObject.OtherFund[item] : 0)); } if (client.SettlementCurrency == 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) { throw new ServiceException($"[{client.Name}]找不到汇率:{_valueDate:yyyy/MM/dd}及前一日{item}{client.SettlementCurrency}"); } } //CurrAvailAmount //LastDayAvailAmount += fundObject.LastDayRemainFund[item] * rateObj.Rate; balance.LastDayRemainFund += fundObject.LastDayRemainFund[item] * rateObj.Rate; balance.InFund += fundObject.InFund[item] * rateObj.Rate; balance.OutFund += fundObject.OutFund[item] * rateObj.Rate; balance.NetFund += fundObject.NetFund[item] * rateObj.Rate; balance.InFundOther += fundObject.InFundOther[item] * rateObj.Rate; balance.OutFundOther += fundObject.OutFundOther[item] * rateObj.Rate; balance.OtherFund += fundObject.OtherFund[item] * rateObj.Rate; } return fundObject; } //抵押品资金价值 private void ProcessClientCashProduct() { var newValuedate = _valueDate.AddDays(1); var clientIds = _clientBalanceDic.Keys; //获取客户所有现存(抵押状态)抵押品信息 var clientProductQuery = from t in DbContext.clientcashincashout_product join u in DbContext.underlying_manager on t.UnderlyingId equals u.id where t.HappenDate < newValuedate && ((t.Status == Clientcashincashout_productStatusEnum.抵押.ToString() && t.OptStatus == ClientCashInCashOut.已确认 || t.Status == Clientcashincashout_productStatusEnum.赎回.ToString() && t.OptStatus != ClientCashInCashOut.已确认) || t.Status == Clientcashincashout_productStatusEnum.赎回.ToString() && t.OptStatus == ClientCashInCashOut.已确认 && t.BackDate >= newValuedate) && clientIds.Contains(t.ClientId) && t.ProductAmount != null && t.Rate != null && u.Price != null select new { ClientId = t.ClientId, ProductAmount = t.ProductAmount.Value, Rate = t.Rate.Value, Price = u.Price.Value }; var clientProductQuerySum = from t in clientProductQuery group t by t.ClientId into g select new { ClientId = g.Key, sum = g.Sum(n => n.ProductAmount * n.Rate * n.Price) }; var datas = clientProductQuerySum.ToArray(); foreach (var data in datas) { var balance = _clientBalanceDic[data.ClientId]; //抵押品资金价值 balance.GuaranteesTotalAmount = data.sum; } } //获取实时持仓 private void ProcessClientPosition() { var clientIds = _clientBalanceDic.Keys; //当日交易 var todayTradeQuery = from t in DbContext.trade where t.TradeDate == _valueDate && clientIds.Contains(t.ClientId) && t.ValidState != "InValid" && t.TradeType != "场内期权" && t.TradeType != "收益互换" && ConsTrade.NeedMarginTradeStatusList.Contains(t.TradeStatus) //最后结算日到当日成交的交易 && GlobalDicionary.SupportTradeTypes.Contains(t.TradeType) select new { id = (int?)t.id }; //持仓信息 var todayPositionQuery = from p in DbContext.intraday_trade_position join trade in DbContext.trade on p.TradeId equals trade.id join tt in todayTradeQuery on p.TradeId equals tt.id into tmp from t in tmp.DefaultIfEmpty() where p.ValueDate == _valueDate && clientIds.Contains(p.ClientId) && ConsTrade.NeedMarginTradeStatusList.Contains(p.TradeStatus) select new { isNewTrade = t == null ? 0 : 1, TradeId = trade.id, p.UnderlyingCode, p.ClientId, p.OptDate, p.Notional, p.ActualPv, p.Pv, p.RoundedPv, p.DailyPnL, p.PositionPnL, p.RoundedPositionPnl, p.Margin, p.Cost, //EodOperationBase.GetSign buysell = p.BuySell == "买入" ? 1 : p.BuySell == "卖出" || p.BuySell == "融券卖出" || p.BuySell == "多头平仓" || p.BuySell == "空头开仓" ? -1 : 0, trade.ParentTradeId, trade.TradeType }; var todayPostionList = todayPositionQuery.ToArray(); using var bondDb = new BondOmsDBContext(); var clientLongIds = new List(); clientIds.ForEach(item => { clientLongIds.Add(Convert.ToInt64(item)); }); var swapPositionQuery = bondDb.client_position.Where(x => clientLongIds.Contains(x.client_id ?? 0)).ToList().GroupBy(g => g.client_id); //持仓合计 var pQuerySum = from t in todayPostionList group t by t.ClientId into g select new { clientId = g.Key, //持仓笔数 PositionCount = g.Count(), UpdateDate = g.Max(n => n.OptDate), ActualPvSum = g.Where(x => x.TradeType != "结构化交易" && (x.TradeType != "收益互换" || (x.TradeType == "收益互换" && x.ParentTradeId == 0))).Sum(n => n.ActualPv), PvSum = g.Where(x => x.TradeType != "结构化交易" && (x.TradeType != "收益互换" || (x.TradeType == "收益互换" && x.ParentTradeId == 0))).Sum(n => n.Pv), SellPvSum = g.Where(x => x.buysell == 1 && x.TradeType != "结构化交易" && (x.TradeType != "收益互换" || (x.TradeType == "收益互换" && x.ParentTradeId == 0))).Sum(n => n.Pv), RoundedPositionPv = g.Where(x => x.TradeType != "结构化交易" && (x.TradeType != "收益互换" || (x.TradeType == "收益互换" && x.ParentTradeId == 0))).Sum(n => n.RoundedPv), DaliyPnl = g.Where(x => x.TradeType != "结构化交易" && (x.TradeType != "收益互换" || (x.TradeType == "收益互换" && x.ParentTradeId == 0))).Sum(n => n.DailyPnL), PositionPnl = g.Where(x => x.TradeType != "结构化交易").Sum(n => n.PositionPnL), //空头浮动盈利,下面会重新处理 ClientSellPositionPnl = 0, RoundedPositionPnl = g.Where(x => x.TradeType != "结构化交易").Sum(n => n.RoundedPositionPnl), //持仓期权费净额 PositionPremiumNetCash = g.Where(x => x.TradeType != "结构化交易" && (x.TradeType != "收益互换" || (x.TradeType == "收益互换" && x.ParentTradeId == 0))).Sum(n => n.Cost * (n.buysell < 0 ? 1 : -1)), SellTradePrice = g.Where(x => x.TradeType != "结构化交易" && (x.TradeType != "收益互换" || (x.TradeType == "收益互换" && x.ParentTradeId == 0))).Where(x => x.buysell == 1).Sum(n => n.Cost), //当日新增预付金 TodayNewMargin = g.Where(x => x.ParentTradeId == 0).Sum(n => n.Margin * (n.buysell == 1 ? -1 : 0)), }; var sumDatas = pQuerySum.ToArray(); var potential = valuedate.PotentialSurplusCalcMode_ActualPv.Equals(_systemDate.PotentialSurplusCalcMode); #region 期权空头浮动盈利=∑max(期权空头持仓*(期权合约成本价-期权合约现价), 0) 从客户角度看的, 结构化交易需要将两条腿的空头Pnl合计 var parentIds = todayPostionList.Where(x => x.TradeType == "结构化交易").Select(x => x.TradeId).ToList(); var parentPositionPnlList = from t in todayPostionList.Where(x => parentIds.Contains(x.ParentTradeId)) group t by new { t.ClientId, t.ParentTradeId } into g select new { clientId = g.Key.ClientId, parentTradeId = g.Key.ParentTradeId, ClientSellPositionPnl = Math.Max(g.Sum(n => n.buysell == 1 ? -n.PositionPnL : 0), 0) }; var parentPositionPnlTotal = from t in parentPositionPnlList group t by t.clientId into g select new { clientId = g.Key, ClientSellPositionPnl = g.Sum(x => x.ClientSellPositionPnl) }; var singlePositionPnlTotal = from t in todayPostionList where t.TradeType != "结构化交易" && t.ParentTradeId == 0 group t by t.ClientId into g select new { clientId = g.Key, ClientSellPositionPnl = g.Sum(n => n.buysell == 1 ? Math.Max(-n.PositionPnL, 0) : 0), }; #endregion foreach (var data in sumDatas) { var balance = _clientBalanceDic[data.clientId]; balance.PositionCount = data.PositionCount; balance.UpdateDate = data.UpdateDate; //潜在行权收益等于实值额 balance.PotentialSurpluses = potential ? -data.ActualPvSum : -Convert.ToDouble(data.PvSum); //持仓市值 balance.PositionPv = Convert.ToDouble(data.PvSum); balance.RoundedPositionPv = Convert.ToDouble(data.RoundedPositionPv); balance.SellPv = Convert.ToDouble(data.SellPvSum); //当日盈亏 balance.DaliyPnl = Convert.ToDouble(data.DaliyPnl); balance.RoundedDaliyPnl = Convert.ToDouble(data.DaliyPnl); //持仓盈亏 balance.PositionPnl = data.PositionPnl; balance.RoundedPositionPnl = data.RoundedPositionPnl; //期权空头浮动盈利=∑max(期权空头持仓*(期权合约成本价-期权合约现价), 0) 从客户角度看的 balance.ClientSellPositionPnl = data.ClientSellPositionPnl; //持仓期权费净额 balance.PositionPremiumNetCash = data.PositionPremiumNetCash; balance.SellTradePrice = data.SellTradePrice; //新交易权利金, 新交易预付金,新交易初保,额外追保,总追保金额 //当日新增预付金 balance.TodayNewMargin = data.TodayNewMargin; balance.ClientSellPositionPnl = (parentPositionPnlTotal.FirstOrDefault(x => x.clientId == data.clientId)?.ClientSellPositionPnl ?? 0) + (singlePositionPnlTotal.FirstOrDefault(x => x.clientId == data.clientId)?.ClientSellPositionPnl ?? 0); } foreach (var data in swapPositionQuery) { var clientId = Convert.ToInt32(data.Key); var balance = _clientBalanceDic[clientId]; var pvSum = data.Sum(s => s.swap_market_value ?? 0); var pnlSum = data.Sum(s => s.position_profit_loss ?? 0); var posiSum = data.Sum(s => s.position_notional_principal ?? 0); balance.PotentialSurpluses += Convert.ToDouble(pvSum); balance.PositionPv += Convert.ToDouble(pvSum); balance.RoundedPositionPv += Convert.ToDouble(pvSum); balance.PositionNotionalPrincipal += Convert.ToDouble(posiSum); } } //获取当日所有出入金记录 private void ProcessClientCash(DateTime lastSettletDate) { var clientIds = _clientBalanceDic.Keys; var newValuedate = _valueDate.AddDays(1); var lastSettletDateAddOne = lastSettletDate.AddDays(1); var ClientCashQuery = from t in DbContext.ClientCashInCashOut join trade in DbContext.trade on t.TradeId equals trade.id into trade from td in trade.DefaultIfEmpty() where t.HappenDate >= lastSettletDateAddOne && t.HappenDate < newValuedate && t.ValidState != "InValid" && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认 || ((t.Direction == "出金"||t.Direction=="划转") && ClientCashInCashOut.outCashCals.Contains(t.State))) && clientIds.Contains(t.ClientId.Value) && t.Money != null && td.TradeType != "收益互换" select new { ClientId = t.ClientId.Value, TradeId = t.TradeId ?? 0, action = t.Direction + "^" + (t.Direction == "应收" ? t.Action : ""), money = t.Money.Value, cash_type = t.cash_type, TransferOutAccount=t.TransferOutAccount, TransferInAccount = t.TransferInAccount }; //获取包含的所有收益互换id列表 var allTradeIds = ClientCashQuery.Select(x => x.TradeId).ToList(); var ClientCashQuerySum = from t in ClientCashQuery group t by new { t.ClientId, t.action, t.cash_type,t.TransferOutAccount,t.TransferInAccount } into g select new { g.Key.ClientId, g.Key.action, g.Key.cash_type, g.Key.TransferOutAccount, g.Key.TransferInAccount, moneySum = g.Sum(n => n.money), swapMoneySun = 0.0 }; var datas = ClientCashQuerySum.ToArray(); foreach (var data in datas) { //资金净流入 + 权利金收支 + 期权费收支 var balance = _clientBalanceDic[data.ClientId]; if (data.action == "应收^" + ClientCashInCashOut.系统操作_期权费) { balance.OptionPremium = data.moneySum; } else if (data.action == "应收^" + ClientCashInCashOut.系统操作_票息) { balance.Coupon = data.moneySum; } else if (data.action == "应收^" + ClientCashInCashOut.系统操作_行权费 || data.action == "应收^" + ClientCashInCashOut.系统操作_平仓费) { balance.SettlementBalance += data.moneySum; if (data.action == "应收^" + ClientCashInCashOut.系统操作_行权费) { balance.ExerciseBalance = data.moneySum; } else { balance.UnwindBalance = data.moneySum; } } if (data.action == "入金^") { if (data.cash_type == CashTypeEnum.初保账户.ToString()) { balance.InFund = data.moneySum; } else { balance.VmInFund = data.moneySum; } } else if (data.action == "出金^") { if (data.cash_type == CashTypeEnum.初保账户.ToString()) { balance.OutFund = data.moneySum; } else { balance.VmOutFund = data.moneySum; } } else if (data.action == "其他收入^") { balance.InFundOther = data.moneySum; } else if (data.action == "其他支出^") { balance.OutFundOther = data.moneySum; } else if (data.action == "划转^") { if (data.TransferOutAccount == "初保账户") { balance.InFund -= data.moneySum; if (data.TransferInAccount == "追保账户") { balance.VmInFund += data.moneySum; } else { balance.WinLoss += data.moneySum; } } else if (data.TransferOutAccount == "追保账户") { balance.VmInFund -= data.moneySum; if (data.TransferInAccount == "初保账户") { balance.InFund += data.moneySum; } else { balance.WinLoss += data.moneySum; } } else { balance.WinLoss -= data.moneySum; if (data.TransferInAccount == "初保账户") { balance.InFund += data.moneySum; } else { balance.VmInFund += data.moneySum; } } } } var ClientCashQuerySwap = from t in DbContext.ClientCashInCashOut join trade in DbContext.trade.Where(x => x.TradeType == "收益互换") on t.TradeId equals trade.id where t.HappenDate >= lastSettletDateAddOne && t.HappenDate < newValuedate && t.ValidState != "InValid" && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认) && clientIds.Contains(t.ClientId.Value) && t.Money != null select new { ClientId = t.ClientId.Value, action = t.Direction + "^" + (t.Direction == "应收" ? t.Action : ""), money = t.Money.Value }; var ClientCashQuerySwapSum = from t in ClientCashQuerySwap group t by new { t.ClientId, t.action } into g select new { g.Key.ClientId, g.Key.action, moneySum = g.Sum(n => n.money) }; var dataSwaps = ClientCashQuerySwapSum.ToArray(); foreach (var data in dataSwaps) { //资金净流入 + 权利金收支 + 期权费收支 var balance = _clientBalanceDic[data.ClientId]; if (data.action == "应收^" + ClientCashInCashOut.系统操作_期权费 || data.action == "应收^" + ClientCashInCashOut.系统操作_应付预付金 || data.action == "应收^" + ClientCashInCashOut.系统操作_预付金返息) { balance.OptionPremiumSwap = data.moneySum; } else if (data.action == "应收^" + ClientCashInCashOut.系统操作_互换) { balance.SwapBalance += data.moneySum; } else if (data.action == "应收^" + ClientCashInCashOut.系统操作_平仓费) { balance.SwapBalance += data.moneySum; balance.WinLoss += data.moneySum; } else if (data.action == "入金^") { balance.InFund += data.moneySum; } else if (data.action == "出金^") { balance.OutFund += data.moneySum; } } } //计算实现盈亏 private void ProcessClientCash2(DateTime startDate) { var clientIds = _clientBalanceDic.Keys; var newValuedate = _valueDate.AddDays(1); var tradeCashQuerySum = from tc in DbContext.trade_cash join td in DbContext.trade on tc.TradeId equals td.id where (tc.HappenedDate >= startDate && tc.HappenedDate < newValuedate || tc.ValueDate >= startDate && tc.ValueDate < newValuedate && tc.HappenedDate == null) && td.TradeType != "收益互换" && clientIds.Contains(td.ClientId) && td.IsGroup != 1 && tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted && (tc.Action == ClientCashInCashOut.系统操作_行权费 || tc.Action == ClientCashInCashOut.系统操作_平仓费 || tc.Action == ClientCashInCashOut.系统操作_票息 || tc.Action == ClientCashInCashOut.系统操作_互换 || tc.Action == ClientCashInCashOut.人工操作_其他) && tc.Amount != 0 group tc by td.ClientId into g select new { ClientId = g.Key, amountSum = g.Sum(n => n.Amount) }; var datas = tradeCashQuerySum.ToArray(); foreach (var data in datas) { var balance = _clientBalanceDic[data.ClientId]; balance.WinLoss2 = -data.amountSum; } } /// /// 新版互换 /// /// private void ProcessClientSwap(DateTime lastSettletDate, DateTime startDate) { var clientIds = _clientBalanceDic.Keys; var trades = DbContext.trade.Where(t => (ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus)) && clientIds.Contains(t.ClientId) && t.ValidState != "InValid" && t.TradeType == "收益互换" &&t.StartDate<= startDate).Select(s => new {s.id,s.ClientId }).ToList(); var tradeIds = trades.Select(s=>s.id).ToList(); var positions = DbContext.swap_position.Where(s => tradeIds.Contains(s.SwapTradeId) && !s.IsInitial && !s.Invalid).ToList(); var queryGroup = trades.GroupBy(t => t.ClientId); foreach (var item in queryGroup) { var balance = _clientBalanceDic[item.Key]; var clientTradeIds = item.Select(s => s.id).ToList(); var clientPositions = positions.Where(x => clientTradeIds.Contains(x.SwapTradeId)).ToList(); var marginList = clientPositions.Where(x =>ConsTrade.InterestMarginModels.Contains(x.InterestMode)).Sum(s=>s.InterestPrincipalFix * (s.InterestDirection == 1 ? -1 : 1)); balance.SwapMargin =Convert.ToDouble(marginList); balance.PositionCount= clientTradeIds.Count(); // balance.PositionNotionalPrincipal = Convert.ToDouble(positions.Sum(s=>s.PosiNotionalValue));//实时 } var eodSwapQuery = DbContext.eod_swap.Where(x => clientIds.Contains(x.ClientId) && x.ValueDate >= lastSettletDate && x.ValueDate <= startDate).AsEnumerable().GroupBy(x => x.SwapTradeId) .Select(g => g.OrderByDescending(x => x.ValueDate).FirstOrDefault()).ToList(); var eodTradeIds = eodSwapQuery.Select(s => s.SwapTradeId).ToList(); var eodTrades = DbContext.trade.Where(t => (ConsTrade.TradeStatusAfterConfirmed.Contains(t.TradeStatus)) && tradeIds.Contains(t.id) && t.ValidState != "InValid" && startDate >= t.StartDate); eodTradeIds = eodTrades.Select(s => s.id).ToList(); eodSwapQuery = eodSwapQuery.Where(x => eodTradeIds.Contains(x.SwapTradeId)).ToList(); var eodSwapPositions = DbContext.eod_swap_position.Where(x => eodTradeIds.Contains(x.SwapTradeId) && x.ValueDate >= lastSettletDate && x.ValueDate <= startDate && x.PosiDirection > 0); var swapFlowEvents = DbContext.swap_flow_event.Where(x => x.DataState == (int)SwapFlowDateStateEnum.完成 && x.EventType == (int)SwapFlowEventTypeEnum.平仓 && x.EventDate > lastSettletDate && tradeIds.Contains(x.SwapTradeId)).AsNoTracking().ToList(); var swapPositions = DbContext.swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && x.IsInitial && !x.Invalid && x.PosiDirection > 0).AsNoTracking().ToList(); var eodSwaps = eodSwapQuery.GroupBy(g => g.ClientId).ToList(); foreach (var data in eodSwaps) { var balance = _clientBalanceDic[data.Key]; var clientEodSwaps = data.GroupBy(g => g.SwapTradeId).ToList(); foreach (var item in clientEodSwaps) { var lastEodSwap = item.First(); var eodPosi = eodSwapPositions.FirstOrDefault(x => x.ValueDate == lastEodSwap.ValueDate && x.SwapTradeId == lastEodSwap.SwapTradeId &&x.PosiStartDate<= lastEodSwap.ValueDate); var posiQty = eodPosi?.PosiQuantity ?? 0; var flowEvents = swapFlowEvents.Where(x => x.SwapTradeId == item.Key).ToList(); var unwindQty = flowEvents.Sum(s => s.Quantity); var pnl = lastEodSwap.PostionValue; pnl=Math.Round(pnl, ConsGlobal.MoneyRound,MidpointRounding.AwayFromZero); decimal unwindPercent = 0; if (unwindQty != 0) { if (posiQty == 0 || unwindQty >= posiQty) { pnl = 0; } else { pnl = pnl - (pnl * unwindQty / posiQty); } unwindPercent = posiQty == 0 ? 0 : unwindQty / posiQty; } var tdRealizedPnL = flowEvents.Where(x => x.EventDate > lastSettletDate).ToList().Sum(s => s.InterestClosePnL + s.FloatPnlSum); var tdRealizedInterestPnL = flowEvents.Where(x => x.EventDate > lastSettletDate).ToList().Sum(s => s.InterestClosePnL); var tradeFee= flowEvents.Where(x => x.EventDate > lastSettletDate).ToList().Sum(s => s.TradingFee+s.TradingFeePending); balance.InterestPnl += Convert.ToDouble(tdRealizedInterestPnL)*-1; balance.TradeFee += Convert.ToDouble(tradeFee) *-1; var currentEvents = flowEvents.Where(x => x.EventDate == startDate).ToList(); var currentRealizedPnl = currentEvents.Sum(s => s.InterestClosePnL + s.FloatPnlSum); //潜在行权收益等于实值额 balance.PotentialSurpluses += -Convert.ToDouble(pnl); //持仓市值 balance.SellPv += -Convert.ToDouble(lastEodSwap.MarketValueShort) * -1; //当日盈亏 balance.DaliyPnl += Convert.ToDouble(currentRealizedPnl) * -1; balance.RoundedDaliyPnl += Math.Round(Convert.ToDouble(currentRealizedPnl), 2) * -1; if (lastEodSwap.ValueDate == startDate) { balance.UpdateDate = balance.UpdateDate > lastEodSwap.OptTime ? balance.UpdateDate : lastEodSwap.OptTime; } //balance.WinLoss += Convert.ToDouble(tdRealizedPnL) * -1; balance.PositionPnl += Convert.ToDouble(pnl) * -1; balance.RoundedPositionPnl += Math.Round(Convert.ToDouble(pnl), 2) * -1; //期权空头浮动盈利=∑max(期权空头持仓*(期权合约成本价-期权合约现价), 0) 从客户角度看的 balance.ClientSellPositionPnl += Convert.ToDouble(lastEodSwap.FloatingPnL) * -1; } } } /// /// 计算客户的冻结资金 /// /// public void ProcessClientFrozen(DateTime startDate, List bondFlows, Dictionary clientBalanceDic) { var clientIds = clientBalanceDic.Keys.ToList(); // step1 获取当天未簿记流水 var flows = DbContext.swap_flow.Where(x => x.OccurTime == startDate && x.DataState == (int)SwapFlowDateStateEnum.等待完成 && clientIds.Contains(x.ClientId ?? 0)).AsNoTracking().ToList(); // step2 获取客户端当天已报,意向待确认,部分成交委托订单 using var bondDb = new BondOmsDBContext(); var nextDate = startDate.AddDays(1); List calcStatus = new List() { -1, 0, 1 }; var clientOrder = bondDb.client_order.Where(x => x.create_time > startDate && x.create_time < nextDate && calcStatus.Contains(x.status ?? 0) && clientIds.Contains(x.client_id ?? 0)).AsNoTracking().ToList(); // 将clientOrder转换为swap_flow,方便合并 clientOrder.ForEach(item => { swap_flow flow = new swap_flow() { BsType = (item.side ?? 0) + 1, UnderlyingCode = item.security_id, ClientId = item.client_id ?? 0, ClientName = item.client_name, TradingQty = (item.order_qty ?? 0) - (item.last_shares ?? 0), TradingAmountAvg = Math.Round(BondPriceConverter.ToStorage(item.full_price ?? 0), ConsGlobal.PriceRound, MidpointRounding.AwayFromZero), TradingAmountFeeAvg = BondPriceConverter.ToStorage(item.full_price ?? 0), TradingFee = 0 }; // clientOrder中数量单位为万 flow.TradingQty *= 10000; bondFlows.Add(flow); }); flows.AddRange(bondFlows); // step3 从swap_position获取已簿记原持仓数据 var positionQuery = from t in DbContext.trade.Where(x => clientIds.Contains(x.ClientId) && x.TradeType == "收益互换" && x.ValidState != ConsGlobal.InValid && ConsTrade.PositionTradeStatusList.Contains(x.TradeStatus)) join p in DbContext.swap_position on t.id equals p.SwapTradeId where !p.IsInitial && !p.Invalid select new swap_flow { ClientId = t.ClientId, TradingQty = p.PosiQuantity, UnderlyingCode = p.UnderlyingCode, BsType = p.PositionType, TradingAmountAvg = p.PosiGrossPrice, TradingAmountFeeAvg = p.PosiNetPrice, TradingFee = 0 }; var positions = positionQuery.ToList(); // step4 将未簿记持仓与已有持仓分别 按客户,标的分组计算各自轧差名义本金 // 取设置的预付金比例 var clientMarginTemplates = DbContext.client_marginrate.Where(x => x.ValueDate <= startDate).OrderByDescending(o => o.ValueDate).AsNoTracking().ToList(); var flowGroup = flows.GroupBy(s => s.ClientId); foreach (var itemGroup in flowGroup) { var balance = clientBalanceDic[itemGroup.Key ?? 0]; var clientPositions = positions.Where(x => x.ClientId == itemGroup.Key).ToList(); foreach (var item in itemGroup.GroupBy(s => s.UnderlyingCode)) { var clientMarginDetail= UnderlyingHelper.GetApplicableMarginRate(itemGroup.Key ?? 0, item.Key, startDate); // 获取当前客户当前标的持仓数据,合并后的名义本金数量 var positionLsit = clientPositions.Where(x => x.UnderlyingCode == item.Key).ToList(); decimal money = CalcDmaMoney(positionLsit, item.ToList(), clientMarginDetail); balance.FrozenMarginMoney += Convert.ToDouble(money); } } } /// /// 计算流水带来的资金变化 /// /// 持仓列表 /// 流水列表 /// 客户保证金模板 /// 资金变化金额 public decimal CalcDmaMoney(List positions, List flows, client_margin_detail clientMarginTemplate) { decimal money = 0; var marginRate = clientMarginTemplate?.init_rate??0; // 处理持仓与流水的平仓逻辑 foreach (var position in positions.ToList()) // 使用 ToList() 避免修改集合时的问题 { var oppositeFlows = flows.Where(x => x.BsType != position.BsType && x.TradingQty > 0).ToList(); foreach (var flow in oppositeFlows) { if (position.TradingQty <= 0) break; // 持仓已处理完毕 decimal tradedQty = Math.Min(position.TradingQty, flow.TradingQty); money += CalculateClosePositionProfit(position, flow, tradedQty, marginRate, isPositionFlow: true); // 更新持仓和流水的数量 position.TradingQty -= tradedQty; flow.TradingQty -= tradedQty; } } // 处理流水之间的轧差逻辑 foreach (var flow in flows.Where(x => x.TradingQty > 0).ToList()) { var oppositeFlows = flows.Where(x => x.BsType != flow.BsType && x.TradingQty > 0).ToList(); foreach (var oppositeFlow in oppositeFlows) { if (flow.TradingQty <= 0) break; // 当前流水已处理完毕 decimal tradedQty = Math.Min(flow.TradingQty, oppositeFlow.TradingQty); money += CalculateClosePositionProfit(flow, oppositeFlow, tradedQty, marginRate, isPositionFlow: false); // 更新流水的数量 flow.TradingQty -= tradedQty; oppositeFlow.TradingQty -= tradedQty; } } // 处理剩余流水的新开仓逻辑 foreach (var flow in flows.Where(x => x.TradingQty > 0)) { // 新开仓保证金 money += marginRate * flow.TradingQty * flow.TradingAmountAvg; } return Math.Round(money, 2, MidpointRounding.AwayFromZero); } /// /// 计算平仓损益和保证金释放 /// private decimal CalculateClosePositionProfit(swap_flow position, swap_flow flow, decimal tradedQty, decimal marginRate, bool isPositionFlow) { decimal floatRatio = -1m; // 默认支付 decimal longRatio = position.BsType == 1 ? 1m : -1m; // 平仓损益 decimal profit = (flow.TradingAmountAvg - position.TradingAmountAvg) * tradedQty * floatRatio * longRatio; // 如果是持仓与流水平仓,计算保证金释放;否则不计算 decimal releasedMargin = isPositionFlow ? marginRate * tradedQty * position.TradingAmountAvg : 0; return profit - releasedMargin; } /// /// 获取预付金率设置 /// /// /// /// private static client_marginrate GetClientMarginRate(int clientId, List clientMarginTemplates) { var marinRate = clientMarginTemplates.Where(x => x.ClientId == clientId).FirstOrDefault(); if (marinRate == null) { marinRate = clientMarginTemplates.Where(x => x.ClientId == 0).FirstOrDefault(); } if (marinRate == null) { marinRate = new client_marginrate { InitMarginRate = 1, MaintenanceRate = 1, }; } return marinRate; } public class ClientBalanceEx : ClientSettleBalance { public double InFundOther { get; set; } /// /// 包含正负号了 /// public double OutFundOther { get; set; } public double WinLoss2 { get; set; } public string SettlementCurrency { get; set; } } } }