Files
zszq-trs/Framework/YLErp.Jobs/Imp/GuoYuanCalcVolJob.cs
T
2024-05-09 14:06:26 +08:00

171 lines
5.9 KiB
C#

using YLErp.DBModels;
using YLErp.Modules;
using YLErp.QdpModule;
using YLErp.Jobs.Configs;
namespace YLErp.Jobs.Imp
{
/// <summary>
/// 国元计算波动率
/// </summary>
public class GuoYuanCalcVolJob : BaseJob
{
static readonly IYcLogger logger;
static GuoYuanCalcVolJob()
{
logger = LogFactory.GetLogger<GuoYuanCalcVolJob>();
}
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<volatility>();
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;
}
}
}