using System.Data; using YLErp.Models; namespace YLErp.Modules.MarginModule { /// /// 标的预付金计算参数数据提供服务 /// public class MarginParamProvider : YLBaseService { readonly DateTime _date; readonly VarietyMarginParamProvider _vProvider; readonly Dictionary _dic; public MarginParamProvider(OptUserInfo userInfo, DateTime valueDate) : base(userInfo) { _date = valueDate; _vProvider = new VarietyMarginParamProvider(userInfo, valueDate); _dic = new Dictionary(StringComparer.OrdinalIgnoreCase); } /// /// 是否已经初始化 /// public bool Initialized => _dic != null && _dic.Count > 0; #region-----初始化数据---- /// /// 初始化数据提供,非必需操作 /// public MarginParamProvider Initialize(HashSet underlyingCodes, MarginParamTypeEnum typeEnum) { if (underlyingCodes is null || typeEnum == MarginParamTypeEnum.None || !underlyingCodes.Any(n => !string.IsNullOrEmpty(n))) { return this; } if (_dic.Count > 0) { throw new Exception("只允许初始化一次"); } underlyingCodes.Remove(null); var udatas = DbContext.underlying_manager.Where(n => underlyingCodes.Contains(n.UnderlyingCode)) .Select(n => new { n.UnderlyingCode, VarietyId = n.CommodityCode == "组合标的" || n.CommodityCode == "篮子标的" ? 0 : n.UnderlyingTypeId }).ToDictionary(n => n.UnderlyingCode, m => m.VarietyId, StringComparer.OrdinalIgnoreCase); var removeList = new List(); foreach (var code in underlyingCodes) { var mp = new MarginParamModelEx { TypeEnum = typeEnum, VaTypeEnum = typeEnum }; if (udatas.TryGetValue(code, out var vid)) { mp.VarietyId = vid; } else { //标的代码在数据库中不存在 removeList.Add(code); mp.MarginRate = mp.VolatilityRate = mp.UpDownLimit = mp.VarietyId = 0; } _dic[code] = mp; } underlyingCodes.RemoveWhere(O => removeList.Contains(O)); var enums = new[] { MarginParamTypeEnum.MarginRate, MarginParamTypeEnum.UpDownLimit, MarginParamTypeEnum.VolatilityRate }; foreach (var enm in enums) { if ((typeEnum & enm) == enm) { InnerInitializeUnderlying(underlyingCodes, enm); } } return this; } //用于初始化标的预付金参数数据 private void InnerInitializeUnderlying(IEnumerable underlyingCodes, MarginParamTypeEnum typeEnum) { var valueType = GetValueType(typeEnum); var predicat = PredicateBuilder.Create(uh => underlyingCodes.Contains(uh.UnderlyingCode) && uh.ValueDate <= _date && uh.ValueType == valueType); var ugQuery = from uh in DbContext.UnderlyingHisData.Where(predicat) group uh by uh.UnderlyingCode into g select new { UnderlyingCode = g.Key, ValueDate = g.Max(n => n.ValueDate) }; var uhQuery = from uh in DbContext.UnderlyingHisData.Where(predicat) join ug in ugQuery on new { uh.UnderlyingCode, uh.ValueDate } equals new { ug.UnderlyingCode, ug.ValueDate } select new { uh.UnderlyingCode, uh.Value, uh.ValueFlag }; var datas = uhQuery.ToArray(); switch (typeEnum) { case MarginParamTypeEnum.MarginRate: foreach (var data in datas) { _dic[data.UnderlyingCode].MarginRate = data.ValueFlag == "N" ? (double?)null : data.Value; } break; case MarginParamTypeEnum.UpDownLimit: foreach (var data in datas) { _dic[data.UnderlyingCode].UpDownLimit = data.ValueFlag == "N" ? (double?)null : data.Value; _dic[data.UnderlyingCode].IsUpDownLimitFixed = data.ValueFlag != "%"; } break; case MarginParamTypeEnum.VolatilityRate: foreach (var data in datas) { _dic[data.UnderlyingCode].VolatilityRate = data.ValueFlag == "N" ? (double?)null : data.Value; } break; } var unsetMps = _dic.Values.Where(n => !n.GetValue(typeEnum).HasValue).ToArray(); var varietyIds = unsetMps.Select(n => n.VarietyId.Value).ToHashSet(); _vProvider.Initialize(varietyIds, typeEnum); foreach (var item in unsetMps) { item.SetValue(typeEnum, _vProvider.GetData(item.VarietyId.Value, typeEnum)); } } #endregion #region----获取预付金参数---- /// /// 获取距valueDate最近的标的预付金率 /// public double? GetMarginRate(string underlyingCode) { return GetData(underlyingCode, MarginParamTypeEnum.MarginRate); } /// /// 获取距valueDate最近的标的预付金率 /// public bool TryGetMarginRate(string underlyingCode, out double marginRate) { return TryGetData(underlyingCode, MarginParamTypeEnum.MarginRate, out marginRate); } /// /// 获取距valueDate最近的标的涨跌幅度 /// public bool TryGetUpdownLimit(string underlyingCode, out double upDownLimit, out bool isFixed) { if (TryGetData(underlyingCode, MarginParamTypeEnum.UpDownLimit, out upDownLimit)) { isFixed = _dic[underlyingCode].IsUpDownLimitFixed; return true; } isFixed = false; upDownLimit = 0; return false; } /// /// 获取距valueDate最近的标的Span波动率变动 /// public double? GetVolatilityRate(string underlyingCode) { return GetData(underlyingCode, MarginParamTypeEnum.VolatilityRate); } /// /// 获取距valueDate最近的标的Span波动率变动 /// public bool TryGetVolatilityRate(string underlyingCode, out double volatilityRate) { return TryGetData(underlyingCode, MarginParamTypeEnum.VolatilityRate, out volatilityRate); } //获取特定预付金参数值 private double? GetData(string underlyingCode, MarginParamTypeEnum typeEnum) { if (string.IsNullOrEmpty(underlyingCode)) { return null; } //从字典中取数据 if (_dic.TryGetValue(underlyingCode, out var mp) && (typeEnum & mp.TypeEnum) == typeEnum) { var dval = mp.GetValue(typeEnum); if (dval.HasValue || (typeEnum & mp.VaTypeEnum) == typeEnum) { return dval; } } else { if (mp == null) { _dic[underlyingCode] = mp = new MarginParamModelEx(); } mp.TypeEnum |= typeEnum; //从underlying_hisdata表中取数据 var valueType = GetValueType(typeEnum); var uhQuery = from uh in DbContext.UnderlyingHisData where uh.UnderlyingCode == underlyingCode && uh.ValueDate <= _date && uh.ValueType == valueType orderby uh.ValueDate descending select new { uh.Value, uh.ValueFlag }; var uhData = uhQuery.FirstOrDefault(); if (uhData != null && uhData.ValueFlag != "N") { mp.SetValue(typeEnum, uhData.Value); if (typeEnum == MarginParamTypeEnum.UpDownLimit) { mp.IsUpDownLimitFixed = uhData.ValueFlag != "%"; } return uhData.Value; } else if (!DbContext.UnderlyingHisData.Any(uh => uh.UnderlyingCode == underlyingCode && uh.ValueDate > _date && uh.ValueType == valueType && uh.ValueFlag != "N")) { var un = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode); switch (typeEnum) { case MarginParamTypeEnum.UpDownLimit: if (NumberHelper.TryParse(un.UpDownLimit, out var pval, out var isPercent) && pval > 0) { mp.SetValue(typeEnum, pval); mp.IsUpDownLimitFixed = !isPercent; return pval; } break; case MarginParamTypeEnum.VolatilityRate: if (NumberHelper.TryParse(un.VolatilityRate, out pval, out isPercent) && pval > 0) { mp.SetValue(typeEnum, pval); return pval; } break; case MarginParamTypeEnum.MarginRate: if (un.MarginRate.HasValue && un.MarginRate > 0) { mp.SetValue(typeEnum, pval = un.MarginRate.Value); return pval; } break; } } } //从variety_hisdata表中取数据 mp.VaTypeEnum |= typeEnum; if (!mp.VarietyId.HasValue) { var un = DbContext.underlying_manager.Where(n => n.UnderlyingCode == underlyingCode) .Select(n => new { n.CommodityCode, n.UnderlyingTypeId }).FirstOrDefault(); mp.VarietyId = un == null || un.CommodityCode == "组合标的" || un.CommodityCode == "篮子标的" ? 0 : un.UnderlyingTypeId; } var dvalue = _vProvider.GetData(mp.VarietyId.Value, typeEnum); mp.SetValue(typeEnum, dvalue); return dvalue; } private bool TryGetData(string underlyingCode, MarginParamTypeEnum typeEnum, out double mpValue) { var dvalue = GetData(underlyingCode, typeEnum); mpValue = dvalue ?? 0; return dvalue.HasValue; } #endregion public double? GetVarietyData(int varietyId, MarginParamTypeEnum typeEnum) { return _vProvider.GetData(varietyId, typeEnum); } private static string GetValueType(MarginParamTypeEnum typeEnum) { switch (typeEnum) { case MarginParamTypeEnum.MarginRate: return nameof(MarginParamModel.MarginRate); case MarginParamTypeEnum.UpDownLimit: return nameof(MarginParamModel.UpDownLimit); case MarginParamTypeEnum.VolatilityRate: return nameof(MarginParamModel.VolatilityRate); default: throw new NotSupportedException(); } } } /// /// 品种级别的预付金参数提供 /// public class VarietyMarginParamProvider : YLBaseService { readonly DateTime _date; readonly Dictionary _dic; bool _hasInitAll; public VarietyMarginParamProvider(OptUserInfo userInfo, DateTime valueDate) : base(userInfo) { _date = valueDate; _dic = new Dictionary(); } #region----初始化---- /// /// 初始化数据(非必需) /// public VarietyMarginParamProvider Initialize(MarginParamTypeEnum typeEnum) { IEnumerable varietyIds; if (_hasInitAll) { varietyIds = _dic.Keys.ToArray(); } else { _hasInitAll = true; varietyIds = DbContext.variety.Select(n => n.id).ToArray(); } return InnerInitializeAllType(varietyIds, typeEnum); } /// /// 初始化数据(非必需) /// public VarietyMarginParamProvider Initialize(HashSet varietyIds, MarginParamTypeEnum typeEnum) { return InnerInitializeAllType(varietyIds, typeEnum); } //typeEnum可以是多个并在一起的枚举值 private VarietyMarginParamProvider InnerInitializeAllType(IEnumerable varietyIds, MarginParamTypeEnum typeEnum) { var enums = new[] { MarginParamTypeEnum.MarginRate, MarginParamTypeEnum.UpDownLimit, MarginParamTypeEnum.VolatilityRate }; foreach (var enm in enums) { if ((typeEnum & enm) == enm) { InnerInitializeOneType(varietyIds, enm); } } return this; } //typeEnum只能是单个具体的枚举值 private void InnerInitializeOneType(IEnumerable varietyIds, MarginParamTypeEnum typeEnum) { if (varietyIds == null || !varietyIds.Any()) return; foreach (var vid in varietyIds) { if (!_dic.TryGetValue(vid, out var mp)) { _dic.Add(vid, mp = new MarginParamModelEx { VarietyId = vid }); } mp.TypeEnum |= typeEnum; } var valueType = GetValueType(typeEnum); var baseQuery = DbContext.VarietyHisData.Where(vh => varietyIds.Contains(vh.VarietyId) && vh.ValueDate <= _date && vh.ValueType == valueType); var vgQuery = from vh in baseQuery group vh by vh.VarietyId into g select new { VarietyId = g.Key, ValueDate = g.Max(n => n.ValueDate) }; var vhQuery = from vh in baseQuery join vg in vgQuery on new { vh.VarietyId, vh.ValueDate } equals new { vg.VarietyId, vg.ValueDate } select new { vh.VarietyId, vh.Value }; var datas = vhQuery.ToArray(); switch (typeEnum) { case MarginParamTypeEnum.MarginRate: foreach (var data in datas) { _dic[data.VarietyId].MarginRate = data.Value; } break; case MarginParamTypeEnum.UpDownLimit: foreach (var data in datas) { _dic[data.VarietyId].UpDownLimit = data.Value; } break; case MarginParamTypeEnum.VolatilityRate: foreach (var data in datas) { _dic[data.VarietyId].VolatilityRate = data.Value; } break; } } #endregion #region----获取预付金参数---- /// /// 获取距valueDate最近的品种预付金率 /// public double? GetMarginRate(int varietyId) { return GetData(varietyId, MarginParamTypeEnum.MarginRate); } /// /// 获取距valueDate最近的品种预付金率 /// public bool TryGetMarginRate(int varietyId, out double marginRate) { return TryGetData(varietyId, MarginParamTypeEnum.MarginRate, out marginRate); } /// /// 获取距valueDate最近的标的涨跌幅度 /// public double? GetUpdownLimit(int varietyId) { return GetData(varietyId, MarginParamTypeEnum.UpDownLimit); } /// /// 获取距valueDate最近的标的涨跌幅度 /// public bool TryGetUpdownLimit(int varietyId, out double upDownLimit) { return TryGetData(varietyId, MarginParamTypeEnum.UpDownLimit, out upDownLimit); } /// /// 获取距valueDate最近的标的Span波动率变动 /// public double? GetVolatilityRate(int varietyId) { return GetData(varietyId, MarginParamTypeEnum.VolatilityRate); } /// /// 获取距valueDate最近的标的Span波动率变动 /// public bool GetVolatilityRate(int varietyId, out double volatilityRate) { return TryGetData(varietyId, MarginParamTypeEnum.VolatilityRate, out volatilityRate); } /// /// /// public double? GetData(int varietyId, MarginParamTypeEnum typeEnum) { if (varietyId < 1) return null; if (_dic.TryGetValue(varietyId, out var mp) && (mp.TypeEnum & typeEnum) == typeEnum) { return mp.GetValue(typeEnum); } if (mp == null) { _dic[varietyId] = mp = new MarginParamModelEx { VarietyId = varietyId }; } var valueType = GetValueType(typeEnum); var hquery = from h in DbContext.VarietyHisData where h.VarietyId == varietyId && h.ValueDate <= _date && h.ValueType == valueType orderby h.ValueDate descending select (double?)h.Value; var mpValue = hquery.FirstOrDefault(); if (mpValue == null) { var va = DataCacheProvider.GetVarietyDataSource().GetData(varietyId); if (va != null) { switch (typeEnum) { case MarginParamTypeEnum.MarginRate: mpValue = va.Margin; break; case MarginParamTypeEnum.UpDownLimit: mpValue = va.UpLimitValue; break; case MarginParamTypeEnum.VolatilityRate: mpValue = va.VolatilityRateValue; break; } } } mp.SetValue(typeEnum, mpValue ?? 0); return mpValue; } /// /// /// public bool TryGetData(int varietyId, MarginParamTypeEnum typeEnum, out double mpValue) { var dvalue = GetData(varietyId, typeEnum); mpValue = dvalue ?? 0; return dvalue.HasValue; } #endregion private static string GetValueType(MarginParamTypeEnum typeEnum) { switch (typeEnum) { case MarginParamTypeEnum.MarginRate: return nameof(MarginParamModel.MarginRate); case MarginParamTypeEnum.UpDownLimit: return nameof(MarginParamModel.UpDownLimit); case MarginParamTypeEnum.VolatilityRate: return nameof(MarginParamModel.VolatilityRate); default: throw new NotSupportedException(); } } } /// /// /// class MarginParamModelEx : MarginParamModel { public int? VarietyId; /// /// 用于检查是否已经向数据库请求过 /// public MarginParamTypeEnum TypeEnum { get; set; } /// /// 用于检查是否已经从数据库请求过品种关联的数据 /// public MarginParamTypeEnum VaTypeEnum { get; set; } /// /// /// public double? GetValue(MarginParamTypeEnum typeEnum) { switch (typeEnum) { case MarginParamTypeEnum.MarginRate: return MarginRate; case MarginParamTypeEnum.UpDownLimit: return UpDownLimit; case MarginParamTypeEnum.VolatilityRate: return VolatilityRate; default: throw new NotSupportedException(); } } /// /// /// public void SetValue(MarginParamTypeEnum typeEnum, double? dvalue) { if (dvalue.HasValue) { dvalue = Math.Abs(dvalue.Value); } switch (typeEnum) { case MarginParamTypeEnum.MarginRate: MarginRate = dvalue; break; case MarginParamTypeEnum.UpDownLimit: UpDownLimit = dvalue; break; case MarginParamTypeEnum.VolatilityRate: VolatilityRate = dvalue; break; default: throw new NotSupportedException(); } } } /// /// 预付金参数类型枚举 /// [Flags] public enum MarginParamTypeEnum { None = 0, //Span波动率变动 MarginRate = 1 << 0, //Span涨跌幅度 UpDownLimit = 1 << 1, //预付金比例 VolatilityRate = 1 << 2, All = MarginRate | UpDownLimit | VolatilityRate } }