using System.Linq.Expressions; using YLErp.DBModels.Enums; using YLErp.DBModels.Helpers; using YLErp.Modules.DataProviderModule; namespace YLErp.Modules.ExchangeTradeModule { /// /// 场内交易保存基类 /// public class ExchangeTradeSaveServiceBase : YLBaseService { public ExchangeTradeSaveServiceBase(YLBaseService baseService) : base(baseService) { } public ExchangeTradeSaveServiceBase(OptUserInfo userInfo) : base(userInfo) { } /// /// 保存对冲交易持仓(dbTradOld为null说明reqTrad是新增交易) /// public void SaveExchangeTradePosition(ExchangeTrade reqTrad, ExchangeTrade dbTradOld = null) { TradePosition dbPosition; TradePositionParam para; //比较请求的数据和数据库中的旧数据的基本要素是否相同 var isSame = dbTradOld == null || reqTrad.AssetBookId == dbTradOld.AssetBookId && reqTrad.TradeSide == dbTradOld.TradeSide && reqTrad.TradeType == dbTradOld.TradeType && string.Equals(reqTrad.UnderlyingCode, dbTradOld.UnderlyingCode, StringComparison.OrdinalIgnoreCase) && (reqTrad.TradeType != "场内期权" || string.Equals(reqTrad.OptionCode, dbTradOld.OptionCode, StringComparison.OrdinalIgnoreCase)); //如果不相同就从旧持仓份额中减去 if (!isSame) { //准备旧数据参数查询持仓 para = PrepareTradePositionParam(dbTradOld); dbPosition = DbContext.TradePosition.FirstOrDefault(para.predicate); if (dbPosition != null) { var dbNotional = TradeHelper.GetPositionNotional(para.tradeSide, dbTradOld.Notional); dbPosition.Position -= dbNotional; dbPosition.PositionCost -= dbNotional * dbTradOld.TradeSinglePrice; dbPosition.UpdateTime = OptDate; } } //准备新的参数进行查询持仓 para = PrepareTradePositionParam(reqTrad); var reqNotional = TradeHelper.GetPositionNotional(para.tradeSide, reqTrad.Notional); dbPosition = DbContext.TradePosition.FirstOrDefault(para.predicate); //如果数据库中不存在对应的持仓就使用请求数据创建持仓数据 if (dbPosition == null) { DateTime maturityDate; if (reqTrad.TradeType == "股票") { maturityDate = DateTime.Today.AddYears(10); } else if (reqTrad.TradeType == "场内期权") { if (!ExchangeOptionDataProvider.TryGetMaturityDate(reqTrad.OptionCode, out maturityDate) && !UnderlyingDataProvider.TryGetMaturityDate(reqTrad.UnderlyingCode, out maturityDate)) { maturityDate = DateTime.Today.AddMonths(1); } } else if (!UnderlyingDataProvider.TryGetMaturityDate(reqTrad.UnderlyingCode, out maturityDate)) { maturityDate = DateTime.Today.AddMonths(1); } dbPosition = new TradePosition { TradeType = reqTrad.TradeType, BookId = reqTrad.AssetBookId, InstrumentCode = reqTrad.OptionCode, PositionType = para.positionType, Position = reqNotional, TradeType1 = para.tradeType1, UnderlyingId = reqTrad.UnderlyingId, UnderlyingCode = reqTrad.UnderlyingCode, CreateTime = OptDate, MaturityDate = maturityDate }; dbPosition.PositionCost = dbPosition.Position * reqTrad.TradeSinglePrice; DbContext.TradePosition.Add(dbPosition); } //如果数据库中不存在旧交易数据在现有持仓数据上增加 else if (dbTradOld == null) { dbPosition.Position += reqNotional; dbPosition.PositionCost += reqNotional * reqTrad.TradeSinglePrice; } else { //如果基本要素相同就删除旧的持仓份额改用新的持仓份额 if (isSame) { var dbNotional = TradeHelper.GetPositionNotional(dbTradOld.TradeSide, dbTradOld.Notional); dbPosition.Position -= dbNotional; dbPosition.PositionCost -= dbNotional * dbTradOld.TradeSinglePrice; } dbPosition.Position += reqNotional; dbPosition.PositionCost += reqNotional * reqTrad.TradeSinglePrice; } if (dbPosition != null) { dbPosition.UpdateTime = OptDate; } } /// /// 根据trde对象生成的持仓参数 /// protected static TradePositionParam PrepareTradePositionParam(ExchangeTrade td) { var para = new TradePositionParam { tradeSide = TradeHelper.GetTradeSide(td.TradeSide), tradeType1 = TradeHelper.GetTradeType1(td.TradeType) }; para.positionType = TradeHelper.GetPositionType(para.tradeSide); var predicate = PredicateBuilder.Create(n => n.BookId == td.AssetBookId && n.TradeType1 == para.tradeType1 && n.UnderlyingCode == td.UnderlyingCode); if (td.TradeType == "场内期权") { predicate = predicate.And(n => n.InstrumentCode == td.OptionCode); } para.predicate = predicate; return para; } /// /// 检查交易的交易类型 /// protected string CheckTradeType(string tradeType) { if (string.IsNullOrWhiteSpace(tradeType)) { throw new ServiceException("交易类型 必须填写"); } switch (tradeType) { case "股票": case "商品现货": case "场内期权": case "商品期货": case "信用债": case "利率债": case "其它债券": return tradeType; case "期货": return "商品期货"; default: throw new ServiceException("交易类型未能识别:" + tradeType); } } /// /// 检查交易的交易方向 /// protected void CheckTradeSide(string tradeSide) { if (string.IsNullOrWhiteSpace(tradeSide)) { throw new ServiceException("交易方向 必须填写"); } switch (tradeSide) { case ConsGlobal.TradeSide.多头开仓: case ConsGlobal.TradeSide.多头平仓: case ConsGlobal.TradeSide.空头开仓: case ConsGlobal.TradeSide.空头平仓: break; default: throw new ServiceException("交易方向未能识别:" + tradeSide); } } /// /// 设置交易的场内期权信息 /// protected void SetTradeOptionInfo(ExchangeTrade td, string optionCode, out ExchangeListOption option, bool useCachedDataSource = true) { if (string.IsNullOrWhiteSpace(optionCode)) { throw new ServiceException("场内期权交易必须有对应的期权合约存在"); } if (useCachedDataSource) { option = DataCacheProvider.GetExchangeListOptionDataSource().GetData(optionCode); } else { option = DbContext.exchange_list_option.AsNoTracking().FirstOrDefault(e => e.ContractCode == optionCode); } if (option == null) { throw new ServiceException("场内期权合约信息不存在:" + optionCode); } if (option.MaturityDate == DateTime.MinValue) { throw new ServiceException("场内期权合约到期日不存在:" + optionCode); } if (option.MaturityDate < td.TradeDate) { throw new ServiceException($"交易日期'{td.TradeDate:yyyy-MM-dd}'不能大于场内期权合约到期日'{option.MaturityDate:yyyy-MM-dd}'"); } td.OptionCode = option.ContractCode; td.UnderlyingCode = option.UnderlyingCode; td.OptionStrike = option.Strike; td.OptionType = option.OptionType; td.MaturityDate = option.MaturityDate; td.ExerciseMode = option.ExerciseMode.TrimToNull() ?? "European"; if (optionCode.StartsWith("IO", StringComparison.OrdinalIgnoreCase)) { td.UnderlyingCode = "000300.SH"; } } /// /// 设置交易的标的信息 /// protected void SetTradeUnderlyingInfo(ExchangeTrade td, string underlyingCode, out underlying_manager um, bool useCachedDataSource = true) { System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(td.TradeType)); if (string.IsNullOrWhiteSpace(underlyingCode)) { throw new ServiceException("标的代码 必须填写"); } if (useCachedDataSource) { um = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode); } else { um = DbContext.underlying_manager.AsNoTracking().FirstOrDefault(un => un.UnderlyingCode == underlyingCode); } if (um == null) { throw new ServiceException("标的信息不存在:" + underlyingCode); } if (td.TradeType != "场内期权") { if (um.IsFutures()) { if (um.MaturityDate == null) { throw new ServiceException("商品期货合约到期日不存在:" + underlyingCode); } if (um.MaturityDate.Value < td.TradeDate) { throw new ServiceException($"交易日期'{td.TradeDate:yyyy-MM-dd}'不能大于商品期货标的到期日'{um.MaturityDate:yyyy-MM-dd}'"); } } td.MaturityDate = um.MaturityDate; } td.UnderlyingId = um.id; td.UnderlyingCode = um.UnderlyingCode; td.InstrumentType = um.UnderlyingInstrumentType; } /// /// 设置交易的数量信息 /// protected void SetTradeLotsInfo(ExchangeTrade td, double lots, double contractSize, int varietyId, bool useCachedDataSource = true) { System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(td.TradeType)); td.TradeLots = lots; if (td.TradeType == "股票") { td.Notional = td.TradeAmount = lots * (contractSize < 1 ? 100 : contractSize); } else { Variety variety = null; if (useCachedDataSource) { variety = DataCacheProvider.GetVarietyDataSource().GetData(varietyId); } else { variety = DbContext.variety.AsNoTracking().FirstOrDefault(n => n.id == varietyId); } if (variety == null) { throw new ServiceException("标的的品种信息不存在:" + td.UnderlyingCode); } if (contractSize < 1) { contractSize = VarietyHelper.GetTradeUnitValue(variety.VarietyCode, variety.TradeUnit) ?? 1; } var countRatio = VarietyHelper.GetCountRatio(variety.QuoteUnit); td.Notional = lots * contractSize; td.TradeAmount = td.Notional / countRatio; } } /// /// 设置交易员信息 /// protected void SetTraderInfo(ExchangeTrade td, string traderName) { td.TraderId = UserId; td.TraderName = UserName; if (!string.IsNullOrWhiteSpace(traderName)) { using (var sysdb = DbContextFactory.GetErpBaseContext()) { var systemUsers = sysdb.SystemUsers.Where(su => su.LoginName == traderName || su.Name == traderName) .Select(n => new { n.Id, n.Name }).ToArray(); if (!systemUsers.Any()) { throw new ServiceException("交易员不存在:" + traderName); } else { var systemUser = sysdb.SystemUsers.FirstOrDefault(su => su.LoginName == traderName) ?? sysdb.SystemUsers.FirstOrDefault(su => su.Name == traderName); td.TraderId = systemUser.Id; td.TraderName = systemUser.Name; } } } } /// /// 设置簿记账户和对冲账户 /// protected void SetAssetAndExchangeAccount(ExchangeTrade td, string assetBookName, string exchangeAccountCode, int varietyId) { //簿记账户 if (!string.IsNullOrWhiteSpace(assetBookName)) { var book = DataCacheProvider.GetAssetUnitDataSource().AsQueryable().FirstOrDefault(n => n.Name == assetBookName); if (book == null) { throw new ServiceException($"簿记账户'{assetBookName}'没有找到"); } td.AssetBookId = book.id; } //对冲账户 if (!string.IsNullOrWhiteSpace(exchangeAccountCode)) { var exAccounts = DataCacheProvider.GetExchangeAccountDataSource().AsQueryable() .Where(a => a.AccountCode == exchangeAccountCode && a.Status == 1).ToArray(); if (!exAccounts.Any()) { throw new ServiceException($"对冲账户'{exchangeAccountCode}'在系统中不存在"); } var exAccount = exAccounts[0]; if (varietyId > 0) { var commaVarietyId = $",{varietyId},"; exAccount = exAccounts.FirstOrDefault(n => string.IsNullOrWhiteSpace(n.VarietyIds) || string.Concat(",", n.VarietyIds, ",").Contains(commaVarietyId)) ?? exAccounts[0]; } //对冲账户 td.ExchangeAccountId = exAccount.id; //使用对冲账户绑定的簿记账户 if (string.IsNullOrWhiteSpace(assetBookName)) { if ((exAccount.DefaultBookId ?? 0) < 1 || DataCacheProvider.GetAssetUnitDataSource().GetData(exAccount.DefaultBookId.Value) == null) { throw new ServiceException($"对冲账户'{exchangeAccountCode}'没有设置簿记账户"); } td.AssetBookId = exAccount.DefaultBookId.Value; } } } /// /// 根据trde对象生成的持仓参数 /// protected class TradePositionParam { public TradeSideFlag tradeSide; public TradeTypeFlag tradeType1; public PositionTypeFlag positionType; public Expression> predicate; } } }