using Microsoft.AspNetCore.Authorization; using System.Collections.Concurrent; using System.Threading; using YLErp.BLL.Eod; using YLErp.DBModels; using YLErp.Model.Enum; using YLErp.Modules.ClientModule; using YLErp.Modules.EodModule; using YLErp.Modules.EodModule.QueryModule; using YLErp.Modules.EodModule.SettlementModule; using YLErp.Modules.FinancialModule; using YLErp.Modules.TradeModule.DealModule; using YLErp.Office; using YLErp.QdpModule; namespace YLErp.Web.Controllers { public class eod_trade_valueController : BaseController { [MyAuthorize("结算管理-收盘操作")] public ActionResult eodExec() { var config = PS.Config.GetSettlementConfig(); ViewData["EodHistory"] = GetEodHistoryData(null, 5); return View("eodExecV2", config); } public Result SendMaturityEmail(DateTime settleDate) { var maturityDate = settleDate; var a = 0; //获取之后三个交易日 for (var i = 0; i < 3;) { a++; maturityDate = maturityDate.AddDays(1); if (!QdpCalendarHelper.IsHoliday(maturityDate)) { i++; } else { continue; } if (a >= 20) { throw new Exception("日历记录异常," + settleDate.ToString("yyyy-MM-dd") + "后未查找到有效的3个交易日!"); }; } //获取交易 var query = (from trade in yldb.trade where trade.TradeStatus == "确认成交" && trade.ValidState != "InValid" && trade.ClientId != 0 && trade.MaturityDate > settleDate && trade.MaturityDate <= maturityDate select trade); var tradeList = query.ToList(); if (tradeList != null && tradeList.Count > 0) { var clientIds = tradeList.Select(t => t.ClientId).ToList(); var clientList = ClientDataQueryService.GetClientsFromDB(t => clientIds.Contains(t.id)); if (clientList != null) { var errorClientList = new List(); var errorMsg = new List(); foreach (var client in clientList) { var clientTradeList = tradeList.Where(t => t.ClientId == client.id).ToList(); var bodyhtml = string.Empty; bodyhtml += ""; bodyhtml += $"

近期到期交易列表

"; bodyhtml += $"

客户名称:{client.Name}

"; bodyhtml += $"

日期:{settleDate:yyyy-MM-dd}

"; bodyhtml += ""; bodyhtml += ""; bodyhtml += " "; bodyhtml += " "; bodyhtml += " "; bodyhtml += " "; bodyhtml += " "; bodyhtml += " "; bodyhtml += " "; bodyhtml += " "; bodyhtml += " "; bodyhtml += " "; foreach (var trade in clientTradeList) { bodyhtml += " "; bodyhtml += $" "; bodyhtml += $" "; bodyhtml += $" "; bodyhtml += $" "; bodyhtml += $" "; bodyhtml += $" "; bodyhtml += $" "; bodyhtml += $" "; //股票类期权 行权价给实际行权价 if (trade.IsMoneynessOptionData) { bodyhtml += $" "; } else { bodyhtml += $" "; } bodyhtml += " "; } bodyhtml += "
交易编号 交易日期 交易方向 标的代码 结构类型 看涨看跌 行权方式 到期日 行权价
{trade.TradeNumber}{(trade.TradeDate == null ? "" : trade.TradeDate.Value.ToString("yyyy-MM-dd")):F}{trade.BuySell}{trade.UnderlyingCode}{trade.TradeType:F}{("Call".Equals(trade.CallPut) ? "看涨" : "看跌")}{trade.ExerciseModeCn:F}{(trade.ExerciseDate == null ? "" : trade.ExerciseDate.Value.ToString("yyyy-MM-dd"))}{trade.Strike * (trade.SpotPrice ?? 0):F}{trade.Strike:F}
"; // 要向该客户的所有订阅了邮件通知的人员发送邮件 //var clientContactMails = db.clientduty.Where(x => x.ClientId == client.id && x.IsReceiveEmail.HasValue && x.IsReceiveEmail == 1).Select(x => x.Email).ToList(); var emails = ClientDataQueryService.GetClientEmails(client.id, false); if (emails != null & emails.Any()) { var sendMail = EmailHelper.SendMail(string.Join(";", emails), "近期到期交易列表", bodyhtml, true, null); if (!string.IsNullOrEmpty(sendMail)) { errorClientList.Add(client.Name); if (!errorMsg.Contains(sendMail)) { errorMsg.Add(sendMail); } } } } //判断是否有发送失败的客户 if (errorClientList.Count > 0) { var msg = $"错误:{string.Join(",", errorClientList.ToArray())}发送报告时出错,{string.Join(",", errorMsg.ToArray())}。"; return new Result { success = false, msg = msg }; } } } return new Result { success = false, msg = "发送客户" + settleDate.ToString("yyyy年MM月dd日") + "近期到期交易报告成功。" }; } /// /// 财务凭证 /// public ActionResult FinancialStatements() { return View(); } /// /// /// public ActionResult FinancialStatementsQuery(FinancialStatementReq req) { var bll = new FinancialVoucherService(CurUser); if (req == null) { return JsonError("请求实体不能为空"); } var returnInfo = bll.GetFinancialStatements(req); if (!returnInfo.Success) { return JsonError(returnInfo.Msg); } if (string.IsNullOrWhiteSpace(req.sidx)) { returnInfo.Content = returnInfo.Content.OrderBy(f => f.AbstractTye).ToList(); } var searchListResult = returnInfo.Content.AsQueryable().ToSearchList(req); return Json(searchListResult); } /// /// /// /// /// 0:导出当前,1:导出同业,2:导出非同业 /// public FileStreamResult ExportFinancialVoucher(FinancialStatementReq req, int exportType = 0) { if (exportType < 0 || exportType > 2) { return null; } if (req == null) { req = new FinancialStatementReq(); } var bll = new FinancialVoucherService(CurUser); if (exportType == 1) { req.IsSamePeer = SamePeerEnum.IsSamePeer.GetHashCode(); req.AbstractList = null; } else if (exportType == 2) { req.IsSamePeer = SamePeerEnum.NotSamePeer.GetHashCode(); req.AbstractList = null; } var returnInfo = bll.GetFinancialStatements(req); if (!returnInfo.Success) { return null; } if (returnInfo.Content == null || returnInfo.Content.Count == 0) { return null; } if (exportType == 0) { var fileDownloadName = $"场外期权财务凭证_{DateTime.Now:yyyyMMdd}.xlsx"; var excelReturn = returnInfo.Content.ListToExcel2(new Dictionary { {"摘要", "Abstract"}, {"同业/非同业", "IsSamePeerDesc"}, {"借贷方向", "Children.DebitOrCreditDesc"}, {"会计科目", "Children.AccountingSubject"}, {"辅助核算单位", "Children.SupplementaryAcountingUnit"}, {"数据字段", "Children.DataField"}, {"备注", "Children.Remark"} }); return File(new FileStream(excelReturn.FileName, FileMode.Open), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileDownloadName); } if (exportType == 1 || exportType == 2) { var fileDownloadName = $"场外期权财务凭证{(exportType == 1 ? "(同业)" : "(非同业)")}_{DateTime.Now:yyyyMMdd}.xlsx"; var group = returnInfo.Content.GroupBy(f => f.AbstractTye).OrderBy(g => g.Key) .ToDictionary(g => EnumHelper.GetDescriptionByName((FinancialVoucherAbstractEnum)g.Key), g => g.ToList()); var excelReturn = group.GroupToExcel2(new Dictionary { {"摘要", "Abstract"}, {"同业/非同业", "IsSamePeerDesc"}, {"借贷方向", "Children.DebitOrCreditDesc"}, {"会计科目", "Children.AccountingSubject"}, {"辅助核算单位", "Children.SupplementaryAcountingUnit"}, {"数据字段", "Children.DataField"}, {"备注", "Children.Remark"} }); return File(new FileStream(excelReturn.FileName, FileMode.Open), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileDownloadName); } return null; } #region----收盘历史---- /// /// 收盘历史 /// public ActionResult EodHistory(int? year) { var datas = GetEodHistoryData(year); return View(datas); } private IEnumerable GetEodHistoryData(int? year, int maxRows = 0) { var today = DateTime.Today; if (year == null || year < 1 || year > 3000) { year = today.Year; } var startDate = new DateTime(year.Value, 1, 1); var endDate = new DateTime(year.Value, 12, 31); var allEodStatus = new EodStatus[400]; var hasExtendVol = SettlementViewModel.HasExtendVol(); var dbQuery = yldb.eodStatus.Where(n => n.ValueDate >= startDate && n.ValueDate <= endDate); if (maxRows > 0) { dbQuery = dbQuery.OrderByDescending(n => n.ValueDate).Take(maxRows); } var dbDatas = dbQuery.ToArray(); foreach (var data in dbDatas) { allEodStatus[data.ValueDate.DayOfYear] = data; if (!hasExtendVol) { data.VolTypeFlag = (data.VolTypeFlag | VolTypeFlagEnum.ExtendVol) ^ VolTypeFlagEnum.ExtendVol; data.VolTypeFlag_S = (data.VolTypeFlag_S | VolTypeFlagEnum.ExtendVol) ^ VolTypeFlagEnum.ExtendVol; } } while (startDate <= endDate && startDate <= today) { if (!BLL.valuedateBLL.IsHoliday(startDate) && allEodStatus[startDate.DayOfYear] == null) { allEodStatus[startDate.DayOfYear] = new EodStatus { ValueDate = startDate, Status = "未执行" }; } startDate = startDate.AddDays(1); } var retQuery = allEodStatus.Where(n => n != null).Reverse(); if (maxRows > 0) { retQuery = retQuery.Take(maxRows); } return retQuery.ToArray(); } /// /// 修改收盘数据核对状态 /// [MyAuthorize("结算管理-收盘数据核对")] public JsonResult UpdateCheckStatus(string encryptId) { try { var intid = DecryptInt(encryptId); var eodstatus = yldb.eodStatus.Find(intid); if (eodstatus.Status != "已收盘") { return JsonSuccess("未收盘"); } eodstatus.CheckStatus = true; eodstatus.CheckDate = DateTime.Now; if (PS.Config.Is润和) { new DongZhengEodTradePositionExportService(CurUser).ExportDongZhengZipFile(eodstatus.ValueDate); } yldb.SaveChanges(); return JsonSuccess("核对状态修改成功"); } catch (Exception ex) { return JsonError("执行失败:" + ex.Message); } } #endregion #region----eodExecV2---- /// /// 保存收盘请求 /// public JsonResult AjaxSaveEodTask(EodTaskSaveReq req, bool isDebug = false) { var debugging = isDebug && System.Diagnostics.Debugger.IsAttached; if (!string.IsNullOrEmpty(req.DividendRateTypes)&&req.DividendRateTypes.Contains("采集")) { req.VolTypes += ",分红率0"; } var task = new EodTaskSaveService(CurUser).SaveData(req, !debugging); if (debugging) { EodTaskRunner.ExecuteDebugAsync(task); } return JsonSuccess(); } /// /// 取消收盘任务 /// public JsonResult AjaxCancelEodTask(int taskId) { new EodTaskSaveService(CurUser).CancelTask(taskId); return JsonSuccess(); } /// /// 继续执行/重新执行 /// public JsonResult AjaxRestartEodTask(int taskId, bool resetAll = true, bool isDelayTrade = false) { new EodTaskSaveService(CurUser).RestartTask(taskId, resetAll, isDelayTrade); return JsonSuccess(); } /// /// 收盘任务报告 /// public ActionResult AjaxGetTaskRunReport(bool firstTask = true) { var datas = new EodTaskQueryService(CurUser).GetEodTasksToShow(firstTask); return PartialView("_eodTaskDetail", datas); } /// /// 收盘历史 /// public ActionResult AjaxGetEodHistoryTable() { var datas = GetEodHistoryData(null, 5); return PartialView("_EodHistoryTable", datas); } /// /// 执行汇率检查 /// public JsonResult AjaxCheckCurrencyRae(DateTime date) { var result = new EodCurrencyRateService(CurUser).CheckEodCurrencyRateMissing(date); return JsonSuccessData(result); } /// /// 复制上日汇率 /// public JsonResult AjaxCopyPreCurrencyRae(DateTime date) { var service = new EodCurrencyRateService(CurUser); var changes = service.CopyPreCurrencyRate(date, out var preDate); var result = service.CheckEodCurrencyRateMissing(date); var resultMsg = $"目标日期'{date:yyyy-MM-dd}',上日日期:'{preDate:yyyy-MM-dd}',复制{changes}条,"; if (!string.IsNullOrEmpty(result.Error)) { resultMsg += "重新检查后报错:" + result.Error; } if (result.HasMissing) { resultMsg += "重新检查后仍然缺失"; } else { resultMsg += "重新检查后未缺失汇率"; } return JsonSuccessData(new { result, resultMsg }); } public object AjaxCheckSettlePrice(DateTime dateStart, DateTime dateEnd) { var NoPriceUnderlyings = new List>(); var start = new DateTime(dateStart.Ticks); while (dateEnd >= dateStart) { var umCodes = new EodCheckPriceService(CurUser).GetNoPriceUnderlyings(dateStart); if (umCodes != null && umCodes.Count > 0) { foreach (var code in umCodes) { NoPriceUnderlyings.Add(new KeyValuePair(dateStart.ToString("yyyy-MM-dd"), code)); } } dateStart = dateStart.AddDays(1); } //if (NoPriceUnderlyings.Count == 0) //{ // return Json("价格无缺失"); //} var bytes = new EodCheckPriceService(CurUser).exportNoPriceUnderlyings(NoPriceUnderlyings); return File(bytes, xlsxMimeType, $"收盘价格缺失{start:yyyyMMdd}-{dateEnd:yyyyMMdd}.xlsx"); } /// /// 执行期权观察状态 /// public JsonResult AjaxSaveEodKnowInOut(EodTaskSaveReq req, bool isDebug = false) { try { List dts = QdpCalendarHelper.AllBizDays(req.StartDate, req.EndDate.AddDays(1)); foreach (var item in dts) { new Modules.EodModule.SettlementModule.EodCheckMonitoredTradeSingle(CurUser).ExecuteTask(item, req.ClientIds); } return JsonSuccess("执行成功"); } catch (Exception ex) { return JsonError("执行失败:" + ex.Message); } } /// /// 检察当日异常标的 /// public JsonResult AjaxCheckExerciseTradeUnderly(bool update = false) { if (!update) { var exerciseTradeUnderly = new TradeDelaySettlementService(CurUser).GetCacheExerciseTradeUnderly(); return JsonSuccess("", exerciseTradeUnderly); } else { var exerciseTradeUnderly = new TradeDelaySettlementService(CurUser).CheckExerciseTradeUnderly(); return JsonSuccess("", exerciseTradeUnderly); } } /// /// 检察收盘时有需要延期的交易 /// /// public JsonResult AjaxCheckEodExerciseTradeUnderly(DateTime dateStart, DateTime dateEnd) { bool isHaveDelayTrade = false; string msg = ""; //只有当天收盘 if (dateStart == dateEnd && dateEnd == valuedateBLL.ValueDate) { var updownUnderly = new TradeDelaySettlementService(CurUser).GetDelayUnderlyIds(dateStart); if (updownUnderly.Any()) { isHaveDelayTrade = true; msg = new TradeDelaySettlementService(CurUser).GetDelayTradeNumber(updownUnderly.ToList()); } } //其他 else { while (dateEnd >= dateStart) { var updownUnderly = new TradeDelaySettlementService(CurUser).GetDelayUnderlyIds(dateStart); if (updownUnderly.Any()) { isHaveDelayTrade = true; break; } dateStart = dateStart.AddDays(1); } } return JsonSuccess("成功", new { isHaveDelayTrade, msg }); } /// /// 继续执行/重新执行 /// public JsonResult AjaxCheckRestartEodTaskTradeUnderly(int taskId) { var task = YLErp.Modules.DbContextFactory.GetYLDbContext().EodTask.FirstOrDefault(n => n.id == taskId); if (task == null) { throw new ServiceException("任务数据已不存在"); } switch (task.TaskState) { case EodTaskState.completed: case EodTaskState.failure: case EodTaskState.canceled: case EodTaskState.interrupted: break; case EodTaskState.running: if (EodTaskRunner.LastTask?.id == taskId) { throw new ServiceException("任务正在运行中"); } break; default: throw new ServiceException("不符合重新执行的条件,任务当前状态:" + EodTaskHelper.GetEodTaskStateDesc(task.TaskState)); } bool isHaveDelayTrade = false; string msg = ""; DateTime dateStart = task.StartDate, dateEnd = task.EndDate; //只有当天收盘 if (dateStart == dateEnd && dateEnd == valuedateBLL.ValueDate) { var updownUnderly = new TradeDelaySettlementService(CurUser).GetDelayUnderlyIds(dateStart); if (updownUnderly.Any()) { isHaveDelayTrade = true; msg = new TradeDelaySettlementService(CurUser).GetDelayTradeNumber(updownUnderly.ToList()); } } //其他 else { while (dateEnd >= dateStart) { var updownUnderly = new TradeDelaySettlementService(CurUser).GetDelayUnderlyIds(dateStart); if (updownUnderly.Any()) { isHaveDelayTrade = true; break; } dateStart = dateStart.AddDays(1); } } return JsonSuccess("成功", new { isHaveDelayTrade, msg }); } #endregion #region 互换持仓明细导入 public ActionResult eodTradeSwapMannual() { return View(); } /// /// 导入互换交易 /// public ActionResult UploadEodTradeSwapMannual() { if (Request.Form.Files.Count == 0) { return JsonError("上传文件不存在"); } var file = Request.Form.Files[0]; if (!Path.GetExtension(file.FileName).Equals(".xlsx", StringComparison.OrdinalIgnoreCase)) { return JsonError("请上传Excel(.xlsx)格式文件"); } DateTime valuedate = Convert.ToDateTime(Request.Form["ValueDate"]); using var inputStream = file.OpenReadStream(); new EodSwapPositionMannualImportService(CurUser).ImportEodSwapPositionMannualFromExcel(inputStream, valuedate, out var TotalNum, out var SuccessNum); return Json(new { success = true, totalNum = TotalNum, successNum = SuccessNum, }); } #endregion #region 历史标的交易收盘 [HttpGet] [AllowAnonymous] public JsonResult ExecuteEodPositionSettle() { try { string volType = "持仓"; bool useClosePrice = true; // 1. 创建用户信息对象 var userInfo = OptUserInfo.SystemUser; // 2. 获取 exchange_trade 表的最早和最晚交易日期 var valueDate = valuedateBLL.ValueDate; var earliestTradeDate = yldb.ExchangeTrade.Where(et => et.IsValid) .Min(et => (DateTime?)et.TradeDate) ?? valueDate; var latestTradeDate = EodOperationBase.GetLastSettlementDate(valueDate); // 计算预计处理的交易日数量 var estimatedDays = 0; var tempDate = earliestTradeDate; while (tempDate <= latestTradeDate) { estimatedDays++; tempDate = BLL.valuedateBLL.GetNonHoliday(tempDate.AddDays(1)); } // 3. 异步执行结算任务 var taskId = Guid.NewGuid().ToString(); // 初始化任务状态 var cancellationTokenSource = new CancellationTokenSource(); Task.Run(async () => { try { await ExecuteEodPositionSettleAsync(userInfo, volType, useClosePrice, earliestTradeDate, latestTradeDate, taskId); } catch (OperationCanceledException) { LogFactory.GetLogger("日终持仓结算").Info($"异步日终持仓结算任务 {taskId} 已被取消"); } catch (Exception ex) { LogFactory.GetLogger("日终持仓结算").Error($"异步日终持仓结算任务 {taskId} 执行失败: {ex.Message}", ex); } }); return JsonSuccess($"日终持仓结算任务已启动,任务ID: {taskId},预计处理 {estimatedDays} 个交易日,从 {earliestTradeDate:yyyy-MM-dd} 到 {latestTradeDate:yyyy-MM-dd}。任务将在后台异步执行。"); } catch (Exception ex) { return JsonError($"启动任务失败: {ex.Message}"); } } /// /// 异步执行日终持仓结算 /// /// 用户信息 /// 波动率类型 /// 是否使用收盘价 /// 开始日期 /// 结束日期 /// 任务ID /// private async Task ExecuteEodPositionSettleAsync(OptUserInfo userInfo, string volType, bool useClosePrice, DateTime startDate, DateTime endDate, string taskId) { var processedDates = new List(); var currentDate = startDate; LogFactory.GetLogger("日终持仓结算").Info($"开始执行异步日终持仓结算任务 {taskId},从 {startDate:yyyy-MM-dd} 到 {endDate:yyyy-MM-dd}"); try { while (currentDate <= endDate) { LogFactory.GetLogger("日终持仓结算").Info($"任务 {taskId}: 正在处理日期 {currentDate:yyyy-MM-dd}"); // 创建结算请求对象 var request = new EodSettlementRequest(userInfo) { VolType = volType, UseClosePrice = useClosePrice, SettleDate = currentDate, ClientIds = null, DelayUnderlyIds = new List() }; // 获取结算配置 var settlementConfig = PS.Config.GetSettlementConfig(); // 获取取消令牌 CancellationToken cancellationToken = CancellationToken.None; // 创建任务管理器 var taskManager = new SimpleTaskManager(); // 在Task.Run中执行CPU密集型操作 await Task.Run(() => { // 创建结算上下文 var currentContext = new EodSettlementContextV2(request, settlementConfig, cancellationToken); // 创建服务实例 var currentEodPositionService = new EodPositionSettleService(currentContext); // 执行当前日期的结算 currentEodPositionService.Execute(volType, useClosePrice, taskManager); }); processedDates.Add(currentDate); currentDate = BLL.valuedateBLL.GetNonHoliday(currentDate.AddDays(1)); // 每处理一个日期后稍作延迟,避免过度占用系统资源 await Task.Delay(100); } LogFactory.GetLogger("日终持仓结算").Info($"异步日终持仓结算任务 {taskId} 执行完成,共处理了 {processedDates.Count} 个交易日"); } catch (Exception ex) { LogFactory.GetLogger("日终持仓结算").Error($"异步日终持仓结算任务 {taskId} 在处理日期 {currentDate:yyyy-MM-dd} 时发生错误: {ex.Message}", ex); throw; } } #endregion } /// /// 简单的任务管理器实现 /// public class SimpleTaskManager : IAsyncTaskManager { /// /// 设置任务步骤 /// /// 步骤描述 public void SetTaskStep(string step) { // 可以在这里记录日志或更新进度 LogFactory.GetLogger("日终持仓结算").Info($"任务步骤: {step}"); } /// /// 等待任务完成 /// /// 要等待的任务数组 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) { LogFactory.GetLogger("日终结算").Error(t.Exception, ""); } }); } Task.WaitAll(tasks); } } }