using YLErp.DBModels; using YLErp.Modules; using YLErp.QdpModule; using YLErp.Jobs.Configs; namespace YLErp.Jobs.Imp { /// /// 国元计算波动率 /// public class GuoYuanCalcVolJob : BaseJob { static readonly IYcLogger logger; static GuoYuanCalcVolJob() { logger = LogFactory.GetLogger(); } GuoYuanCalcVolJobConfig _guoYuanCalcVolJobConfig; public GuoYuanCalcVolJob(GuoYuanCalcVolJobConfig guoYuanCalcVolJobConfig) { _guoYuanCalcVolJobConfig= guoYuanCalcVolJobConfig; } public override Task JobImp() { var startDate = DateTime.MinValue; var endDate = DateTime.MinValue; try { startDate = _guoYuanCalcVolJobConfig.StartDate.Value; endDate = _guoYuanCalcVolJobConfig.EndDate.Value; } catch (Exception ex) { logger.Error(ex, "参数错误"); } for (DateTime date = startDate; date <= endDate; date = date.AddDays(1)) { try { logger.Info($"开始计算 {date:yyyy-MM-dd}"); if (QdpCalendarHelper.IsHoliday(date)) { logger.Info($"{date:yyyy-MM-dd} 非交易日,跳过执行"); continue; } Execute(date); logger.Info($"计算完成 {date:yyyy-MM-dd}"); } catch (Exception ex) { logger.Error(ex, $"计算异常 {date:yyyy-MM-dd}"); } } return Task.CompletedTask; } #region JobStatus Manager static JobStatus jobStatus = JobStatus.Init; public override JobStatus GetJobStatus() { return jobStatus; } public override void SetJobStatus(JobStatus status) { jobStatus = status; } #endregion public Task Execute(DateTime valueDate) { logger.Info($"执行每日计算Vol {valueDate:yyyy-MM-dd}"); var db = DbContextFactory.GetYLDbContext(); var umList = (from um in db.underlying_manager join vol in db.volatility.Where(O => O.QuotationDate == valueDate) on um.UnderlyingCode equals vol.ContractCode into temp from vol in temp.DefaultIfEmpty() where um.UnderlyingState != "Matured" && um.LaunchState == "1" && vol == null select new { um.id, um.UnderlyingCode }).ToArray(); var volList = new List(); foreach (var um in umList) { try { logger.Info($"{um.UnderlyingCode} 准备计算Vol"); var prices = db.eod_stock_price .Where(O => O.UnderlyingCode == um.UnderlyingCode && O.ValueDate < valueDate) .Select(O => new { O.ValueDate, O.ClosePrice }) .OrderByDescending(O => O.ValueDate) .Take(121).Select(O => O.ClosePrice).ToArray(); if (prices.Length <= 1) { logger.Info($"{um.UnderlyingCode} 价格缺失,跳过计算Vol"); continue; } if (prices.Length < 121) { logger.Info($"{um.UnderlyingCode} 价格不足121,继续计算Vol"); } logger.Info($"{um.UnderlyingCode} 计算Vol价格:{prices.ToJson()}"); var vol = CalcVol(prices); logger.Info($"{um.UnderlyingCode} 计算完成 {vol}"); var midVol = VolatilityBuilder.CreateMoneynessVolBuilder(valueDate, "交易").SetUnderlying(um.id, um.UnderlyingCode).SetOpt(0, "系统计算").Build(vol); var bidVol = VolatilityBuilder.CreateMoneynessVolBuilder(valueDate, "报价Bid").SetUnderlying(um.id, um.UnderlyingCode).SetOpt(0, "系统计算").Build(vol); var askVol = VolatilityBuilder.CreateMoneynessVolBuilder(valueDate, "报价Ask").SetUnderlying(um.id, um.UnderlyingCode).SetOpt(0, "系统计算").Build(vol); volList.Add(midVol); volList.Add(bidVol); volList.Add(askVol); } catch (Exception ex) { logger.Info($"{um.UnderlyingCode} 计算Vol错误:{ex.ToJson()}"); } } db.volatility.AddRange(volList); db.SaveChanges(); logger.Info("每日计算Vol执行完成"); return Task.CompletedTask; } private double CalcVol(double[] arrData) { var result = 0d; if (arrData.Length < 2) { throw new Exception("数据不足!"); } #region Log var log = new double[arrData.Length - 1]; for (int i = 1; i < arrData.Length; i++) { log[i - 1] = Math.Log(arrData[i] / arrData[i - 1]); } #endregion #region STDEV.P var avg = log.Average(); var sum = log.Sum(O => Math.Pow(O - avg, 2)); var sqrt = Math.Sqrt(sum / log.Length); #endregion #region Annualized result = sqrt * Math.Sqrt(240); #endregion return result; } } }