Files
zszq-trs/YLErpDAL/BLL/EodSettlement/RealTimeClientBanlanceService.cs
T

1620 lines
91 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.SwapModule.Margin;
using YLErp.Modules.UnderlyingModule;
namespace YLErp.BLL.Eod
{
/// <summary>
/// 计算客户实时资金
/// </summary>
public class RealTimeClientBanlanceService : YLBaseService
{
private static readonly IYcLogger logger = LogFactory.GetLogger("实时客户资金");
readonly valuedate _systemDate; //系统参数
readonly DateTime _valueDate; //系统交易日
readonly Dictionary<int, ClientBalanceEx> _clientBalanceDic;
Dictionary<int, FundObject> _clientFundObject;
string[] _currencyCodes;
EodCurrencyProvider _currencyProvider;
public RealTimeClientBanlanceService(OptUserInfo optUser) : base(optUser)
{
_systemDate = valuedateBLL.SystemDate;
_valueDate = _systemDate.ValueDate;
_clientBalanceDic = new Dictionary<int, ClientBalanceEx>();
_clientFundObject = new Dictionary<int, FundObject>();
_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);
}
/// <summary>
/// 获取DMA资金
/// </summary>
/// <returns></returns>
public IEnumerable<ClientSettleBalance> 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<ClientSettleBalance> GetBanlances(IEnumerable<int> clientIds, DateTime startDate, DateTime? endDate = null, DateTime? calcDate = null)
{
if (null == clientIds || !clientIds.Any())
{
return Enumerable.Empty<ClientSettleBalance>();
}
var set = new HashSet<int>();
Dictionary<int, string> clientRatingDic;
Dictionary<int, Client> 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)
//最新概念:负数代表客户应缴预付金,正数代表客户应收预付金
//合约维度盯市(MarginWatchRule==0)客户成交时已产生出入金,保证金不再计入占用(PayableMargin),避免双重体现
PayableMargin = client.MarginWatchRule == 0 ? 0 : (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<ClientSettleBalance>();
}
//获取客户昨日资金结算信息
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<swap_flow>(), _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<int, DateTime?> 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<int, DateTime?> 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);
//阶段三 §3.2 估值报告口径输入(与 ClientBalanceUtility EOD/报告路径共用同一查询,保证三处一致):
//已使用授信(授信出入表)、互换初始保证金(应付预付金净额)、交易维度追加保证金合计(Σ(维持−累计))
var clientIdList = clientIds.ToList();
var usedCreditDic = Modules.SwapModule.ClientCreditInoutService.GetUsedCreditByClients(clientIdList, DbContext);
var swapInitMarginDic = SwapSpanBalanceQueryService.GetSwapInitMarginByClients(clientIdList, calcDate.Value, DbContext);
var swapAdditionalDic = SwapSpanBalanceQueryService.GetTradeAdditionalMarginByClients(clientIdList, calcDate.Value, DbContext);
//合约维度追保金额新口径(2026-08-28):当日追加保证金现金部分合计(日间 EOD 未跑时无当日记录 → 0,与通知书口径一致)
var todayCashAddDic = SwapSpanBalanceQueryService.GetTodayCashAdditionalMarginByClients(clientIdList, calcDate.Value, DbContext);
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;
//阶段三 §3.2:已使用授信/互换初始保证金/交易维度追加保证金(与 EOD/报告口径一致)
item.UsedCredit = usedCreditDic.TryGetValue(clientId, out var usedCredit) ? usedCredit : 0;
item.SwapInitMargin = swapInitMarginDic.TryGetValue(clientId, out var swapInit) ? swapInit : 0;
item.SwapAdditionalMarginTotal = swapAdditionalDic.TryGetValue(clientId, out var swapAdd) ? swapAdd : 0;
// 可用资金:配置了追保规则维度的客户按维度分流(R2公式,实时/EOD三处一致);
// 未配置(MarginWatchRule=NULL,存量客户)维持旧口径:期末结存-冻结资金
var ruleClient = DataCacheProvider.GetClientDataSource().GetData(clientId);
if (ruleClient?.MarginWatchRule == 0)
{
//定稿 2026-08-28Max(现金结存 + 授信额度 − 已使用授信, 0),不再减交易维度追加保证金(避免双重扣减)
item.AvailableAmount = SwapSpanBalanceCalc.CalcContractDimensionAvailable(
item.AmountFund, item.TotalCredit, item.UsedCredit);
}
else if (ruleClient?.MarginWatchRule == 1)
{
//维持保证金(净收取为正)= -MySideMarginclient_span 维持保证金反号聚合)
item.AvailableAmount = SwapSpanBalanceCalc.CalcClientDimensionAvailable(
item.AmountFund, item.TotalCredit, item.UsedCredit,
item.SwapInitMargin, -item.MySideMargin);
}
else
{
item.AvailableAmount = item.MarginBalance - item.FrozenMarginMoney;
}
// 是否追保/追保金额(按维度分流,与 ClientBalanceUtility 报告口径一致,定稿 2026-08-28 起均 Max(...,0) 截断):
// 客户维度(==1)= Max((维持−初始) − (现金+授信−已使用), 0);
// 合约维度(==0)= 当日追加保证金现金部分合计(2026-08-28 新口径:不与现金结存轧差、闲置现金不冲抵、授信部分不追);
// NULL 存量维持旧口径
if (ruleClient?.MarginWatchRule == 1)
{
item.MarginByPayableMarginTotal = SwapSpanBalanceCalc.CalcClientDimensionCallMargin(
item.AmountFund, item.TotalCredit, item.UsedCredit, item.SwapInitMargin, -item.MySideMargin);
item.NeedAddMargin = item.MarginByPayableMarginTotal > 0;
}
else if (ruleClient?.MarginWatchRule == 0)
{
item.MarginByPayableMarginTotal = SwapSpanBalanceCalc.CalcContractDimensionCallMargin(
todayCashAddDic.TryGetValue(clientId, out var todayCashAdd) ? todayCashAdd : 0);
item.NeedAddMargin = item.MarginByPayableMarginTotal > 0;
}
else
{
// 是否追保=盯市金额小于维持保证金额
item.NeedAddMargin = item.SwapMarketAmount < item.MaintenanceMargin;
// 追保金额=初始保证金金额-盯市金额
item.MarginByPayableMarginTotal = item.NeedAddMargin ? (item.MySideMargin - item.SwapMarketAmount) : 0;
}
// 可取资金(定稿 2026-08-28 分维度口径,只算现金部分;min(持仓盈亏,0) 只扣浮亏、浮盈不放行):
// 合约维度(==0= Max(现金结存 + min(持仓盈亏,0), 0)
// 客户维度(==1)= Max(现金结存 + 初始保证金 − 维持保证金 + min(持仓盈亏,0), 0)
// NULL 存量维持旧口径(含冻结保证金扣减)
if (ruleClient?.MarginWatchRule == 0)
{
item.DesirableFund = SwapSpanBalanceCalc.CalcContractDimensionDesirableFund(
item.AmountFund, item.RoundedPositionPnl);
}
else if (ruleClient?.MarginWatchRule == 1)
{
item.DesirableFund = SwapSpanBalanceCalc.CalcClientDimensionDesirableFund(
item.AmountFund, item.SwapInitMargin, -item.MySideMargin, item.RoundedPositionPnl);
}
else
{
item.DesirableFund = Math.Max(item.MarginBalance - item.FrozenMarginMoney + Math.Min(item.RoundedPositionPnl, 0), 0);
}
logger.Info($"实时客户资金:客户{clientId} MarginWatchRule={ruleClient?.MarginWatchRule?.ToString() ?? "NULL"} 可用资金={item.AvailableAmount:0.00} 追保金额={item.MarginByPayableMarginTotal:0.00} 是否追保={item.NeedAddMargin} 可取资金={item.DesirableFund:0.00}(期末结存{item.AmountFund:0.00} 授信{item.TotalCredit:0.00} 已用授信{item.UsedCredit:0.00} 互换初始{item.SwapInitMargin:0.00} 维持{-item.MySideMargin:0.00} 追加合计{item.SwapAdditionalMarginTotal:0.00}");
}
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<trade> 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<trade> 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<ClientBalanceDaily> clientBalanceDailyList = null;
List<ClientCashInCashOut> entryexitsList = null;
List<ClientCashInCashOut> 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<ClientBalanceDaily>();
}
if (entryexitsList == null)
{
entryexitsList = new List<ClientCashInCashOut>();
}
if (entryexits_swapList == null)
{
entryexits_swapList = new List<ClientCashInCashOut>();
}
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);
}
}
}
/// <summary>
/// 多币种获得fundjson
/// </summary>
public FundObject GetClientFundObject(Client client, DateTime endDate, bool OuterCall = false, DateTime? lastSettleDayArg = null, List<ClientBalanceDaily> clientBalanceDailyList = null, List<ClientCashInCashOut> entryexitsList = null, List<ClientCashInCashOut> 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<FundObject>(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<ClientCashInCashOut> 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<ClientCashInCashOut> 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)
//阶段四 §4.1:追加保证金(EOD 结算产生的合约维度追保入金)与应付预付金同口径计入当日资金变动
|| 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<long>();
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.系统操作_预付金返息
//阶段四 §4.1:追加保证金与应付预付金同桶(互换应收),当日已结算/已确认记录计入
|| data.action == "应收^" + ClientCashInCashOut.系统操作_追加保证金)
{
//同桶多 Action(应付预付金+追加保证金等)并存须累加——与多币种分支(OptionPremiumSwap +=)一致(BUG-08
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;
}
}
/// <summary>
/// 新版互换
/// </summary>
/// <param name="startDate"></param>
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 => MarginModes.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;
}
}
}
/// <summary>
/// 计算客户的冻结资金
/// </summary>
/// <param name="startDate"></param>
public void ProcessClientFrozen(DateTime startDate, List<swap_flow> bondFlows, Dictionary<int, ClientBalanceEx> 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<int> calcStatus = new List<int>() { -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);
}
}
}
/// <summary>
/// 计算流水带来的资金变化
/// </summary>
/// <param name="positions">持仓列表</param>
/// <param name="flows">流水列表</param>
/// <param name="clientMarginTemplate">客户保证金模板</param>
/// <returns>资金变化金额</returns>
public decimal CalcDmaMoney(List<swap_flow> positions, List<swap_flow> 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);
}
/// <summary>
/// 计算平仓损益和保证金释放
/// </summary>
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;
}
/// <summary>
/// 获取预付金率设置
/// </summary>
/// <param name="clientId"></param>
/// <param name="clientMarginTemplates"></param>
/// <returns></returns>
private static client_marginrate GetClientMarginRate(int clientId, List<client_marginrate> 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; }
/// <summary>
/// 包含正负号了
/// </summary>
public double OutFundOther { get; set; }
public double WinLoss2 { get; set; }
public string SettlementCurrency { get; set; }
}
}
}