Files
zszq-trs/YLErpDAL/Modules/TradeModule/TradeHisDataService.cs
T
2024-05-09 14:06:26 +08:00

411 lines
14 KiB
C#

using YLErp.BLL;
using YLErp.BLL.Eod;
using YLErp.DBModels.Consts;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule
{
/// <summary>
/// 交易历史记录服务
/// </summary>
public class TradeHisDataService : TradeServiceBase
{
public TradeHisDataService(YLBaseService baseService) : base(baseService)
{
}
public TradeHisDataService(OptUserInfo userInfo) : base(userInfo)
{
}
#region----对冲波动率----
/// <summary>
/// 根据交易ID获取最新的对冲波动率
/// </summary>
/// <returns>返回交易ID为Key,对冲波动率为Value的字典</returns>
public Dictionary<int, double> GetLatestHedgeVol(IEnumerable<int> tradeIds)
{
if (tradeIds is null)
{
throw new ArgumentNullException(nameof(tradeIds));
}
var count = tradeIds.Count();
var dic = new Dictionary<int, double>();
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;
}
/// <summary>
/// 保存对冲波动率
/// </summary>
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);
}
/// <summary>
/// 保存对冲波动率
/// </summary>
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);
}
/// <summary>
/// 保存对冲波动率
/// </summary>
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----无风险利率/分红率---
/// <summary>
/// 保存无风险利率历史
/// </summary>
public int SaveNoRiskRate(int tradeId, DateTime valueDate, double value)
{
return SaveHisData(tradeId, valueDate, ConsTradeField.NoRiskRate, value);
}
/// <summary>
/// 保存无风险利率历史
/// </summary>
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);
}
/// <summary>
/// 保存分红率历史
/// </summary>
public int SaveDividendRate(int tradeId, DateTime valueDate, double value)
{
return SaveHisData(tradeId, valueDate, ConsTradeField.DividendRate, value);
}
/// <summary>
/// 保存分红率历史
/// </summary>
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);
}
/// <summary>
/// 保存交易数据历史(valueType:ConsTradeField)
/// </summary>
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);
}
/// <summary>
/// 保存交易数据历史(valueType:ConsTradeField)
/// </summary>
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
/// <summary>
/// 保存交易持仓波动率
/// </summary>
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<trade>() { td }, UserInfo);
}
AddTradeOperationHistoryAndSetParentTradeInfo(true, td, "修改交易持仓波动率");
return dbModel;
}
/// <summary>
/// 使用历史数据更新交易数据(一般用于历史日期平仓结算)
/// </summary>
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;
}
}
}
}
}