using BaseOUDAL; using Confluent.Kafka; using DocumentFormat.OpenXml.Wordprocessing; using Microsoft.Office.Interop.Excel; using CsvHelper; using Newtonsoft.Json; using Qdp.Foundation.Utilities; using YLErp.Abstract; using YLErp.BLL; using YLErp.BLL.Eod; using YLErp.Commons; using YLErp.DBModels; using YLErp.DBModels.Enums; using YLErp.Enums; using YLErp.Helpers; using YLErp.Model; using YLErp.Model.HengTaiModel; using YLErp.Models; using YLErp.Modules.AppModule; using YLErp.Modules.ClientModule; using YLErp.Modules.ExchangeTradeModule; using YLErp.Modules.TradeModule; using static YLErp.ConsGlobal; namespace YLErp.Modules.SwapModule { public class SwapConsumerService : YLBaseService { static readonly IYcLogger logger = LogFactory.GetLogger(); private IKafkaProduce kafkaProduceHelper; private string reqMarginInsert = string.Empty; public SwapConsumerService(OptUserInfo optUser) : base(optUser) { reqMarginInsert = Environment.GetEnvironmentVariable("KafkaConfig_ReqMarginInsertTopic"); } public void SetKafKaProduce(IKafkaProduce kafkaProduce) { kafkaProduceHelper = kafkaProduce; } /// /// 4.8.1. 消费收益互换回执交易接口 /// /// public void ConsumerTradeResp(KafkaConsumerHelper _kafkaConsumer) { _kafkaConsumer.Subscribe(msg => { if (!string.IsNullOrEmpty(msg)) { var result = JsonHelper.Deserialize>(msg); var pushLogService = new PushLogService(UserInfo); var serialNo = result.HEADER.SERIAL_NO; var extNo = result.HEADER.EXT_NO; var pushLog = pushLogService.GetLogBySerialNo(serialNo, (int)SwapPushDataEnum.收益互换交易回执); var resultState = SwapPushDataStateEnum.接收处理成功; if (pushLog != null && pushLog.data_state == (int)resultState) { return; } string dealResult = ""; if (result.RECORD == null) { resultState = SwapPushDataStateEnum.接收处理失败; dealResult = "收益互换信息不能为空"; } else { if (string.IsNullOrEmpty(extNo) && result.RECORD.BASEINFO != null) { extNo = result.RECORD.BASEINFO.EXT_NO; } try { var trade = DbContext.trade.FirstOrDefault(x => x.TradeNumber == extNo); if (trade != null) { var HTStatus = TryFormatInt(result.RECORD.BASEINFO.ORDSTATUS); trade.HTStatus = HTStatus; DbContext.SaveChanges(); if (HTStatus != 7) { return; } DealUpdateConsumerTrade(result.RECORD, trade); } else { throw new Exception($"交易{extNo}不存在"); } } catch (Exception e) { resultState = SwapPushDataStateEnum.接收处理失败; dealResult = e.Message; LogFactory.GetLogger("SwapConsumerService").Error("消费收益互换回执交易接口", e); } } pushLogService.SaveConsumerLog(serialNo, extNo, SwapPushDataEnum.收益互换交易回执, resultState, msg, dealResult); } }); } /// /// 4.8.2. 消费互换资产交易回执接口 /// /// public void ConsumerTradePosiResp(KafkaConsumerHelper _kafkaConsumer) { _kafkaConsumer.Subscribe(msg => { if (!string.IsNullOrEmpty(msg)) { var result = JsonHelper.Deserialize>(msg); var pushLogService = new PushLogService(UserInfo); var serialNo = result.HEADER.SERIAL_NO; var extNo = result.HEADER.EXT_NO; var pushLog = pushLogService.GetLogBySerialNo(serialNo, (int)SwapPushDataEnum.互换资产交易回执); var resultState = SwapPushDataStateEnum.接收处理成功; string dealResult = ""; if (pushLog != null && pushLog.data_state == (int)resultState) { return; } if (result.RECORD == null || result.RECORD.BASEINFO == null) { resultState = SwapPushDataStateEnum.接收处理失败; dealResult = "找不到合约编号"; } else { try { var account = result.RECORD.BASEINFO.SECU_INT; var HTAccounts = DbContext.exchange_account.Where(x => !string.IsNullOrEmpty(x.AccountCode)).Select(s => s.AccountCode).Distinct().ToList(); if (HTAccounts.Contains(account)) { PrepareExchangeTradeData(result.RECORD,"衡泰同步"); return; } if (OtcAppConfigHelper.HTUpdate != 1) { return; } var tradeNumber = result.RECORD.BASEINFO.TRD_RELATED_NO; var HTStatus = TryFormatInt(result.RECORD.BASEINFO.ORDSTATUS); var trade = CheckTradeExist(tradeNumber); // 衡泰状态不是成交确认或者 当前交易有平仓,不给修改,如日内平仓,或者dma部分平仓 if (HTStatus != 7) { return; } if (pushLog == null && !CheckPositionNumber(extNo)) { if (long.TryParse(extNo, out long dataId))//平仓 { pushLog = pushLogService.GetLogByDataId(dataId); if (pushLog != null)//我方平仓,不做操作 { extNo = pushLog.data_number; } } else //衡泰平仓 { var posiGrossPrice = TryFormatDecimal(result.RECORD.TRADEINFO.ORDPRICE) / 100; var orderDate = TryFormatDate(result.RECORD.BASEINFO.ORDDATE).Date; dealResult = DealUnwindPosiEvent(posiGrossPrice, trade, orderDate); } } else if (trade.UnWindDate == null) { PreparePosiData(result.RECORD, trade, extNo); } } catch (Exception e) { resultState = SwapPushDataStateEnum.接收处理失败; dealResult = e.Message; LogFactory.GetLogger("SwapConsumerService").Error("消费互换资产交易回执接口", e); } } new PushLogService(UserInfo).SaveConsumerLog(serialNo, extNo, SwapPushDataEnum.互换资产交易回执, resultState, msg, dealResult); } }); } /// /// 对冲交易回执接口 /// /// public void ConsumerExchangeTradeResp(KafkaConsumerHelper _kafkaConsumer,string tradeSource) { _kafkaConsumer.Subscribe(msg => { var hedgingSource = OtcAppConfigHelper.HedgingSource == 2 || OtcAppConfigHelper.HedgingSource == 0; if (hedgingSource) { return; } if (!string.IsNullOrEmpty(msg)) { var result = JsonHelper.Deserialize(msg); if (result == null) { return; } try { var account = result.SECU_INT; var HTAccounts = DbContext.exchange_account.Where(x => !string.IsNullOrEmpty(x.AccountCode)).Select(s => s.AccountCode).Distinct().ToList(); if (HTAccounts.Contains(account)) { PrepareExchangeTradeData(result, tradeSource); return; } } catch (Exception e) { LogFactory.GetLogger("SwapConsumerService").Error("对冲交易回执接口", e); } } }); } /// /// Trs对冲交易接口 /// /// public void ConsumerTrsExchangeTradeResp(KafkaConsumerHelper _kafkaConsumer, string tradeSource) { _kafkaConsumer.Subscribe(msg => { var hedgingSource = OtcAppConfigHelper.HedgingSource==1|| OtcAppConfigHelper.HedgingSource==0; if (hedgingSource) { return; } if (!string.IsNullOrEmpty(msg)) { var result = JsonHelper.Deserialize(msg); if (result == null) { return; } try { var account = result.SECU_INT; var HTAccounts = DbContext.exchange_account.Where(x => !string.IsNullOrEmpty(x.AccountCode)).Select(s => s.AccountCode).Distinct().ToList(); if (HTAccounts.Contains(account)) { PrepareExchangeTradeData(result, tradeSource); Task.Run(() => { RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service)); }); return; } } catch (Exception e) { LogFactory.GetLogger("SwapConsumerService").Error("TRS对冲交易回执接口", e); } } }); } /// /// 4.8.3. 消费预付金交易回执接口 /// /// public void ConsumerTradeMarginResp(KafkaConsumerHelper _kafkaConsumer) { _kafkaConsumer.Subscribe(msg => { if (!string.IsNullOrEmpty(msg)) { var result = JsonHelper.Deserialize>(msg); var pushLogService = new PushLogService(UserInfo); var serialNo = result.HEADER.SERIAL_NO; var extNo = result.HEADER.EXT_NO; var pushLog = pushLogService.GetLogBySerialNo(serialNo, (int)SwapPushDataEnum.预付金交易回执); var resultState = SwapPushDataStateEnum.接收处理成功; if (pushLog != null && pushLog.data_state == (int)resultState) { return; } string dealResult = ""; if (result.RECORD == null || result.RECORD.RESERVE == null) { resultState = SwapPushDataStateEnum.接收处理失败; dealResult = "找不到合约编号"; } else { try { if (OtcAppConfigHelper.HTUpdate != 1) { return; } //处理回执 var trade = CheckTradeExist(result.RECORD.RESERVE.CONTRACT_CODE); var HTStatus = TryFormatInt(result.RECORD.BASEINFO.ORDSTATUS); // 衡泰状态不是成交确认或者 当前交易有平仓,不给修改,如日内平仓,或者dma部分平仓 if (HTStatus != 7) { return; } if (pushLog == null && !CheckPositionNumber(extNo))//可能平仓 { if (long.TryParse(extNo, out long dataId))//平仓 { pushLog = pushLogService.GetLogByDataId(dataId); if (pushLog != null)// 我方平仓 { extNo = pushLog.data_number; } } else //衡泰平仓 { PrepareUnwindMarginData(result.RECORD, trade); } } else if (trade.UnWindDate == null) { PrepareMarginData(result.RECORD, trade, extNo); } } catch (Exception e) { resultState = SwapPushDataStateEnum.接收处理失败; dealResult = e.Message; LogFactory.GetLogger("SwapConsumerService").Error("消费预付金交易回执接口", e); } } new PushLogService(UserInfo).SaveConsumerLog(serialNo, extNo, SwapPushDataEnum.预付金交易回执, resultState, msg, dealResult); } }); } /// /// 4.8.4. 消费合约终止交易回执接口 /// /// public void ConsumerTradeUnwindResp(KafkaConsumerHelper _kafkaConsumer) { _kafkaConsumer.Subscribe(msg => { if (!string.IsNullOrEmpty(msg)) { var result = JsonHelper.Deserialize>(msg); var pushLogService = new PushLogService(UserInfo); var serialNo = result.SERIAL_NO; var pushLog = pushLogService.GetLogBySerialNo(serialNo, (int)SwapPushDataEnum.合约终止交易回执); var resultState = SwapPushDataStateEnum.接收处理成功; if (pushLog != null && pushLog.data_state == (int)resultState) { return; } string dealResult = ""; string extNo = result.EXT_NO; try { //处理回执 if (result.RECORD.ACCTSWAP_TERMINATE==null) { return; } extNo = result.RECORD.ACCTSWAP_TERMINATE.CONTRACT_CODE; var trade = CheckTradeExist(extNo); var HTStatus = TryFormatInt(result.RECORD.ACCTSWAP_TERMINATE.ORDSTATUS); if (HTStatus!=7) { return; } PrepareUnwindData(result.RECORD, trade); } catch (Exception e) { resultState = SwapPushDataStateEnum.接收处理失败; dealResult = e.Message; LogFactory.GetLogger("SwapConsumerService").Error("消费合约终止交易回执接口", e); } pushLogService.SaveConsumerLog(serialNo, result.EXT_NO, SwapPushDataEnum.合约终止交易回执, resultState, msg, dealResult); } }); } /// /// 4.8.6. 消费收益互换资金流水数据回执接口 /// /// public void ConsumerTradeCashResp(KafkaConsumerHelper _kafkaConsumer) { _kafkaConsumer.Subscribe(msg => { if (!string.IsNullOrEmpty(msg)) { var result = JsonHelper.Deserialize>(msg); var pushLogService = new PushLogService(UserInfo); var serialNo = result.HEADER.SERIAL_NO; var pushLog = pushLogService.GetLogBySerialNo(serialNo, (int)SwapPushDataEnum.收益互换资金流水数据回执); var resultState = SwapPushDataStateEnum.接收处理成功; if (pushLog != null && pushLog.data_state == (int)resultState) { return; } string dealResult = ""; List clientIds = new List(); try { var extNos = result.RECORD.ACCTSWAP_CASH_LIST.Where(x => !string.IsNullOrEmpty(x.EXT_NO)).Select(s => s.EXT_NO).ToList(); var trades = CheckTradeExistByList(extNos); //处理回执 clientIds = PrepareTradeCashData(result.RECORD, trades); } catch (Exception e) { resultState = SwapPushDataStateEnum.接收处理失败; dealResult = e.Message; LogFactory.GetLogger("SwapConsumerService").Error("消费收益互换资金流水数据回执接口", e); } pushLogService.SaveConsumerLog(serialNo, "", SwapPushDataEnum.收益互换资金流水数据回执, resultState, msg, dealResult); //if (clientIds.Count>0) //{ // PushClientCash(clientIds, onRspAccountCapitalTopicTopic); //} } }); } public bool CheckPositionNumber(string extNo) { return DbContext.swap_position.Any(x => x.PosiNumber == extNo); } /// /// 收盘 收益互换回执查衡泰库返回 /// /// public void ConsumerIRSDb(KafkaConsumerHelper _kafkaConsumer) { _kafkaConsumer.Subscribe(msg => { if (!string.IsNullOrEmpty(msg)) { var result = JsonHelper.Deserialize(msg); var respMappers = GetHTDbRespMapperData(result); } }); } /// /// 客户资金推送 /// /// public void ConsumerCashNotice(KafkaConsumerHelper _kafkaConsumer) { _kafkaConsumer.Subscribe(msg => { if (!string.IsNullOrEmpty(msg)) { var result = JsonHelper.Deserialize(msg); if (result!=null) { PushCashToHT(result.cashId, null); } } }); } /// /// 对返回数据库数据进行映射 /// /// /// /// private List GetHTDbRespMapperData(IRDbRespBase iRDbResp) where T : new() { List values = new List(); string[] fields = iRDbResp.Header.Split(','); var records = iRDbResp.Records; for (int j = 0; j < records.Count; j++) { string[] recordFields = records[j].Split(','); T iRSDb = Activator.CreateInstance(); for (int i = 0; i < fields.Length; i++) { string fieldName = fields[i]; string fieldValue = recordFields[i]; typeof(T).GetProperty(fieldName)?.SetValue(iRSDb, fieldValue); values.Add(iRSDb); } } return values; } #region 交易回执相关私有方法 /// /// 修改交易 /// /// private void DealUpdateConsumerTrade(SwapTradeReq swapTradeReq, trade trade) { var HTStatus = trade.HTStatus; if (OtcAppConfigHelper.HTUpdate != 1) { return; } //0新建、-4交易执行中、-3风险预审中、7成交确认、9交易撤单、1审批中、5审批通过、4审批拒绝、10交易终止 if (HTStatus == 9) { new SwapTradeService(UserInfo).deleteTrade(trade.id, false); return; } // 衡泰状态不是成交确认或者 当前交易有平仓,不给修改,如日内平仓,或者dma部分平仓 if (trade.UnWindDate!=null) { throw new Exception($"交易{trade.TradeNumber}已经产生平仓信息,不支持再修改"); } ValidateTrade(swapTradeReq, trade); trade.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == trade.id); PrepareTradeData(swapTradeReq, trade, false); if (trade.TradeStatus == ConsTrade.确认成交) { AfterUpdateDeal(trade, (int)SwapEventTypeEnum.修改交易, "接口修改交易"); } } /// /// 互换交易数据准备 /// /// /// private void PrepareTradeData(SwapTradeReq swapTradeReq, trade trade, bool isAdd) { var tradeDirection = swapTradeReq.BASEINFO.TRDTYPE == "151" ? (int)SwapDirectionEnum.收取 : (int)SwapDirectionEnum.支付; var positions = DbContext.swap_position.Where(x => x.SwapTradeId == trade.id && x.IsInitial && !x.Invalid).ToList(); positions.ForEach(x => { x.PosiStartDate = trade.StartDate.Value; x.PosiMatuirityDate = trade.ExerciseDate.Value; }); var interestList = positions.Where(x => ConsTrade.InterestModels.Contains(x.InterestMode)).ToList(); var posiList = positions.Where(x => x.InterestDirection == 0).ToList(); interestList.ForEach(x => { x.PosiStartDate = trade.StartDate.Value; x.PosiMatuirityDate = trade.ExerciseDate.Value; }); var floatRateInterest = positions.FirstOrDefault(x => !string.IsNullOrEmpty(x.FloatRateUnderlyingCode)); swap_position interest; if (interestList.Any()) { interest = interestList[0]; } else { throw new Exception($"交易{trade.TradeNumber}找不到固定端信息"); } var rate = swapTradeReq.ACCTSWAP.REGULAR_INFO.SPREAD; if (!string.IsNullOrEmpty(swapTradeReq.ACCTSWAP.REGULAR_INFO.RATE)) { var rated= TryFormatDecimal(rate); if (rated!=0) { rate = swapTradeReq.ACCTSWAP.REGULAR_INFO.RATE; } } interest.InterestRateDefault = TryFormatDecimal(rate); interest.InterestDirection = tradeDirection; if (!string.IsNullOrEmpty(swapTradeReq.ACCTSWAP.IR_I_CODE)) { interest.FloatRateUnderlyingCode = swapTradeReq.ACCTSWAP.IR_I_CODE; interest.IsAnnualized = true; } else { interest.IsAnnualized = interest.InterestRateDefault != 0; } interest.InterestType = swapTradeReq.ACCTSWAP.FLOAT_METHOD == "0" ? (int)InterestTypeEnum.单利 : (int)InterestTypeEnum.复利; if (trade.trade_extend != null) { var calcMode = GetInterestRule(swapTradeReq.ACCTSWAP.FIXED_INTEREST_RULE); TradeExtendJson tradeExtendJson = trade.trade_extend.ExtendObj; tradeExtendJson.InterestCalcMode = calcMode; trade.trade_extend.ExtendJson = JsonHelper.Serialize(tradeExtendJson); if (isAdd) { DbContext.trade_extend.Add(trade.trade_extend); } } var rateDate = TryFormatDate(swapTradeReq.ACCTSWAP.REGULAR_INFO.INTEREST_END_DAY); IntervalModel intervalModel = new IntervalModel() { Date = rateDate, Rate = interest.InterestRateDefault, Settlement = 0 }; List intervalModels = new List { intervalModel }; interest.InterestSwapInterval = JsonHelper.Serialize(intervalModels); if (interest.id == 0) { DbContext.swap_position.Add(interest); } DbContext.SaveChanges(); var observation = DbContext.trade_obervation.FirstOrDefault(x => x.PositionId == interest.id); if (observation == null) { observation = new TradeObervation() { PositionId = interest.id, IsDeductPrincipal = true, ObservationAlignEnd = true, ObservationNum = 1, ObservationUnit = "D", ObservationHolidayType = "Following", ObservationStart = trade.TradeDate }; } observation.ObservationInterval = interest.InterestSwapInterval; if (observation.id == 0) { DbContext.trade_obervation.Add(observation); } DbContext.SaveChanges(); } /// /// 校验交易信息 /// /// /// /// private Client ValidateTrade(SwapTradeReq swapTradeReq, trade td) { //CheckTradeStatus(td); if (string.IsNullOrEmpty(swapTradeReq.BASEINFO.PARTY_NAME)) { throw new ServiceException($"参数PARTY_NAME为空"); } Client client = null; string partyNo = string.Empty; client = DataCacheProvider.GetClientDataSource().AsQueryable().FirstOrDefault(x => x.Name == swapTradeReq.BASEINFO.PARTY_NAME); partyNo = swapTradeReq.BASEINFO.PARTY_NAME; if (client == null) { throw new ServiceException($"交易对手方{partyNo}信息不存在"); } td.ClientId = client.id; td.ClientName = client.Name; td.ClientNumber = client.Number; var orddDate = TryFormatDate(swapTradeReq.BASEINFO.ORDDATE); var endDate = TryFormatDate(swapTradeReq.ACCTSWAP.END_DAY); var stockNotional = TryFormatDouble(swapTradeReq.ACCTSWAP.PRINCIPAL); td.TradeDate = orddDate.Date; td.ExerciseDate = endDate.Date; td.OriginalStockEqvNotional = stockNotional; td.StockEqvNotional = td.OriginalStockEqvNotional.Value; return client; } /// /// 创建交易 /// /// private trade CreateTrade(SwapTradeReq swapTradeReq) { TradeExtendJson tradeExtendJson = new TradeExtendJson() { FlowBookMode = (int)FlowBookModeEnum.重置, FloatingPnlAnnualized = false, NeedOpenFee = false, OpenFeeType = 0, Direction = 2, }; var r = new trade() { TradeType = "收益互换", BuySell = "卖出", UnderlyingInstrumentType = "Stock", StartDate = valuedateBLL.ValueDate, TradeDate = valuedateBLL.ValueDate, TraderId = UserId, TraderName = UserName, MarginTemplateName = "系统默认", OpponentRole = "乙方", OriginalStockEqvNotional = 0, StructureType = "普通债券类收益互换", InitialMargin = 0, TradeStatus = "确认成交" }; r.ExerciseDate = r.TradeDate.Value.AddDays(7); r.trade_extend = new trade_extend() { ExtendJson = JsonHelper.Serialize(tradeExtendJson) }; //山证衡泰互换簿记 var client = DataCacheProvider.GetClientDataSource().AsQueryable().FirstOrDefault(x => x.Name == swapTradeReq.BASEINFO.PARTY_NAME); if (client == null) { throw new ServiceException($"找不到名称为{swapTradeReq.BASEINFO.PARTY_NAME}的交易对手方"); } var etradeRule = new EtradingRuleService(UserInfo).GetEtradingRuleAccont(client.BoundSide, client.Number); if (etradeRule == null || string.IsNullOrEmpty(etradeRule.AssetAccount_0)) { throw new ServiceException($"{client.Number}未设置TRS对客簿记账户"); } string clearingAgency = etradeRule.ClearingAgency_0; var asset = DataCacheProvider.GetAssetUnitDataSource().AsQueryable(x => x.Name == etradeRule.AssetAccount_0).FirstOrDefault();//取对客簿记账户 if (asset == null) { throw new ServiceException($"找不到名为{etradeRule.AssetAccount_0}的簿记账户信息"); } if (asset.TraderIdsInt.Count == 0) { throw new ServiceException($"{etradeRule.AssetAccount_0}的簿记账户未设置交易员"); } r.AssetBookName = asset.Name; r.AssetId = asset.id; r.TraderId = asset.TraderIdsInt.FirstOrDefault(); r.TraderName = asset.TraderNamesList.FirstOrDefault(); r.TradeOldStatus = ConsTrade.新增待确认; r.OptId = UserId; r.OptName = UserName; r.OptDate = OptDate; r.CreateDate = OptDate; r.ValidState = ConsGlobal.Valid; r.TradeSource = "系统交易"; r.TradeNumber = swapTradeReq.BASEINFO.EXT_NO; return r; } private trade CheckTradeExist(string extNo) { var trade = DbContext.trade.FirstOrDefault(x => x.TradeNumber == extNo); if (trade == null) { Thread.Sleep(5000); trade = DbContext.trade.FirstOrDefault(x => x.TradeNumber == extNo); if (trade == null) { throw new ServiceException($"不存在交易编号为{extNo}的交易,无法修改"); } } return trade; } /// /// 批量校验交易是否存在 /// /// /// /// private List CheckTradeExistByList(List extNos) { List tradeList = new List(); var trades = DbContext.trade.Where(x => extNos.Contains(x.TradeNumber)).AsNoTracking(); foreach (var extNo in extNos) { var trade = trades.FirstOrDefault(); //if (trade==null) //{ // throw new ServiceException($"交易编号为{extNo}的交易不存在"); //} if (trade != null) { tradeList.Add(trade); } } return tradeList; } /// /// 校验交易状态 /// /// /// private void CheckTradeStatus(trade trade) { if (trade.UnWindDate.HasValue) { throw new ServiceException($"交易已经产生平仓或收益结算,无法修改"); } } /// /// 保存交易后需要做的处理 /// /// /// /// private void AfterUpdateDeal(trade td, int eventType, string msg) { SwapTradeService swapTradeService = new SwapTradeService(this); swapTradeService.ClearSwapPositions(td); DbContext.SaveChanges(); swapTradeService.InitialPosition(td, true); swapTradeService.AddPositionEvent(td,"衡泰同步"); DbContext.SaveChanges(); //new SwapEodPositionService(this).InitSaveEodSwapPosition(td); var swapEventService = new SwapEventService(this); swapEventService.AddSwapEventDate(td.TradeDate.Value, td.id, eventType, "", 0, true, msg + "确认"); swapEventService.AddSwapEventDate(td.TradeDate.Value, td.id, (int)SwapEventTypeEnum.确认交易, "", 0, true, msg + "确认"); } private void DeleteSwapEvent(int tradeId) { new SwapEventService(this).DeleteEvent(tradeId); } /// /// 添加交易元数据 /// private void AddTradeMeta(bool saveChanges, int tradeId, string metaKey, string metaValue) { DbContext.TradeMeta.Add(new TradeMeta { TradeId = tradeId, MetaKey = metaKey, MetaValue = metaValue, CreateTime = DateTime.Now }); if (saveChanges) { DbContext.SaveChanges(); } } #endregion #region 互换资产回执相关私有方法 private void PreparePosiData(SwapPosiReq swapPosiReq, trade td, string extNo) { var underlying = CheckUnderlying(swapPosiReq.TRADEINFO.I_CODE, swapPosiReq.TRADEINFO.MARKET); var positions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && x.PosiDirection > 0 && !x.Invalid).ToList(); var position = positions.Where(x => x.PosiNumber == extNo && x.IsInitial).FirstOrDefault(); var realPosition = positions.Where(x => x.PosiNumber == extNo && !x.IsInitial).FirstOrDefault(); if (position == null) { throw new Exception($"交易{td.TradeNumber}找不到持仓信息"); } decimal price = Convert.ToDecimal(underlying.Price ?? 100); bool init = swapPosiReq.TRADEINFO.OCFLAG == "0"; if (swapPosiReq.TRADEINFO.OCFLAG == "0") { position.UnderlyingCode= underlying.UnderlyingCode; position.UnderlyingInstrumentType = underlying.UnderlyingInstrumentType; position.PositionType = swapPosiReq.BASEINFO.TRDTYPE == "10" ? (int)PositionTypeFlag.Long : (int)PositionTypeFlag.Short; position.PosiQuantity = TryFormatDecimal(swapPosiReq.TRADEINFO.PARVALUE); position.PosiGrossPrice = TryFormatDecimal(swapPosiReq.TRADEINFO.ORDPRICE) / price; int tradeSide = position.PositionType == (int)EnumDirection.Long ? 1 : -1; position.PosiNetPrice = position.PosiQuantity == 0 ? position.PosiGrossPrice : position.PosiGrossPrice + position.PosiTradingFee * tradeSide / position.PosiQuantity; td.OriginalNotional = Convert.ToDouble(position.PosiQuantity); td.Notional = td.OriginalNotional ?? 0; td.UnderlyingCode = underlying.UnderlyingCode; td.UnderlyingAssetName = underlying.UnderlyingInstrumentTypeCn; td.UnderlyingName = underlying.UnderlyingName; td.SpotPrice = Convert.ToDouble(position.PosiNetPrice); td.TradePrice = Convert.ToDouble(position.PosiTradingFee); td.UnderlyingInstrumentType = underlying.UnderlyingInstrumentType; position.PosiNotionalValue = position.PosiQuantity * position.ContractSize; if (realPosition!=null) { realPosition.UnderlyingCode = underlying.UnderlyingCode; realPosition.UnderlyingInstrumentType= underlying.UnderlyingInstrumentType; realPosition.PositionType = position.PositionType; realPosition.PosiQuantity= position.PosiQuantity; realPosition.PosiGrossPrice = position.PosiGrossPrice; realPosition.PosiNetPrice = position.PosiNetPrice; realPosition.PosiNotionalValue = position.PosiNotionalValue; } var flowEvent = DbContext.swap_flow_event.FirstOrDefault(x => x.PositionId == position.id &&x.EventType==(int)SwapFlowEventTypeEnum.开仓 && x.DataState == (int)SwapFlowDateStateEnum.完成); if (flowEvent!=null) { flowEvent.PositionQty = position.PosiQuantity; flowEvent.UnderlyingCode = position.UnderlyingCode; flowEvent.UnderlyingInstrumentType= position.UnderlyingInstrumentType; flowEvent.PositionType= position.PositionType; flowEvent.Quantity= position.PosiQuantity; flowEvent.TradingAmount= position.PosiNotionalValue; } DbContext.SaveChanges(); } } /// /// 修改衡泰平仓明细价格 /// /// /// /// private string DealUnwindPosiEvent(decimal price,trade td,DateTime valueDate) { var posi = DbContext.swap_position.Where(x=>!x.Invalid&&x.IsInitial&&x.PosiDirection>0).FirstOrDefault(); if (posi==null) { throw new Exception("找不到资产端信息"); } var flowEvent = GetSwapFlowEvent(posi.id,valueDate); if (flowEvent==null) { throw new Exception("找不到资产端平仓事件明细信息"); } flowEvent.TradingAmountAvg = price; int tradeSide = posi.PositionType == (int)EnumDirection.Long ? 1 : -1; flowEvent.TradingAmountFeeAvg = flowEvent.Quantity == 0 ? flowEvent.TradingAmountAvg : flowEvent.TradingAmountAvg + flowEvent.TradingFeePending * tradeSide / flowEvent.Quantity; flowEvent.TradingAmountNetFeeAvg = flowEvent.Quantity == 0 ? flowEvent.TradingAmountNetAvg : flowEvent.TradingAmountNetAvg + flowEvent.TradingFeePending * tradeSide / flowEvent.Quantity; DbContext.SaveChanges(); return ""; } /// /// 获取平仓明细 /// /// /// /// /// /// private swap_flow_event GetSwapFlowEvent(long posiId,DateTime valueDate,int attempts=0) { var flowEvent = DbContext.swap_flow_event.FirstOrDefault(x=>x.PositionId== posiId&&x.EventDate==valueDate&&x.EventReason== "接口合约终止交易"&&x.DataState==(int)SwapFlowDateStateEnum.完成); if (flowEvent==null) { attempts++; if (attempts>10) { return null; } Thread.Sleep(1000* attempts); return GetSwapFlowEvent(posiId, valueDate, attempts); } else { return flowEvent; } } /// /// 接入对冲标的 /// /// private void PrepareExchangeTradeData(SwapPosiReq swapPosiReq,string tradeSource) { var underlying = CheckUnderlying(swapPosiReq.TRADEINFO.I_CODE, swapPosiReq.TRADEINFO.MARKET); var orddDate = TryFormatDate(swapPosiReq.BASEINFO.ORDDATE); var longType = swapPosiReq.BASEINFO.TRDTYPE == "10" ? "多头" : "空头"; var openType = swapPosiReq.TRADEINFO.OCFLAG == "0" ? "开仓" : "平仓"; if (string.IsNullOrEmpty(swapPosiReq.BASEINFO.TRD_RELATED_NO) || !double.TryParse(swapPosiReq.TRADEINFO.PARVALUE, out _) || !double.TryParse(swapPosiReq.TRADEINFO.ORDPRICE, out _)) { logger.Error("同步场内交易失败,数据格式不正确:" + JsonHelper.Serialize(swapPosiReq)); return; } ExchangeTradeSaveApiReq req = new ExchangeTradeSaveApiReq { ExchangeAccountCode = swapPosiReq.BASEINFO.SECU_INT, BuySell = longType + openType, Commission = 0, IsHistory = orddDate.Date < valuedateBLL.ValueDate ? true : false, Lots = double.Parse(swapPosiReq.TRADEINFO.PARVALUE) / underlying.ContractSize, TradeDate = orddDate.Date, TradeNumber = swapPosiReq.BASEINFO.TRD_RELATED_NO, TradePrice = double.Parse(swapPosiReq.TRADEINFO.ORDPRICE) / (underlying.Price ?? 100), TradeType = underlying.UnderlyingInstrumentTypeCn, UnderlyingCode = underlying.UnderlyingCode }; var service = new ExchangeTradeSyncApiService(UserInfo); service.Save(req, tradeSource); } /// /// 接入对冲标的 /// /// private void PrepareExchangeTradeData(SwapAsset swapPosiReq,string tradeSource) { var underlying = CheckUnderlying(swapPosiReq.I_CODE, swapPosiReq.MARKET); var orddDate = TryFormatDate(swapPosiReq.ORD_DATE); var longType = swapPosiReq.TRD_TYPE == "10" ? "多头" : "空头"; var openType = swapPosiReq.OC_FLAG == "0" ? "开仓" : "平仓"; if (string.IsNullOrEmpty(swapPosiReq.EXT_NO) || !double.TryParse(swapPosiReq.PAR_VALUE, out _) || !double.TryParse(swapPosiReq.ORD_PRICE, out _)) { logger.Error("同步场内交易失败,数据格式不正确:" + JsonHelper.Serialize(swapPosiReq)); return; } ExchangeTradeSaveApiReq req = new ExchangeTradeSaveApiReq { ExchangeAccountCode = swapPosiReq.SECU_INT, BuySell = longType + openType, Commission = 0, IsHistory = orddDate.Date < valuedateBLL.ValueDate ? true : false, Lots = double.Parse(swapPosiReq.PAR_VALUE) / underlying.ContractSize, TradeDate = orddDate.Date, TradeNumber = swapPosiReq.EXT_NO, TradePrice = double.Parse(swapPosiReq.ORD_PRICE) / (underlying.Price ?? 100), TradeType = underlying.UnderlyingInstrumentTypeCn, UnderlyingCode = underlying.UnderlyingCode }; var service = new ExchangeTradeSyncApiService(UserInfo); service.Save(req, tradeSource); } #endregion #region 预付金交易回执相关私有方法 private void PrepareMarginData(SwapMarginReq swapMarginReq, trade td, string extNo) { var positions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && x.PosiNumber == extNo).ToList(); var position = positions.FirstOrDefault(x => x.IsInitial); var realPosition = positions.FirstOrDefault(x => !x.IsInitial); if (position == null) { throw new Exception($"找不到持仓编码{extNo}的信息"); } var marginDirection = swapMarginReq.BASEINFO.TRDTYPE == "128" ? (int)SwapDirectionEnum.收取 : (int)SwapDirectionEnum.支付; position.InterestDirection = marginDirection; position.InterestPrincipalFix = TryFormatDecimal(swapMarginReq.TRADEINFO.PARVALUE); position.InterestRateDefault = TryFormatDecimal(swapMarginReq.RESERVE.RATE); position.IsAnnualized = position.InterestRateDefault != 0; position.Currency = swapMarginReq.RESERVE.CURRENCY; IntervalModel intervalModel = new IntervalModel() { Date = td.ExerciseDate.Value, Rate = position.InterestRateDefault, Settlement = 0 }; List intervalModels = new List { intervalModel }; var marginDateStr = swapMarginReq.BASEINFO.ORDDATE; if (!string.IsNullOrEmpty(swapMarginReq.BASEINFO.SETDATE)) { marginDateStr = swapMarginReq.BASEINFO.SETDATE; } var marginDate = TryFormatDate(marginDateStr); position.HappenDate=marginDate; position.InterestSwapInterval = JsonHelper.Serialize(intervalModels); var observation = DbContext.trade_obervation.FirstOrDefault(x => x.PositionId == position.id); if (observation == null) { observation = new TradeObervation() { PositionId = position.id, IsDeductPrincipal = true, ObservationAlignEnd = true, ObservationNum = 1, ObservationUnit = "D", ObservationHolidayType = "Following", ObservationStart = td.TradeDate }; } observation.ObservationInterval = position.InterestSwapInterval; if (observation.id == 0) { DbContext.trade_obervation.Add(observation); } if (realPosition!=null) { realPosition.InterestDirection = position.InterestDirection; realPosition.InterestPrincipalFix = position.InterestPrincipalFix; realPosition.InterestRateDefault = position.InterestRateDefault; realPosition.InterestSwapInterval = position.InterestSwapInterval; realPosition.HappenDate = position.HappenDate; realPosition.IsAnnualized = position.IsAnnualized; realPosition.Currency = realPosition.Currency; } var flowEvent = DbContext.swap_flow_event.FirstOrDefault(x => x.PositionId == position.id && x.EventType == (int)SwapFlowEventTypeEnum.开仓&&x.DataState==(int)SwapFlowDateStateEnum.完成); if (flowEvent != null) { flowEvent.InterestDirection = position.InterestDirection; flowEvent.InterestPrincipal = position.InterestPrincipalFix; flowEvent.InterestRate = position.InterestRateDefault; flowEvent.InterestSwapInterval = position.InterestSwapInterval; } DbContext.SaveChanges(); // AfterUpdateDeal(td, (int)SwapEventTypeEnum.修改交易, $"接口预付金{msg}交易", true); AddMarginCash(position, td); } private void PrepareUnwindMarginData(SwapMarginReq swapMarginReq, trade td) { var posi = DbContext.swap_position.Where(x => !x.Invalid && x.IsInitial && ConsTrade.InterestMarginModels.Contains(x.InterestMode)).FirstOrDefault(); if (posi == null) { throw new Exception($"{td.TradeNumber}找不到持仓信息"); } var marginDate = TryFormatDate(swapMarginReq.BASEINFO.ORDDATE); var flowEvent = GetSwapFlowEvent(posi.id, marginDate); if (flowEvent == null) { throw new Exception($"{posi.PosiNumber}找不到平仓事件"); } var swapEvent = DbContext.swap_event.Where(x=>x.id==flowEvent.EventId).FirstOrDefault(); if (swapEvent==null|| string.IsNullOrEmpty(swapEvent.EventData)) { throw new Exception($"{td.TradeNumber}找不到平仓事件"); } swapEvent.unwindData = JsonConvert.DeserializeObject(swapEvent.EventData); decimal unwindMarginMoney = 0; unwindMarginMoney = TryFormatDecimal(swapMarginReq.TRADEINFO.PARVALUE); var interestAmount = TryFormatDecimal(swapMarginReq.TRADEINFO.INTEREST_AMOUNT); var currentMarginMoney = posi.InterestPrincipalFix; var origamount = currentMarginMoney * swapEvent.unwindData.ClosePercent; var difMarginMoney = currentMarginMoney- (unwindMarginMoney> origamount? unwindMarginMoney: origamount); posi.InterestPrincipalFix = difMarginMoney; flowEvent.InterestPrincipal = unwindMarginMoney; flowEvent.TdInterestAmount = interestAmount; flowEvent.InterestAmount = interestAmount; flowEvent.InterestClosePnL = interestAmount; DbContext.SaveChanges(); UpdateUnwindMarginCash(posi, td, -unwindMarginMoney, marginDate, ClientCashInCashOut.系统操作_应付预付金); new SwapTradeBaseService(this).AddClientCashInCashOut(td, Convert.ToDouble(-interestAmount), ClientCashInCashOut.系统操作_预付金返息, marginDate); } /// /// 添加预付金资金记录 /// /// /// private void AddMarginCash(swap_position marginPosition, trade td) { var cashService = new ClientCashInCashOutService(this); var marginAmount = marginPosition.InterestPrincipalFix * (marginPosition.InterestDirection == 1 ? -1 : 1); cashService.SaveSwapTradeClientCash(td, Convert.ToDouble(marginAmount), marginPosition.HappenDate.HasValue ? marginPosition.HappenDate.Value : td.TradeDate.Value, marginPosition.id, ClientCashInCashOut.系统操作_应付预付金); } /// /// 修改预付金返息记录 /// /// /// /// /// private void UpdateUnwindMarginCash(swap_position marginPosition, trade td, decimal marginAmount, DateTime marginDate,string actionName) { var cashService = new ClientCashInCashOutService(this); cashService.SaveSwapTradeClientCash(td, Convert.ToDouble(marginAmount), marginDate, marginPosition.id, actionName); } #endregion #region 合约终止回执相关私有方法 private void PrepareUnwindData(SwapUnwindReq swapUnwindReq, trade td) { var eventIdStr = swapUnwindReq.ACCTSWAP_TERMINATE.EXT_NO; bool allClose = swapUnwindReq.ACCTSWAP_TERMINATE.TERMINATE_TYPE == "1"; var closeQty = TryFormatDecimal(swapUnwindReq.ACCTSWAP_TERMINATE.TERMINATE_COUNT); var unwindDate = TryFormatDate(swapUnwindReq.ACCTSWAP_TERMINATE.TERMINATE_DAY).Date; var payDate = TryFormatDate(swapUnwindReq.ACCTSWAP_TERMINATE.PAY_DAY).Date; var markClosePnl = TryFormatDecimal(swapUnwindReq.ACCTSWAP_TERMINATE.ZCD_AMOUNT); var interestAmount= TryFormatDecimal(swapUnwindReq.ACCTSWAP_TERMINATE.GDD_AMOUNT); var tradeinfFee = TryFormatDecimal(swapUnwindReq.ACCTSWAP_TERMINATE.ASSET_FEE); var fixFee = TryFormatDecimal(swapUnwindReq.ACCTSWAP_TERMINATE.FIX_FEE); interestAmount -= fixFee; markClosePnl += tradeinfFee; swap_event swapEvent; if (!long.TryParse(eventIdStr, out long eventId))//不识别,为衡泰新增的平仓信息 { swapEvent = null; } else { swapEvent = DbContext.swap_event.FirstOrDefault(x => x.id == eventId); } // 先不支持衡泰修改平仓信息 if (swapEvent == null&& OtcAppConfigHelper.HTUpdate == 1&&td.TradeStatus!= ConsTrade.已平仓) { new SwapDealService(this).AutoSwapUnwindFromConsumer(td, unwindDate, payDate, markClosePnl, tradeinfFee, interestAmount, fixFee, closeQty, allClose); } } #endregion #region 互换资金流水回执相关私有方法 private List PrepareTradeCashData(SwapCashReq swapCashReq, List trades) { var tradenumbers = trades.Select(s => s.TradeNumber).ToList(); List clientIds = new List(); foreach (var cashItem in swapCashReq.ACCTSWAP_CASH_LIST) { var money = cashItem.CASH_AMOUNT; var backCard = cashItem.BANK_ACC_CODE; var bankCardInfo = new ClientBankCardService(UserInfo).GetBankCardByBankCode(backCard); if (bankCardInfo == null) { throw new ServiceException("根据银行卡信息找不到对应客户"); } var trade = trades.FirstOrDefault(x => x.TradeNumber == cashItem.EXT_NO); var client = DataCacheProvider.GetClientDataSource().GetData(bankCardInfo.ClientId ?? 0); var cash = new ClientCashInCashOut(); cash.ClientId = bankCardInfo.ClientId; cash.ClientName = client?.Name; cash.ClientNumber = client?.Number; cash.Direction = money > 0 ? "入金" : "出金"; cash.Money = Math.Abs(money); cash.TradeId = trade?.id; cash.TradeNumber = trade?.TradeNumber; cash.HappenDate = TryFormatDate(cashItem.SETDATE_REAL).Date; cash.OptDate = TryFormatDate(cashItem.SETDATE); cash.CurrencyCode = "CNY"; cash.Number = UniqueTimeId.GetStr(); cash.State = "未确认"; cash.SetOpt(UserInfo); cash.Comments = "互换资金流水回执"; cash.OpenBankCard = backCard; cash.OpenBankId = bankCardInfo?.id; DbContext.ClientCashInCashOut.Add(cash); if (client?.SwapTradeType == 1) { clientIds.Add(cash.ClientId ?? 0); } } DbContext.SaveChanges(); return clientIds.Distinct().ToList(); } /// /// 推送客户资金信息到客户端 /// /// /// private void PushClientCash(List clientIds, string onRspAccountCapitalTopicTopic) { try { var clientSettles = new RealTimeClientBanlanceService(new OptUserInfo(0, "实时客户资金服务", OptUserFrom.Service)).GetBanlances(clientIds, valuedateBLL.ValueDate); foreach (var cb in clientSettles) { Result result = new Result(); try { var obj = new ClientBalanceForTrsResponse { TotalAmountTotal = cb.RoundedTotalAmountTotal, AvailableAmount = Math.Round(cb.AvailableAmount, 2), PositionPv = cb.RoundedPositionPv, PositionPnl = cb.RoundedPositionPnl, DaliyPnl = Math.Round(cb.DaliyPnl, 2), ClientId = cb.ClientId, ClientType = cb.ClientType, Credit = cb.TotalCredit }; result.success = true; result.obj = obj; } catch (Exception ex) { result.msg = ex.Message; result.success = false; } kafkaProduceHelper.Produce(onRspAccountCapitalTopicTopic, JsonConvert.SerializeObject(result)); } } catch (Exception ex) { } } #endregion #region 出入金相关私有方法 public void PushCashToHT(int cashId, PushLog pushLog) { //var cash= DbContext.ClientCashInCashOut.FirstOrDefault(x=>x.id==cashId); // if (cash==null) // { // return; // } // var req = new RequestBase(); // var pushService = new PushLogService(UserInfo); // var enumType = cash.Direction=="出金"? SwapPushDataEnum.客户出金新增: SwapPushDataEnum.客户入金新增; // if (pushLog==null) // { // pushLog = pushService.AddLog(cashId, enumType, cash.Number); // } // req.RECORD = PrepareTradeMarginReq(cash); // if (req.RECORD == null) // { // return; // } // req.EXT_NO = cash.Number; // req.SERIAL_NO = pushLog.id.ToString(); // req.ACTION = "2043"; // var data_req = JsonHelper.Serialize(req); // pushService.UpdateLogReq(pushLog.id, data_req); // kafkaProduceHelper.Produce(reqMarginInsert, data_req); } /// /// 互换预付金请求数据组装 /// /// /// private SwapMarginReq PrepareTradeMarginReq(ClientCashInCashOut model) { SwapMarginReq swapMarginReq = new SwapMarginReq(); var etradeAccount = DbContext.etrade_account.FirstOrDefault(); #region BASEINFO swapMarginReq.BASEINFO.ORDDATE = model.HappenDate.Value.ToString("yyyy-MM-dd"); swapMarginReq.BASEINFO.SETDATE = swapMarginReq.BASEINFO.ORDDATE; swapMarginReq.BASEINFO.PARTY_NAME = model.ClientName; swapMarginReq.BASEINFO.ORDSTATUS = "1"; swapMarginReq.BASEINFO.TRDTYPE = model.Direction == "入金" ? "128" : "127"; swapMarginReq.BASEINFO.TRADER_ID = etradeAccount?.trader_account; swapMarginReq.BASEINFO.SECU_INT = etradeAccount?.balance_account_trade; #endregion #region TRADEINFO swapMarginReq.TRADEINFO.PARVALUE = model.Money.OtcFormatMoney(false, 4); #endregion return swapMarginReq; } #endregion /// /// 校验标的信息 /// /// /// /// private underlying_manager CheckUnderlying(string underlyingCode, string market) { var underlying = DbContext.underlying_manager.FirstOrDefault(x => x.UnderlyingCode.StartsWith(underlyingCode + ".") && x.MarketCode == market); if (underlying == null) { throw new ServiceException($"标的代码{underlyingCode}不存在"); } return underlying; } private DateTime TryFormatDate(string val) { if (!DateTime.TryParse(val, out DateTime formatDate)) { throw new ServiceException($"参数{val}不是正确的日期格式"); } return formatDate; } private double TryFormatDouble(string val) { if (!double.TryParse(val, out double doubleVal)) { throw new ServiceException($"参数{val}不是数字"); } return doubleVal; } private decimal TryFormatDecimal(string val) { if (!decimal.TryParse(val, out decimal decimalVal)) { throw new ServiceException($"参数{val}不是数字"); } return decimalVal; } private int TryFormatInt(string val) { if (!int.TryParse(val, out int intVal)) { throw new ServiceException($"参数{val}不是数字"); } return intVal; } private string GetInterestRule(string interestRule) { switch (interestRule) { case "1": return "01"; case "2": return "11"; default: return "10"; } } private string GetTradeStatus(string orderStatus) { switch (orderStatus) { //0新建、-4交易执行中、-3风险预审中、7成交确认、9交易撤单、1审批中、5审批通过、4审批拒绝、10交易终止 case "0": return ConsTrade.新增待确认; case "1": return ConsTrade.审批中; case "4": case "9"://撤单 return ""; case "10": return ConsTrade.已平仓; default: return ConsTrade.确认成交; } } } }