396 lines
19 KiB
C#
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($"客户资金买卖权计算结束");
|
|
}
|
|
}
|
|
}
|