internal string AssignJobNameAndAddToDictionary(BacktestJobSettingsModel settings)
        {
            do
            {
                settings.JobName = GetNextJobName(settings.StrategyName, settings.StrategyVersion);
            } while (!jobs.TryAdd(settings.JobName, settings));

            return(settings.JobName);
        }
 public CreateJobStep1ViewModel()
 {
     Settings = new BacktestJobSettingsModel()
     {
         StartDate        = DateTimeUtils.GetLastBusinessDayInHKT(),
         EndDate          = DateTimeUtils.GetLastBusinessDayInHKT(),
         StartTime        = new DateTime(1, 1, 1, 6, 15, 0),
         EndTime          = new DateTime(1, 1, 1, 16, 30, 0),
         UseHistoDatabase = true
     };
 }
        public BacktestJobSettingsModel GetJobSettings(string jobName)
        {
            if (!jobs.TryGetValue(jobName, out BacktestJobSettingsModel settings))
            {
                logger.Error($"Failed to retrieve settings for job {jobName}");
                settings = new BacktestJobSettingsModel()
                {
                    JobName    = jobName,
                    Parameters = new List <BacktestJobStrategyParameterModel>()
                };
            }

            return(settings);
        }
 public CreateJobStep1ViewModel(BacktestJobSettingsModel settings)
 {
     Settings = settings;
 }
        public IActionResult Step2(string jobName, List <CreateJobStep1bPairViewModel> pairs)
        {
            BacktestJobSettingsModel settings = createJobControllerUtils.SetCrosses(jobName, pairs);

            return(View(new CreateJobStep2ViewModel(settings)));
        }
        public IActionResult Step1b(string jobName)
        {
            BacktestJobSettingsModel settings = createJobControllerUtils.GetJobSettings(jobName);

            return(View(new CreateJobStep1bViewModel(settings)));
        }
        public CreateJobStep1bViewModel(BacktestJobSettingsModel settings)
        {
            Settings = settings;

            PopulatePairsList();
        }
        public (bool Success, string Message, List <BacktestJobSettingsModel> JobsSettings) ReadFile(string filePath)
        {
            (bool Success, string Message, List <BacktestJobSettingsModel> JobsSettings)result = (false, null, null);

            try
            {
                if (string.IsNullOrEmpty(filePath))
                {
                    throw new ArgumentNullException(nameof(filePath));
                }

                if (!File.Exists(filePath))
                {
                    result.Message = $"File {filePath} does not exist";
                    return(result);
                }

                using (ExcelPackage excel = new ExcelPackage(new FileInfo(filePath)))
                {
                    var ws = excel.Workbook.Worksheets.FirstOrDefault();

                    if (ws == null)
                    {
                        result.Message = "Excel file is empty";
                        return(result);
                    }

                    // Validate header
                    if (ws.Dimension.End.Row < 2)
                    {
                        result.Message = $"Incorrect length of the Excel file: expected at least 1 header row and 1 job row";
                        return(result);
                    }

                    if (ws.Dimension.End.Column < 6)
                    {
                        result.Message = $"Incorrect length of the Excel file: expected at least 5 columns (not counting strat parameters)";
                        return(result);
                    }

                    if (ws.Cells[1, 1].Value.ToString() != "DLL")
                    {
                        result.Message = $"Incorrect value for column 1: expected 'DLL'";
                        return(result);
                    }

                    List <BacktestJobSettingsModel> jobsSettings = new List <BacktestJobSettingsModel>();

                    int rowIndex = 2;
                    while (ws.Cells[rowIndex, 1].Value != null)
                    {
                        #region DLL
                        string dll = ws.Cells[rowIndex, 1].Value?.ToString();
                        if (string.IsNullOrEmpty(dll))
                        {
                            return(false, $"Failed to parse job definition on row {rowIndex}: missing value for 'DLL'", null);
                        }

                        var readDllResult = ReadDll(dll);

                        if (!readDllResult.Success)
                        {
                            result.Message = $"Failed to read DLL: {readDllResult.Message}";
                            logger.Error(result.Message);
                            return(result);
                        }
                        #endregion

                        #region Crosses
                        Dictionary <Cross, int> crossesAndTicketSizes = new Dictionary <Cross, int>();

                        int    colIndex = 2;
                        string header   = ws.Cells[1, colIndex].Value?.ToString();
                        while (!string.IsNullOrEmpty(header) && header.ToLower().StartsWith("cross"))
                        {
                            string crossStrValue = ws.Cells[rowIndex, colIndex].Value?.ToString();

                            if (!string.IsNullOrEmpty(crossStrValue))
                            {
                                string[] parts = crossStrValue.Split(':');

                                if (parts.Length == 2)
                                {
                                    if (CrossUtils.TryParse(parts[0], out Cross cross) && int.TryParse(parts[1], out int quantity))
                                    {
                                        if (!crossesAndTicketSizes.ContainsKey(cross))
                                        {
                                            crossesAndTicketSizes.Add(cross, quantity);
                                        }
                                    }
                                }
                            }

                            header = ws.Cells[1, ++colIndex].Value?.ToString();
                        }

                        if (crossesAndTicketSizes.IsNullOrEmpty())
                        {
                            result.Message = $"Failed to parse at least one cross for job on row {rowIndex}";
                            return(result);
                        }
                        #endregion

                        #region StartDate
                        if (ws.Cells[1, colIndex].Value.ToString() != "StartDate")
                        {
                            result.Message = $"Incorrect value for column {colIndex}: expected 'StartDate'";
                            return(result);
                        }

                        if (string.IsNullOrEmpty(ws.Cells[rowIndex, colIndex].Value.ToString()) || !DateTime.TryParse(ws.Cells[rowIndex, colIndex++].Value.ToString(), out DateTime startDate))
                        {
                            return(false, $"Failed to parse job definition on row {rowIndex}: missing or invalid value for 'StartDate'", null);
                        }
                        #endregion

                        #region EndDate
                        if (ws.Cells[1, colIndex].Value.ToString() != "EndDate")
                        {
                            result.Message = $"Incorrect value for column {colIndex}: expected 'EndDate'";
                            return(result);
                        }

                        if (string.IsNullOrEmpty(ws.Cells[rowIndex, colIndex].Value.ToString()) || !DateTime.TryParse(ws.Cells[rowIndex, colIndex++].Value.ToString(), out DateTime endDate))
                        {
                            return(false, $"Failed to parse job definition on row {rowIndex}: missing or invalid value for 'EndDate'", null);
                        }
                        #endregion

                        #region StartTime
                        if (ws.Cells[1, colIndex].Value.ToString() != "StartTime")
                        {
                            result.Message = $"Incorrect value for column {colIndex}: expected 'StartTime'";
                            return(result);
                        }

                        if (string.IsNullOrEmpty(ws.Cells[rowIndex, colIndex].Value.ToString()) || !DateTime.TryParse(ws.Cells[rowIndex, colIndex++].Value.ToString(), out DateTime startTime))
                        {
                            return(false, $"Failed to parse job definition on row {rowIndex}: missing or invalid value for 'StartTime'", null);
                        }
                        #endregion

                        #region EndTime
                        if (ws.Cells[1, colIndex].Value.ToString() != "EndTime")
                        {
                            result.Message = $"Incorrect value for column {colIndex}: expected 'EndTime'";
                            return(result);
                        }

                        if (string.IsNullOrEmpty(ws.Cells[rowIndex, colIndex].Value.ToString()) || !DateTime.TryParse(ws.Cells[rowIndex, colIndex++].Value.ToString(), out DateTime endTime))
                        {
                            return(false, $"Failed to parse job definition on row {rowIndex}: missing or invalid value for 'EndTime'", null);
                        }
                        #endregion

                        #region UseHistoDatabase
                        if (ws.Cells[1, colIndex].Value.ToString() != "UseHistoDatabase")
                        {
                            result.Message = $"Incorrect value for column {colIndex}: expected 'UseHistoDatabase'";
                            return(result);
                        }

                        if (string.IsNullOrEmpty(ws.Cells[rowIndex, colIndex].Value.ToString()) || !bool.TryParse(ws.Cells[rowIndex, colIndex++].Value.ToString(), out bool useHistoDatabase))
                        {
                            return(false, $"Failed to parse job definition on row {rowIndex}: missing or invalid value for 'UseHistoDatabase'", null);
                        }
                        #endregion

                        #region Parameters
                        Dictionary <string, BacktestJobStrategyParameterModel> parameters = new Dictionary <string, BacktestJobStrategyParameterModel>();

                        for (int paramColIndex = colIndex; paramColIndex <= ws.Dimension.End.Column; paramColIndex++)
                        {
                            string key   = ws.Cells[1, paramColIndex].Value.ToString();
                            string value = ws.Cells[rowIndex, paramColIndex].Value.ToString();

                            if (!string.IsNullOrEmpty(key) && !parameters.ContainsKey(key))
                            {
                                parameters.Add(key, new BacktestJobStrategyParameterModel()
                                {
                                    Name    = key,
                                    Tooltip = null,
                                    Value   = value
                                });
                            }
                        }
                        #endregion

                        var jobSettings = new BacktestJobSettingsModel()
                        {
                            AlgorithmClass        = readDllResult.AlgorithmClass,
                            CrossesAndTicketSizes = crossesAndTicketSizes,
                            EndDate          = endDate,
                            EndTime          = endTime,
                            NewFileName      = readDllResult.DllFileName,
                            OriginalFileName = readDllResult.DllFileName,
                            Parameters       = parameters.Values.ToList(),
                            StartDate        = startDate,
                            StartTime        = startTime,
                            StrategyClass    = readDllResult.StrategyClass,
                            StrategyName     = readDllResult.StrategyName,
                            StrategyVersion  = readDllResult.StrategyVersion,
                            UseHistoDatabase = useHistoDatabase
                        };

                        jobSettings.JobName = createJobControllerUtils.AssignJobNameAndAddToDictionary(jobSettings);

                        jobsSettings.Add(jobSettings);

                        rowIndex++;
                    }

                    if (jobsSettings.Count > 0)
                    {
                        result.Success      = true;
                        result.Message      = $"Parsed {jobsSettings.Count} jobs";
                        result.JobsSettings = jobsSettings;
                    }
                    else
                    {
                        result.Message = "Found no job in the file";
                    }

                    return(result);
                }
            }
            catch (ArgumentNullException ex)
            {
                result.Message = $"Not reading Excel file: missing or invalid parameter {ex.ParamName}";
                logger.Error(result.Message);
                return(result);
            }
            catch (Exception ex)
            {
                result.Message = "Failed to read Excel file";
                logger.Error(result.Message, ex);
                return(result);
            }
        }