1502 lines
82 KiB
C#
1502 lines
82 KiB
C#
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.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
|
|
{
|
|
/// <summary>
|
|
/// 计算客户实时资金
|
|
/// </summary>
|
|
public class RealTimeClientBanlanceService : YLBaseService
|
|
{
|
|
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)
|
|
//最新概念:负数代表客户应缴预付金,正数代表客户应收预付金
|
|
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<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);
|
|
}
|
|
|
|
//计算winloss2
|
|
ProcessClientCash2(startDate);
|
|
//获取客户所有现存(抵押状态)抵押品信息
|
|
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);
|
|
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.OptionPremium + item.OptionPremiumSwap + item.SwapBalance + item.SettlementBalance+ item.VmInFund-item.VmOutFund;
|
|
|
|
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.VmInFundSum - item.VmOutFundSum) - 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<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))
|
|
{
|
|
//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 == "出金" && 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
|
|
};
|
|
|
|
//获取包含的所有收益互换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 } into g
|
|
select new
|
|
{
|
|
g.Key.ClientId,
|
|
g.Key.action,
|
|
g.Key.cash_type,
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
/// <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 =>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.MarkClosePnl);
|
|
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.MarkClosePnl);
|
|
//潜在行权收益等于实值额
|
|
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 = (item.full_price ?? 0)*0.01m,
|
|
TradingAmountFeeAvg = (item.full_price ?? 0) * 0.01m,
|
|
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; }
|
|
}
|
|
}
|
|
}
|