Files
zszq-trs/YLErpDAL/Modules/EodModule/SettlementModule/EodClientBalanceCalcBS.cs
T
2024-05-09 14:06:26 +08:00

396 lines
19 KiB
C#

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