using YLErp.BLL; using YLErp.DBModels.Converts; namespace YLErp.Modules.TradeModule.ForwardModule { /// /// 远期交易平仓服务 /// public class TradeForwardUnwindService : TradeServiceBase { public TradeForwardUnwindService(OptUserInfo userInfo) : base(userInfo) { } /// /// from fowardTradeController.TradeUnwind /// public PrepareForwardUnwindResult PrepareUnwind(int tradeId, bool isUseApproval = false) { var result = new PrepareForwardUnwindResult(); var td = DbContext.trade.Find(tradeId); var tc = new trade_cash(); if (td == null) { throw new ServiceException("找不到现金交割交易"); } var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); result.HasProcess = HasTradeProcess(); if (((valuedateBLL.SystemDate.CloseReCheck == 1) || (valuedateBLL.SystemDate.CloseReApprove == 1 && result.HasProcess)) && td.TradeStatus == ConsTrade.平仓待复核) { tc = DbContext.trade_cash.FirstOrDefault(t => t.TradeId == tradeId && t.Action == ClientCashInCashOut.系统操作_平仓费 && t.ValidState == ConsGlobal.InValid && !t.IsDeleted); if (tc == null) { throw new ServiceException("该笔交易状态为平仓待复核,而trade_cash表没有相关记录,请检查该笔交易是否有效"); } } else { tc = SetTradeUnwind(td, um); } tc.InitialSpotPrice = td.SpotPrice ?? 0; tc.UnderlyingCode = um.UnderlyingCode; tc.UnderlyingId = um.id; tc.TradeOriginalAmount = td.OriginalNotional / um.CountRatio; tc.UnderlyingInstrumentType = td.UnderlyingInstrumentType; tc.BondType = td.TradeType; tc.TradeType = BuySellConvert.GetClientBuySell(td.BuySell); tc.UnwindType = string.IsNullOrEmpty(tc.UnwindType) ? "全部平仓" : tc.UnwindType; if (!isUseApproval) { tc.TradeAmount = td.TradeAmount; tc.UnwindPrice = 0; } result.Trade = td; result.TradeCash = tc; result.OtcTradeForward = new TradeForwardService(OptUser).GetDetail(tradeId); if (!string.IsNullOrEmpty(td.PairTrade)) { var pid = Convert.ToInt32(td.PairTrade); result.PairTrade = DbContext.trade.Find(pid); } result.Underlying = um; result.Variety = new VarietyBasic { CountRatio = um.CountRatio, TradeUnitValue = um.ContractSize, VarietyCode = um.CommodityCode }; //在当日收盘前部分了解,预付金成本累加获取 //远期交易最后一笔平仓时的预付金成本特殊情况处理: 触发条件: //1.最后一笔平仓 //2.当前交易日和前一个交易日中间有假期 // 处理规则: // 预付金成本 = 这笔平仓上一交易日预付金成本 + 这笔平仓假期日的预付金成本 var valueDate = valuedateBLL.ValueDate; if (valueDate > td.ExerciseDate) { valueDate = td.ExerciseDate.Value; } var lastMarginRecord = DbContext.eod_forward_margin.Where(f => f.TradeId == tc.TradeId && f.ValueDate < valueDate).OrderByDescending(x => x.ValueDate).FirstOrDefault(); result.MaxUnwindDate = td.ExerciseDate.Value; if (td.ExerciseDate.Value > valuedateBLL.ValueDate) { result.MaxUnwindDate = valuedateBLL.ValueDate; } if (!isUseApproval) { result.TradeCash.ValueDate = valuedateBLL.ValueDate <= td.ExerciseDate ? valuedateBLL.ValueDate : td.ExerciseDate.Value; } if (lastMarginRecord != null) { //这段代码注释掉,在前端平仓页面使用MaxUnwindDate限制最大平仓日期选择 //var lastSettleDate = QdpCalendarHelper.GetNonHolidayDefore(valueDate.AddDays(-1)); //if (lastSettleDate != lastMarginRecord.ValueDate) //{ // throw new ServiceException($"请确保上一交易日({lastSettleDate:yyyy-MM-dd})已结算收盘!请尝试从{lastMarginRecord.ValueDate}之后,开始收盘!"); //} var totaldays = (valuedateBLL.ValueDate.Date - lastMarginRecord.ValueDate.Date).TotalDays; result.HolidayMargin = lastMarginRecord.SettlePrice * lastMarginRecord.MarginRate * lastMarginRecord.AnnualRate * totaldays / 365; result.LastMarginRecord = lastMarginRecord; } else { //交易日当天 result.HolidayMargin = 0; result.LastMarginRecord = new eod_forward_margin(); } return result; } private trade_cash SetTradeUnwind(trade td, underlying_manager um) { var r = new trade_cash { TradeId = td.id, FinalPrice = um.Price ?? 0, ExceciseType = "现金", CallPut = td.CallPut, Strike = td.Strike, Notional = td.Notional, InitialSpotPrice = td.SpotPrice ?? 0, UnderlyingInstrumentType = td.UnderlyingInstrumentType, StockEqvNotional = td.StockEqvNotional, UnwindNotional = td.Notional, Amount = 0, ValueDate = valuedateBLL.ValueDate <= td.ExerciseDate ? valuedateBLL.ValueDate : td.ExerciseDate.Value, TradeType = BuySellConvert.GetClientBuySell(td.BuySell) }; r.TradeAmount = r.Notional / um.CountRatio; r.UnwindTradeAmount = r.UnwindNotional / um.CountRatio; return r; } } public class PrepareForwardUnwindResult { public trade Trade { get; set; } public trade_cash TradeCash { get; set; } public OtcTradeForward OtcTradeForward { get; set; } public trade PairTrade { get; set; } public underlying_manager Underlying { get; set; } public VarietyBasic Variety { get; set; } public double HolidayMargin { get; set; } public eod_forward_margin LastMarginRecord { get; set; } public DateTime MaxUnwindDate { get; set; } /// /// 是否有审批步骤 /// public bool HasProcess { get; set; } } }