using BaseOUDAL; using YLErp.BLL; using YLErp.DBModels.Enums; using YLErp.DBModels.Helpers; using YLErp.QdpModule; namespace YLErp.Modules.ExchangeTradeModule { /// /// 对冲交易同步API服务 /// public class ExchangeTradeSyncApiService : ExchangeTradeSaveServiceBase { public ExchangeTradeSyncApiService(YLBaseService baseService) : base(baseService) { } public ExchangeTradeSyncApiService(OptUserInfo userInfo) : base(userInfo) { } public int Save(ExchangeTradeSaveApiReq req, string tradeSource) { if (req.TradeDate == DateTime.MinValue) { throw new ServiceException("交易日期 必须填写"); } if (!req.IsHistory && req.TradeDate < valuedateBLL.ValueDate) { throw new ServiceException("交易日期 不能小于系统日期:" + valuedateBLL.ValueDate.ToString("yyyy-MM-dd")); } if (string.IsNullOrWhiteSpace(req.UnderlyingCode)) { throw new ServiceException("交易产品代码 必须填写"); } if (string.IsNullOrWhiteSpace(req.BuySell)) { throw new ServiceException("交易方向 必须填写"); } if (string.IsNullOrWhiteSpace(req.ExchangeAccountCode)) { throw new ServiceException("交易账户代码 必须填写"); } switch (req.TradeType) { case "股票": case "商品现货": case "场内期权": case "利率债": case "信用债": case "其它债券": break; case "商品期货": if (req.UnderlyingCode.Length > 8) { req.TradeType = "场内期权"; } break; default: throw new ServiceException("交易类型未能识别:" + req.TradeType); } //20220415:金仕达接口给的商品期货数据,如果是来源于场内期权行权产生的,则会和场内期权交易编号一致 ExchangeTrade dbTrade = null; var canUpdate = false; //两个交易日内交易编号相同的在数据库存在数据,就忽略不做处理;不存在,就插入一条交易日为上一日的交易。 if (req.TradeNumber.EndsWith("_dzrh_")) { var lastTradeDate = QdpCalendarHelper.GetNonHolidayDefore(req.TradeDate.AddDays(-1)); if (DbContext.ExchangeTrade.Any(x => (x.TradeDate == req.TradeDate || x.TradeDate == lastTradeDate) && x.TradeNumber == req.TradeNumber)) { return 0; } else { req.TradeDate = lastTradeDate; } } else { dbTrade = DbContext.ExchangeTrade.FirstOrDefault(x => x.TradeDate == req.TradeDate && x.TradeType == req.TradeType && x.TradeNumber == req.TradeNumber); canUpdate = dbTrade != null //&& req.TradeType == dbTrade.TradeType && req.BuySell == dbTrade.TradeSide && req.ExchangeAccountCode.Equals(dbTrade.ExchangeAccountCode, StringComparison.OrdinalIgnoreCase) && req.UnderlyingCode.Equals(dbTrade.TradeType == "场内期权" ? dbTrade.OptionCode : dbTrade.UnderlyingCode, StringComparison.OrdinalIgnoreCase); } var reqTrade = PrepareReqTrad(req, tradeSource); SaveExchangeTradePosition(reqTrade, canUpdate ? dbTrade : null); if (canUpdate) { dbTrade.UnderlyingCode = reqTrade.UnderlyingCode; dbTrade.TradeSinglePrice = reqTrade.TradeSinglePrice; dbTrade.Notional = reqTrade.Notional; dbTrade.TradeAmount = reqTrade.TradeAmount; dbTrade.TradeLots = reqTrade.TradeLots; dbTrade.TraderId = reqTrade.TraderId; dbTrade.TraderName = reqTrade.TraderName; dbTrade.UnderlyingId = reqTrade.UnderlyingId; dbTrade.MaturityDate = reqTrade.MaturityDate; dbTrade.OptionStrike = reqTrade.OptionStrike; dbTrade.OptionType = reqTrade.OptionType; dbTrade.ExerciseMode = reqTrade.ExerciseMode; dbTrade.AssetBookId = reqTrade.AssetBookId; dbTrade.ExchangeAccountId = reqTrade.ExchangeAccountId; dbTrade.InstrumentType = reqTrade.InstrumentType; dbTrade.OptDate = DateTime.Now; } else { DbContext.ExchangeTrade.Add(reqTrade); } return DbContext.SaveChanges(); } //将req转换交易对象 private ExchangeTrade PrepareReqTrad(ExchangeTradeSaveApiReq req, string tradeSource = "下单同步") { var td = new ExchangeTrade { IsValid = true, TradeType = req.TradeType, TradeDate = req.TradeDate, TradeNumber = req.TradeNumber, TradeSide = req.BuySell, TradeSinglePrice = req.TradePrice, UnderlyingCode = req.UnderlyingCode, ExchangeAccountCode = req.ExchangeAccountCode, Commission = 0, CommissionType = CommissionType.系统计算, OptId = 0, OptDate = DateTime.Now, OptName = UserName, CreateTime = DateTime.Now, TradeSource = tradeSource, Notional = 0, TradeAmount = 0, TradeLots = 0, TraderId = 0, TraderName = UserName, UnderlyingId = 0, MaturityDate = null, OptionCode = null, OptionStrike = null, OptionType = null, ExerciseMode = null, AssetBookId = 0, ExchangeAccountId = 0, InstrumentType = null, Comments = null, }; var contractSize = 0d; if (td.TradeType == "场内期权") { SetTradeOptionInfo(td, td.UnderlyingCode, out var option, false); contractSize = option.ContractSize; } //标的信息 var um = DbContext.underlying_manager.Where(un => un.UnderlyingCode == td.UnderlyingCode) .Select(n => new { n.id, n.UnderlyingType, n.MaturityDate, n.UnderlyingTypeId, n.UnderlyingInstrumentType, n.ContractSize }).FirstOrDefault(); if (um == null) { throw new ServiceException($"未找到标的信息:" + td.UnderlyingCode); } td.UnderlyingId = um.id; td.InstrumentType = um.UnderlyingInstrumentType; if (td.TradeType != "场内期权") { td.MaturityDate = um.MaturityDate; } //交易数量 if (td.TradeType == "股票") { td.TradeLots = req.Lots / 100; td.Notional = td.TradeAmount = req.Lots; } else { if (contractSize < 1) { contractSize = um.ContractSize; } var variety = DbContext.variety.Where(n => n.id == um.UnderlyingTypeId).FirstOrDefault(); if (variety == null) { throw new ServiceException($"未找到{td.UnderlyingCode}的品种信息"); } var CountRatio = VarietyHelper.GetCountRatio(variety.QuoteUnit); if (contractSize > 1) { td.TradeLots = req.Lots; td.Notional = req.Lots * contractSize; td.TradeAmount = td.Notional / variety.CountRatio; } else { td.TradeLots = req.Lots; td.Notional = req.Lots * (variety.TradeUnitValue ?? 1); td.TradeAmount = td.Notional / variety.CountRatio; } } //簿记等信息 PrepareBook(td, um.UnderlyingTypeId); if (req.Commission.HasValue) { td.Commission = req.Commission.Value; td.CommissionType = CommissionType.手动录入; } return td; } //为trade准备对冲账户/簿记/交易员信息 private void PrepareBook(ExchangeTrade td, int varietyId) { var query = from a in DbContext.exchange_account join b in DbContext.assetunit on a.DefaultBookId equals b.id into bs from b in bs.DefaultIfEmpty() where a.AccountCode == td.ExchangeAccountCode && a.Status == 1 select new { AccountId = a.id, a.AccountCode, a.VarietyIds, a.TraderIds, BookId = b != null ? b.id : 0, BookName = b != null ? b.Name : "", a.Description }; var accbooks = query.ToList(); if (accbooks.Count == 0) { throw new ServiceException($"对冲账户'{td.ExchangeAccountCode}'在系统中不存在"); } var accbook = accbooks[0]; var commaVarietyId = $",{varietyId},"; if (accbooks.Count == 1) { if (varietyId > 0 && !(string.IsNullOrWhiteSpace(accbook.VarietyIds) || string.Concat(",", accbook.VarietyIds.Trim(), ",").Contains(commaVarietyId))) { throw new ServiceException($"对冲账户'{td.ExchangeAccountCode}'配置品种不包含此交易的品种"); } } else if (varietyId > 0) { accbook = accbooks.FirstOrDefault(n => string.Concat(",", n.VarietyIds.Trim(), ",").Contains(commaVarietyId)) ?? accbooks.FirstOrDefault(n => string.IsNullOrWhiteSpace(n.VarietyIds)); if (accbook == null) { throw new ServiceException($"对冲账户'{td.ExchangeAccountCode}'配置品种不包含此交易的品种"); } } //对冲账户 td.ExchangeAccountId = accbook.AccountId; td.ExchangeAccountCode = accbook.AccountCode; //簿记帐户 if (accbook.BookId < 1) { throw new ServiceException($"对冲账户'{td.ExchangeAccountCode}'没有找到簿记账户"); } td.AssetBookId = accbook.BookId; //交易员信息 var traderId = td.TraderId; var trader = traderId > 0 ? UserBLL.GetById(traderId) : null; if (trader == null) { traderId = DataConvert.ConvertCommaValuesToInt32Array(accbook.TraderIds).FirstOrDefault(); if (traderId > 0) { trader = UserBLL.GetById(traderId); } } td.TraderId = trader?.Id ?? UserId; td.TraderName = (trader == null && !string.IsNullOrWhiteSpace(accbook?.Description)) ? accbook.Description.Trim() : trader?.Name ?? UserName ?? "系统"; } } }