Files
zszq-trs/YLErpDAL/Modules/EodModule/SettlementModule/EodTaskRunner.cs
T

753 lines
27 KiB
C#

using System.Text;
using YLErp.BLL.EodSettlement;
using YLErp.Configuration;
using YLErp.Modules.EodFileModule;
using YLErp.Modules.EodModule.CompanySpecial;
using YLErp.Modules.ExchangeOptionTradeModule;
using YLErp.Modules.RiskModule;
using YLErp.Modules.SystemModule;
using YLErp.Modules.TradeDalModule;
using YLErp.Modules.TradeModule.DealModule;
namespace YLErp.Modules.EodModule.SettlementModule
{
/// <summary>
///
/// </summary>
public static class EodTaskRunner
{
[ThreadStatic]
static string TaskStep;
static InnerRunner _runner;
static readonly EodTaskReportInner _report;
static EodTaskRunner()
{
_report = new EodTaskReportInner();
}
/// <summary>
/// 锁定对象
/// </summary>
public static object LockObj => _report;
/// <summary>
/// 最后一次执行的任务
/// </summary>
public static EodTask LastTask => _report.EodTask;
/// <summary>
/// 定时计划任务调用
/// </summary>
public static void Execute()
{
lock (_report)
{
if (_runner != null && _runner?.StopCountDown() > 0)
{
return;
}
_runner = new InnerRunner();
}
try
{
using (var db = DbContextFactory.GetYLDbContext())
{
EodTask t;
var table = db.GetTableName<EodTask>();
var startDate = DateTime.Today.AddDays(-3);
var startDateStr = startDate.ToString(ConsGlobal.DateFormat);
if (db.EodTask.Any(n => n.TaskStartTime > startDate && (n.TaskState == EodTaskState.running || n.TaskState == EodTaskState.canceling)))
{
var sql = $@"
update {table} set {nameof(t.TaskState)}={(int)EodTaskState.canceled}
where {nameof(t.TaskStartTime)}>'{startDateStr}' and {nameof(t.TaskState)}={(int)EodTaskState.canceling};
update {table} set {nameof(t.TaskState)}={(int)EodTaskState.interrupted}
where {nameof(t.TaskStartTime)}>'{startDateStr}' and {nameof(t.TaskState)}={(int)EodTaskState.running};";
db.Database.ExecuteSqlRaw(sql);
}
}
_runner.Execute();
}
catch (Exception ex)
{
LogFactory.GetLogger("日终结算").Error(ex);
}
finally
{
var runner = _runner;
_runner = null;
runner?.Dispose();
}
}
/// <summary>
/// 调试状态下调用
/// </summary>
public static Task ExecuteDebugAsync(EodTask eodTask)
{
if (eodTask is null)
{
throw new ArgumentNullException(nameof(eodTask));
}
lock (_report)
{
if (_runner != null && _runner?.StopCountDown() > 0)
{
throw new ServiceException("正在执行中");
}
_runner = new InnerRunner();
}
return Task.Run(() =>
{
_runner.Execute(eodTask);
}).ContinueWith(t =>
{
if (t.Exception != null)
{
LogFactory.GetLogger("日终结算").Error(t.Exception);
}
var runner = _runner;
_runner = null;
runner?.Dispose();
});
}
/// <summary>
/// 停止正在执行的任务
/// </summary>
public static bool Stop(long taskId)
{
if (_runner != null && _runner.CurTask?.id == taskId)
{
_runner.Stop();
return true;
}
return false;
}
#region----任务运行报告----
class EodTaskReportInner
{
EodTask _eodTask;
readonly List<EodTaskDetail> _details;
public EodTaskReportInner()
{
_details = new List<EodTaskDetail>();
}
public EodTask EodTask
{
get
{
if (_eodTask == null/* || _eodTask.TaskStartTime < DateTime.Today*/)
{
return null;
}
var clone = _eodTask;
if (clone.TaskState == EodTaskState.running)
{
clone = _eodTask.Clone();
clone.TaskEndTime = DateTime.Now;
StopTaskDetail();
}
clone.Details = _details.Count < 5 ? _details.AsEnumerable() : _details.Skip(_details.Count - 4).ToArray();
return clone;
}
}
private void StopTaskDetail()
{
if (_details.Count > 0)
{
var last = _details.Last();
last.ElapsedMilliseconds = (int)(DateTime.Now - last.StartTime).TotalMilliseconds;
}
}
public void StartEodTask(EodTask eodTask)
{
if (eodTask != null)
{
_eodTask = eodTask;
_details.Clear();
}
}
public void StartTaskDetail(EodTaskDetail detail)
{
StopTaskDetail();
_details.Add(detail.Clone());
}
public void SetErrorTips(string errorTips)
{
if (_eodTask != null && !string.IsNullOrWhiteSpace(errorTips))
{
_eodTask.ErrorTips += errorTips;
}
}
public void StopEodTask()
{
if (_eodTask != null)
{
StopTaskDetail();
_eodTask = _eodTask.Clone();
}
}
}
#endregion
#region----执行批量收盘----
class InnerRunner : YLBaseService
{
readonly CancellationTokenSource _cancellationTokenSource;
int _stopCountDown = int.MaxValue;
public InnerRunner() : base(OptUserInfo.SystemUser)
{
_cancellationTokenSource = new CancellationTokenSource();
}
/// <summary>
/// 正在运行的任务
/// </summary>
public EodTask CurTask { get; private set; }
/// <summary>
/// 停止倒数计数
/// </summary>
/// <returns></returns>
public int StopCountDown()
{
return _stopCountDown--;
}
/// <summary>
/// 停止
/// </summary>
public void Stop()
{
if (CurTask != null && CurTask.TaskState < EodTaskState.completed)
{
CurTask.TaskState = EodTaskState.canceling;
DbContext.SaveChanges();
}
_stopCountDown = 600;
try
{
//可能有ObjectDisposedException异常
_cancellationTokenSource.Cancel();
}
catch { }
}
/// <summary>
/// 执行
/// </summary>
public void Execute(EodTask eodTask = null)
{
if (eodTask == null)
{
eodTask = QueryFirstAvailableTask();
}
CurTask = eodTask;
if (eodTask == null)
{
return;
}
_report.StartEodTask(eodTask);
try
{
if (eodTask.ValueDate < eodTask.StartDate)
{
eodTask.ValueDate = eodTask.StartDate;
}
eodTask.TaskStartTime = DateTime.Now;
eodTask.TaskState = EodTaskState.running;
var settlementConfig = PS.Config.GetSettlementConfig();
if (settlementConfig.SkipLastPvCheck)
{
eodTask.StepFlag = 1;
}
if (!string.IsNullOrWhiteSpace(eodTask.ClientIds))
{
eodTask.ListClientId = eodTask.ClientIds.Split(',').Select(l => int.Parse(l)).ToList();
eodTask.ClientNameStr = String.Join(",", ClientDataModelV1.GetAllOpenClient(isContainClosed: true).Where(l => eodTask.ListClientId.Contains(int.Parse(l.Value))).Select(l => l.Text));
}
RunEodTask(eodTask, settlementConfig);
}
catch (Exception ex)
{
try
{
//可能有ObjectDisposedException异常
_cancellationTokenSource.Cancel();
}
catch { }
eodTask.SetFailure(ex.Messages());
LogFactory.GetLogger("收盘报错").Error(ex);
}
finally
{
if (eodTask.TaskState < EodTaskState.completed)
{
eodTask.TaskState = EodTaskState.completed;
}
else if (eodTask.TaskState == EodTaskState.canceling)
{
eodTask.TaskState = EodTaskState.canceled;
}
eodTask.TaskEndTime = DateTime.Now;
DbContext.SaveChanges();
_report.StopEodTask();
}
}
//根据波动率和价格模式进行步骤分解
private void RunEodTask(EodTask eodTask, SettlementConfig settlementConfig)
{
var userInfo = new OptUserInfo(eodTask.OptId, eodTask.OptName, OptUserFrom.System);
var request = new EodSettlementRequest(userInfo)
{
VolType = null,
UseClosePrice = true,
SettleDate = eodTask.ValueDate,
ClientIds = eodTask.ListClientId,
};
request.IsDelayTrade = eodTask.IsDelayTrade;
if (!string.IsNullOrWhiteSpace(eodTask.PartFlag) && eodTask.PartFlag != "全部")
{
request.IsSettleOtcTrades = eodTask.PartFlag == "场外";
request.IsSettleExchangeTrades = !request.IsSettleOtcTrades;
}
var priceTypes = new[] { EodSettlePriceType.ClosePrice, EodSettlePriceType.SettlePrice };
//从开始日期递增执行到结束日期
for (var i = 0; i < 10000; i++)
{
_cancellationTokenSource.Token.ThrowIfCancellationRequested();
eodTask.ValueDate = BLL.valuedateBLL.GetNonHoliday(eodTask.ValueDate);
if (eodTask.ValueDate > eodTask.EndDate)
{
break;
}
DbContext.SaveChanges();
//检查任务是否可以正确执行
var cloneTask = eodTask.Clone();
cloneTask.StartDate = eodTask.ValueDate;
var result = new EodTaskSaveService(this).CheckEodTask(cloneTask);
if (result.HasError)
{
eodTask.SetError(result.IsFatalError, result.ErrorMsg);
return;
}
//准备执行
request.SettleDate = eodTask.ValueDate;
if (request.IsDelayTrade)
{
request.DelayUnderlyIds = new TradeDelaySettlementService(userInfo).GetDelayUnderlyIds(eodTask.ValueDate);
}
else
{
request.DelayUnderlyIds = new List<int>();
}
//任务明细
var detail = new EodTaskDetail
{
TaskId = eodTask.id,
ValueDate = eodTask.ValueDate,
StartTime = DateTime.Now,
ElapsedMilliseconds = 0
};
_report.StartTaskDetail(detail);
if (eodTask.id > 0)
{
DbContext.EodTaskDetail.Add(detail);
}
foreach (var priceType in priceTypes)
{
if (eodTask.PriceType.HasFlag(priceType))
{
foreach (var volType in result.VolTypes)
{
request.VolType = volType;
request.UseClosePrice = priceType == EodSettlePriceType.ClosePrice;
var runner = new InnerTaskRunner(request.Clone(true), settlementConfig, _cancellationTokenSource);
runner.Execute(out var elapsedMilliseconds);
detail.ElapsedMilliseconds += (int)elapsedMilliseconds;
}
}
}
ClientBalanceUtility.saveClientRiskMonitor(eodTask.ValueDate);
new EodFileService(this.OptUser).GenerateFileAfterEod(eodTask.ValueDate);
//执行下一日
eodTask.ValueDate = eodTask.ValueDate.AddDays(1);
eodTask.TaskEndTime = DateTime.Now;
}
}
/// <summary>
/// 查找第一个可用的任务
/// </summary>
private EodTask QueryFirstAvailableTask()
{
var startDate = DateTime.Today.AddDays(-3);
return DbContext.EodTask.Where(n => n.TaskStartTime > startDate && n.TaskState < EodTaskState.completed)
.OrderBy(n => n.StartDate).ThenBy(n => n.id).FirstOrDefault();
}
}
#endregion
#region----执行每日收盘----
class InnerTaskRunner : IAsyncTaskManager
{
readonly EodSettlementRequest _request;
readonly EodSettlementContextV2 _context;
readonly CancellationTokenSource _cancellationTokenSource;
public InnerTaskRunner(EodSettlementRequest request, SettlementConfig settlementConfig, CancellationTokenSource cancellationTokenSource)
{
_request = request ?? throw new ArgumentNullException(nameof(request));
_cancellationTokenSource = cancellationTokenSource ?? throw new ArgumentNullException(nameof(cancellationTokenSource));
_context = new EodSettlementContextV2(request, settlementConfig, _cancellationTokenSource.Token);
}
public void Execute(out long elapsedMilliseconds)
{
var sw = new System.Diagnostics.Stopwatch();
_context.LogInfo("!!!!!!收盘!!!!!!");
_context.LogInfo($"开始收盘:{_request.SettleDate:yyyy-MM-dd} + {_request.VolType} + {_request.GetPriceType()}");
sw.Start();
if (!_request.IsPartialSettlement)
{
AddOrUpdateEodStatus(_request, true);
}
else
{
UpdateEodCheckStatus(_request);
}
var execBasicTask = _request.VolType == "持仓" && _request.UseClosePrice;
Task[] tasks;
if (execBasicTask)
{
SetTaskStep("检查日终汇率");
if (PS.Config.ErpElement.SupportMultiCurrency)
{
new EodCheckCurrency(_context).Execute();
}
SetTaskStep("检查日终流水");
new EodCheckSwapFlow(_context).Execute();
tasks = new Task[3];
tasks[0] = Task.Run(() =>
{
SetTaskStep("检查标的到期日");
new EodCheckMaturityDate(_context).Execute();//t
if (PS.Config.ErpElement.SupportMultiCalendar)
{
SetTaskStep("为处于节假日的交易标的复制上日收盘价");
new EodCopyPreSettlePrice(_context).Execute();
}
SetTaskStep("检查标的出入金状态");
new EodClientBalanceCalc(_context).ClientCashInCashOutCheck();
SetTaskStep("检查标的结算价格缺失");
new EodCheckSettlePrice(_context).Execute();//t
});
WaitTasks(tasks);
if (_request.IsSettleOtcTrades)
{
SetTaskStep("检查当日交易");
new EodCheckNewTrade(_context).Execute();//t
SetTaskStep("检查当日到期交易");
new EodCheckMaturityTrade(_context).Execute();//t
SetTaskStep("定期归档收益互换");
new EodCheckMonitoredTrade(_context).ExecuteEodSwapPosition(SetTaskStep);//t
}
}
SetTaskStep("日终持仓结算");
var eodPositionService = new EodPositionSettleService(_context);
eodPositionService.Execute(_request.VolType, _request.UseClosePrice, this);//t?
_context.LogInfo("结束任务:日终持仓结算");
if (_request.IsSettleExchangeTrades)
{
if (_context.Request.ClientIds == null || !_context.Request.ClientIds.Any())
{
SetTaskStep("场内期权结算");
new ExchangeOptionSettlementService(_context.UserInfo).Execute(_context.SettleDate); //t
}
}
if (execBasicTask)
{
//交易员结算:按簿记账户计算资金和盈亏等信息
//new EodBookBalanceCalc(context).BookBalanceCalc();
SetTaskStep("结算客户资金");
new EodClientBalanceCalc(_context).ClientBalanceCalc();
_context.LogInfo("结束任务:结算客户资金");
}
_cancellationTokenSource.Token.ThrowIfCancellationRequested();
//if (!_request.IsPartialSettlement)
//{
AddOrUpdateEodStatus(_request);
//}
sw.Stop();
elapsedMilliseconds = sw.ElapsedMilliseconds;
_context.LogInfo($"结束收盘:{_request.SettleDate:yyyy-MM-dd} + {_request.VolType} + {_request.GetPriceType()},用时:{elapsedMilliseconds}毫秒");
}
public void SetTaskStep(string step)
{
TaskStep = step;
_context.LogInfo("开始任务:" + step);
_cancellationTokenSource.Token.ThrowIfCancellationRequested();
}
public void WaitTasks(params Task[] tasks)
{
if (tasks is null || tasks.Length < 1)
{
return;
}
if (tasks.Any(n => n == null))
{
tasks = tasks.Where(n => n != null).ToArray();
if (tasks.Length < 1)
{
return;
}
}
foreach (var task in tasks)
{
task.ContinueWith(t =>
{
//!t.Exception.InnerExceptions.All(n => n is OperationCanceledException)
if (t.Exception != null)
{
_cancellationTokenSource.Cancel();
LogFactory.GetLogger("日终结算").Error(t.Exception, TaskStep);
}
_context.LogInfo("结束任务:" + TaskStep);
});
}
_cancellationTokenSource.Token.ThrowIfCancellationRequested();
Task.WaitAll(tasks);
}
/// <summary>
/// 保存日终场外交易数据
/// </summary>
private static void SaveEodTrade(EodSettlementContextV2 context, CancellationToken cancellationToken)
{
//在context中取OtcTrades时存在隐含赋值 MetaDic["from_eod_trade"] = "true"
var otcTrades = context.InitOtcTrades(true);
var trades = otcTrades.Where(t => !t.MetaDic.ContainsKey("from_eod_trade")).ToList();
var parentTradeIds = otcTrades.Where(x => x.ParentTradeId > 0).Select(x => x.ParentTradeId).ToHashSet();
//解决测试环境中存在的交易重复问题(比如新功能的增加导致前置逻辑未来得及处理)
foreach (var t in trades)
{
parentTradeIds.Remove(t.id);
}
using var db = DbContextFactory.GetYLDbContext();
var eodGroupTradeIds = db.eod_trade.Where(x => parentTradeIds.Contains(x.TradeId) && x.ValueDate == context.SettleDate).Select(x => x.TradeId);
var groupTrades = db.trade.AsNoTracking().Where(x => parentTradeIds.Contains(x.id) && !eodGroupTradeIds.Contains(x.id)).ToList();
groupTrades.ForEach(x =>
{
EodSettleDataQueryService.ProcessTrade(context.SettleDate, new DataProviderModule.TradeCashDataProvider(), new DataProviderModule.TradeCashPreDataProvider(), x);
});
trades.AddRange(groupTrades);
var tradeIds = trades.Select(O => O.id);
//移除修改了分红率或无风险利率的交易,以更新收盘数据;
db.eod_trade.RemoveRange(db.eod_trade.Where(O => tradeIds.Contains(O.TradeId) && O.ValueDate == context.SettleDate));
var sql = new StringBuilder("delete from ").Append(db.GetTableName<eod_trade>())
.AppendFormat(" where ValueDate='{0}'", context.SettleDateStr)
.Append(" and not exists(select * from trade where id=TradeId)");
db.Database.ExecuteSqlRaw(sql.ToString());
foreach (var t in trades)
{
if (!t.MetaDic.ContainsKey("from_eod_trade"))
{
db.eod_trade.Add(new eod_trade
{
ValueDate = context.SettleDate,
TradeId = t.id,
TradeNumber = t.TradeNumber,
ClientId = t.ClientId,
AssetId = t.AssetId,
UnderlyingId = t.UnderlyingId,
UnderlyingCode = t.UnderlyingCode,
TradeStatus = t.TradeStatus,
ParentTradeId = t.ParentTradeId,
TradeType = t.TradeType,
TradeJson = t.Serialize(context.SettleDate),
OptDate = DateTime.Now
});
}
}
db.SaveChanges(cancellationToken);
}
/// <summary>
/// 新增或更新EodStatus表(重新执行收盘前重置)
/// </summary>
private static void AddOrUpdateEodStatus(EodSettlementRequest request, bool reset = false)
{
using var db = DbContextFactory.GetYLDbContext();
var eodStatus = db.eodStatus.FirstOrDefault(n => n.ValueDate == request.SettleDate);
if (eodStatus == null)
{
eodStatus = new EodStatus
{
ValueDate = request.SettleDate,
IsHoliday = true,
OptDate = DateTime.Now,
OptId = request.UserInfo.UserId,
OptName = request.UserInfo.UserName,
Status = string.Empty,
StepStatus = string.Empty,
VolTypeFlag = VolTypeFlagEnum.None,
VolTypeFlag_S = VolTypeFlagEnum.None
};
db.eodStatus.Add(eodStatus);
}
if (request.VolType == "持仓" && request.UseClosePrice)
{
eodStatus.Status = String.Empty;
}
eodStatus.OptDate = DateTime.Now;
eodStatus.OptId = request.UserInfo.UserId;
eodStatus.OptName = request.UserInfo.UserName;
eodStatus.CheckStatus = null;
eodStatus.CheckDate = null;
var volTypeFlag = VolTypeFlagEnumHelper.GetVolTypeFlag(request.VolType);
if (reset)
{
volTypeFlag = (eodStatus.VolTypeFlag | volTypeFlag) ^ volTypeFlag;
if (request.UseClosePrice)
{
eodStatus.VolTypeFlag = volTypeFlag;
}
else
{
eodStatus.VolTypeFlag_S = volTypeFlag;
}
}
else
{
eodStatus.Status = "已收盘";
if (request.UseClosePrice)
{
eodStatus.VolTypeFlag |= volTypeFlag;
}
else
{
eodStatus.VolTypeFlag_S |= volTypeFlag;
}
}
db.SaveChanges();
}
private static void UpdateEodCheckStatus(EodSettlementRequest request)
{
using var db = DbContextFactory.GetYLDbContext();
var eodStatus = db.eodStatus.FirstOrDefault(n => n.ValueDate == request.SettleDate);
if (eodStatus != null)
{
eodStatus.CheckStatus = null;
eodStatus.CheckDate = null;
db.SaveChanges();
}
}
}
#endregion
}
}