using YLErp.BLL; using YLErp.BLL.Eod; using YLErp.DBModels.Consts; using YLErp.QdpModule; namespace YLErp.Modules.TradeModule { /// /// 交易历史记录服务 /// public class TradeHisDataService : TradeServiceBase { public TradeHisDataService(YLBaseService baseService) : base(baseService) { } public TradeHisDataService(OptUserInfo userInfo) : base(userInfo) { } #region----对冲波动率---- /// /// 根据交易ID获取最新的对冲波动率 /// /// 返回交易ID为Key,对冲波动率为Value的字典 public Dictionary GetLatestHedgeVol(IEnumerable tradeIds) { if (tradeIds is null) { throw new ArgumentNullException(nameof(tradeIds)); } var count = tradeIds.Count(); var dic = new Dictionary(); for (var i = 0; i < count; i += 1000) { var subIds = tradeIds.Skip(i).Take(1000); var query2 = from tv in DbContext.trade_hedge_vol where subIds.Contains(tv.TradeId) group tv by tv.TradeId into g select g.Max(n => n.id); var query3 = from tv2 in query2 join tv in DbContext.trade_hedge_vol on tv2 equals tv.id select new { tv.TradeId, tv.TradeSavedVol }; foreach (var item in query3) { dic.Add(item.TradeId, item.TradeSavedVol); } } return dic; } /// /// 保存对冲波动率 /// public int SaveHedgeVol(string tradeNumber, DateTime valueDate, double value) { if (string.IsNullOrEmpty(tradeNumber)) { throw new ArgumentException("参数不能为空值", nameof(tradeNumber)); } var td = DbContext.trade.FirstOrDefault(n => n.TradeNumber == tradeNumber); if (td == null) { throw new ServiceException("保存对冲波动率失败,找不到交易信息,交易编号:" + tradeNumber); } return InnerSaveHedgeVol(td, valueDate, value); } /// /// 保存对冲波动率 /// public int SaveHedgeVol(int tradeId, DateTime valueDate, double value) { if (tradeId <= 0) { throw new ServiceException($"{nameof(tradeId)}应该大于0"); } var td = DbContext.trade.Find(tradeId); if (td == null) { throw new ServiceException("保存对冲波动率失败,找不到交易信息,交易ID:" + tradeId); } return InnerSaveHedgeVol(td, valueDate, value); } /// /// 保存对冲波动率 /// public int InnerSaveHedgeVol(trade td, DateTime valueDate, double value) { if (td == null) { throw new ServiceException("保存对冲波动率失败,找不到交易信息"); } if (valueDate == valuedateBLL.ValueDate) { td.TradeSavedVol = value; } else if (valueDate > valuedateBLL.ValueDate) { throw new ServiceException("保存对冲波动率失败,因为赋值日期大于系统日期"); } var hedgeTradeVol = DbContext.trade_hedge_vol.FirstOrDefault(v => v.TradeId == td.id && v.ValueDate == valueDate); if (hedgeTradeVol == null) { hedgeTradeVol = new trade_hedge_vol { TradeId = td.id, ValueDate = valueDate, }; DbContext.trade_hedge_vol.Add(hedgeTradeVol); } SetDBModelOpt(hedgeTradeVol); hedgeTradeVol.TradeSavedVol = value; return DbContext.SaveChanges(); } #endregion #region----无风险利率/分红率--- /// /// 保存无风险利率历史 /// public int SaveNoRiskRate(int tradeId, DateTime valueDate, double value) { return SaveHisData(tradeId, valueDate, ConsTradeField.NoRiskRate, value); } /// /// 保存无风险利率历史 /// public int SaveNoRiskRate(string tradeNumber, DateTime valueDate, double value) { if (string.IsNullOrEmpty(tradeNumber)) { throw new ArgumentException("参数不能为空值", nameof(tradeNumber)); } var td = DbContext.trade.FirstOrDefault(n => n.TradeNumber == tradeNumber); if (td == null) { throw new ServiceException("保存无风险利率失败,找不到交易信息,交易编号:" + tradeNumber); } return InnerSaveHisData(td, valueDate, ConsTradeField.NoRiskRate, value); } /// /// 保存分红率历史 /// public int SaveDividendRate(int tradeId, DateTime valueDate, double value) { return SaveHisData(tradeId, valueDate, ConsTradeField.DividendRate, value); } /// /// 保存分红率历史 /// public int SaveDividendRate(string tradeNumber, DateTime valueDate, double value) { if (string.IsNullOrEmpty(tradeNumber)) { throw new ArgumentException("参数不能为空值", nameof(tradeNumber)); } var td = DbContext.trade.FirstOrDefault(n => n.TradeNumber == tradeNumber); if (td == null) { throw new ServiceException("保存分红率失败,找不到交易信息,交易编号:" + tradeNumber); } return InnerSaveHisData(td, valueDate, ConsTradeField.DividendRate, value); } /// /// 保存交易数据历史(valueType:ConsTradeField) /// public int SaveHisData(int tradeId, DateTime valueDate, string valueType, double value) { if (tradeId < 1) { throw new ServiceException($"{nameof(tradeId)}应该大于0"); } if (string.IsNullOrWhiteSpace(valueType)) { throw new ArgumentException("不能为空", nameof(valueType)); } var td = DbContext.trade.Find(tradeId); if (td == null) { throw new ServiceException("更新失败,找不到交易信息,交易ID:" + tradeId); } return InnerSaveHisData(td, valueDate, valueType, value); } /// /// 保存交易数据历史(valueType:ConsTradeField) /// private int InnerSaveHisData(trade td, DateTime valueDate, string valueType, double value) { if (td == null) { throw new ServiceException("更新失败,找不到交易信息"); } switch (valueType) { case ConsTradeField.NoRiskRate: if (valueDate == valuedateBLL.ValueDate) { td.NoRiskRate = value; } else if (valueDate > valuedateBLL.ValueDate) { throw new ServiceException("保存无风险利率失败,因为赋值日期大于系统日期"); } break; case ConsTradeField.DividendRate: if (valueDate == valuedateBLL.ValueDate) { td.DividendRate = value; } else if (valueDate > valuedateBLL.ValueDate) { throw new ServiceException("保存分红率失败,因为赋值日期大于系统日期"); } break; default: throw new ServiceException("不支持:" + valueType); } var dbModel = DbContext.TradeHisData.FirstOrDefault(v => v.TradeId == td.id && v.ValueDate == valueDate && v.ValueType == valueType); if (dbModel == null) { dbModel = new TradeHisData { TradeId = td.id, ValueDate = valueDate, ValueType = valueType }; DbContext.TradeHisData.Add(dbModel); } dbModel.Value = value; SetDBModelOpt(dbModel); return DbContext.SaveChanges(); } #endregion /// /// 保存交易持仓波动率 /// public TradeVolatility SaveTradeVolatility(TradeVolatilityDto req, bool refreshRisk) { var tradePositionVolatility = req.TradePositionVolatility ?? 0; var tradeCloseVolatility = req.TradeCloseVolatility ?? 0; var numOfSmoothingDays = req.NumOfSmoothingDays ?? 0; if (tradePositionVolatility <= 0) { throw new ServiceException("持仓波动率必须大于0"); } if (tradeCloseVolatility <= 0) { throw new ServiceException("目标波动率必须大于0"); } if (numOfSmoothingDays <= 0) { throw new ServiceException("平滑天数必须大于0"); } trade td = null; if (req.TradeId > 0) { td = DbContext.trade.Find(req.TradeId); } else if (!string.IsNullOrWhiteSpace(req.TradeNumber)) { td = DbContext.trade.FirstOrDefault(n => n.TradeNumber == req.TradeNumber); } else { throw new ServiceException("参数错误:req.TradeId(TradeNumber)"); } if (td == null) { throw new ServiceException("找不到交易数据:" + req.TradeNumber); } var valueDate = valuedateBLL.ValueDate; var remianDays = QdpCalendarHelper.GetNonHolidayDaysBetween(valueDate, td.ExerciseDate.Value) + 1; if (td.ExerciseDate.HasValue && numOfSmoothingDays > remianDays) { throw new ServiceException($"平滑过渡天数{req.NumOfSmoothingDays}不能大于存续天数{remianDays}天"); } td.NumOfSmoothingDays = numOfSmoothingDays; td.TradeCloseVolatility = tradeCloseVolatility; //数据库中tradeid+valuedate是唯一索引 var dbModel = DbContext.TradeVolatility.FirstOrDefault(x => x.ValueDate == valueDate && x.TradeId == td.id); if (dbModel == null) { dbModel = new TradeVolatility { TradeId = td.id, ValueDate = valueDate }; DbContext.TradeVolatility.Add(dbModel); } SetDBModelOpt(dbModel); dbModel.NumOfSmoothingDays = numOfSmoothingDays; dbModel.TradePositionVolatility = tradePositionVolatility; dbModel.TradeCloseVolatility = tradeCloseVolatility; dbModel.IsFromTradeAdd = false; DbContext.SaveChanges(); if (refreshRisk) { RealtimePnlCalc.RefreshRiskAndIntradayPositionForOtc(new List() { td }, UserInfo); } AddTradeOperationHistoryAndSetParentTradeInfo(true, td, "修改交易持仓波动率"); return dbModel; } /// /// 使用历史数据更新交易数据(一般用于历史日期平仓结算) /// public void UpdateTradeWithHisData(OtcTradeBase td, DateTime hisDate) { if (td is null) { throw new ArgumentNullException(nameof(td)); } var query1 = DbContext.TradeHisData.AsNoTracking() .Where(v => v.TradeId == td.id && v.ValueDate <= hisDate && v.ValueType == ConsTradeField.NoRiskRate) .OrderByDescending(n => n.ValueDate).Select(n => new { n.ValueType, n.Value }); var query2 = DbContext.TradeHisData.AsNoTracking() .Where(v => v.TradeId == td.id && v.ValueDate <= hisDate && v.ValueType == ConsTradeField.DividendRate) .OrderByDescending(n => n.ValueDate).Select(n => new { n.ValueType, n.Value }); var datas = query1.Concat(query2).ToArray(); if (!datas.Any()) { return; } foreach (var data in datas) { if (ConsTradeField.NoRiskRate.Equals(data.ValueType, StringComparison.OrdinalIgnoreCase)) { td.NoRiskRate = data.Value; } else if (ConsTradeField.DividendRate.Equals(data.ValueType, StringComparison.OrdinalIgnoreCase)) { td.DividendRate = data.Value; } } } } }