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

339 lines
13 KiB
C#

using YLErp.Commons;
using YLErp.DBModels.Consts;
using YLErp.Models;
using YLErp.QdpModule.Constants;
namespace YLErp.Modules.VolatilityModule
{
/// <summary>
/// 波动率保存服务
/// </summary>
public class VolatilitySaveService : YLBaseService
{
public VolatilitySaveService(OptUserInfo userInfo) : base(userInfo)
{
}
public VolatilitySaveService(YLBaseService baseService) : base(baseService)
{
}
/// <summary>
///
/// </summary>
public List<volatility> SaveVol(volatility vol)
{
if (vol is null)
{
throw new ArgumentNullException(nameof(vol));
}
return SaveVols(new List<volatility> { vol });
}
/// <summary>
///
/// </summary>
public List<volatility> SaveVols(List<volatility> vols, bool overridByMainCode = false)
{
if (ConsUserGroup.HasGroup)
{
if (string.IsNullOrEmpty(OptUser.UserGroup))
{
throw new ServiceException("缺少用户组");
}
if (!ConsUserGroup.GetGroups().Any(n => n.ItemValue == OptUser.UserGroup))
{
throw new ServiceException("用户组不存在:" + OptUser.UserGroup);
}
}
var checkResult = PrepareVols(vols);
if (!checkResult.IsSuccess)
{
throw new ServiceException(checkResult.Message);
}
var date = vols[0].QuotationDate;
if (overridByMainCode)
{
Dictionary<string, volatility[]> mainDic = null;
//PrepareVols方法处理,可能会产生多个波动率类型(比如交易会分出来mid,ask,bid)
foreach (var vl in vols.GroupBy(n => n.ContractCode.ToLowerInvariant()))
{
var ContractCode = vl.Key;
if (System.Text.RegularExpressions.Regex.IsMatch(ContractCode, "^[a-zA-Z]+00$"))
{
var un = DataCacheProvider.GetUnderlyingDataSource().GetData(ContractCode);
if (un != null && un.IsFutures() && !string.IsNullOrEmpty(un.CommodityCode))
{
if (mainDic == null)
{
mainDic = new Dictionary<string, volatility[]>(StringComparer.OrdinalIgnoreCase);
}
mainDic[un.CommodityCode] = vl.ToArray();
}
}
}
if (mainDic != null)
{
var existsCodes = vols.Select(n => n.ContractCode).ToHashSet(StringComparer.OrdinalIgnoreCase);
var query = from un in DbContext.underlying_manager
where un.CommodityCode != null
&& (un.MaturityDate == null || un.MaturityDate.Value >= date)
&& mainDic.Keys.Contains(un.CommodityCode)
&& !existsCodes.Contains(un.UnderlyingCode)
select new { un.CommodityCode, un.UnderlyingCode, un.id };
var overrideUns = query.ToArray();
var overrideVols = overrideUns.SelectMany(un => mainDic[un.CommodityCode].Select(n =>
{
var clone = n.Clone();
clone.UnderlyingId = un.id;
clone.ContractCode = un.UnderlyingCode;
return clone;
}));
vols.AddRange(overrideVols);
}
}
foreach (var v in vols)
{
v.UserGroup = OptUser.UserGroup;
var dbVol = DbContext.volatility.FirstOrDefault(n =>
n.ContractCode == v.ContractCode && n.VolType == v.VolType &&
n.UserGroup == v.UserGroup && n.QuotationDate == date);
if (v.InterpolationMethod == null)
{
v.InterpolationMethod = ConsVolMethod.Default;
}
if (dbVol == null)
{
dbVol = DbContext.volatility.Add(v).Entity;
}
else
{
dbVol.Data = v.Data;
dbVol.InterpolationMethod = v.InterpolationMethod;
}
dbVol.OptId = UserId;
dbVol.OptName = UserName;
dbVol.OptDate = OptDate;
}
int changes = DbContext.SaveChanges();
return vols;
}
/// <summary>
///
/// </summary>
private HandleResult PrepareVols(List<volatility> vols)
{
if (vols is null || !vols.Any())
{
throw new ArgumentException("参数不能为空", nameof(vols));
}
if (vols.Any(n => string.IsNullOrEmpty(n.ContractCode)))
{
return "标的代码不允许为空!";
}
var firstVol = vols.First();
if (firstVol.QuotationDate.Year < 2000)
{
return "报价日期不正确:" + firstVol.QuotationDate.ToString("yyyy-MM-dd");
}
if (vols.Select(O => O.QuotationDate).Distinct().Count() > 1)
{
return "存在多个报价日期的波动率!";
}
//检查标的信息
if (!firstVol.UnderlyingId.HasValue)
{
var unCodes = vols.Select(n => n.ContractCode).ToHashSet();
var unCodeMap = DbContext.underlying_manager.Where(n => unCodes.Contains(n.UnderlyingCode))
.ToDictionary(n => n.UnderlyingCode, n => n.id);
foreach (var vol in vols)
{
if (!unCodeMap.TryGetValue(vol.ContractCode, out var unId))
{
return "标的信息未找到:" + vol.ContractCode;
}
vol.UnderlyingId = unId;
}
}
//检查标的到期日期是否大于等于报价日期
var unIds = vols.Select(n => n.UnderlyingId).ToHashSet();
var maturedUns = DbContext.underlying_manager.Where(n =>
unIds.Contains(n.id) && n.UnderlyingInstrumentType == "CommodityFutures" && n.MaturityDate.Value < firstVol.QuotationDate)
.Select(n => new { n.UnderlyingCode, MaturityDate = n.MaturityDate.Value }).ToArray();
if (maturedUns.Any())
{
var arr = maturedUns.Select(n => $"{n.UnderlyingCode}(到期日:{n.MaturityDate:yyyyMMdd})").ToArray();
return $"标的到期日期需大于等于报价日期({firstVol.QuotationDate:yyyyMMdd}),到期标的:{string.Join(",", arr)}";
}
//检查到期日和行权价是否一致
if (vols.Any(O => ConsVolInfos.subTradeVolType.Contains(O.VolType)))
{
var dict = vols.GroupBy(O => $"{O.ContractCode}").ToDictionary(K => K.Key, V => V.ToList());
var volTypeGroup = dict.Select(O => O.Value.Count()).Distinct().ToArray();
if (volTypeGroup.Length > 1)
{
return "列表中存在多个波动率类型!";
}
if (volTypeGroup[0] == 3)
{
if (!CheckVolTables(vols, out var errMessage))
{
return errMessage;
}
}
else if (volTypeGroup[0] == 2)
{
if (!CheckVolTables(vols, out var errMessage))
{
return errMessage;
}
foreach (var item in dict)
{
if (item.Value.Count != 2)
{
return $"列表中 {item.Key} 波动率有重复!";
}
volatility vol1 = item.Value[0];
volatility vol2 = item.Value[1];
volatility vol = vol1.Clone();
vol.VolType = "交易";
var volTable = new List<SingleVol>();
foreach (var volItem in vol2.VolTable)
{
var singleVol1 = vol1.VolTable.Find(O => O.Expire == volItem.Expire && O.Strike == volItem.Strike);
volTable.Add(new SingleVol(volItem.Strike, volItem.Expire, ((volItem.Vol + singleVol1.Vol) / 2).FormatValue(2)));
}
vol.Data = volTable.ToJson();
vols.Add(vol);
}
}
else if (volTypeGroup[0] == 1)
{
var dbVols = new VolatilityQueryService(OptUser).GetVolatilities(new BatchVolatilityRequest
{
QuotationDate = vols[0].QuotationDate,
TradeVolWithBidAsk = false,
UnderlyingCodes = dict.Keys,
UserGroup = OptUser.UserGroup,
VolType = "交易"
}, false);
foreach (var item in dict)
{
volatility tempVol = null;
if ((tempVol = dbVols.FirstOrDefault(O => O.ContractCode == item.Key)) == null)
{
return $"系统中不存在 {item.Key} 的波动率!";
}
if (!CheckVolTables(new List<volatility>() { item.Value[0], tempVol }, out var errMessage))
{
return errMessage;
}
}
}
}
else if (vols.GroupBy(O => O.VolType).Count() > 1)
{
return "列表中存在多个波动率类型的波动率!";
}
else if (vols.All(O => O.VolType == "交易") && !PS.Config.Is光大光子)
{
List<volatility> tempList = new List<volatility>();
foreach (var item in vols)
{
volatility bidVol = item.Clone();
bidVol.VolType = "报价Bid";
volatility askVol = item.Clone();
askVol.VolType = "报价Ask";
List<SingleVol> bidVolTable = new List<SingleVol>();
List<SingleVol> askVolTable = new List<SingleVol>();
foreach (var volItem in item.VolTable)
{
double bVol = volItem.Vol + (item.Bid_Deviation ?? 0);
double aVol = volItem.Vol + (item.Ask_Deviation ?? 0);
if (bVol < 0 || aVol < 0)
{
return "波动率偏离后不应小于0";
}
bidVolTable.Add(new SingleVol(volItem.Strike, volItem.Expire, bVol));
askVolTable.Add(new SingleVol(volItem.Strike, volItem.Expire, aVol));
}
bidVol.Data = bidVolTable.ToJson();
askVol.Data = askVolTable.ToJson();
tempList.Add(bidVol);
tempList.Add(askVol);
}
vols.AddRange(tempList);
}
return HandleResult.Success;
}
/// <summary>
/// 检查VolTable是否匹配
/// </summary>
private bool CheckVolTables(IEnumerable<volatility> vols, out string errMessage)
{
Dictionary<string, List<volatility>> volDict = vols.GroupBy(O => $"{O.ContractCode}{O.UserGroup}{O.QuotationDate}").ToDictionary(K => K.Key, V => V.ToList());
errMessage = "";
foreach (var singleVols in volDict)
{
if (singleVols.Value.Count() < 2)
{
return true;
}
var first = singleVols.Value.First();
var set = first.VolTable.Select(n => n.Expire.ToUpper() + "^" + n.Strike.ToString("F6")).ToHashSet();
foreach (var item in singleVols.Value.Skip(1))
{
if (first.VolTable.Count != item.VolTable.Count)
{
errMessage = $"{first.ContractCode} 波动率行列不匹配!";
return false;
}
if (!item.VolTable.All(n => set.Contains(n.Expire.ToUpper() + "^" + n.Strike.ToString("F6"))))
{
errMessage = $"{first.ContractCode} 行权价或期限不匹配!";
return false;
}
}
}
return true;
}
}
}