/// <summary> /// Сортировка списка с результатами оптимизации /// </summary> /// <param name="results">Текущая коллекция с результатами</param> /// <param name="order">Направленность сортировки</param> /// <param name="sortingFlags">Список коэффициентов сортировки</param> /// <param name="sortMethod">Метод сортировки</param> /// <returns></returns> public static IEnumerable <OptimisationResult> SortOptimisations(this IEnumerable <OptimisationResult> results, OrderBy order, IEnumerable <SortBy> sortingFlags) { // Получаем уникальный список флагов для сортировки sortingFlags = sortingFlags.Distinct(); // Проверяем наличие флагов if (sortingFlags.Count() == 0) { return(null); } // Если флаг один то сортируем чисто по этому показателю if (sortingFlags.Count() == 1) { if (order == GetSortingDirection(sortingFlags.ElementAt(0))) { return(results.OrderBy(x => x.GetResult(sortingFlags.ElementAt(0)))); } else { return(results.OrderByDescending(x => x.GetResult(sortingFlags.ElementAt(0)))); } } // Формируем границы минимум и максимум по переданным флагам оптимизации Dictionary <SortBy, MinMax> Borders = sortingFlags.ToDictionary(x => x, x => new MinMax { Max = double.MinValue, Min = double.MaxValue }); #region create Borders min max dictionary // Цикл по списку проходов оптимизаций for (int i = 0; i < results.Count(); i++) { // Цикл по флагам сортировки foreach (var item in sortingFlags) { // получаем значение текущего коэффициента double value = results.ElementAt(i).GetResult(item); MinMax mm = Borders[item]; // Задаем значения минимум и максимум mm.Max = Math.Max(mm.Max, value); mm.Min = Math.Min(mm.Min, value); Borders[item] = mm; } } #endregion // Вес взвешенной суммы нормированных коэффициентов double coef = (1.0 / Borders.Count); // Переводим список результатов оптимизации к массиву типа List // Так как с ним быстрее работать List <OptimisationResult> listOfResults = results.ToList(); // Цикл по результатам оптимизации for (int i = 0; i < listOfResults.Count; i++) { // Присваиваем значение текущему коэффициент OptimisationResult data = listOfResults[i]; // Зануляем текущий коэффициент сортировки data.SortBy = 0; // Проводим цикл сформированным границам максимумов и минимумов foreach (var item in Borders) { // Получаем значение текущего результата double value = listOfResults[i].GetResult(item.Key); MinMax mm = item.Value; // Если минимум меньше нуля - сдвигаемвсе данные на велечину отрицательного минимума if (mm.Min < 0) { value += Math.Abs(mm.Min); mm.Max += Math.Abs(mm.Min); } // Если максимум больше нуля - делаем подсччеты if (mm.Max > 0) { // В зависимости от метода сортировки - высчитываем коэффициент if (GetSortingDirection(item.Key) == OrderBy.Descending) { // высчитываем коэффициент для сортировки по убыванию data.SortBy += (1 - value / mm.Max) * coef; } else { // Высчитываем коэффициент для сортировки по возрастанию data.SortBy += value / mm.Max * coef; } } } // Замещаем значение текущего коэффициента коэффициентом с параметром сортировки listOfResults[i] = data; } // Сортируем в зависимости от переданного типа сортировки if (order == OrderBy.Ascending) { return(listOfResults.OrderBy(x => x.SortBy)); } else { return(listOfResults.OrderByDescending(x => x.SortBy)); } }
/// <summary> /// Запись конкретного оптимизационного прозода /// </summary> /// <param name="resultItem">Значение оптимизационного прохода</param> /// <param name="writer">Писатель</param> private static void WriteResultItem(OptimisationResult resultItem, XmlTextWriter writer) { // Запись коэффициентов #region Coefficients writer.WriteStartElement("Coefficients"); // Пишем VaR #region VaR writer.WriteStartElement("VaR"); WriteItem(writer, "90", resultItem.GetResult(SortBy.Q_90).ToString()); // Квантиль 90 WriteItem(writer, "95", resultItem.GetResult(SortBy.Q_95).ToString()); // Квантиль 95 WriteItem(writer, "99", resultItem.GetResult(SortBy.Q_99).ToString()); // Квантиль 99 WriteItem(writer, "Mx", resultItem.GetResult(SortBy.Mx).ToString()); // Среднее по PL WriteItem(writer, "Std", resultItem.GetResult(SortBy.Std).ToString()); // среднеквадратическое отклонение по PL writer.WriteEndElement(); #endregion // Пишем параметры PL / DD - крайние точки #region Max PL DD writer.WriteStartElement("Max_PL_DD"); WriteItem(writer, "Profit", resultItem.GetResult(SortBy.MaxProfit).ToString()); // Суммарная прибыль WriteItem(writer, "DD", resultItem.GetResult(SortBy.MaxDD).ToString()); // Суммарный убыток WriteItem(writer, "Total Profit Trades", ((int)resultItem.GetResult(SortBy.MaxProfitTotalTrades)).ToString()); // Общее кол - во прибыльных трейдов WriteItem(writer, "Total Loose Trades", ((int)resultItem.GetResult(SortBy.MaxDDTotalTrades)).ToString()); // Общее кол - во убыточных трейдов WriteItem(writer, "Consecutive Wins", ((int)resultItem.GetResult(SortBy.MaxProfitConsecutivesTrades)).ToString()); // Прибыльных трейдов подряд WriteItem(writer, "Consecutive Loose", ((int)resultItem.GetResult(SortBy.MaxDDConsecutivesTrades)).ToString()); // Убыточный трейдов подряд writer.WriteEndElement(); #endregion // Пишем результаты торгов по дням #region Trading_Days // Метод пишуший результаты торгов void AddDay(string Day, double Profit, double DD, int ProfitTrades, int DDTrades) { writer.WriteStartElement(Day); WriteItem(writer, "Profit", Profit.ToString()); // прибыли WriteItem(writer, "DD", DD.ToString()); // убытки WriteItem(writer, "Number Of Profit Trades", ProfitTrades.ToString()); // кол - во прибыльных трейдов WriteItem(writer, "Number Of Loose Trades", DDTrades.ToString()); // кол - во убыточных трейдов writer.WriteEndElement(); } writer.WriteStartElement("Trading_Days"); // Пн AddDay("Mn", resultItem.GetResult(SortBy.AverageDailyProfit_Mn), resultItem.GetResult(SortBy.AverageDailyDD_Mn), (int)resultItem.GetResult(SortBy.AverageDailyProfitTrades_Mn), (int)resultItem.GetResult(SortBy.AverageDailyDDTrades_Mn)); // Вт AddDay("Tu", resultItem.GetResult(SortBy.AverageDailyProfit_Tu), resultItem.GetResult(SortBy.AverageDailyDD_Tu), (int)resultItem.GetResult(SortBy.AverageDailyProfitTrades_Tu), (int)resultItem.GetResult(SortBy.AverageDailyDDTrades_Tu)); // Ср AddDay("We", resultItem.GetResult(SortBy.AverageDailyProfit_We), resultItem.GetResult(SortBy.AverageDailyDD_We), (int)resultItem.GetResult(SortBy.AverageDailyProfitTrades_We), (int)resultItem.GetResult(SortBy.AverageDailyDDTrades_We)); // Чт AddDay("Th", resultItem.GetResult(SortBy.AverageDailyProfit_Th), resultItem.GetResult(SortBy.AverageDailyDD_Th), (int)resultItem.GetResult(SortBy.AverageDailyProfitTrades_Th), (int)resultItem.GetResult(SortBy.AverageDailyDDTrades_Th)); // Пт AddDay("Fr", resultItem.GetResult(SortBy.AverageDailyProfit_Fr), resultItem.GetResult(SortBy.AverageDailyDD_Fr), (int)resultItem.GetResult(SortBy.AverageDailyProfitTrades_Fr), (int)resultItem.GetResult(SortBy.AverageDailyDDTrades_Fr)); writer.WriteEndElement(); #endregion // Пишем остальные коэййициенты WriteItem(writer, "Custom", resultItem.GetResult(SortBy.Custom).ToString()); WriteItem(writer, "Payoff", resultItem.GetResult(SortBy.Payoff).ToString()); WriteItem(writer, "Profit factor", resultItem.GetResult(SortBy.ProfitFactor).ToString()); WriteItem(writer, "Average Profit factor", resultItem.GetResult(SortBy.AverageProfitFactor).ToString()); WriteItem(writer, "Recovery factor", resultItem.GetResult(SortBy.RecoveryFactor).ToString()); WriteItem(writer, "Average Recovery factor", resultItem.GetResult(SortBy.AverageRecoveryFactor).ToString()); WriteItem(writer, "Total trades", ((int)resultItem.GetResult(SortBy.TotalTrades)).ToString()); WriteItem(writer, "PL", resultItem.GetResult(SortBy.PL).ToString()); WriteItem(writer, "DD", resultItem.GetResult(SortBy.DD).ToString()); WriteItem(writer, "Altman Z Score", resultItem.GetResult(SortBy.AltmanZScore).ToString()); writer.WriteEndElement(); #endregion // Пишем коэффициенты робота #region Bot params foreach (var item in resultItem.report.BotParams) { WriteItem(writer, item.Key, item.Value); } #endregion }