Files
zszq-trs/YLErpDAL/Modules/SwapModule/SwapTradeBaseService.cs
T
张名锐 0755233ab6 fix(swap): 统一处理【名义本金】数值精度舍入问题
- 在多个位置添加 Math.Round 函数确保金额计算精度
- 新增 NormalizeNotionalValues 方法统一处理 UnwindData 中的名义本金舍入
- 修复交易平仓时 StockEqvNotional 扣减计算的精度问题
- 解决持仓数据 PosiNotionalValue 的精度舍入处理
- 修复前端页面显示格式化问题
- 添加单元测试验证名义本金舍入逻辑正确性
2026-07-22 13:05:49 +08:00

597 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using 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 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 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>
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 = true;
bool calcLast = true;
if (td.trade_extend != null)
{
calcFirst = td.trade_extend.ExtendObj.InterestCalcMode.StartsWith("1");//算头
calcLast = td.trade_extend.ExtendObj.InterestCalcMode.EndsWith("1");//算尾
}
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();
}
}
}