/* * 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 System; using System.Collections.Generic; using System.Globalization; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using QuantConnect.Optimizer.Parameters; using QuantConnect.Statistics; using QuantConnect.Util; namespace QuantConnect.Api { /// /// Json converter for which creates a light weight easy to consume serialized version /// public class OptimizationBacktestJsonConverter : JsonConverter { /// /// Determines whether this instance can convert the specified object type. /// /// Type of the object. /// /// true if this instance can convert the specified object type; otherwise, false. /// public override bool CanConvert(Type objectType) { return objectType == typeof(OptimizationBacktest); } /// /// Writes the JSON representation of the object. /// /// The to write to. /// The value. /// The calling serializer. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var optimizationBacktest = value as OptimizationBacktest; if (ReferenceEquals(optimizationBacktest, null)) return; writer.WriteStartObject(); if (!string.IsNullOrEmpty(optimizationBacktest.Name)) { writer.WritePropertyName("name"); writer.WriteValue(optimizationBacktest.Name); } if (!string.IsNullOrEmpty(optimizationBacktest.BacktestId)) { writer.WritePropertyName("id"); writer.WriteValue(optimizationBacktest.BacktestId); writer.WritePropertyName("progress"); writer.WriteValue(optimizationBacktest.Progress); writer.WritePropertyName("exitCode"); writer.WriteValue(optimizationBacktest.ExitCode); } if (optimizationBacktest.StartDate != default) { writer.WritePropertyName("startDate"); writer.WriteValue(optimizationBacktest.StartDate.ToStringInvariant(DateFormat.UI)); } if (optimizationBacktest.EndDate != default) { writer.WritePropertyName("endDate"); writer.WriteValue(optimizationBacktest.EndDate.ToStringInvariant(DateFormat.UI)); } if (optimizationBacktest.OutOfSampleMaxEndDate != null) { writer.WritePropertyName("outOfSampleMaxEndDate"); writer.WriteValue(optimizationBacktest.OutOfSampleMaxEndDate.ToStringInvariant(DateFormat.UI)); writer.WritePropertyName("outOfSampleDays"); writer.WriteValue(optimizationBacktest.OutOfSampleDays); } if (!optimizationBacktest.Statistics.IsNullOrEmpty()) { writer.WritePropertyName("statistics"); writer.WriteStartArray(); foreach (var keyValuePair in optimizationBacktest.Statistics.OrderBy(pair => pair.Key)) { switch (keyValuePair.Key) { case PerformanceMetrics.PortfolioTurnover: case PerformanceMetrics.SortinoRatio: case PerformanceMetrics.StartEquity: case PerformanceMetrics.EndEquity: case PerformanceMetrics.DrawdownRecovery: continue; } var statistic = keyValuePair.Value.Replace("%", string.Empty); if (Currencies.TryParse(statistic, out var result)) { writer.WriteValue(result); } } writer.WriteEndArray(); } if (optimizationBacktest.ParameterSet != null) { writer.WritePropertyName("parameterSet"); serializer.Serialize(writer, optimizationBacktest.ParameterSet.Value); } if (optimizationBacktest.Equity != null) { writer.WritePropertyName("equity"); var equity = JsonConvert.SerializeObject(optimizationBacktest.Equity.Values); writer.WriteRawValue(equity); } writer.WriteEndObject(); } /// /// Reads the JSON representation of the object. /// /// The to read from. /// Type of the object. /// The existing value of object being read. /// The calling serializer. /// /// The object value. /// public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jObject = JObject.Load(reader); var name = jObject["name"].Value(); var hostName = jObject["hostName"]?.Value(); var backtestId = jObject["id"].Value(); var progress = jObject["progress"].Value(); var exitCode = jObject["exitCode"].Value(); var outOfSampleDays = jObject["outOfSampleDays"]?.Value() ?? default; var startDate = jObject["startDate"]?.Value() ?? default; var endDate = jObject["endDate"]?.Value() ?? default; var outOfSampleMaxEndDate = jObject["outOfSampleMaxEndDate"]?.Value(); var jStatistics = jObject["statistics"]; Dictionary statistics = default; if (jStatistics != null) { statistics = new Dictionary { { PerformanceMetrics.Alpha, jStatistics[0].Value() }, { PerformanceMetrics.AnnualStandardDeviation, jStatistics[1].Value() }, { PerformanceMetrics.AnnualVariance, jStatistics[2].Value() }, { PerformanceMetrics.AverageLoss, jStatistics[3].Value() }, { PerformanceMetrics.AverageWin, jStatistics[4].Value() }, { PerformanceMetrics.Beta, jStatistics[5].Value() }, { PerformanceMetrics.CompoundingAnnualReturn, jStatistics[6].Value() }, { PerformanceMetrics.Drawdown, jStatistics[7].Value() }, { PerformanceMetrics.EstimatedStrategyCapacity, jStatistics[8].Value() }, { PerformanceMetrics.Expectancy, jStatistics[9].Value() }, { PerformanceMetrics.InformationRatio, jStatistics[10].Value() }, { PerformanceMetrics.LossRate, jStatistics[11].Value() }, { PerformanceMetrics.NetProfit, jStatistics[12].Value() }, { PerformanceMetrics.ProbabilisticSharpeRatio, jStatistics[13].Value() }, { PerformanceMetrics.ProfitLossRatio, jStatistics[14].Value() }, { PerformanceMetrics.SharpeRatio, jStatistics[15].Value() }, // TODO: Add SortinoRatio // TODO: Add StartingEquity // TODO: Add EndingEquity // TODO: Add DrawdownRecovery { PerformanceMetrics.TotalFees, jStatistics[16].Value() }, { PerformanceMetrics.TotalOrders, jStatistics[17].Value() }, { PerformanceMetrics.TrackingError, jStatistics[18].Value() }, { PerformanceMetrics.TreynorRatio, jStatistics[19].Value() }, { PerformanceMetrics.WinRate, jStatistics[20].Value() }, }; } var parameterSet = serializer.Deserialize(jObject["parameterSet"].CreateReader()); var equity = new CandlestickSeries(); if (jObject["equity"] != null) { foreach (var point in JsonConvert.DeserializeObject>(jObject["equity"].ToString())) { equity.AddPoint(point); } } var optimizationBacktest = new OptimizationBacktest(parameterSet, backtestId, name) { HostName = hostName, Progress = progress, ExitCode = exitCode, Statistics = statistics, Equity = equity, EndDate = endDate, StartDate = startDate, OutOfSampleDays = outOfSampleDays, OutOfSampleMaxEndDate = outOfSampleMaxEndDate, }; return optimizationBacktest; } } }