/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ using Newtonsoft.Json; using QuantConnect.Logging; using QuantConnect.Optimizer.Parameters; using QuantConnect.Packets; using QuantConnect.Statistics; using System.Collections.Generic; using System.Globalization; using System.Linq; namespace QuantConnect.Optimizer { /// /// Lightweight per-backtest record extracted at time to avoid retaining the full backtest JSON. /// public class OptimizationBacktestMetrics : BacktestSummary { /// /// The backtest's total performance (wraps , /// , and list); null when absent from the backtest result. /// public AlgorithmPerformance TotalPerformance { get; set; } /// /// Number of orders the backtest produced. /// public int TotalOrders { get; set; } /// /// Names of the diagnostic entries the backtest attached. /// public IReadOnlyList AnalysisNames { get; set; } /// /// Extracts the fields the analyzer needs from a backtest result JSON; returns null when the parameter set is invalid. /// /// The backtest id. /// The parameter set the backtest was run with. /// The serialized backtest result JSON. public static OptimizationBacktestMetrics ExtractFrom(string backtestId, ParameterSet parameterSet, string jsonBacktestResult) { if (parameterSet == null) { return null; } var parameters = ParseParameterSet(parameterSet); if (parameters.Count == 0) { return null; } BacktestResult parsed = null; if (!string.IsNullOrEmpty(jsonBacktestResult)) { try { // DeserializeJson uses the LEAN-wide JsonSerializer (CamelCaseNamingStrategy + OrderJsonConverter), // so polymorphic Orders and camelCase JSON both work without extra configuration. parsed = jsonBacktestResult.DeserializeJson(); } catch (JsonException ex) { Log.Error(ex, $"OptimizationBacktestMetrics.ExtractFrom(): failed to parse backtest result for '{backtestId}'"); } } var analysisNames = parsed?.Analysis == null ? (IReadOnlyList)System.Array.Empty() : parsed.Analysis .Where(a => !string.IsNullOrEmpty(a?.Name)) .Select(a => a.Name) .ToList(); return new OptimizationBacktestMetrics { BacktestId = backtestId, Parameters = parameters, SharpeRatio = parsed?.TotalPerformance?.PortfolioStatistics?.SharpeRatio ?? 0m, TotalPerformance = parsed?.TotalPerformance, TotalOrders = parsed?.Orders?.Count ?? 0, AnalysisNames = analysisNames }; } private static Dictionary ParseParameterSet(ParameterSet parameterSet) { var result = new Dictionary(); if (parameterSet?.Value == null) return result; foreach (var kv in parameterSet.Value) { if (decimal.TryParse(kv.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var d)) { result[kv.Key] = d; } } return result; } } }