InitInterestDate 返回 true 的语义是"计息窗口为空→不计利息"(利率与金额归零), 判定只看日期窗口(interestStart>interestEnd)、与事件类型无关;旧名 swap 把 典型原因(互换当日已结息)当成了效果的名字,读者会误读为事件类型判断。 - SwapDealService: GetInterests 局部变量 + GetFloatRate/CalcUnwindInterest/ InitSwapDealInterest/CalcMarginInterest 四个签名参数及分支/日志/注释同步 - SwapTradeBaseService.InitInterestDate 返回值语义补入 XML 文档 - MarginInterestGoldenReplay/Shadow 测试命名参数同步 零行为变更。 验证:相关套件 51/51;全量 978 例 145 败与基线 diff=0
593 lines
27 KiB
C#
593 lines
27 KiB
C#
using CsvHelper;
|
||
using DocumentFormat.OpenXml.Drawing.Diagrams;
|
||
using DocumentFormat.OpenXml.Office2010.PowerPoint;
|
||
using Newtonsoft.Json;
|
||
using NPOI.SS.Formula.Functions;
|
||
using Qdp.Pricing.Base.Enums;
|
||
using System.Linq.Expressions;
|
||
using YLErp.BLL;
|
||
using YLErp.Commons;
|
||
|
||
namespace YLErp.Modules.SwapModule
|
||
{
|
||
public class SwapTradeBaseService : YLBaseService
|
||
{
|
||
public SwapTradeBaseService(OptUserInfo optUser) : base(optUser)
|
||
{
|
||
}
|
||
public SwapTradeBaseService(YLBaseService baseService) : base(baseService)
|
||
{
|
||
|
||
}
|
||
|
||
#region 可测试化接缝(Seams)——子类共用,override 可在测试中替换 DB/外部调用
|
||
|
||
/// <summary>查找交易(生产: DbContext.trade.Find;测试: 返回内存对象)。
|
||
/// SwapDealService/SwapEodPositionService/SwapFlowEventService 三处实现完全一致,上提基类消除重复。</summary>
|
||
protected virtual trade FindTrade(int tradeId)
|
||
{
|
||
return DbContext.trade.Find(tradeId);
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 校验标的是否存在
|
||
/// </summary>
|
||
/// <param name="underlyingCode"></param>
|
||
/// <returns>不存在返回空</returns>
|
||
public bool GetUnderlyingCode(string underlyingCode)
|
||
{
|
||
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
|
||
return underlying == null ? false : true;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
///新增日终归档信息时 修改 持仓腿信息
|
||
/// </summary>
|
||
/// <param name="eodPayPosition"></param>
|
||
public void UpdateSwapPosition(eod_swap_position eodPayPosition, string tradeNumber)
|
||
{
|
||
var position = DbContext.swap_position.Find(eodPayPosition.PositionId);
|
||
position.ContractSize = eodPayPosition.ContractSize;
|
||
position.PositionType = eodPayPosition.PositionType;
|
||
position.PosiTradingFee = eodPayPosition.PosiTradingFee;
|
||
position.PosiTradingFeePending = eodPayPosition.PosiFeePending;
|
||
position.PosiTradingFeeUnit = eodPayPosition.PosiQuantity == 0 ? 0 : eodPayPosition.PosiTradingFee / eodPayPosition.PosiQuantity;
|
||
position.UnderlyingCode = eodPayPosition.UnderlyingCode;
|
||
position.UnderlyingInstrumentType = eodPayPosition.UnderlyingInstrumentType;
|
||
position.PosiDirection = eodPayPosition.PosiDirection;
|
||
position.PosiNetPrice = eodPayPosition.PosiNetPrice;
|
||
position.PosiGrossPrice = eodPayPosition.PosiGrossPrice;
|
||
position.PosiNetFeePrice = eodPayPosition.PosiNetFeePrice;
|
||
position.PosiNetNoFeePrice = eodPayPosition.PosiNetNoFeePrice;
|
||
position.PosiNotionalValue = Math.Round(eodPayPosition.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
||
position.PosiQuantity = eodPayPosition.PosiQuantity;
|
||
position.PosiStartDate = eodPayPosition.PosiStartDate;
|
||
position.OptTime = DateTime.Now;
|
||
position.OptId = UserInfo.UserId;
|
||
position.OptName = UserInfo.UserName;
|
||
position.PosiNumber = $"{tradeNumber}-{position.id}";
|
||
}
|
||
/// <summary>
|
||
/// 平仓后更新持仓
|
||
/// </summary>
|
||
/// <param name="eodPayPosition"></param>
|
||
public void UpdateSwapPositionWithRealTime(eod_swap_position eodPayPosition)
|
||
{
|
||
var position = DbContext.swap_position.FirstOrDefault(x => x.PositionId == eodPayPosition.PositionId);
|
||
if (position != null)
|
||
{
|
||
position.PosiQuantity = eodPayPosition.PosiQuantity;
|
||
position.PosiTradingFee = eodPayPosition.PosiTradingFee;
|
||
position.PosiNetPrice = eodPayPosition.PosiNetPrice;
|
||
position.PosiGrossPrice = eodPayPosition.PosiGrossPrice;
|
||
position.PosiNetFeePrice = eodPayPosition.PosiNetFeePrice;
|
||
position.PosiNetNoFeePrice = eodPayPosition.PosiNetNoFeePrice;
|
||
position.PosiNotionalValue = Math.Round(eodPayPosition.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
||
position.PosiTradingFeePending = eodPayPosition.PosiFeePending;
|
||
position.PosiQuantity = eodPayPosition.PosiQuantity;
|
||
position.PosiDirection = eodPayPosition.PosiDirection;
|
||
position.PositionType = eodPayPosition.PositionType;
|
||
position.OptTime = DateTime.Now;
|
||
position.OptId = UserInfo.UserId;
|
||
position.OptName = UserInfo.UserName;
|
||
}
|
||
else
|
||
{
|
||
position = DbContext.swap_position.FirstOrDefault(x => x.id == eodPayPosition.PositionId);
|
||
var posi = position.Clone();
|
||
posi.id = 0;
|
||
posi.PositionId = position.id;
|
||
posi.IsInitial = false;
|
||
DbContext.swap_position.Add(posi);
|
||
}
|
||
|
||
}
|
||
/// <summary>
|
||
/// 获取同一互换编码,标的 持仓id
|
||
/// </summary>
|
||
/// <param name="swapTradeId">互换框架合约id</param>
|
||
/// <param name="underlyingCode">标的代码</param>
|
||
/// <returns></returns>
|
||
public long GetMaxPositionId(swap_flow_merge swap_Flow_Summary, DateTime matuirityDate, int direction, string tradeNumber)
|
||
{
|
||
long max = 0;
|
||
var position = DbContext.swap_position.FirstOrDefault(x => x.SwapTradeId == swap_Flow_Summary.SwapTradeId && x.UnderlyingCode == swap_Flow_Summary.UnderlyingCode && x.PosiQuantity != 0 && !x.IsInitial&&!x.Invalid);
|
||
if (position != null)
|
||
{
|
||
max = position.PositionId;
|
||
}
|
||
else
|
||
{
|
||
swap_position swap_Position = new swap_position();
|
||
swap_Position.PosiDirection = direction;
|
||
swap_Position.PositionType = swap_Flow_Summary.BsType;
|
||
swap_Position.SwapTradeId = swap_Flow_Summary.SwapTradeId ?? 0;
|
||
swap_Position.UnderlyingCode = swap_Flow_Summary.UnderlyingCode;
|
||
swap_Position.PosiQuantity = swap_Flow_Summary.TradingQty;
|
||
swap_Position.PosiTradingFee = swap_Flow_Summary.TradingFee;
|
||
swap_Position.ContractSize = swap_Flow_Summary.ContractSize;
|
||
swap_Position.IsInitial = true;
|
||
swap_Position.PosiStartDate = swap_Flow_Summary.OccurTime;
|
||
swap_Position.PosiMatuirityDate = matuirityDate;
|
||
swap_Position.OptId = UserId;
|
||
swap_Position.OptName = UserName;
|
||
swap_Position.OptTime = DateTime.Now;
|
||
DbContext.swap_position.Add(swap_Position);
|
||
DbContext.SaveChanges();
|
||
max = swap_Position.id;
|
||
swap_Position.PosiNumber = $"{tradeNumber}-{max}";
|
||
}
|
||
return max;
|
||
}
|
||
public long GetPositionId(swap_flow_merge swap_Flow_Summary, DateTime matuirityDate, int direction, string tradeNumber)
|
||
{
|
||
long max = 0;
|
||
swap_position swap_Position = new swap_position();
|
||
swap_Position.PosiDirection = direction;
|
||
swap_Position.PositionType = swap_Flow_Summary.BsType;
|
||
swap_Position.SwapTradeId = swap_Flow_Summary.SwapTradeId ?? 0;
|
||
swap_Position.UnderlyingCode = swap_Flow_Summary.UnderlyingCode;
|
||
swap_Position.PosiQuantity = swap_Flow_Summary.TradingQty;
|
||
swap_Position.PosiTradingFee = swap_Flow_Summary.TradingFee;
|
||
swap_Position.ContractSize = swap_Flow_Summary.ContractSize;
|
||
swap_Position.IsInitial = true;
|
||
swap_Position.PosiStartDate = swap_Flow_Summary.OccurTime;
|
||
swap_Position.PosiMatuirityDate = matuirityDate;
|
||
swap_Position.OptId = UserId;
|
||
swap_Position.OptName = UserName;
|
||
swap_Position.OptTime = DateTime.Now;
|
||
DbContext.swap_position.Add(swap_Position);
|
||
DbContext.SaveChanges();
|
||
max = swap_Position.id;
|
||
swap_Position.PosiNumber = $"{tradeNumber}-{max}";
|
||
return max;
|
||
}
|
||
/// <summary>
|
||
/// 添加交易操作日志
|
||
/// </summary>
|
||
public void AddTradeOperationHistory(bool saveChanges, OtcTradeBase trade, string optType, string comments = null)
|
||
{
|
||
DbContext.TradeAuditLog.Add(new TradeAuditLog
|
||
{
|
||
TradeId = trade.id,
|
||
AuditFlag = TradeAuditFlag.operation,
|
||
OptType = optType,
|
||
Changes = comments ?? string.Empty,
|
||
DataType = "00",
|
||
OptId = UserId,
|
||
OptName = UserName,
|
||
OptDate = OptDate
|
||
});
|
||
|
||
if (saveChanges)
|
||
{
|
||
DbContext.SaveChanges();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取平仓/互换记录
|
||
/// </summary>
|
||
/// <param name="tradeId">交易编码</param>
|
||
/// <param name="valueDate">日期</param>
|
||
/// <param name="eventTypes">互换事件类型</param>
|
||
/// <param name="lessValueDate">是否查询小于日期</param>
|
||
/// <returns></returns>
|
||
public List<swap_flow_event> GetSwapFlowEvents(int tradeId, DateTime? valueDate, List<int> eventTypes)
|
||
{
|
||
Expression<Func<swap_flow_event, bool>> eventExpression = x => x.SwapTradeId == tradeId && x.DataState == (int)SwapFlowDateStateEnum.完成 && eventTypes.Contains(x.EventType);
|
||
if (valueDate.HasValue)
|
||
{
|
||
eventExpression = eventExpression.And(x => x.UnwindDate == valueDate);
|
||
}
|
||
List<swap_flow_event> swapFlowEvents = DbContext.swap_flow_event.Where(eventExpression).ToList();
|
||
return swapFlowEvents;
|
||
}
|
||
/// <summary>
|
||
/// 获取交易平仓/互换事件所有信息
|
||
/// </summary>
|
||
/// <param name="tradeId"></param>
|
||
/// <param name="eventTypes"></param>
|
||
/// <returns></returns>
|
||
public List<swap_event> GetSwapEvents(int tradeId, List<int> eventTypes)
|
||
{
|
||
Expression<Func<swap_event, bool>> eventExpression = x => x.SwapTradeId == tradeId && !x.Invalid && eventTypes.Contains(x.EventType) && x.ClientCashId > 0;
|
||
var swapEvents = DbContext.swap_event.Where(eventExpression).ToList();
|
||
var swapFlowEvents = DbContext.swap_flow_event.Where(x => x.SwapTradeId == tradeId && x.DataState == (int)SwapFlowDateStateEnum.完成);
|
||
var swapPositions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && !string.IsNullOrEmpty(x.UnderlyingCode) && !x.Invalid).ToList();
|
||
foreach (var item in swapEvents)
|
||
{
|
||
item.unwindData = JsonConvert.DeserializeObject<UnwindData>(item.EventData);
|
||
item.unwindData.FlowEvents = swapFlowEvents.Where(x => x.EventId == item.id).ToList();
|
||
item.unwindData.FlowEvents.ForEach(x =>
|
||
{
|
||
var position = swapPositions.FirstOrDefault(n => n.id == x.PositionId && n.IsInitial);
|
||
var positionReal = swapPositions.FirstOrDefault(n => n.PositionId == x.PositionId && !n.IsInitial);
|
||
if (position != null)
|
||
{
|
||
x.PosiGrossPrice = position.PosiGrossPrice;
|
||
x.PosiNetPrice = position.PosiNetPrice;
|
||
}
|
||
//if (positionReal!=null)
|
||
//{
|
||
// x.PositionQty = positionReal.PosiQuantity;
|
||
//}
|
||
});
|
||
}
|
||
return swapEvents;
|
||
}
|
||
/// <summary>
|
||
/// 获取平仓/互换信息
|
||
/// </summary>
|
||
/// <param name="tradeId"></param>
|
||
/// <param name="eventType"></param>
|
||
/// <returns></returns>
|
||
public swap_event GetSwapEvent(int tradeId, int eventType)
|
||
{
|
||
Expression<Func<swap_event, bool>> eventExpression = x => x.SwapTradeId == tradeId && !x.Invalid && x.EventType == eventType && x.ClientCashId == 0;
|
||
var swapEvent = DbContext.swap_event.Where(eventExpression).OrderByDescending(o => o.id).FirstOrDefault();
|
||
var swapFlowEvents = DbContext.swap_flow_event.Where(x => x.EventId == swapEvent.id);
|
||
var swapPositions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && !string.IsNullOrEmpty(x.UnderlyingCode) && !x.Invalid).ToList();
|
||
if (swapEvent != null)
|
||
{
|
||
swapEvent.unwindData = JsonConvert.DeserializeObject<UnwindData>(swapEvent.EventData);
|
||
swapEvent.unwindData.FlowEvents = swapFlowEvents.Where(x => x.EventDate == swapEvent.ValueDate).ToList();
|
||
swapEvent.unwindData.FlowEvents.ForEach(x =>
|
||
{
|
||
var position = swapPositions.FirstOrDefault(n => n.id == x.PositionId && n.IsInitial);
|
||
if (position != null)
|
||
{
|
||
x.PosiGrossPrice = position.PosiGrossPrice;
|
||
x.PosiNetPrice = position.PosiNetPrice;
|
||
//if (eventType==(int)SwapEventTypeEnum.互换)
|
||
//{
|
||
// x.TradingAmountAvg = x.TradingAmount / (x.PositionQty*x.ContractSize);
|
||
//}
|
||
//else
|
||
//{
|
||
// x.TradingAmountAvg = x.TradingAmount / (swapEvent.unwindData.CloseQty * x.ContractSize);
|
||
//}
|
||
|
||
}
|
||
|
||
});
|
||
}
|
||
return swapEvent;
|
||
}
|
||
/// <summary>
|
||
/// 获取上一互换交易事件处理日期
|
||
/// </summary>
|
||
/// <param name="tradeId"></param>
|
||
/// <param name="valueDate"></param>
|
||
/// <param name="eventTypes"></param>
|
||
/// <returns></returns>
|
||
public virtual DateTime? GetPreDealDate(int tradeId, DateTime valueDate, List<int> eventTypes)
|
||
{
|
||
Expression<Func<swap_event, bool>> eventExpression = x => x.SwapTradeId == tradeId && x.ValueDate <= valueDate && !x.Invalid && eventTypes.Contains(x.EventType);
|
||
var swapEvent = DbContext.swap_event.Where(eventExpression).OrderByDescending(o => o.ValueDate).FirstOrDefault();
|
||
if (swapEvent == null)
|
||
{
|
||
return null;
|
||
}
|
||
return swapEvent.ValueDate;
|
||
}
|
||
|
||
#region 可测试化接缝(ClearSwapPositions 相关)
|
||
|
||
/// <summary>查找该交易的 flow_event(生产: DbContext.swap_flow_event;测试: 内存列表)</summary>
|
||
protected virtual List<swap_flow_event> FindFlowEventsByEventIds(List<long> eventIds)
|
||
{
|
||
return DbContext.swap_flow_event.Where(x => x.EventId.HasValue && eventIds.Contains(x.EventId.Value)).ToList();
|
||
}
|
||
|
||
/// <summary>查找手动互换的 ClientCashId(生产: DbContext.swap_event;测试: 内存列表)</summary>
|
||
protected virtual List<int> FindManualClientCashIds(int swapTradeId)
|
||
{
|
||
return DbContext.swap_event
|
||
.Where(x => x.SwapTradeId == swapTradeId
|
||
&& x.ClientCashId > 0
|
||
&& x.EventType != (int)SwapEventTypeEnum.自动互换)
|
||
.Select(x => x.ClientCashId)
|
||
.ToList();
|
||
}
|
||
|
||
/// <summary>查找该交易的资金记录(生产: DbContext.ClientCashInCashOut;测试: 内存列表)</summary>
|
||
protected virtual List<ClientCashInCashOut> FindClientCashRecords(int tradeId)
|
||
{
|
||
return DbContext.ClientCashInCashOut.Where(x => x.TradeId == tradeId).ToList();
|
||
}
|
||
|
||
#endregion
|
||
|
||
public virtual int AddClientCashInCashOut(OtcTradeBase td, double amount, string action, DateTime valueDate)
|
||
{
|
||
var cl = DataCacheProvider.GetClientDataSource().GetData(td.ClientId);
|
||
|
||
if (cl == null)
|
||
{
|
||
throw new Exception("客户信息未找到,交易编号:" + td.TradeNumber);
|
||
}
|
||
//增加出入金记录
|
||
var ee = new ClientCashInCashOut();
|
||
ee.CreateDate = DateTime.Now;
|
||
ee.CreatorId = UserId;
|
||
ee.CreatorName = UserName;
|
||
ee.Direction = "应收";
|
||
ee.Number = UniqueTimeId.GetStr();
|
||
ee.ClientId = cl.id;
|
||
ee.ClientNumber = cl.Number;
|
||
ee.ClientName = cl.Name;
|
||
ee.Money = amount;
|
||
ee.CurrencyCode = td.SettlementCurrency;
|
||
ee.HappenDate = valueDate;
|
||
ee.State = ClientCashInCashOut.已确认;
|
||
ee.OptId = UserId;
|
||
ee.OptName = UserName;
|
||
ee.OptDate = DateTime.Now;
|
||
ee.TradeId = td.id;
|
||
ee.Action = action;
|
||
ee.ValidState = "Valid";
|
||
ee.TradeNumber = td.TradeNumber;
|
||
ee.CurrencyCode = "CNY";
|
||
DbContext.ClientCashInCashOut.Add(ee);
|
||
DbContext.SaveChanges();
|
||
return ee.id;
|
||
}
|
||
/// <summary>
|
||
/// 初始化 利息计算起始日期
|
||
/// </summary>
|
||
/// <param name="valueDate"></param>
|
||
/// <param name="preSettleDate">上一交易日</param>
|
||
/// <param name="td">互换交易主信息</param>
|
||
/// <param name="interestMode">计息方式</param>
|
||
/// <param name="interestStart">计息开始日期</param>
|
||
/// <param name="interestEnd">计息结束日期</param>
|
||
/// <returns>true=计息窗口为空(interestStart>interestEnd,本次不计利息,调用方将利率与金额归零;
|
||
/// 典型场景=互换当日已结息/不算头首日,但判定只看日期窗口,与事件类型无关);false=正常计息。</returns>
|
||
public bool InitInterestDate(DateTime valueDate, DateTime? preSettleDate, trade td, bool tdClose, out DateTime interestStart, out DateTime interestEnd)
|
||
{
|
||
interestStart = td.StartDate.Value;
|
||
var exerciseDate = td.ExerciseDate.Value;
|
||
interestEnd = valueDate > exerciseDate ? exerciseDate : valueDate;
|
||
bool calcFirst = td.trade_extend?.ExtendObj.CalcFirst ?? true;
|
||
bool calcLast = td.trade_extend?.ExtendObj.CalcLast ?? true;
|
||
interestStart = calcFirst ? interestStart : interestStart.AddDays(1);
|
||
if (preSettleDate.HasValue && preSettleDate >= interestStart)
|
||
{
|
||
interestStart = preSettleDate.Value;
|
||
}
|
||
if ((interestEnd == exerciseDate && !calcLast))
|
||
{
|
||
interestEnd = interestEnd.AddDays(-1);
|
||
}
|
||
if (interestStart > interestEnd || td.StartDate > interestStart)
|
||
{
|
||
interestStart = interestEnd;
|
||
return true;//不记利息
|
||
}
|
||
if (tdClose)
|
||
{
|
||
interestStart = valueDate;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public virtual void UpdateDbOption(DBModelBaseV2 dBModel)
|
||
{
|
||
dBModel.OptTime = DateTime.Now;
|
||
dBModel.OptName = UserName;
|
||
dBModel.OptId = UserId;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 合成持仓/日终归档 清除互换持仓所有信息
|
||
/// </summary>
|
||
/// <param name="tradeId"></param>
|
||
public virtual void ClearSwapPositions(trade td, DateTime valueDate, List<int> eventTypes, bool delAfter)
|
||
{
|
||
var swapEvents = DbContext.swap_event.Where(x => x.SwapTradeId == td.id && x.ValueDate >= valueDate && eventTypes.Contains(x.EventType));
|
||
var eventIds = swapEvents.Select(s => s.id).ToList();
|
||
var eodSwaps = DbContext.eod_swap.Where(x => x.SwapTradeId == td.id && x.ValueDate >= valueDate).ToList();
|
||
if (delAfter)
|
||
{
|
||
var swapFlowEvents = DbContext.swap_flow_event.Where(x => x.SwapTradeId == td.id && x.UnwindDate >= valueDate && x.DataState > (int)SwapFlowDateStateEnum.废弃 && eventTypes.Contains(x.EventType)).ToList();
|
||
var eodSwapPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == td.id && x.ValueDate >= valueDate).ToList();
|
||
DbContext.eod_swap_position.RemoveRange(eodSwapPositions);
|
||
DbContext.swap_flow_event.RemoveRange(swapFlowEvents);
|
||
|
||
// 删除自动互换产生的资金记录(client_cash_in_out)
|
||
var autoSwapEvents = swapEvents.ToList();
|
||
if (autoSwapEvents.Any())
|
||
{
|
||
// 通过 swap_event 的 ClientCashId 删除对应的资金记录(利息腿)
|
||
var clientCashIds = autoSwapEvents
|
||
.Where(s => s.ClientCashId > 0)
|
||
.Select(s => s.ClientCashId)
|
||
.ToList();
|
||
|
||
var legacyAutoEvents = new List<swap_event>();
|
||
foreach (var swapEvent in autoSwapEvents)
|
||
{
|
||
var eventCashIds = GetAutoSwapClientCashIds(swapEvent);
|
||
if (eventCashIds.Any())
|
||
{
|
||
clientCashIds.AddRange(eventCashIds);
|
||
}
|
||
else
|
||
{
|
||
legacyAutoEvents.Add(swapEvent);
|
||
}
|
||
}
|
||
|
||
clientCashIds = clientCashIds.Distinct().ToList();
|
||
if (clientCashIds.Any())
|
||
{
|
||
var clientCashRecords = DbContext.ClientCashInCashOut.Where(x => clientCashIds.Contains(x.id)).ToList();
|
||
DbContext.ClientCashInCashOut.RemoveRange(clientCashRecords);
|
||
}
|
||
|
||
var legacyCashRecords = GetLegacyAutoSwapClientCashRecords(legacyAutoEvents, clientCashIds);
|
||
if (legacyCashRecords.Any())
|
||
{
|
||
DbContext.ClientCashInCashOut.RemoveRange(legacyCashRecords);
|
||
}
|
||
}
|
||
}
|
||
DbContext.swap_event.RemoveRange(swapEvents);
|
||
DbContext.eod_swap.RemoveRange(eodSwaps);
|
||
DbContext.SaveChanges();
|
||
}
|
||
|
||
private List<int> GetAutoSwapClientCashIds(swap_event swapEvent)
|
||
{
|
||
if (swapEvent == null || string.IsNullOrWhiteSpace(swapEvent.EventData))
|
||
{
|
||
return new List<int>();
|
||
}
|
||
|
||
try
|
||
{
|
||
var unwindData = JsonConvert.DeserializeObject<UnwindData>(swapEvent.EventData);
|
||
return unwindData?.ClientCashIds?.Where(x => x > 0).Distinct().ToList() ?? new List<int>();
|
||
}
|
||
catch
|
||
{
|
||
return new List<int>();
|
||
}
|
||
}
|
||
|
||
protected virtual List<ClientCashInCashOut> GetLegacyAutoSwapClientCashRecords(List<swap_event> swapEvents, List<int> excludedClientCashIds)
|
||
{
|
||
if (swapEvents == null || !swapEvents.Any())
|
||
{
|
||
return new List<ClientCashInCashOut>();
|
||
}
|
||
|
||
var eventIds = swapEvents.Select(x => x.id).ToList();
|
||
var flowEvents = FindFlowEventsByEventIds(eventIds);
|
||
var manualClientCashIds = FindManualClientCashIds(swapEvents.First().SwapTradeId);
|
||
var records = new List<ClientCashInCashOut>();
|
||
|
||
foreach (var swapEvent in swapEvents)
|
||
{
|
||
UnwindData unwindData = null;
|
||
if (!string.IsNullOrWhiteSpace(swapEvent.EventData))
|
||
{
|
||
try
|
||
{
|
||
unwindData = JsonConvert.DeserializeObject<UnwindData>(swapEvent.EventData);
|
||
}
|
||
catch
|
||
{
|
||
}
|
||
}
|
||
|
||
var eventFlowEvents = flowEvents.Where(x => x.EventId == swapEvent.id).ToList();
|
||
var candidateDates = new HashSet<DateTime> { swapEvent.ValueDate.Date };
|
||
if (unwindData?.PayDate != null)
|
||
{
|
||
candidateDates.Add(unwindData.PayDate.Value.Date);
|
||
}
|
||
eventFlowEvents.Where(x => x.PayDate.HasValue).ToList().ForEach(x => candidateDates.Add(x.PayDate.Value.Date));
|
||
|
||
var allCashRecords = FindClientCashRecords(swapEvent.SwapTradeId);
|
||
var eventRecords = allCashRecords
|
||
.Where(x => !excludedClientCashIds.Contains(x.id)
|
||
&& !manualClientCashIds.Contains(x.id)
|
||
&& (x.Action == ClientCashInCashOut.系统操作_预付金返息 || x.Action == ClientCashInCashOut.系统操作_互换))
|
||
.ToList()
|
||
.Where(x => x.HappenDate.HasValue && candidateDates.Contains(x.HappenDate.Value.Date))
|
||
.Where(x => IsLegacyAutoSwapClientCashRecord(x, unwindData))
|
||
.ToList();
|
||
|
||
records.AddRange(eventRecords);
|
||
}
|
||
|
||
return records.GroupBy(x => x.id).Select(x => x.First()).ToList();
|
||
}
|
||
|
||
private bool IsLegacyAutoSwapClientCashRecord(ClientCashInCashOut cashRecord, UnwindData unwindData)
|
||
{
|
||
if (unwindData == null)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
var amount = Convert.ToDecimal(cashRecord.Money ?? 0);
|
||
if (cashRecord.Action == ClientCashInCashOut.系统操作_预付金返息)
|
||
{
|
||
return unwindData.SwapMarginRebatePnl != 0
|
||
&& amount == -unwindData.SwapMarginRebatePnl;
|
||
}
|
||
|
||
if (cashRecord.Action == ClientCashInCashOut.系统操作_互换)
|
||
{
|
||
return (unwindData.SwapCloseAmount != 0 && amount == -unwindData.SwapCloseAmount)
|
||
|| (unwindData.SwapDividendPnl != 0 && amount == -unwindData.SwapDividendPnl)
|
||
|| (unwindData.SwapRealizedPnL != 0 && amount == -unwindData.SwapRealizedPnL);
|
||
}
|
||
|
||
return false;
|
||
}
|
||
/// <summary>
|
||
/// 获取上一交易日
|
||
/// </summary>
|
||
/// <param name="valueDate">当前交易日</param>
|
||
/// <returns></returns>
|
||
public DateTime GetPreValueDate(DateTime valueDate)
|
||
{
|
||
var preSettleDate = valuedateBLL.GetNonHolidayDefore(valueDate.AddDays(-1));//上一交易日
|
||
return preSettleDate;
|
||
}
|
||
/// <summary>
|
||
/// 判断交易上一交易日是否收盘
|
||
/// </summary>
|
||
/// <param name="valueDate">交易日期</param>
|
||
/// <param name="tradeStartDate">交易开始日期</param>
|
||
/// <returns></returns>
|
||
/// <exception cref="ServiceException"></exception>
|
||
public DateTime CheckLastEod(DateTime valueDate, DateTime tradeStartDate, int tradeId)
|
||
{
|
||
var preSettleDate = GetPreValueDate(valueDate);//上一交易日期
|
||
List<eod_swap_position> lastEodPositions = new SwapEodPositionService(this).GetPreEodPositions(tradeId, preSettleDate);//上一交易数据
|
||
if (preSettleDate > tradeStartDate && lastEodPositions.Count == 0)
|
||
{
|
||
throw new ServiceException($"上一交易日【{preSettleDate:D}】未收盘");
|
||
}
|
||
return preSettleDate;
|
||
}
|
||
/// <summary>
|
||
/// 是否有审批流程
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public bool HasTradeProcess()
|
||
{
|
||
return DbContext.approvalprocess.Where(t => t.processType == "TradeProcess").Any();
|
||
}
|
||
}
|
||
}
|