using Microsoft.EntityFrameworkCore.Storage; using YLErp.BLL; using YLErp.BLL.Eod; using YLErp.Commons; using YLErp.CustomizedBizLogic; using YLErp.DBModels.Consts; using YLErp.DBModels.Helpers; using YLErp.Enums; using YLErp.Model; using YLErp.Model.Enum; using YLErp.Modules.ClientModule; using YLErp.Modules.EodModule; using YLErp.Modules.TradeDalModule; using YLErp.QdpModule; namespace YLErp.Modules.TradeModule.DealModule { /// /// 交易平仓操作 /// public class TradeUnwindService : TradeCashService { public TradeUnwindService(YLBaseService baseService) : base(baseService) { } public TradeUnwindService(OptUserInfo userInfo) : base(userInfo) { } /// /// isPairTrade是否 配对交易平仓 /// 规则:配对被执行的那一条的amount(trade_cash表格)和money(clientcashincashout 表格)应该是手动执行那一条的数值*-1 /// public trade_cash UnwindTrade(trade td, TradeCashReq req, bool isFromRecheck = false, bool isFromExercise = false) { if (td is null) { throw new ArgumentNullException(nameof(td)); } if (td.TradeType == "结构化交易") { throw new ServiceException("操作失败,结构化交易主交易不支持此操作"); } if (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus)) { throw new ServiceException("操作失败,交易已了结!"); } if (PS.Config.TradeElement.ExecuteAfterGeneratedConfirmDoc && !td.HasGeneratedConfirmBook()) { if (td.TradeType != "远期" && td.TradeType != "自定义交易" && td.TradeType != "现金流交易")//临时写法,需要完善远期确认书 { throw new ServiceException("请先去生成交易确认书"); } } //是否部分平仓 var isPartialClose = true; var tradeCashInvalid = DbContext.trade_cash.FirstOrDefault(t => t.TradeId == td.id && t.Action == ClientCashInCashOut.系统操作_平仓费 && t.ValidState == ConsGlobal.InValid && !t.IsDeleted); if (req != null) { unwindPreHandle(req, td, isFromExercise, out isPartialClose); } else { if (td.IsUsePremiumRate == true) { if (tradeCashInvalid != null && td.OriginalStockEqvNotional > 0 && (tradeCashInvalid.UnwindPercentRate >= td.StockEqvNotional / td.OriginalStockEqvNotional || tradeCashInvalid.UnwindType == "全部平仓")) { isPartialClose = false; } } else { if (tradeCashInvalid != null && (tradeCashInvalid.UnwindNotional >= td.Notional || tradeCashInvalid.UnwindType == "全部平仓")) { isPartialClose = false; } } } td.OptId = UserId; td.OptName = UserName; td.OptDate = DateTime.Now; if (req != null) { td.UnWindNotional = req.UnwindNotional; } else { td.UnWindNotional = tradeCashInvalid == null ? 0 : tradeCashInvalid.UnwindNotional; } if (isPartialClose) { td.TradeStatus = ConsTrade.确认成交; if (td.TradeType == "累计期权" && !isFromExercise) { var unwindPercent = (td.UnWindNotional ?? 0) / td.Notional; var accumulator = DbContext.trade_accumulator_option.FirstOrDefault(x => x.TradeId == td.id); if (accumulator != null) { accumulator.AccumuTradeAmount -= accumulator.OriginalAccumuTradeAmount * unwindPercent; } } td.Notional -= (td.UnWindNotional ?? 0); if (td.TradeType != "现金流交易") { var CountRatio = UnderlyingDataProvider.GetCountRatio(td.UnderlyingCode); td.TradeAmount = td.Notional / CountRatio; } else { td.TradeAmount -= (tradeCashInvalid == null ? 0 : tradeCashInvalid.UnwindNotional ?? 0); } if (td.IsUsePremiumRate == true) { if (req != null) { td.StockEqvNotional -= (td.OriginalStockEqvNotional * req.UnwindPercentRate) ?? 0; } else { td.StockEqvNotional -= (td.OriginalStockEqvNotional * tradeCashInvalid.UnwindPercentRate) ?? 0; } } else { if (td.TradeType == "远期" && PS.Config.ErpElement.ForwardTradePriceModel == 0 && !string.IsNullOrWhiteSpace(td.BasisUnderlyingCode)) { td.StockEqvNotional -= TradeHelper.GetStockEqvNotional((req != null ? req.UnwindNotional : tradeCashInvalid.UnwindNotional) / td.OriginalNotional * td.OriginalStockEqvNotional, 1, 1); } else { td.StockEqvNotional = TradeHelper.GetStockEqvNotional(Math.Abs(td.Notional * (td.SpotPrice ?? 0.0)), td.ParticipationRate, td.AnnualizeFactor); } } //td.PrincipalSum = TradeHelper.GetPrincipalSumReal(td.StockEqvNotional, td.OriginalStockEqvNotional, td.OriginalPrincipalSum); td.HasPartialUnWind = 1; } else if (isFromExercise) { td.TradeStatus = ConsTrade.已执行; } else { td.TradeStatus = ConsTrade.已平仓; } //增加现金交割交易记录 var tc = CloseTrade_TradeCashSave(td, req, isFromRecheck, !isFromExercise, !isPartialClose); if (!isPartialClose) { td.Notional = 0; td.TradeAmount = 0; td.StockEqvNotional = 0; } var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == td.id && x.Status != "已完成"); if (groupAction != null) { groupAction.Status = "已完成"; tc.ParentTradeCashId = groupAction.ParentTradeCashId; tc.ParentTradeId = groupAction.ParentTradeId; } tc.ValidState = "Valid"; tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; //加入平仓份额和平仓日期 td.UnWindDate = tc.ValueDate; td.FinalPrice = tc.FinalPrice; if (td.TradeType != "收益互换") { new TradeCashService(this).SaveTradeCashDetail(tc); } // if (isPairTrade) req.UnwindNotional = req.UnwindNotional * -1; var dt = (td.TradeType == "远期" || td.TradeType == "掉期") && PS.Config.Is浙期 && td.ExerciseDate == tc.ValueDate ? td.SettlementDate : tc.ValueDate; //增加出入金记录 new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, dt); AddTradeOperationHistoryAndSetParentTradeInfo(false, td, isPartialClose ? "部分平仓" : "全部平仓"); //删除E/Bod数据 RemoveEodTradeAndFutureInfo(false, td.id, tc.ValueDate); DbContext.SaveChanges(); return tc; } /// /// 来自:UnwindTradeJson /// public TradeUnwindResultModel UnwindTrade(TradeCashReq req, IDbContextTransaction trans = null) { if (req?.ValueDate == null) { throw new Exception("平仓日期不应为空"); } //修改交易主表信息交易状态为已执行 var td = DbContext.trade.Find(req.TradeId); var valueDate = SystemValueDate; //从页面操作上来看,req.ValueDate是无用的 //当有用的时候需要赋值给valueDate if (req.ValueDate.HasValue) { if (req.ValueDate < td.TradeDate) { throw new ServiceException("平仓日期不能小于交易日期"); } if (req.ValueDate > valueDate) { throw new ServiceException("平仓日期不能大于系统日期"); } } valueDate = req.ValueDate ?? valueDate; var isFromRecheck = false; trade_cash tc = null; var notrans = trans == null; if (notrans) { trans = DbContext.Database.BeginTransaction(); } try { //平仓复核逻辑验证 if ((valuedateBLL.SystemDate.CloseReCheck == 1) && td.TradeStatus == ConsTrade.平仓待复核) { //检查平仓单价 tc = DbContext.trade_cash.OrderByDescending(x => x.id).FirstOrDefault(dd => dd.TradeId == req.TradeId && dd.Action == ClientCashInCashOut.系统操作_平仓费 && dd.ValidState == ConsGlobal.InValid && !dd.IsDeleted).Clone(); if (td.TradeType == "远期") { if (req.FinalPrice != tc.FinalPrice) { throw new ServiceException("远期交易,标的价格不一致!复核不通过"); } if (req.ValueDate != tc.ValueDate) { throw new ServiceException("远期交易,平仓日期不一致!复核不通过"); } } if (valuedateBLL.SystemDate.UnwindSinglePriceAngle == 1) { //添加根据交易方向判断资金方向 tc.UnwindPrice = tc.UnwindPrice * (td.TradeType == "远期" || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell)); tc.UnwindPricePercentRate = tc.UnwindPricePercentRate * (td.TradeType == "远期" || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell)); } else { //添加根据交易方向判断资金方向 tc.UnwindPrice = tc.UnwindPrice * (td.TradeType == "远期" || !ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell)); tc.UnwindPricePercentRate = tc.UnwindPricePercentRate * (td.TradeType == "远期" || !ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell)); } if (valuedateBLL.SystemDate.UnwindAmountAngle == 0) { tc.Amount = tc.Amount * (td.TradeType == "远期" || td.TradeType == "收益互换" ? 1 : EodOperationBase.GetSign(td.BuySell)); tc.QuoteAmount = tc.QuoteAmount * (td.TradeType == "远期" || td.TradeType == "收益互换" ? 1 : EodOperationBase.GetSign(td.BuySell)); } if (req.AmountCheck == (tc.QuoteAmount ?? tc.Amount) || req.UnwindPricePercentRateCheck == tc.UnwindPricePercentRate && req.UnwindPricePercentRateCheck != null || req.UnwindPriceCheck == tc.UnwindPrice && req.UnwindPriceCheck != null) { req.UnwindPercentRate = (tc.UnwindPercentRate * 100) ?? req.UnwindPercentRate; isFromRecheck = true; td.CheckStatus = Convert.ToInt32(TradeCheckEnum.StatusOfUnwind); td.UnWindDate = valueDate; DbContext.SaveChanges(); } else { //审核失败 throw new ServiceException("平仓费不一致!复核不通过"); } } var result = new TradeUnwindResultModel(td); //存在审批流程时,走审批成功流程(需求②:按交易状态查对应流程) if (valuedateBLL.SystemDate.CloseReApprove == 1 && TradeProcessCountByCategory(td) > 0) { result.ApprovalProcess = true; var reqModel = new TradeOpenReqModel { tradeId = td.id, status = "pass", comments = "平仓复核审批成功", ignoreMoneyCheck = false, notNeedOperationHistory = true }; var openResult = new TradeOpenService(this).UpdateTradeProcessLog(reqModel); if (openResult.RetCode != TradeOpenRetCode.Success && !string.IsNullOrWhiteSpace(openResult.ErrorMsg)) { throw new ServiceException("复核审批失败"); } //生成文档 if (PS.Config.IsAutoGenerateContracts && (td.TradeStatus == ConsTrade.已平仓 || (td.TradeStatus == ConsTrade.确认成交 && td.HasPartialUnWind == 1))) { new TradeContractGenerateService(this).GenerateContractsAsync(new List { td.id }); } SaveTradeOperationHistory(td, "平仓复核"); if (notrans) { trans.Commit(); } if (td.TradeStatus == ConsTrade.已平仓 || (td.TradeStatus == ConsTrade.确认成交 && td.HasPartialUnWind == 1)) { if (PS.Config.SalesCommissionCalculation == "公式1") { //销售提成 new SalesModule.SalesCommissionDetailDataService(this).CalcuSalesCommissionDetail(tc); } if (PS.Config.Company == Configuration.CompanyEnum.招证) { new BizLogicZhaoZheng().GenerateZhaoZhengDealNumber(td, tc); } if (PS.Config.Company == Configuration.CompanyEnum.物产中大) { new BizLogicWCZD().GenerateWCZDNumber(DbContext, td, tc.ValueDate, tc.id); } } return result; } else { if (td.Warning) { new TradeDalService(OptUser).RollbackToBeforeSettle(td, req.ValueDate.Value); } result.TradeCash = UnwindTrade(td, req, isFromRecheck); //配对 平仓(不需要复核且不需要审批的平仓操作) if (req.IsUnwindPairTrade && !string.IsNullOrEmpty(td.PairTrade)) { result.HasPairTrade = true; var pairTradeId = Convert.ToInt32(td.PairTrade); //var pairTrade = DbContext.trade.Find(pairTradeId); var pairTrade = new TradeDalService(this).GetTradeOrEodTrade(new[] { pairTradeId }, valueDate, true).FirstOrDefault(); if (pairTrade.Warning) { new TradeDalService(OptUser).RollbackToBeforeSettle(pairTrade, valueDate); } //配对交易平仓总份额取各自剩余的总份额 req.Notional = pairTrade.Notional; //UnwindNotional >= Notional代表全部品仓,配对交易的平仓份额为自己剩余的总份额 //UnwindNotional < td.Notional部分平仓,配对的两笔交易部分平仓份额相等,即req.UnwindNotional不变 if (req.UnwindNotional >= pairTrade.Notional) { req.UnwindNotional = pairTrade.Notional; req.UnwindPercentRate = req.Notional > 0 ? (req.UnwindNotional / req.Notional) ?? 0 : 0; } try { UnwindTrade(pairTrade, req, isFromRecheck); result.PairTradeUnwindSuccess = true; SaveTradeOperationHistory(pairTrade, "提交平仓"); } catch (Exception ex) { result.PairTradeUnwindException = ex; LogFactory.GetLogger("平仓配对交易").Error(ex); } } //生成文档 if (PS.Config.IsAutoGenerateContracts) { var tradeIdList = new List { td.id }; if (req.IsUnwindPairTrade && result.HasPairTrade && result.PairTradeUnwindSuccess) { tradeIdList.Add(Convert.ToInt32(td.PairTrade)); } new TradeContractGenerateService(this).GenerateContractsAsync(tradeIdList); } SaveTradeOperationHistory(td, "提交平仓"); if (notrans) { trans.Commit(); } return result; } } catch { if (notrans) { trans.Rollback(); } throw; } finally { if (notrans) { trans.Dispose(); } } } /// /// 分组了结 主交易平仓复核 /// /// /// /// public void UnwindParentTrade(SaveGroupTradeCashReq req) { var parentTradeId = req.trade_cash.TradeId; var td = DbContext.trade.Find(parentTradeId); var subTradeIds = DbContext.trade_cash_group_action.Where(x => x.ParentTradeId == parentTradeId && x.Status == "待完成").Select(x => x.TradeId).ToList(); var allTd = DbContext.trade.Where(x => x.id == parentTradeId || subTradeIds.Contains(x.id)).ToList(); using (var trans = BeginTransaction()) { //平仓复核逻辑验证 if ((valuedateBLL.SystemDate.CloseReCheck == 1) && (td.TradeStatus == ConsTrade.平仓待复核 || td.TradeStatus == ConsTrade.行权待复核)) { //检查平仓单价 var tc = DbContext.trade_cash.OrderByDescending(x => x.id).FirstOrDefault(dd => dd.TradeId == req.trade_cash.TradeId && (dd.Action == ClientCashInCashOut.系统操作_平仓费 || dd.Action == ClientCashInCashOut.系统操作_行权费) && dd.ValidState == ConsGlobal.InValid && dd.Status == null && !dd.IsDeleted); if (valuedateBLL.SystemDate.UnwindAmountAngle == 0 && td.BuySell == "卖出") { req.trade_cash.Amount *= -1; } if (tc.Amount == req.trade_cash.Amount) { allTd.ForEach(x => { x.CheckStatus = Convert.ToInt32(TradeCheckEnum.StatusOfUnwind); x.UnWindDate = tc.ValueDate; }); DbContext.SaveChanges(); } else { //审核失败 throw new ServiceException("平仓费不一致!复核不通过"); } } //存在审批流程时,走审批成功流程(需求②:按交易状态查对应流程) if (valuedateBLL.SystemDate.CloseReApprove == 1 && TradeProcessCountByCategory(td) > 0) { var reqModel = new TradeOpenReqModel { tradeId = td.id, status = "pass", comments = "平仓复核审批成功", ignoreMoneyCheck = false, notNeedOperationHistory = true }; var openResult = new TradeOpenService(this).UpdateParentTradeProcessLog(reqModel); if (openResult.RetCode != TradeOpenRetCode.Success && !string.IsNullOrWhiteSpace(openResult.ErrorMsg)) { throw new ServiceException("复核审批失败"); } allTd.ForEach(x => { //生成文档 if (PS.Config.IsAutoGenerateContracts && (x.TradeStatus == ConsTrade.已平仓 || (x.TradeStatus == ConsTrade.确认成交 && x.HasPartialUnWind == 1))) { new TradeContractGenerateService(this).GenerateContractsAsync(new List { x.id }); } }); } else { td.IsApproval = false;//复核完成将审核重置 td.TradeOldStatus = td.TradeStatus; if (req.trade_cash.UnwindType == "全部平仓") { td.TradeStatus = "已平仓"; } else if (req.trade_cash.UnwindType == "部分平仓" || req.trade_cash.UnwindType == "部分提前行权") { td.TradeStatus = "确认成交"; } else if (req.trade_cash.UnwindType == "全部提前行权" || req.trade_cash.UnwindType == "到期行权") { td.TradeStatus = "已执行"; } else if (req.trade_cash.UnwindType == "到期") { td.TradeStatus = "已到期"; } var tradeCashGroupActions = DbContext.trade_cash_group_action.Where(x => x.ParentTradeId == td.id && x.Status == "待完成"); var parentTradeCashId = tradeCashGroupActions.FirstOrDefault().ParentTradeCashId; var parentTradeCash = DbContext.trade_cash.Find(parentTradeCashId); parentTradeCash.ValidState = ConsGlobal.Valid; //设置成开户状态,写日志 td.CheckTradeUpdate = Convert.ToInt32(TradeCheckEnum.StatusOfNew); td.OptId = UserId; td.OptName = UserName; td.OptDate = OptDate; td.TradeAmount -= (parentTradeCash.UnwindTradeAmount ?? 0); td.Notional -= (parentTradeCash.UnwindNotional ?? 0); td.StockEqvNotional -= (td.OriginalStockEqvNotional * parentTradeCash.UnwindPercentRate ?? 0); td.UnWindNotional = parentTradeCash.UnwindNotional; td.UnWindDate = parentTradeCash.ValueDate; td.FinalPrice = parentTradeCash.FinalPrice; var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); req.childTradeCashs.ForEach(x => { var tradeCashReq = new TradeCashReq() { ValueDate = req.trade_cash.ValueDate, TradeId = x.TradeId, TradeAmount = x.TradeAmount, Notional = x.Notional, UnwindFee = x.Amount, Amount = x.Amount, UnwindTradeAmount = x.UnwindTradeAmount, UnwindNotional = (x.UnwindTradeAmount ?? 0) * underlying.CountRatio, UnwindPercentRate = x.UnwindPercentRate ?? 0, UnwindStockEqvNotional = x.UnwindStockEqvNotional, CallPut = x.OptionType, ExceciseType = "现金", FinalPrice = req.trade_cash.FinalPrice, UnwindType = req.trade_cash.UnwindType }; if (req.trade_cash.UnwindType.Contains("到期")) { new TradeCashService(this).ExecutionTradeCash(tradeCashReq); } else { var subTrade = allTd.FirstOrDefault(y => y.id == x.TradeId); UnwindTrade(subTrade, null, true); } }); //存黑箱主交易ClientCashInCashOut需要赋值0 //TradeCash在下面FinishGroupTradeCash里会根据子交易重新统计赋值 parentTradeCash.Amount = 0; new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, parentTradeCash, parentTradeCash.ValueDate); DbContext.SaveChanges(); FinishGroupTradeCash(req.trade_cash.id);//只有复核,无审批,直接将group_action变为已完成 } trans.Commit(); } } /// /// /// public TradeUnwindResultModel ApplyUnwindSwapTrade(TradeCashReq req) { var td = DbContext.trade.Find(req.TradeId); //平仓时如果盈亏为正,则以换汇买价结算,如果盈亏为负,则以换汇卖价结算(客户方向,和TradeCashReq方向相反) var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, req.ValueDate.Value , seekPreday: true, currencyRateType: req.Amount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); req.QuoteUnwindFee = req.UnwindFee; var getQuoteInitialAmount = req.trade_cash_swap.GetInitialAmount; var payQuoteInitialAmount = req.trade_cash_swap.PayInitialAmount; var getQuoteExtraAmount = req.trade_cash_swap.GetExtraAmount; var payQuoteExtraAmount = req.trade_cash_swap.PayExtraAmount; var getQuoteCostFee = req.trade_cash_swap.GetCostFee; var payQuoteCostFee = req.trade_cash_swap.PayCostFee; req.Amount *= currencyRate; req.UnwindFee *= currencyRate; req.InitialAmount *= currencyRate; req.ExtraAmount *= currencyRate; req.trade_cash_swap.GetAmount *= currencyRate; req.trade_cash_swap.GetInitialAmount *= currencyRate; req.trade_cash_swap.GetExtraAmount *= currencyRate; req.trade_cash_swap.GetCostFee *= currencyRate; req.trade_cash_swap.PayAmount *= currencyRate; req.trade_cash_swap.PayInitialAmount *= currencyRate; req.trade_cash_swap.PayExtraAmount *= currencyRate; req.trade_cash_swap.PayCostFee *= currencyRate; req.UnwindPercentRate /= 100; if (td.StockEqvNotional / td.OriginalStockEqvNotional - req.UnwindPercentRate <= 1e-5) { req.UnwindType = "全部平仓"; req.UnwindPercentRate = td.OriginalStockEqvNotional > 0 ? td.StockEqvNotional / td.OriginalStockEqvNotional.Value : 0; } req.UnwindNotional = (td.OriginalNotional * req.UnwindPercentRate) ?? 0; req.Notional = td.Notional;//前端传回来的数量小数位不足,所以这里要重新赋值,保证不会有精度损失; req.TradeAmount = td.TradeAmount; if (req.UnwindPercentRate <= 0) { throw new Exception("平仓比例不合理"); } var result = new TradeUnwindResultModel(td); //增加现金交割交易记录 req.ValidState = ConsGlobal.InValid; var tc = CloseTrade_TradeCashSave(td, req, false, false, req.UnwindType != "部分平仓"); var tradeCash = DbContext.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= tc.ValueDate).OrderByDescending(y => y.id).FirstOrDefault(); var tradeCashSwap = tradeCash == null ? null : DbContext.trade_cash_swap.FirstOrDefault(x => x.TradeCashId == tradeCash.id); tc.StartDate = tradeCash?.ValueDate ?? td.StartDate.Value; tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; tc.CurrencyRate = currencyRate; var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId); tc.FinalPrice = trade_swap.IsGetFloatingProfit ? req.trade_cash_swap.GetFinalPrice : req.trade_cash_swap.PayFinalPrice; var hasTradeProcess = HasTradeProcess(); CloseReCheck_SetTrade(td.id, false, hasTradeProcess); var trade_cash_swap = req.trade_cash_swap; trade_cash_swap.StartDate = tc.StartDate; trade_cash_swap.GetStartPrice = tradeCashSwap?.GetFinalPrice ?? trade_swap.GetSpotPrice; trade_cash_swap.PayStartPrice = tradeCashSwap?.PayFinalPrice ?? trade_swap.PaySpotPrice; trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; DbContext.trade_cash_swap.Add(trade_cash_swap); var tcdGetInitialAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetInitialAmount, QuoteAmount = getQuoteInitialAmount, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdGetInitialAmount); var tcdGetExtraAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetExtraAmount, QuoteAmount = getQuoteExtraAmount, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdGetExtraAmount); var tcdGetCostFee = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetCostFee, QuoteAmount = getQuoteCostFee, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdGetCostFee); var tcdPayInitialAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayInitialAmount, QuoteAmount = -payQuoteInitialAmount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdPayInitialAmount); var tcdPayExtraAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayExtraAmount, QuoteAmount = -payQuoteExtraAmount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdPayExtraAmount); var tcdPayCostFee = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayCostFee, QuoteAmount = -payQuoteCostFee, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdPayCostFee); DbContext.SaveChanges(); SaveTradeOperationHistory(td, "提交平仓"); return result; } /// /// /// public TradeUnwindResultModel ApproveUnwindSwapTrade(trade td) { var result = new TradeUnwindResultModel(td); //增加现金交割交易记录 var tc = CloseTrade_TradeCashSave(td, null, true, false); var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == td.id && x.Status != "已完成"); if (groupAction != null) { groupAction.Status = "已完成"; tc.ParentTradeCashId = groupAction.ParentTradeCashId; tc.ParentTradeId = groupAction.ParentTradeId; } tc.ValidState = "Valid"; tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; //加入平仓份额和平仓日期 if (tc.UnwindType == "全部平仓") { td.TradeStatus = ConsTrade.已平仓; tc.IsLastAction = true; } else { td.TradeStatus = ConsTrade.确认成交; td.HasPartialUnWind = 1; tc.IsLastAction = false; } td.UnWindDate = tc.ValueDate; td.StockEqvNotional -= (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0); td.Notional -= (td.OriginalNotional ?? 0) * (tc.UnwindPercentRate ?? 0); //给TradeAmount赋对应的值 var CountRatio = UnderlyingDataProvider.GetCountRatio(td.UnderlyingCode); td.TradeAmount = td.Notional / CountRatio; td.UnWindNotional = tc.UnwindNotional; //增加出入金记录 new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); if (td.ParentTradeId > 0) { var parentTrade = DbContext.trade.Find(td.ParentTradeId); if (parentTrade != null) { parentTrade.StockEqvNotional -= (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0); if (parentTrade.StockEqvNotional <= 0) { parentTrade.TradeStatus = "已平仓"; } var tradeSwapDetail = DbContext.trade_swap_detail.FirstOrDefault(x => x.TradeId == parentTrade.id && x.ChildTradeId == td.id); if (tradeSwapDetail != null) { tradeSwapDetail.Notional = td.Notional; } } } DbContext.SaveChanges(); //删除E/Bod数据 var delTcIds = tc.Action == ClientCashInCashOut.系统操作_互换 ? GetRemovedTcIdsForSwapSettlement(td.id, tc) : null; RemoveEodTradeAndFutureInfo(saveChanges: true, tradeId: td.id, removeStartDate: tc.ValueDate, tradeCashIds: delTcIds); return result; } /// /// 互换交易平仓 /// /// /// 请求是否来自前端用户 /// public TradeUnwindResultModel UnwindSwapTrade(TradeCashReq req) { var trade = DbContext.trade.Find(req.TradeId); //平仓时如果盈亏为正,则以换汇买价结算,如果盈亏为负,则以换汇卖价结算(客户方向,和TradeCashReq方向相反) var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(trade.QuoteCurrency, trade.SettlementCurrency, req.ValueDate.Value , seekPreday: true, currencyRateType: req.Amount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); req.QuoteUnwindFee = req.UnwindFee; var getQuoteInitialAmount = req.trade_cash_swap.GetInitialAmount; var payQuoteInitialAmount = req.trade_cash_swap.PayInitialAmount; var getQuoteExtraAmount = req.trade_cash_swap.GetExtraAmount; var payQuoteExtraAmount = req.trade_cash_swap.PayExtraAmount; var getQuoteCostFee = req.trade_cash_swap.GetCostFee; var payQuoteCostFee = req.trade_cash_swap.PayCostFee; req.Amount *= currencyRate; req.UnwindFee *= currencyRate; req.InitialAmount *= currencyRate; req.ExtraAmount *= currencyRate; req.trade_cash_swap.GetAmount *= currencyRate; req.trade_cash_swap.GetInitialAmount *= currencyRate; req.trade_cash_swap.GetExtraAmount *= currencyRate; req.trade_cash_swap.GetCostFee *= currencyRate; req.trade_cash_swap.PayAmount *= currencyRate; req.trade_cash_swap.PayInitialAmount *= currencyRate; req.trade_cash_swap.PayExtraAmount *= currencyRate; req.trade_cash_swap.PayCostFee *= currencyRate; req.UnwindPercentRate /= 100; var td = DbContext.trade.Find(req.TradeId); if (td.StockEqvNotional / td.OriginalStockEqvNotional - req.UnwindPercentRate <= 1e-5) { req.UnwindType = "全部平仓"; req.UnwindPercentRate = td.OriginalStockEqvNotional > 0 ? td.StockEqvNotional / td.OriginalStockEqvNotional.Value : 0; } req.UnwindNotional = (td.OriginalNotional * req.UnwindPercentRate) ?? 0; req.Notional = td.Notional;//前端传回来的数量小数位不足,所以这里要重新赋值,保证不会有精度损失; req.TradeAmount = td.TradeAmount; if (req.UnwindPercentRate <= 0) { throw new Exception("平仓比例不合理"); } var result = new TradeUnwindResultModel(td); //增加现金交割交易记录 var tc = CloseTrade_TradeCashSave(td, req, false, false, req.UnwindType != "部分平仓"); var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == td.id && x.Status != "已完成"); if (groupAction != null) { groupAction.Status = "已完成"; tc.ParentTradeCashId = groupAction.ParentTradeCashId; tc.ParentTradeId = groupAction.ParentTradeId; } var tradeCash = DbContext.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= tc.ValueDate).OrderByDescending(y => y.id).FirstOrDefault(); var tradeCashSwap = tradeCash == null ? null : DbContext.trade_cash_swap.FirstOrDefault(x => x.TradeCashId == tradeCash.id); tc.StartDate = tradeCash?.ValueDate ?? td.StartDate.Value; tc.ValidState = "Valid"; tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; tc.CurrencyRate = currencyRate; var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId); tc.FinalPrice = trade_swap.IsGetFloatingProfit ? req.trade_cash_swap.GetFinalPrice : req.trade_cash_swap.PayFinalPrice; //加入平仓份额和平仓日期 if (req.UnwindType == "全部平仓") { td.TradeStatus = "已平仓"; } else { td.HasPartialUnWind = 1; } td.UnWindDate = tc.ValueDate; td.StockEqvNotional -= (td.OriginalStockEqvNotional ?? 0) * req.UnwindPercentRate; td.Notional -= (td.OriginalNotional ?? 0) * req.UnwindPercentRate; td.UnWindNotional = tc.UnwindNotional; //给TradeAmount赋对应的值 var CountRatio = UnderlyingDataProvider.GetCountRatio(td.UnderlyingCode); td.TradeAmount = td.Notional / CountRatio; //增加出入金记录 new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); td.OptId = (int)tc.OptId; td.OptName = tc.OptName; td.OptDate = DateTime.Now; var trade_cash_swap = req.trade_cash_swap; trade_cash_swap.StartDate = tc.StartDate; trade_cash_swap.GetStartPrice = tradeCashSwap?.GetFinalPrice ?? trade_swap.GetSpotPrice; trade_cash_swap.PayStartPrice = tradeCashSwap?.PayFinalPrice ?? trade_swap.PaySpotPrice; trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; DbContext.trade_cash_swap.Add(trade_cash_swap); var tcdGetInitialAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetInitialAmount, QuoteAmount = getQuoteInitialAmount, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdGetInitialAmount); var tcdGetExtraAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetExtraAmount, QuoteAmount = getQuoteExtraAmount, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdGetExtraAmount); var tcdGetCostFee = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetCostFee, QuoteAmount = getQuoteCostFee, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdGetCostFee); var tcdPayInitialAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayInitialAmount, QuoteAmount = -payQuoteInitialAmount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdPayInitialAmount); var tcdPayExtraAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayExtraAmount, QuoteAmount = -payQuoteExtraAmount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdPayExtraAmount); var tcdPayCostFee = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayCostFee, QuoteAmount = -payQuoteCostFee, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdPayCostFee); if (td.ParentTradeId > 0) { var parentTrade = DbContext.trade.Find(td.ParentTradeId); if (parentTrade != null) { parentTrade.StockEqvNotional -= (td.OriginalStockEqvNotional ?? 0) * req.UnwindPercentRate; if (parentTrade.StockEqvNotional <= 0) { parentTrade.TradeStatus = "已平仓"; } var tradeSwapDetail = DbContext.trade_swap_detail.FirstOrDefault(x => x.TradeId == parentTrade.id && x.ChildTradeId == td.id); if (tradeSwapDetail != null) { tradeSwapDetail.Notional = td.Notional; } } } #region 补充期末价格,对其部分重新赋值 trade_swap.GetFinalPrice = trade_cash_swap.GetFinalPrice; trade_swap.PayFinalPrice = trade_cash_swap.PayFinalPrice; #endregion DbContext.SaveChanges(); SaveTradeOperationHistory(td, "提交平仓"); //删除E/Bod数据 RemoveEodTradeAndFutureInfo(true, td.id, tc.ValueDate); return result; } /// /// 提交互换收益结算申请 /// public TradeUnwindResultModel ApplySwapTradeSettlement(TradeCashReq req) { var trade = DbContext.trade.Find(req.TradeId); //平仓时如果盈亏为正,则以换汇买价结算,如果盈亏为负,则以换汇卖价结算(客户方向,和TradeCashReq方向相反) var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(trade.QuoteCurrency, trade.SettlementCurrency, req.ValueDate.Value , currencyRateType: req.Amount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); req.QuoteUnwindFee = req.UnwindFee; var getQuoteInitialAmount = req.trade_cash_swap.GetInitialAmount; var payQuoteInitialAmount = req.trade_cash_swap.PayInitialAmount; var getQuoteExtraAmount = req.trade_cash_swap.GetExtraAmount; var payQuoteExtraAmount = req.trade_cash_swap.PayExtraAmount; var getQuoteCostFee = req.trade_cash_swap.GetCostFee; var payQuoteCostFee = req.trade_cash_swap.PayCostFee; req.UnwindFee *= currencyRate; req.Amount *= currencyRate; req.InitialAmount *= currencyRate; req.ExtraAmount *= currencyRate; req.trade_cash_swap.GetAmount *= currencyRate; req.trade_cash_swap.GetInitialAmount *= currencyRate; req.trade_cash_swap.GetExtraAmount *= currencyRate; req.trade_cash_swap.GetCostFee *= currencyRate; req.trade_cash_swap.PayAmount *= currencyRate; req.trade_cash_swap.PayInitialAmount *= currencyRate; req.trade_cash_swap.PayExtraAmount *= currencyRate; req.trade_cash_swap.PayCostFee *= currencyRate; req.UnwindPercentRate /= 100; var td = DbContext.trade.Find(req.TradeId); req.Notional = td.Notional;//前端传回来的数量小数位不足,所以这里要重新赋值,保证不会有精度损失; req.TradeAmount = td.TradeAmount; var result = new TradeUnwindResultModel(td); //增加现金交割交易记录 req.ValidState = ConsGlobal.InValid; var tc = CloseTrade_TradeCashSave(td, req, false, false); var tradeCash = DbContext.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= tc.ValueDate).OrderByDescending(y => y.id).FirstOrDefault(); var tradeCashSwap = tradeCash == null ? null : DbContext.trade_cash_swap.FirstOrDefault(x => x.TradeCashId == tradeCash.id); tc.StartDate = tradeCash?.ValueDate ?? td.StartDate.Value; tc.Action = ClientCashInCashOut.系统操作_互换; tc.UnwindType = null; tc.CurrencyRate = currencyRate; var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId); tc.FinalPrice = trade_swap.IsGetFloatingProfit ? req.trade_cash_swap.GetFinalPrice : req.trade_cash_swap.PayFinalPrice; if (td.ExerciseDate > req.ValueDate) { tc.UnwindNotional = 0; tc.UnwindTradeAmount = 0; tc.UnwindPercentRate = 0; tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; tc.IsLastAction = false; } else { tc.ExerciseWay = TradeCashExerciseWayEnum.到期行权; tc.IsLastAction = true; if (td.ExerciseDate == req.ValueDate) { tc.ValueDate = td.SettlementDate ?? td.ExerciseDate.Value; tc.HappenedDate = td.ExerciseDate; tc.HappenedDate = QdpCalendarHelper.GetNonHoliday(tc.HappenedDate.Value); } } var hasTradeProcess = HasTradeProcess(); CloseReCheck_SetTrade(td.id, true, hasTradeProcess); var lastGetFinalPrice = tradeCashSwap?.GetFinalPrice; var lastPayFinalPrice = tradeCashSwap?.PayFinalPrice; var trade_cash_swap = req.trade_cash_swap; trade_cash_swap.StartDate = tc.StartDate; trade_cash_swap.GetStartPrice = lastGetFinalPrice ?? trade_swap.GetSpotPrice; trade_cash_swap.PayStartPrice = lastPayFinalPrice ?? trade_swap.PaySpotPrice; trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; DbContext.trade_cash_swap.Add(trade_cash_swap); var tcdGetInitialAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetInitialAmount, QuoteAmount = getQuoteInitialAmount, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdGetInitialAmount); var tcdGetExtraAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetExtraAmount, QuoteAmount = getQuoteExtraAmount, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdGetExtraAmount); var tcdGetCostFee = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetCostFee, QuoteAmount = getQuoteCostFee, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdGetCostFee); var tcdPayInitialAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayInitialAmount, QuoteAmount = -payQuoteInitialAmount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdPayInitialAmount); var tcdPayExtraAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayExtraAmount, QuoteAmount = -payQuoteExtraAmount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdPayExtraAmount); var tcdPayCostFee = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayCostFee, QuoteAmount = -payQuoteCostFee, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdPayCostFee); DbContext.SaveChanges(); SaveTradeOperationHistory(td, "提交互换"); return result; } /// /// 审批通过互换交易申请 /// public TradeUnwindResultModel ApproveSwapTrade(trade td) { var result = new TradeUnwindResultModel(td); //增加现金交割交易记录 var tc = CloseTrade_TradeCashSave(td, null, true, false); var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == td.id && x.Status != "已完成"); if (groupAction != null) { groupAction.Status = "已完成"; tc.ParentTradeCashId = groupAction.ParentTradeCashId; tc.ParentTradeId = groupAction.ParentTradeId; } tc.ValidState = "Valid"; //加入平仓份额和平仓日期 td.UnWindDate = tc.ValueDate; if (td.ExerciseDate > tc.ValueDate) { td.TradeStatus = ConsTrade.确认成交; tc.IsLastAction = false; } else { td.TradeStatus = ConsTrade.已到期; td.Notional = 0; td.StockEqvNotional = 0; td.TradeAmount = 0; tc.IsLastAction = true; } var tradeCashSwap = DbContext.trade_cash_swap.FirstOrDefault(x => x.TradeCashId == tc.id); if (tradeCashSwap != null) { var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId); trade_swap.GetFinalPrice = tradeCashSwap.GetFinalPrice; trade_swap.PayFinalPrice = tradeCashSwap.PayFinalPrice; tc.FinalPrice = trade_swap.IsGetFloatingProfit ? tradeCashSwap.GetFinalPrice : tradeCashSwap.PayFinalPrice; } //增加出入金记录 var ee = new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); DbContext.SaveChanges(); //删除E/Bod数据 var delTcIds = tc.Action == ClientCashInCashOut.系统操作_互换 ? GetRemovedTcIdsForSwapSettlement(td.id, tc) : null; RemoveEodTradeAndFutureInfo(saveChanges: true, tradeId: td.id, removeStartDate: tc.ValueDate, tradeCashIds: delTcIds); return result; } /// /// 获取互换收益结算执行后需要删除的后续trade cash ids /// private IEnumerable GetRemovedTcIdsForSwapSettlement(int tradeId, trade_cash currentTc) { var query = from tc in DbContext.trade_cash where tc.TradeId == tradeId && tc.Action == ClientCashInCashOut.系统操作_互换 && tc.ValueDate > currentTc.ValueDate && tc.id != currentTc.id && (tc.HappenedDate == null || tc.HappenedDate > currentTc.ValueDate) && tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted select tc.id; var ids = query.ToArray(); return ids.Any() ? ids : new[] { 0 }; } public TradeUnwindResultModel SwapTrade(TradeCashReq req) { var trade = DbContext.trade.Find(req.TradeId); //平仓时如果盈亏为正,则以换汇买价结算,如果盈亏为负,则以换汇卖价结算(客户方向,和TradeCashReq方向相反) var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(trade.QuoteCurrency, trade.SettlementCurrency, req.ValueDate.Value , currencyRateType: req.Amount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); req.QuoteUnwindFee = req.UnwindFee; var getQuoteInitialAmount = req.trade_cash_swap.GetInitialAmount; var payQuoteInitialAmount = req.trade_cash_swap.PayInitialAmount; var getQuoteExtraAmount = req.trade_cash_swap.GetExtraAmount; var payQuoteExtraAmount = req.trade_cash_swap.PayExtraAmount; var getQuoteCostFee = req.trade_cash_swap.GetCostFee; var payQuoteCostFee = req.trade_cash_swap.PayCostFee; req.UnwindFee *= currencyRate; req.Amount *= currencyRate; req.InitialAmount *= currencyRate; req.ExtraAmount *= currencyRate; req.trade_cash_swap.GetAmount *= currencyRate; req.trade_cash_swap.GetInitialAmount *= currencyRate; req.trade_cash_swap.GetExtraAmount *= currencyRate; req.trade_cash_swap.PayAmount *= currencyRate; req.trade_cash_swap.PayInitialAmount *= currencyRate; req.trade_cash_swap.PayExtraAmount *= currencyRate; req.UnwindPercentRate /= 100; var td = DbContext.trade.Find(req.TradeId); req.Notional = td.Notional;//前端传回来的数量小数位不足,所以这里要重新赋值,保证不会有精度损失; req.TradeAmount = td.TradeAmount; var result = new TradeUnwindResultModel(td); //增加现金交割交易记录 var tc = CloseTrade_TradeCashSave(td, req, false, false); var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == td.id && x.Status != "已完成"); if (groupAction != null) { groupAction.Status = "已完成"; tc.ParentTradeCashId = groupAction.ParentTradeCashId; tc.ParentTradeId = groupAction.ParentTradeId; } var tradeCash = DbContext.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= tc.ValueDate).OrderByDescending(y => y.id).FirstOrDefault(); var tradeCashSwap = tradeCash == null ? null : DbContext.trade_cash_swap.FirstOrDefault(x => x.TradeCashId == tradeCash.id); tc.StartDate = tradeCash?.ValueDate ?? td.StartDate.Value; tc.ValidState = "Valid"; tc.Action = ClientCashInCashOut.系统操作_互换; tc.CurrencyRate = currencyRate; tc.UnwindType = null; var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId); tc.FinalPrice = trade_swap.IsGetFloatingProfit ? req.trade_cash_swap.GetFinalPrice : req.trade_cash_swap.PayFinalPrice; if (td.ExerciseDate > req.ValueDate) { tc.UnwindNotional = 0; tc.UnwindTradeAmount = 0; tc.UnwindPercentRate = 0; tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; tc.IsLastAction = false; } else { tc.ExerciseWay = TradeCashExerciseWayEnum.到期行权; //执行到期时,发生日期记为到期日 tc.HappenedDate = tc.ValueDate; if (tc.ValueDate == td.ExerciseDate) { //非延期交易,如果有结算日期,资金记在结算日 tc.ValueDate = td.SettlementDate ?? tc.ValueDate; } tc.UnwindNotional = td.Notional; tc.UnwindTradeAmount = td.TradeAmount; tc.UnwindPercentRate = td.StockEqvNotional / td.OriginalStockEqvNotional; td.Notional = 0; td.StockEqvNotional = 0; td.TradeAmount = 0; td.TradeStatus = "已到期"; tc.IsLastAction = true; if (td.ParentTradeId > 0) { var parentTrade = DbContext.trade.Find(td.ParentTradeId); if (parentTrade != null) { parentTrade.StockEqvNotional -= (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0); if (parentTrade.StockEqvNotional <= 0) { parentTrade.TradeStatus = "已平仓"; } } } } //加入平仓份额和平仓日期 td.UnWindDate = tc.ValueDate; var lastGetFinalPrice = tradeCashSwap?.GetFinalPrice; var lastPayFinalPrice = tradeCashSwap?.PayFinalPrice; trade_swap.GetFinalPrice = req.trade_cash_swap.GetFinalPrice; trade_swap.PayFinalPrice = req.trade_cash_swap.PayFinalPrice; //增加出入金记录 var ee = new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); var trade_cash_swap = req.trade_cash_swap; trade_cash_swap.StartDate = tc.StartDate; trade_cash_swap.GetStartPrice = lastGetFinalPrice ?? trade_swap.GetSpotPrice; trade_cash_swap.PayStartPrice = lastPayFinalPrice ?? trade_swap.PaySpotPrice; trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; DbContext.trade_cash_swap.Add(trade_cash_swap); var getAmount = trade_cash_swap.GetAmount ?? 0; var getExtraAmount = trade_cash_swap.GetExtraAmount ?? 0; var tcdGetInitialAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetInitialAmount, QuoteAmount = getQuoteInitialAmount, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdGetInitialAmount); var tcdGetExtraAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetExtraAmount, QuoteAmount = getQuoteExtraAmount, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdGetExtraAmount); var tcdGetCostFee = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = trade_cash_swap.GetCostFee, QuoteAmount = getQuoteCostFee, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdGetCostFee); var tcdPayInitialAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayInitialAmount, QuoteAmount = -payQuoteInitialAmount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdPayInitialAmount); var tcdPayExtraAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayExtraAmount, QuoteAmount = -payQuoteExtraAmount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdPayExtraAmount); var tcdPayCostFee = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -trade_cash_swap.PayCostFee, QuoteAmount = -payQuoteCostFee, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdPayCostFee); DbContext.SaveChanges(); SaveTradeOperationHistory(td, "收益互换"); //删除E/Bod数据 var delTcIds = GetRemovedTcIdsForSwapSettlement(td.id, tc); RemoveEodTradeAndFutureInfo(saveChanges: true, tradeId: td.id, removeStartDate: tc.ValueDate, tradeCashIds: delTcIds); return result; } /// /// 黑箱交易主交易tradecash保存子交易了结费用加和的值 /// clientcashincashout保存一条0的记录 /// 主要为了解决我们是按照子交易进行结算,但是客户结算报告,又想以主交易来展示主交易资金 /// /// public void SaveGroupTradeCash(SaveGroupTradeCashReq req) { if (DbContext.trade_cash.Any(x => x.TradeId == req.trade_cash.TradeId && x.Status == null && x.ValidState != "InValid" && x.Action != "系统操作-期权费" && !x.IsDeleted)) { throw new Exception("上一次分组了结还未完成,完成后才可以继续后需了结操作"); } //如果选择的是主交易,则代表所有活着的子交易都参与结算 bool parentTrade = false; if (req.tradeIds.Count == 1 && req.tradeIds.First() == req.trade_cash.TradeId) { parentTrade = true; req.tradeIds = DbContext.trade.Where(x => x.ValidState != "InValid" && x.ParentTradeId == req.trade_cash.TradeId && x.TradeStatus == "确认成交").Select(x => x.id).ToList(); if (!req.tradeIds.Any()) { throw new Exception("该分组交易没有可以了结操作的子交易,添加失败"); } } var action = req.trade_cash.Action; var trade_cash = req.trade_cash; var trade = DbContext.trade.Find(trade_cash.TradeId); trade_cash.OptId = UserId; trade_cash.OptName = UserName; trade_cash.OptDate = DateTime.Now; trade_cash.ValidState = ConsGlobal.InValid; var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode); if (trade.IsUsePremiumRate == true) { if (trade_cash.IsUnwindStockEqvNotional == true) { trade_cash.UnwindPercentRate = trade_cash.UnwindStockEqvNotional / trade.OriginalStockEqvNotional; } else { trade_cash.UnwindPercentRate = trade_cash.UnwindPercentRate / 100; } trade_cash.UnwindNotional = trade.OriginalNotional * trade_cash.UnwindPercentRate; trade_cash.UnwindTradeAmount = trade_cash.UnwindNotional / underlying.CountRatio; } else { trade_cash.UnwindNotional = trade_cash.UnwindTradeAmount * underlying.CountRatio; trade_cash.UnwindPercentRate = trade.OriginalNotional != 0 ? trade_cash.UnwindNotional / trade.OriginalNotional : 0; } bool isFromExercise = true; if (trade_cash.Action == "全部平仓") { trade_cash.Action = ClientCashInCashOut.系统操作_平仓费; trade_cash.IsLastAction = true; trade_cash.ExerciseWay = "提前终止行权"; trade_cash.UnwindType = "全部平仓"; trade.TradeStatus = "已平仓"; isFromExercise = false; } else if (trade_cash.Action == "部分平仓") { trade_cash.Action = ClientCashInCashOut.系统操作_平仓费; trade_cash.IsLastAction = false; trade_cash.ExerciseWay = "提前终止行权"; trade_cash.UnwindType = "部分平仓"; isFromExercise = false; if (trade_cash.UnwindNotional > trade.Notional) { throw new Exception("部分平仓,平仓数量需要小于剩余数量"); } } else if (trade_cash.Action == "部分提前行权") { trade_cash.Action = ClientCashInCashOut.系统操作_平仓费; trade_cash.IsLastAction = false; trade_cash.ExerciseWay = "提前终止行权"; trade_cash.UnwindType = "部分行权"; if (trade_cash.UnwindNotional >= trade.Notional) { throw new Exception("部分行权,行权数量需要小于剩余数量"); } } else if (trade_cash.Action == "全部提前行权") { trade_cash.Action = ClientCashInCashOut.系统操作_行权费; trade_cash.IsLastAction = true; trade_cash.ExerciseWay = "提前终止行权"; trade_cash.UnwindType = "全部行权"; trade.TradeStatus = "已执行"; } else if (trade_cash.Action == "到期行权") { trade_cash.Action = ClientCashInCashOut.系统操作_行权费; trade_cash.IsLastAction = true; trade_cash.ExerciseWay = "到期行权"; trade_cash.UnwindType = "到期行权"; trade.TradeStatus = "已执行"; } else if (trade_cash.Action == "到期") { trade_cash.Action = ClientCashInCashOut.系统操作_行权费; trade_cash.IsLastAction = true; trade_cash.ExerciseWay = "到期行权"; trade_cash.UnwindType = "到期"; trade.TradeStatus = "已到期"; } using (var trans = BeginTransaction()) { if (valuedateBLL.SystemDate.UnwindAmountAngle == 0 && trade.BuySell == "卖出") { trade_cash.Amount *= -1; } DbContext.trade_cash.Add(trade_cash); DbContext.SaveChanges(); var hasProcess = DbContext.approvalprocess.Where(t => t.processType == "TradeProcess").Any(); var childTradeId = 0; req.tradeIds.ForEach(x => { if (x != req.trade_cash.TradeId && x != 0) { var groupAction = new trade_cash_group_action() { TradeId = x, ParentTradeId = trade_cash.TradeId, ParentTradeCashId = trade_cash.id, Status = "待完成", OptId = UserId, OptName = UserName, OptDate = DateTime.Now }; if ((valuedateBLL.SystemDate.CloseReCheck == 1 || (valuedateBLL.SystemDate.CloseReApprove == 1 && hasProcess)) && !parentTrade) { UpdateTradeApproval(x); } childTradeId = x; DbContext.trade_cash_group_action.Add(groupAction); } }); trade.IsApproval = parentTrade && !req.from_group_options_close_start_v1; if (req.childTradeCashs != null && req.childTradeCashs.Count > 0) { SaveGroupChildTradeCashs(req, underlying.CountRatio, hasProcess, trade_cash.id, trade.TradeStatus, trans); } if ((valuedateBLL.SystemDate.CloseReCheck == 1 || (valuedateBLL.SystemDate.CloseReApprove == 1 && hasProcess)) && parentTrade)//主交易进审批流 { new TradeCloseService(this).CloseReCheck_SetTrade(trade.id, isFromExercise, true); } else { #region---存入ClientCashInCashOut--- var cl = ClientDataQueryService.GetClient(trade.ClientId, true); var ee = new ClientCashInCashOut { Direction = "应收", Number = UniqueTimeId.GetStr(), ClientId = cl.id, ClientNumber = cl.Number, ClientName = cl.Name, HappenDate = trade_cash.ValueDate, State = ClientCashInCashOut.已确认, OptId = trade_cash.OptId, OptName = trade_cash.OptName, OptDate = trade_cash.OptDate, CreatorId = trade_cash.OptId, CreatorName = trade_cash.OptName, CreateDate = trade_cash.OptDate, TradeId = trade_cash.TradeId, TradeCashId = trade_cash.id, Action = trade_cash.Action, TradeNumber = trade.TradeNumber, IsGroup = trade.IsGroup }; DbContext.ClientCashInCashOut.Add(ee); #endregion trade.TradeAmount -= (trade_cash.UnwindTradeAmount ?? 0); trade.Notional -= (trade_cash.UnwindNotional ?? 0); trade.StockEqvNotional -= (trade.OriginalStockEqvNotional * trade_cash.UnwindPercentRate ?? 0); trade.UnWindNotional = trade_cash.UnwindNotional; trade.UnWindDate = trade_cash.ValueDate; trade.FinalPrice = req.trade_cash.FinalPrice; trade_cash.ValidState = ConsGlobal.Valid; if (parentTrade) //无复核无审批,group_action直接完成 { FinishGroupTradeCash(trade_cash.id); } } AddTradeOperationHistoryAndSetParentTradeInfo(false, trade, action); //删除E/Bod数据 RemoveEodTradeAndFutureInfo(false, trade.id, req.trade_cash.ValueDate); DbContext.SaveChanges(); trans.Commit(); } } /// /// 修改交易分组了结 标识 /// /// private void UpdateTradeApproval(int tradeId) { var trade = DbContext.trade.Find(tradeId); trade.IsApproval = true; } public void SaveGroupChildTradeCashs(SaveGroupTradeCashReq req, int countRatio, bool hasProcess, int parentTradeCashId, string tradeStatus, IDbContextTransaction trans) { req.childTradeCashs.ForEach(x => { trade td = DbContext.trade.Where(l => l.TraderId == x.id).FirstOrDefault(); var unwindPrice = x.UnwindTradeAmount == 0 ? 0 : x.Amount / x.UnwindTradeAmount; var tradeCashReq = new TradeCashReq() { ValueDate = req.trade_cash.ValueDate, TradeId = x.TradeId, TradeAmount = x.TradeAmount, Notional = x.Notional, UnwindFee = x.Amount, Amount = x.Amount, UnwindPrice = unwindPrice, UnwindPricePercentRate = TradeHelper.GetPremiumRateByTradeSinglePrice(unwindPrice, td?.SpotPrice), UnwindTradeAmount = x.UnwindTradeAmount, UnwindNotional = (x.UnwindTradeAmount ?? 0) * countRatio, UnwindPercentRate = x.UnwindPercentRate ?? 0, UnwindStockEqvNotional = x.UnwindStockEqvNotional, CallPut = x.OptionType, ExceciseType = "现金", FinalPrice = req.trade_cash.FinalPrice, UnwindType = req.trade_cash.UnwindType, ParentTradeCashId = parentTradeCashId, TradeStatus = tradeStatus }; if (valuedateBLL.SystemDate.CloseReCheck == 1 || (valuedateBLL.SystemDate.CloseReApprove == 1 && hasProcess)) { if (req.trade_cash.UnwindType.Contains("到期")) { tradeCashReq.IsExpire = true; tradeCashReq.ValidState = ConsGlobal.InValid; new TradeExerciseService(this).ApplyExerciseRecheck(tradeCashReq); } else { new TradeCloseService(this).CloseReCheck(tradeCashReq, false); } } else { if (req.trade_cash.UnwindType.Contains("到期")) { new TradeCashService(this).ExecutionTradeCash(tradeCashReq); } else { UnwindTrade(tradeCashReq, trans); } } }); } public void FinishGroupTradeCash(int TcId) { var tradeCash = DbContext.trade_cash.Find(TcId); if (tradeCash != null) { tradeCash.Status = "已执行"; var childrenTradeCash = DbContext.trade_cash.Where(x => x.ParentTradeCashId == TcId).ToList(); tradeCash.Amount = childrenTradeCash.Sum(x => x.Amount); tradeCash.UnwindPrice = tradeCash.UnwindNotional != 0 ? tradeCash.Amount / tradeCash.UnwindNotional : 0; var trade = DbContext.trade.Find(tradeCash.TradeId); tradeCash.UnwindPricePercentRate = TradeHelper.GetPremiumRateByTradeSinglePrice(tradeCash.UnwindPrice, trade.SpotPrice); var groupActions = DbContext.trade_cash_group_action.Where(x => x.ParentTradeCashId == TcId).ToList(); groupActions.ForEach(x => x.Status = "已完成"); DbContext.SaveChanges(); } else { throw new Exception("未找到该笔了结记录"); } } public void CloseReCheck_SetTrade(int tradeId, bool isSwap, bool hasTradeProcess) { var td = DbContext.trade.Find(tradeId); td.OptId = UserId; td.OptName = UserName; td.OptDate = OptDate; if (td.TradeStatus!=ConsTrade.确认成交) { throw new Exception("当前交易状态不是确认成交"); } if (isSwap) { td.TradeStatus = ConsTrade.互换待复核; } else { td.TradeStatus = ConsTrade.平仓待复核; } td.CheckStatus = Convert.ToInt32(TradeCheckEnum.StatusOfOld); //检查是否有交易审批流程(需求②:按交易状态查对应流程,而非只查开仓流程) var needApproval = (valuedateBLL.SystemDate.CloseReApprove == 1 || isSwap) && TradeProcessCountByCategory(td) > 0; if (needApproval) { // 需求①:进入审批前先判断触发条件——若所有节点都不满足触发条件,跳过审批。 needApproval = NeedApprovalByTrigger(td); } if (needApproval) { // 需要审批:进入审批流程等待人工审批 InitTradeProcessOrder(td, UserId); AddTradeOperationHistoryAndSetParentTradeInfo(false, td, isSwap ? "互换审核提交" : "平仓审核提交"); } else { // 不需要审批:只标记 ProcessOrderId=审批通过。 // 互换的 swap_event 记录要等调用方 ApplySwapTrade 的 SaveSwapDeal 执行后才创建, // 所以互换不在这里调 UpdateTradeProcessLog(否则 FindSwapEvent 找不到记录), // 由 ApplySwapTrade 在 SaveSwapDeal 之后判断并触发审批通过流程。 td.ProcessOrderId = ProcessTradeLog.审批通过; } } } /// /// 交易平仓操作结果 /// public class TradeUnwindResultModel { public trade Trade; public trade_cash TradeCash; /// /// 是否复核审批 /// public bool ApprovalProcess; //---------------------------- //以下是平仓处理结果 /// /// 是否有配对交易 /// public bool HasPairTrade; /// /// 是否配对交易平仓成功 /// public bool PairTradeUnwindSuccess; /// /// 配对交易平仓出错消息 /// public Exception PairTradeUnwindException; public TradeUnwindResultModel(trade trade) { Trade = trade; } } }