//internal async Task<Dictionary<string, BacktestJobStatus>> GetStatuses(IEnumerable<string> jobIds) //{ // if (jobIds.IsNullOrEmpty()) // return new Dictionary<string, BacktestJobStatus>(); // Dictionary<string, BacktestJobStatus> statuses = new Dictionary<string, BacktestJobStatus>(); // foreach (var jobId in jobIds) // { // BacktestJobStatus status; // if (!statusesDict.TryGetValue(jobId, out status)) // status = (await actioner.Get(jobId))?.Status; // if (status != null) // statuses.Add(jobId, status); // } // return statuses; //} internal async Task <(bool Success, string Message)> AddJob(BacktestJob job) { logger.Info($"Adding job {job.Name} to database and active queue"); var result = await actioner.AddOrUpdate(job.Name, job); if (!result.Success) { return(result); } pendingJobs.Enqueue(job.Name); return(true, $"Successfully added job {job.Name}"); }
public static BacktestJobModel ToBacktestJobModel(this BacktestJob job) { if (job == null) { return(null); } return(new BacktestJobModel() { CreateTime = job.CreateTime, Day = job.Day, EndTime = job.EndTime, GroupId = job.GroupId, Name = job.Name, Output = job.Output.ToBacktestJobOutputDataModel(), StartTime = job.StartTime, Status = job.Status.ToBacktestStatusModel(), UsedHistoMarketData = job.UsedHistoData }); }
public async Task <GenericActionResult> UpdateStatus(string backtestJobName, BacktestJob job, CancellationToken ct = default(CancellationToken)) { try { ct.ThrowIfCancellationRequested(); if (string.IsNullOrEmpty(backtestJobName)) { throw new ArgumentNullException(nameof(backtestJobName)); } if (job == null) { throw new ArgumentNullException(nameof(job)); } logger.Info($"About to send GET request to {controllerEndpoint}/api/jobs/update-status/{backtestJobName}"); return(await controllerEndpoint.AppendPathSegment($"/api/jobs/update-status/{backtestJobName}").PostJsonAsync(job, ct).ReceiveJson <GenericActionResult>()); } catch (OperationCanceledException) { string err = "Not posting job update: operation cancelled"; logger.Error(err); return(new GenericActionResult(false, err)); } catch (ArgumentNullException ex) { string err = $"Not posting job update: missing or invalid parameter {ex.ParamName}"; logger.Error(err); return(new GenericActionResult(false, err)); } catch (Exception ex) { string err = "Failed to post job update"; logger.Error(err, ex); return(new GenericActionResult(false, $"{err}: {ex.Message}")); } }
public async Task TestNotifyJobStarted() { string controllerEndpoint = "https://localhost:44379/"; JobsConnector connector = JobsConnector.GetConnector(controllerEndpoint); string groupName = "RegTestJobGroup"; string jobName = "RegTestJob"; BacktestJob job = new BacktestJob(groupName, jobName) { Day = DateTime.Today, EndTime = DateTimeOffset.Now, StartTime = DateTimeOffset.Now }; job.Status.ActualStartTime = DateTimeOffset.Now; job.Status.CompletionTime = DateTimeOffset.Now; job.Status.Worker = "RegtestWorker"; var result = await connector.UpdateStatus(jobName, job); Assert.IsNotNull(result); }
public async Task <GenericActionResult> UpdateStatus(string backtestJobName, [FromBody] BacktestJob job) { var result = await utils.UpdateJob(backtestJobName, job); return(new GenericActionResult(result.Success, result.Message)); }
internal async Task <(bool Success, string Message)> UpdateJob(string jobName, BacktestJob job) { try { logger.Info($"Updating job {jobName} in database and dictionary"); var result = await actioner.AddOrUpdate(jobName, job); if (!result.Success) { return(result); } else { return(true, $"Successfully updated job {job.Name}"); } } catch (Exception ex) { string err = $"Failed to update job {job?.Name}"; logger.Error(err, ex); return(false, $"{err}: {ex.Message}"); } }
internal async Task <(bool Success, string Message, string JobName)> CreateJob(string jobName) { (bool Success, string Message, string JobName)result = (false, null, jobName); try { var jobSettings = GetJobSettings(jobName); if (jobSettings == null) { result.Message = $"Failed to get settings for job {jobName}"; return(result); } if (string.IsNullOrEmpty(jobSettings?.StrategyName)) { throw new ArgumentNullException("StrategyName"); } if (string.IsNullOrEmpty(jobSettings?.StrategyVersion)) { throw new ArgumentNullException("StrategyVersion"); } if (string.IsNullOrEmpty(jobSettings?.StrategyClass)) { throw new ArgumentNullException("StrategyClass"); } if (jobSettings.CrossesAndTicketSizes.IsNullOrEmpty()) { throw new ArgumentNullException("Crosses"); } if (jobSettings.Parameters.IsNullOrEmpty()) { throw new ArgumentNullException("Parameters"); } var days = DateTimeUtils.EachBusinessDay(jobSettings.StartDate, jobSettings.EndDate); if (days.IsNullOrEmpty()) { throw new ArgumentOutOfRangeException(nameof(days), $"Invalid days interval: start {jobSettings.StartDate}, end: {jobSettings.EndDate}"); } var jobGroup = new BacktestJobGroup(jobName) { EndDate = jobSettings.EndDate, EndTime = jobSettings.EndTime, StartDate = jobSettings.StartDate, StartTime = jobSettings.StartTime, Strategy = new Strategy() { AlgoTypeName = jobSettings.AlgorithmClass, CrossesAndTicketSizes = jobSettings.CrossesAndTicketSizes, Name = jobSettings.StrategyName, Parameters = jobSettings.Parameters.ToStrategyParameters("Param"), StrategyDllPath = jobSettings.NewFileName, StrategyTypeName = jobSettings.StrategyClass, Version = jobSettings.StrategyVersion }, UseHistoDatabase = jobSettings.UseHistoDatabase }; List <GenericActionResult> failed = new List <GenericActionResult>(); int dayCounter = 1; foreach (var day in days) { var nycOffsetForDay = day.GetNewYorkTimeUtcOffset(); BacktestJob job = new BacktestJob(jobName, $"{jobName}_{dayCounter++}") { Day = day, EndTime = new DateTimeOffset(jobSettings.EndTime, nycOffsetForDay), // End time is expressed in NYC timezone. UTC offset can vary depending on the day StartTime = new DateTimeOffset(jobSettings.StartTime, TimeSpan.FromHours(8)) // Start time is expressed in HK timezone. UTC offset is fixed to +08:00 }; logger.Info($"Inserting new backtest job {job.Name} in dictionary database"); CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(5)); var addResult = await jobsControllerUtils.AddJob(job); if (addResult.Success) { jobGroup.Jobs.Add(job.Name, new BacktestJobLight() { DayStr = day.ToString("yyyy-MM-dd") }); } else { failed.Add(new GenericActionResult(addResult.Success, addResult.Message)); } } if (!jobGroup.Jobs.IsNullOrEmpty() && failed.IsNullOrEmpty()) { var addResult = await jobGroupsControllerUtils.AddJobGroup(jobGroup); if (addResult.Success) { result.Success = true; result.Message = $"Successfully split backtest job {jobName} into {jobGroup.Jobs.Count} subjobs ({string.Join(", ", jobGroup.Jobs.Keys)}) and submitted."; } else { result.Message = $"Failed to submit group job {jobName}: {result.Message}"; } } else { result.Message = $"Failed to submit one or more subjobs: {string.Join(", ", failed.Select(f => f.Message))}"; } return(result); } catch (ArgumentNullException ex) { result.Message = $"Failed to insert backtest job: missing or invalid parameter {ex.ParamName}"; logger.Error(result.Message); return(result); } catch (Exception ex) { logger.Error("Failed to insert backtest job", ex); result.Message = $"Failed to insert backtest job: {ex.Message}"; return(result); } }