From d40ec77379c38b99622eacca2d963c42f941380f Mon Sep 17 00:00:00 2001 From: Martin-Molinero Date: Wed, 8 May 2024 12:58:33 -0300 Subject: [PATCH] Refactor to use camel case (#7981) * Camel case results - Camel case lean results and streamed packets * Update packets to camel case --- .../Alphas/Serialization/SerializedInsight.cs | 115 ++++++++-- Common/AlgorithmConfiguration.cs | 16 +- Common/Api/RestResponse.cs | 5 +- Common/Chart.cs | 3 +- Common/Packets/AlgorithmNameUpdatePacket.cs | 2 - Common/Packets/AlgorithmNodePacket.cs | 19 -- Common/Packets/AlgorithmStatusPacket.cs | 9 +- Common/Packets/AlgorithmTagsUpdatePacket.cs | 4 - Common/Packets/AlphaNodePacket.cs | 5 +- Common/Packets/AlphaResultPacket.cs | 10 +- Common/Packets/BacktestNodePacket.cs | 9 - Common/Packets/BacktestResultPacket.cs | 15 -- Common/Packets/DebugPacket.cs | 7 +- Common/Packets/HandledErrorPacket.cs | 5 +- Common/Packets/LiveNodePacket.cs | 9 - Common/Packets/LiveResultPacket.cs | 15 +- Common/Packets/LogPacket.cs | 4 +- Common/Packets/OrderEventPacket.cs | 4 +- Common/Packets/Packet.cs | 2 - Common/Packets/PythonEnvironmentPacket.cs | 1 - Common/Packets/RuntimeErrorPacket.cs | 6 +- Common/Packets/SecurityTypesPacket.cs | 3 +- Common/Result.cs | 18 +- Common/Util/SeriesJsonConverter.cs | 28 +-- Engine/Results/BaseResultsHandler.cs | 51 +++-- Engine/Results/LiveTradingResultHandler.cs | 2 +- Optimizer/OptimizationNodePacket.cs | 15 +- Report/NullResultValueTypeJsonConverter.cs | 22 +- .../PortfolioLooperAlgorithm.cs | 2 +- .../InsightJsonConverterTests.cs | 205 +++++++----------- Tests/Common/AlgorithmConfigurationTests.cs | 37 +++- Tests/Common/Util/SeriesJsonConverterTests.cs | 62 +++++- Tests/Report/ResultDeserializationTests.cs | 7 +- 33 files changed, 365 insertions(+), 352 deletions(-) diff --git a/Common/Algorithm/Framework/Alphas/Serialization/SerializedInsight.cs b/Common/Algorithm/Framework/Alphas/Serialization/SerializedInsight.cs index 282c37e79ec5..5e4eade0d846 100644 --- a/Common/Algorithm/Framework/Alphas/Serialization/SerializedInsight.cs +++ b/Common/Algorithm/Framework/Alphas/Serialization/SerializedInsight.cs @@ -31,26 +31,22 @@ public class SerializedInsight /// /// See /// - [JsonProperty("id")] public string Id { get; set; } /// /// See /// - [JsonProperty("group-id")] public string GroupId { get; set; } /// /// See /// - [JsonProperty("source-model")] public string SourceModel { get; set; } /// /// Pass-through for /// [Obsolete("Deprecated as of 2020-01-23. Please use the `CreatedTime` property instead.")] - [JsonProperty("generated-time")] public double GeneratedTime { get { return _createdTime; } @@ -60,7 +56,6 @@ public double GeneratedTime /// /// See /// - [JsonProperty("created-time")] public double CreatedTime { get { return _createdTime; } @@ -70,27 +65,23 @@ public double CreatedTime /// /// See /// - [JsonProperty("close-time")] public double CloseTime { get; set; } /// /// See /// The symbol's security identifier string /// - [JsonProperty("symbol")] public string Symbol { get; set; } /// /// See /// The symbol's ticker at the generated time /// - [JsonProperty("ticker")] public string Ticker { get; set; } /// /// See /// - [JsonProperty("type")] public InsightType Type { get; set; } /// @@ -102,72 +93,61 @@ public double CreatedTime /// /// See /// - [JsonProperty("reference-final")] public decimal ReferenceValueFinal { get; set; } /// /// See /// - [JsonProperty("direction")] public InsightDirection Direction { get; set; } /// /// See /// - [JsonProperty("period")] public double Period { get; set; } /// /// See /// - [JsonProperty("magnitude")] [JsonConverter(typeof(JsonRoundingConverter))] public double? Magnitude { get; set; } /// /// See /// - [JsonProperty("confidence")] [JsonConverter(typeof(JsonRoundingConverter))] public double? Confidence { get; set; } /// /// See /// - [JsonProperty("weight")] public double? Weight { get; set; } /// /// See /// - [JsonProperty("score-final")] public bool ScoreIsFinal { get; set; } /// /// See /// - [JsonProperty("score-magnitude")] [JsonConverter(typeof(JsonRoundingConverter))] public double ScoreMagnitude { get; set; } /// /// See /// - [JsonProperty("score-direction")] [JsonConverter(typeof(JsonRoundingConverter))] public double ScoreDirection { get; set; } /// /// See /// - [JsonProperty("estimated-value")] [JsonConverter(typeof(JsonRoundingConverter))] public decimal EstimatedValue { get; set; } /// /// See /// - [JsonProperty("tag")] public string Tag { get; set; } /// @@ -204,5 +184,100 @@ public SerializedInsight(Insight insight) Weight = insight.Weight; Tag = insight.Tag; } + + #region BackwardsCompatibility + [JsonProperty("group-id")] + string OldGroupId + { + set + { + GroupId = value; + } + } + + [JsonProperty("source-model")] + string OldSourceModel + { + set + { + SourceModel = value; + } + } + + [JsonProperty("generated-time")] + double OldGeneratedTime + { + set + { + GeneratedTime = value; + } + } + + [JsonProperty("created-time")] + public double OldCreatedTime + { + set + { + CreatedTime = value; + } + } + + [JsonProperty("close-time")] + public double OldCloseTime + { + set + { + CloseTime = value; + } + } + + [JsonProperty("reference-final")] + decimal OldReferenceValueFinal + { + set + { + ReferenceValueFinal = value; + } + } + + [JsonProperty("score-final")] + bool OldScoreIsFinal + { + set + { + ScoreIsFinal = value; + } + } + + [JsonProperty("score-magnitude")] + [JsonConverter(typeof(JsonRoundingConverter))] + double OldScoreMagnitude + { + set + { + ScoreMagnitude = value; + } + } + + [JsonProperty("score-direction")] + [JsonConverter(typeof(JsonRoundingConverter))] + double OldScoreDirection + { + set + { + ScoreDirection = value; + } + } + + [JsonProperty("estimated-value")] + [JsonConverter(typeof(JsonRoundingConverter))] + decimal OldEstimatedValue + { + set + { + EstimatedValue = value; + } + } + #endregion } } diff --git a/Common/AlgorithmConfiguration.cs b/Common/AlgorithmConfiguration.cs index ea7f41983f16..bb7ca8109816 100644 --- a/Common/AlgorithmConfiguration.cs +++ b/Common/AlgorithmConfiguration.cs @@ -32,71 +32,61 @@ public class AlgorithmConfiguration /// /// The algorithm's name /// - [JsonProperty(PropertyName = "Name")] public string Name; /// /// List of tags associated with the algorithm /// - [JsonProperty(PropertyName = "Tags")] public ISet Tags; /// /// The algorithm's account currency /// - [JsonProperty(PropertyName = "AccountCurrency", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string AccountCurrency; /// /// The algorithm's brokerage model /// /// Required to set the correct brokerage model on report generation. - [JsonProperty(PropertyName = "Brokerage")] - public BrokerageName BrokerageName; + public BrokerageName Brokerage; /// /// The algorithm's account type /// /// Required to set the correct brokerage model on report generation. - [JsonProperty(PropertyName = "AccountType")] public AccountType AccountType; /// /// The parameters used by the algorithm /// - [JsonProperty(PropertyName = "Parameters")] public IReadOnlyDictionary Parameters; /// /// Backtest maximum end date /// - [JsonProperty(PropertyName = "OutOfSampleMaxEndDate")] public DateTime? OutOfSampleMaxEndDate; /// /// The backtest out of sample day count /// - [JsonProperty(PropertyName = "OutOfSampleDays")] public int OutOfSampleDays; /// /// The backtest start date /// - [JsonProperty(PropertyName = "StartDate")] [JsonConverter(typeof(DateTimeJsonConverter), DateFormat.UI)] public DateTime StartDate; /// /// The backtest end date /// - [JsonProperty(PropertyName = "EndDate")] [JsonConverter(typeof(DateTimeJsonConverter), DateFormat.UI)] public DateTime EndDate; /// /// Number of trading days per year for Algorithm's portfolio statistics. /// - [JsonProperty(PropertyName = "TradingDaysPerYear")] public int TradingDaysPerYear; /// @@ -112,7 +102,7 @@ public AlgorithmConfiguration(string name, ISet tags, string accountCurr TradingDaysPerYear = tradingDaysPerYear; OutOfSampleDays = outOfSampleDays; AccountCurrency = accountCurrency; - BrokerageName = brokerageName; + Brokerage = brokerageName; AccountType = accountType; Parameters = parameters; StartDate = startDate; diff --git a/Common/Api/RestResponse.cs b/Common/Api/RestResponse.cs index c2bbdc067bc0..0f3bb63fe1d7 100644 --- a/Common/Api/RestResponse.cs +++ b/Common/Api/RestResponse.cs @@ -13,7 +13,6 @@ * limitations under the License. */ using System.Collections.Generic; -using Newtonsoft.Json; namespace QuantConnect.Api { @@ -34,13 +33,11 @@ public RestResponse() /// /// Indicate if the API request was successful. /// - [JsonProperty(PropertyName = "success")] public bool Success { get; set; } /// /// List of errors with the API call. /// - [JsonProperty(PropertyName = "errors")] public List Errors { get; set; } } -} \ No newline at end of file +} diff --git a/Common/Chart.cs b/Common/Chart.cs index 3fece8a1fba5..782b869ffcf4 100644 --- a/Common/Chart.cs +++ b/Common/Chart.cs @@ -18,8 +18,8 @@ using System.Data; using System.Drawing; using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; using QuantConnect.Logging; -using QuantConnect.Util; namespace QuantConnect { @@ -28,7 +28,6 @@ namespace QuantConnect /// public class Chart { - /// /// Name of the Chart /// diff --git a/Common/Packets/AlgorithmNameUpdatePacket.cs b/Common/Packets/AlgorithmNameUpdatePacket.cs index 0f8286a24683..c2abfc4368dd 100644 --- a/Common/Packets/AlgorithmNameUpdatePacket.cs +++ b/Common/Packets/AlgorithmNameUpdatePacket.cs @@ -27,13 +27,11 @@ public class AlgorithmNameUpdatePacket : Packet /// /// Algorithm id for this order event /// - [JsonProperty(PropertyName = "sAlgorithmID")] public string AlgorithmId; /// /// The new name /// - [JsonProperty(PropertyName = "sName")] public string Name; /// diff --git a/Common/Packets/AlgorithmNodePacket.cs b/Common/Packets/AlgorithmNodePacket.cs index 20fb6084a02c..f63fbc36fc60 100644 --- a/Common/Packets/AlgorithmNodePacket.cs +++ b/Common/Packets/AlgorithmNodePacket.cs @@ -36,39 +36,32 @@ public AlgorithmNodePacket(PacketType type) /// /// The host name to use if any /// - [JsonProperty(PropertyName = "sHostName")] public string HostName; /// /// User Id placing request /// - [JsonProperty(PropertyName = "iUserID")] public int UserId = 0; /// User API Token - [JsonProperty(PropertyName = "sUserToken")] public string UserToken = ""; /// User Organization Id - [JsonProperty(PropertyName = "sOrganizationID")] public string OrganizationId = ""; /// /// Project Id of the request /// - [JsonProperty(PropertyName = "iProjectID")] public int ProjectId = 0; /// /// Project name of the request /// - [JsonProperty(PropertyName = "sProjectName")] public string ProjectName; /// /// Algorithm Id - BacktestId or DeployId - Common Id property between packets. /// - [JsonProperty(PropertyName = "sAlgorithmID")] public string AlgorithmId { get @@ -84,56 +77,47 @@ public string AlgorithmId /// /// User session Id for authentication /// - [JsonProperty(PropertyName = "sSessionID")] public string SessionId = ""; /// /// Language flag: Currently represents IL code or Dynamic Scripted Types. /// - [JsonProperty(PropertyName = "eLanguage")] public Language Language = Language.CSharp; /// /// Server type for the deployment (512, 1024, 2048) /// - [JsonProperty(PropertyName = "sServerType")] public ServerType ServerType = ServerType.Server512; /// /// Unique compile id of this backtest /// - [JsonProperty(PropertyName = "sCompileID")] public string CompileId = ""; /// /// Version number identifier for the lean engine. /// - [JsonProperty(PropertyName = "sVersion")] public string Version; /// /// An algorithm packet which has already been run and is being redelivered on this node. /// In this event we don't want to relaunch the task as it may result in unexpected behaviour for user. /// - [JsonProperty(PropertyName = "bRedelivered")] public bool Redelivered = false; /// /// Algorithm binary with zip of contents /// - [JsonProperty(PropertyName = "oAlgorithm")] public byte[] Algorithm = new byte[] { }; /// /// Request source - Web IDE or API - for controling result handler behaviour /// - [JsonProperty(PropertyName = "sRequestSource")] public string RequestSource = "WebIDE"; /// /// The maximum amount of RAM (in MB) this algorithm is allowed to utilize /// - [JsonProperty(PropertyName = "iMaxRamAllocation")] public int RamAllocation { get { return Controls.RamAllocation; } } @@ -141,19 +125,16 @@ public int RamAllocation { /// /// Specifies values to control algorithm limits /// - [JsonProperty(PropertyName = "oControls")] public Controls Controls; /// /// The parameter values used to set algorithm parameters /// - [JsonProperty(PropertyName = "aParameters")] public Dictionary Parameters = new Dictionary(); /// /// String name of the HistoryProvider we're running with /// - [JsonProperty(PropertyName = "sHistoryProvider")] public string HistoryProvider = ""; /// diff --git a/Common/Packets/AlgorithmStatusPacket.cs b/Common/Packets/AlgorithmStatusPacket.cs index f988293b88b0..76951f6e1570 100644 --- a/Common/Packets/AlgorithmStatusPacket.cs +++ b/Common/Packets/AlgorithmStatusPacket.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -27,44 +27,37 @@ public class AlgorithmStatusPacket : Packet /// /// Current algorithm status /// - [JsonProperty(PropertyName = "eStatus")] [JsonConverter(typeof(StringEnumConverter))] public AlgorithmStatus Status; /// /// Chart we're subscribed to for live trading. /// - [JsonProperty(PropertyName = "sChartSubscription")] public string ChartSubscription; /// /// Optional message or reason for state change. /// - [JsonProperty(PropertyName = "sMessage")] public string Message; /// /// Algorithm Id associated with this status packet /// - [JsonProperty(PropertyName = "sAlgorithmID")] public string AlgorithmId; /// /// OptimizationId for this result packet if any /// - [JsonProperty(PropertyName = "sOptimizationID")] public string OptimizationId; /// /// Project Id associated with this status packet /// - [JsonProperty(PropertyName = "iProjectID")] public int ProjectId; /// /// The current state of the channel /// - [JsonProperty(PropertyName = "sChannelStatus")] public string ChannelStatus; /// diff --git a/Common/Packets/AlgorithmTagsUpdatePacket.cs b/Common/Packets/AlgorithmTagsUpdatePacket.cs index 13ffe1989ecb..7136213a66fc 100644 --- a/Common/Packets/AlgorithmTagsUpdatePacket.cs +++ b/Common/Packets/AlgorithmTagsUpdatePacket.cs @@ -14,8 +14,6 @@ * */ - -using Newtonsoft.Json; using System.Collections.Generic; namespace QuantConnect.Packets @@ -28,13 +26,11 @@ public class AlgorithmTagsUpdatePacket : Packet /// /// Algorithm id for this order event /// - [JsonProperty(PropertyName = "sAlgorithmID")] public string AlgorithmId; /// /// The new tags /// - [JsonProperty(PropertyName = "aTags")] public HashSet Tags = new(); /// diff --git a/Common/Packets/AlphaNodePacket.cs b/Common/Packets/AlphaNodePacket.cs index 0198306afc87..fe112699deff 100644 --- a/Common/Packets/AlphaNodePacket.cs +++ b/Common/Packets/AlphaNodePacket.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -26,7 +26,6 @@ public class AlphaNodePacket : LiveNodePacket /// /// Gets or sets the alpha id /// - [JsonProperty(PropertyName = "sAlphaID")] public string AlphaId { get; set; } /// @@ -37,4 +36,4 @@ public AlphaNodePacket() Type = PacketType.AlphaNode; } } -} \ No newline at end of file +} diff --git a/Common/Packets/AlphaResultPacket.cs b/Common/Packets/AlphaResultPacket.cs index 37d8e6abeeb5..10b3f79d82a3 100644 --- a/Common/Packets/AlphaResultPacket.cs +++ b/Common/Packets/AlphaResultPacket.cs @@ -29,38 +29,36 @@ public class AlphaResultPacket : Packet /// /// The user's id that deployed the alpha stream /// - [JsonProperty("user-id")] public int UserId { get; set; } /// /// The deployed alpha id. This is the id generated upon submssion to the alpha marketplace. /// If this is a user backtest or live algo then this will not be specified /// - [JsonProperty("alpha-id", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public string AlphaId { get; set; } /// /// The algorithm's unique identifier /// - [JsonProperty("algorithm-id")] public string AlgorithmId { get; set; } /// /// The generated insights /// - [JsonProperty("insights", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public List Insights { get; set; } /// /// The generated OrderEvents /// - [JsonProperty("order-events", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public List OrderEvents { get; set; } /// /// The new or updated Orders /// - [JsonProperty("orders", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public List Orders { get; set; } /// diff --git a/Common/Packets/BacktestNodePacket.cs b/Common/Packets/BacktestNodePacket.cs index f0c0bd386f67..65c66f3c2074 100644 --- a/Common/Packets/BacktestNodePacket.cs +++ b/Common/Packets/BacktestNodePacket.cs @@ -34,55 +34,46 @@ private static readonly string DefaultId /// /// Name of the backtest as randomly defined in the IDE. /// - [JsonProperty(PropertyName = "sName")] public string Name = ""; /// /// BacktestId / Algorithm Id for this task /// - [JsonProperty(PropertyName = "sBacktestID")] public string BacktestId = DefaultId; /// /// Optimization Id for this task /// - [JsonProperty(PropertyName = "sOptimizationID")] public string OptimizationId; /// /// Backtest start-date as defined in the Initialize() method. /// - [JsonProperty(PropertyName = "dtPeriodStart")] public DateTime? PeriodStart; /// /// Backtest end date as defined in the Initialize() method. /// - [JsonProperty(PropertyName = "dtPeriodFinish")] public DateTime? PeriodFinish; /// /// Backtest maximum end date /// - [JsonProperty(PropertyName = "dtOutOfSampleMaxEndDate")] public DateTime? OutOfSampleMaxEndDate; /// /// The backtest out of sample day count /// - [JsonProperty(PropertyName = "iOutOfSampleDays")] public int OutOfSampleDays; /// /// Estimated number of trading days in this backtest task based on the start-end dates. /// - [JsonProperty(PropertyName = "iTradeableDates")] public int TradeableDates = 0; /// /// True, if this is a debugging backtest /// - [JsonProperty(PropertyName = "bDebugging")] public bool IsDebugging; /// diff --git a/Common/Packets/BacktestResultPacket.cs b/Common/Packets/BacktestResultPacket.cs index e4ffdad8f408..ba83da9a5138 100644 --- a/Common/Packets/BacktestResultPacket.cs +++ b/Common/Packets/BacktestResultPacket.cs @@ -31,91 +31,76 @@ public class BacktestResultPacket : Packet /// /// User Id placing this task /// - [JsonProperty(PropertyName = "iUserID")] public int UserId; /// /// Project Id of the this task. /// - [JsonProperty(PropertyName = "iProjectID")] public int ProjectId; /// /// User Session Id /// - [JsonProperty(PropertyName = "sSessionID")] public string SessionId = string.Empty; /// /// BacktestId for this result packet /// - [JsonProperty(PropertyName = "sBacktestID")] public string BacktestId = string.Empty; /// /// OptimizationId for this result packet if any /// - [JsonProperty(PropertyName = "sOptimizationID")] public string OptimizationId; /// /// Compile Id for the algorithm which generated this result packet. /// - [JsonProperty(PropertyName = "sCompileID")] public string CompileId = string.Empty; /// /// Start of the backtest period as defined in Initialize() method. /// - [JsonProperty(PropertyName = "dtPeriodStart")] public DateTime PeriodStart; /// /// End of the backtest period as defined in the Initialize() method. /// - [JsonProperty(PropertyName = "dtPeriodFinish")] public DateTime PeriodFinish; /// /// DateTime (EST) the user requested this backtest. /// - [JsonProperty(PropertyName = "dtDateRequested")] public DateTime DateRequested; /// /// DateTime (EST) when the backtest was completed. /// - [JsonProperty(PropertyName = "dtDateFinished")] public DateTime DateFinished; /// /// Progress of the backtest as a percentage from 0-1 based on the days lapsed from start-finish. /// - [JsonProperty(PropertyName = "dProgress")] public decimal Progress; /// /// Name of this backtest. /// - [JsonProperty(PropertyName = "sName")] public string Name = string.Empty; /// /// Result data object for this backtest /// - [JsonProperty(PropertyName = "oResults")] public BacktestResult Results = new (); /// /// Processing time of the algorithm (from moment the algorithm arrived on the algorithm node) /// - [JsonProperty(PropertyName = "dProcessingTime")] public double ProcessingTime; /// /// Estimated number of tradeable days in the backtest based on the start and end date or the backtest /// - [JsonProperty(PropertyName = "iTradeableDates")] public int TradeableDates; /// diff --git a/Common/Packets/DebugPacket.cs b/Common/Packets/DebugPacket.cs index 5b6ed61da057..ce9f8813dda0 100644 --- a/Common/Packets/DebugPacket.cs +++ b/Common/Packets/DebugPacket.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -26,32 +26,27 @@ public class DebugPacket : Packet /// /// String debug message to send to the users console /// - [JsonProperty(PropertyName = "sMessage")] public string Message; /// /// Associated algorithm Id. /// - [JsonProperty(PropertyName = "sAlgorithmID")] public string AlgorithmId; /// /// Compile id of the algorithm sending this message /// - [JsonProperty(PropertyName = "sCompileID")] public string CompileId; /// /// Project Id for this message /// - [JsonProperty(PropertyName = "iProjectID")] public int ProjectId; /// /// True to emit message as a popup notification (toast), /// false to emit message in console as text /// - [JsonProperty(PropertyName = "bToast")] public bool Toast; /// diff --git a/Common/Packets/HandledErrorPacket.cs b/Common/Packets/HandledErrorPacket.cs index 87200fa2a16b..7a4ec3d699fa 100644 --- a/Common/Packets/HandledErrorPacket.cs +++ b/Common/Packets/HandledErrorPacket.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -27,19 +27,16 @@ public class HandledErrorPacket : Packet /// /// Runtime error message from the exception /// - [JsonProperty(PropertyName = "sMessage")] public string Message; /// /// Algorithm id which generated this runtime error /// - [JsonProperty(PropertyName = "sAlgorithmID")] public string AlgorithmId; /// /// Error stack trace information string passed through from the Lean exception /// - [JsonProperty(PropertyName = "sStackTrace")] public string StackTrace; /// diff --git a/Common/Packets/LiveNodePacket.cs b/Common/Packets/LiveNodePacket.cs index 6d3443f56884..15b899327f70 100644 --- a/Common/Packets/LiveNodePacket.cs +++ b/Common/Packets/LiveNodePacket.cs @@ -28,55 +28,46 @@ public class LiveNodePacket : AlgorithmNodePacket /// /// Deploy Id for this live algorithm. /// - [JsonProperty(PropertyName = "sDeployID")] public string DeployId = ""; /// /// String name of the brokerage we're trading with /// - [JsonProperty(PropertyName = "sBrokerage")] public string Brokerage = ""; /// /// String-String Dictionary of Brokerage Data for this Live Job /// - [JsonProperty(PropertyName = "aBrokerageData")] public Dictionary BrokerageData = new Dictionary(); /// /// String name of the DataQueueHandler or LiveDataProvider we're running with /// - [JsonProperty(PropertyName = "sDataQueueHandler")] public string DataQueueHandler = ""; /// /// String name of the DataChannelProvider we're running with /// - [JsonProperty(PropertyName = "sDataChannelProvider")] public string DataChannelProvider = ""; /// /// Gets flag indicating whether or not the message should be acknowledged and removed from the queue /// - [JsonProperty(PropertyName = "DisableAcknowledgement")] public bool DisableAcknowledgement; /// /// A list of event types to generate notifications for, which will use /// - [JsonProperty(PropertyName = "aNotificationEvents")] public HashSet NotificationEvents; /// /// A list of notification targets to use /// - [JsonProperty(PropertyName = "aNotificationTargets")] public List NotificationTargets; /// /// List of real time data types available in the live trading environment /// - [JsonProperty(PropertyName = "aLiveDataTypes")] public HashSet LiveDataTypes; /// diff --git a/Common/Packets/LiveResultPacket.cs b/Common/Packets/LiveResultPacket.cs index 615e5acc73ae..4225c555ed44 100644 --- a/Common/Packets/LiveResultPacket.cs +++ b/Common/Packets/LiveResultPacket.cs @@ -32,43 +32,36 @@ public class LiveResultPacket : Packet /// /// User Id sending result packet /// - [JsonProperty(PropertyName = "iUserID")] public int UserId = 0; /// /// Project Id of the result packet /// - [JsonProperty(PropertyName = "iProjectID")] public int ProjectId = 0; /// /// User session Id who issued the result packet /// - [JsonProperty(PropertyName = "sSessionID")] public string SessionId = ""; /// /// Live Algorithm Id (DeployId) for this result packet /// - [JsonProperty(PropertyName = "sDeployID")] public string DeployId = ""; /// /// Compile Id algorithm which generated this result packet /// - [JsonProperty(PropertyName = "sCompileID")] public string CompileId = ""; /// /// Result data object for this result packet /// - [JsonProperty(PropertyName = "oResults")] public LiveResult Results = new LiveResult(); /// /// Processing time / running time for the live algorithm. /// - [JsonProperty(PropertyName = "dProcessingTime")] public double ProcessingTime = 0; /// @@ -153,7 +146,7 @@ public class LiveResult : Result /// /// Holdings dictionary of algorithm holdings information /// - [JsonProperty(PropertyName = "Holdings", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public IDictionary Holdings; /// @@ -179,19 +172,19 @@ public CashBook CashBook /// /// Cash for the algorithm's live results. /// - [JsonProperty(PropertyName = "Cash", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public Dictionary Cash; /// /// The algorithm's account currency /// - [JsonProperty(PropertyName = "AccountCurrency", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string AccountCurrency; /// /// The algorithm's account currency /// - [JsonProperty(PropertyName = "AccountCurrencySymbol", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string AccountCurrencySymbol; /// diff --git a/Common/Packets/LogPacket.cs b/Common/Packets/LogPacket.cs index 1f7afef7945e..47ffeb6dc889 100644 --- a/Common/Packets/LogPacket.cs +++ b/Common/Packets/LogPacket.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -26,13 +26,11 @@ public class LogPacket : Packet /// /// Log message to the users console: /// - [JsonProperty(PropertyName = "sMessage")] public string Message; /// /// Algorithm Id requesting this logging /// - [JsonProperty(PropertyName = "sAlgorithmID")] public string AlgorithmId; /// diff --git a/Common/Packets/OrderEventPacket.cs b/Common/Packets/OrderEventPacket.cs index 61fa6d97918e..c1367a257e78 100644 --- a/Common/Packets/OrderEventPacket.cs +++ b/Common/Packets/OrderEventPacket.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -28,13 +28,11 @@ public class OrderEventPacket : Packet /// /// Order event object /// - [JsonProperty(PropertyName = "oOrderEvent")] public OrderEvent Event; /// /// Algorithm id for this order event /// - [JsonProperty(PropertyName = "sAlgorithmID")] public string AlgorithmId; /// diff --git a/Common/Packets/Packet.cs b/Common/Packets/Packet.cs index 1732fa7f1f28..d3a220586d7f 100644 --- a/Common/Packets/Packet.cs +++ b/Common/Packets/Packet.cs @@ -27,13 +27,11 @@ public class Packet /// /// Packet type defined by a string enum /// - [JsonProperty(PropertyName = "eType")] public PacketType Type { get; set; } = PacketType.None; /// /// User unique specific channel endpoint to send the packets /// - [JsonProperty(PropertyName = "sChannel")] public virtual string Channel { get; set; } = ""; /// diff --git a/Common/Packets/PythonEnvironmentPacket.cs b/Common/Packets/PythonEnvironmentPacket.cs index c8440769fc92..838dc39d9d66 100644 --- a/Common/Packets/PythonEnvironmentPacket.cs +++ b/Common/Packets/PythonEnvironmentPacket.cs @@ -36,7 +36,6 @@ protected PythonEnvironmentPacket(PacketType type) : base(type) /// Virtual environment ID used to find PythonEvironments /// Ideally MD5, but environment names work as well. /// - [JsonProperty(PropertyName = "sPythonVirtualEnvironment")] public string PythonVirtualEnvironment { get; set; } } } diff --git a/Common/Packets/RuntimeErrorPacket.cs b/Common/Packets/RuntimeErrorPacket.cs index 6a9bbaea4abd..fdf0c04bce14 100644 --- a/Common/Packets/RuntimeErrorPacket.cs +++ b/Common/Packets/RuntimeErrorPacket.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -27,25 +27,21 @@ public class RuntimeErrorPacket : Packet /// /// Runtime error message from the exception /// - [JsonProperty(PropertyName = "sMessage")] public string Message; /// /// Algorithm id which generated this runtime error /// - [JsonProperty(PropertyName = "sAlgorithmID")] public string AlgorithmId; /// /// Error stack trace information string passed through from the Lean exception /// - [JsonProperty(PropertyName = "sStackTrace")] public string StackTrace; /// /// User Id associated with the backtest that threw the error /// - [JsonProperty(PropertyName = "iUserID")] public int UserId = 0; /// diff --git a/Common/Packets/SecurityTypesPacket.cs b/Common/Packets/SecurityTypesPacket.cs index 348d6f35ae5c..06ec9f840e82 100644 --- a/Common/Packets/SecurityTypesPacket.cs +++ b/Common/Packets/SecurityTypesPacket.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -27,7 +27,6 @@ public class SecurityTypesPacket : Packet /// /// List of Security Type the user has requested (Equity, Forex, Futures etc). /// - [JsonProperty(PropertyName = "aMarkets")] public List Types = new List(); /// diff --git a/Common/Result.cs b/Common/Result.cs index e55210c43ca5..23ec99d8c475 100644 --- a/Common/Result.cs +++ b/Common/Result.cs @@ -31,56 +31,56 @@ public class Result /// /// Charts updates for the live algorithm since the last result packet /// - [JsonProperty(PropertyName = "Charts", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public IDictionary Charts; /// /// Order updates since the last result packet /// - [JsonProperty(PropertyName = "Orders", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public IDictionary Orders; /// /// OrderEvent updates since the last result packet /// - [JsonProperty(PropertyName = "OrderEvents", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public List OrderEvents; /// /// Trade profit and loss information since the last algorithm result packet /// - [JsonProperty(PropertyName = "ProfitLoss", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public IDictionary ProfitLoss; /// /// Statistics information sent during the algorithm operations. /// /// Intended for update mode -- send updates to the existing statistics in the result GUI. If statistic key does not exist in GUI, create it - [JsonProperty(PropertyName = "Statistics", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public IDictionary Statistics; /// /// Runtime banner/updating statistics in the title banner of the live algorithm GUI. /// - [JsonProperty(PropertyName = "RuntimeStatistics", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public IDictionary RuntimeStatistics; /// /// State of the result packet. /// - [JsonProperty(PropertyName = "State", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public IDictionary State; /// /// Server status information, including CPU/RAM usage, ect... /// - [JsonProperty(PropertyName = "ServerStatistics", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public IDictionary ServerStatistics; /// /// The algorithm's configuration required for report generation /// - [JsonProperty(PropertyName = "AlgorithmConfiguration", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public AlgorithmConfiguration AlgorithmConfiguration; /// diff --git a/Common/Util/SeriesJsonConverter.cs b/Common/Util/SeriesJsonConverter.cs index 4f22315bfa25..0ef5c7850486 100644 --- a/Common/Util/SeriesJsonConverter.cs +++ b/Common/Util/SeriesJsonConverter.cs @@ -27,6 +27,8 @@ namespace QuantConnect.Util /// public class SeriesJsonConverter : JsonConverter { + private ColorJsonConverter _colorJsonConverter = new (); + /// /// Write Series to Json /// @@ -43,30 +45,30 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s writer.WriteStartObject(); - writer.WritePropertyName("Name"); + writer.WritePropertyName("name"); writer.WriteValue(baseSeries.Name); - writer.WritePropertyName("Unit"); + writer.WritePropertyName("unit"); writer.WriteValue(baseSeries.Unit); - writer.WritePropertyName("Index"); + writer.WritePropertyName("index"); writer.WriteValue(baseSeries.Index); - writer.WritePropertyName("SeriesType"); + writer.WritePropertyName("seriesType"); writer.WriteValue(baseSeries.SeriesType); if (baseSeries.ZIndex.HasValue) { - writer.WritePropertyName("ZIndex"); + writer.WritePropertyName("zIndex"); writer.WriteValue(baseSeries.ZIndex.Value); } if (baseSeries.IndexName != null) { - writer.WritePropertyName("IndexName"); + writer.WritePropertyName("indexName"); writer.WriteValue(baseSeries.IndexName); } if (baseSeries.Tooltip != null) { - writer.WritePropertyName("Tooltip"); + writer.WritePropertyName("tooltip"); writer.WriteValue(baseSeries.Tooltip); } @@ -85,18 +87,18 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s } // have to add the converter we want to use, else will use default - serializer.Converters.Add(new ColorJsonConverter()); + serializer.Converters.Add(_colorJsonConverter); - writer.WritePropertyName("Values"); + writer.WritePropertyName("values"); serializer.Serialize(writer, values); - writer.WritePropertyName("Color"); + writer.WritePropertyName("color"); serializer.Serialize(writer, series.Color); - writer.WritePropertyName("ScatterMarkerSymbol"); + writer.WritePropertyName("scatterMarkerSymbol"); serializer.Serialize(writer, series.ScatterMarkerSymbol); break; default: - writer.WritePropertyName("Values"); + writer.WritePropertyName("values"); serializer.Serialize(writer, (value as BaseSeries).Values); break; } @@ -114,7 +116,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist var name = (jObject["Name"] ?? jObject["name"]).Value(); var unit = (jObject["Unit"] ?? jObject["unit"]).Value(); var index = (jObject["Index"] ?? jObject["index"]).Value(); - var seriesType = (SeriesType)((jObject["SeriesType"] ?? jObject["seriesType"]).Value()); + var seriesType = (SeriesType)(jObject["SeriesType"] ?? jObject["seriesType"]).Value(); var values = (JArray)(jObject["Values"] ?? jObject["values"]); var zindex = jObject.TryGetPropertyValue("ZIndex") ?? jObject.TryGetPropertyValue("zIndex"); diff --git a/Engine/Results/BaseResultsHandler.cs b/Engine/Results/BaseResultsHandler.cs index 7dd875ba00c5..bda0041cc1cf 100644 --- a/Engine/Results/BaseResultsHandler.cs +++ b/Engine/Results/BaseResultsHandler.cs @@ -21,6 +21,7 @@ using System.Linq; using System.Threading; using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; using QuantConnect.Configuration; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; @@ -82,6 +83,21 @@ public abstract class BaseResultsHandler /// protected int LastDeltaOrderEventsPosition; + /// + /// Serializer settings to use + /// + protected JsonSerializerSettings SerializerSettings { get; set; } = new () + { + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy + { + ProcessDictionaryKeys = false, + OverrideSpecifiedNames = true + } + } + }; + /// /// The current aggregated equity bar for sampling. /// It will be aggregated with values from the @@ -238,11 +254,6 @@ protected Bar CurrentAlgorithmEquity /// protected string ResultsDestinationFolder; - /// - /// The order event json converter instance to use - /// - protected OrderEventJsonConverter OrderEventJsonConverter { get; set; } - /// /// The map file provider instance to use /// @@ -278,7 +289,7 @@ protected BaseResultsHandler() ["OrderCount"] = "0", ["InsightCount"] = "0" }; - _previousSalesVolume = new (2); + _previousSalesVolume = new(2); _previousSalesVolume.Add(0); _customSummaryStatistics = new(); } @@ -329,7 +340,7 @@ protected virtual void StoreOrderEvents(DateTime utcTime, List order var filename = $"{AlgorithmId}-order-events.json"; var path = GetResultsPath(filename); - var data = JsonConvert.SerializeObject(orderEvents, Formatting.None, OrderEventJsonConverter); + var data = JsonConvert.SerializeObject(orderEvents, Formatting.None, SerializerSettings); File.WriteAllText(path, data); } @@ -357,7 +368,7 @@ protected virtual void StoreInsights() directory.Create(); } var orderedInsights = allInsights.OrderBy(insight => insight.GeneratedTimeUtc); - File.WriteAllText(alphaResultsPath, JsonConvert.SerializeObject(orderedInsights, Formatting.Indented)); + File.WriteAllText(alphaResultsPath, JsonConvert.SerializeObject(orderedInsights, Formatting.Indented, SerializerSettings)); } } @@ -412,11 +423,23 @@ public virtual void Initialize(ResultHandlerInitializeParameters parameters) AlgorithmId = parameters.Job.AlgorithmId; ProjectId = parameters.Job.ProjectId; RamAllocation = parameters.Job.RamAllocation.ToStringInvariant(); - OrderEventJsonConverter = new OrderEventJsonConverter(AlgorithmId); _updateRunner = new Thread(Run, 0) { IsBackground = true, Name = "Result Thread" }; _updateRunner.Start(); State["Hostname"] = _hostName; MapFileProvider = parameters.MapFileProvider; + + SerializerSettings = new() + { + Converters = new [] { new OrderEventJsonConverter(AlgorithmId) }, + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy + { + ProcessDictionaryKeys = false, + OverrideSpecifiedNames = true + } + } + }; } /// @@ -463,7 +486,7 @@ public virtual string SaveLogs(string id, List logs) /// The results to save public virtual void SaveResults(string name, Result result) { - File.WriteAllText(GetResultsPath(name), JsonConvert.SerializeObject(result, Formatting.Indented)); + File.WriteAllText(GetResultsPath(name), JsonConvert.SerializeObject(result, Formatting.Indented, SerializerSettings)); } /// @@ -518,7 +541,7 @@ protected virtual decimal GetPortfolioValue() /// Time to resolve benchmark value at protected virtual decimal GetBenchmarkValue(DateTime time) { - if(Algorithm == null || Algorithm.Benchmark == null) + if (Algorithm == null || Algorithm.Benchmark == null) { // this could happen if the algorithm exploded mid initialization return 0; @@ -631,9 +654,9 @@ protected virtual void SampleDrawdown(DateTime time, decimal currentPortfolioVal /// Current equity value protected virtual void SamplePortfolioTurnover(DateTime time, decimal currentPortfolioValue) { - if(currentPortfolioValue != 0) + if (currentPortfolioValue != 0) { - if(Algorithm.StartDate == time.ConvertFromUtc(Algorithm.TimeZone)) + if (Algorithm.StartDate == time.ConvertFromUtc(Algorithm.TimeZone)) { // the first sample in backtesting is at start, we only want to sample after a full algorithm execution date return; @@ -816,7 +839,7 @@ protected void SetAlgorithmState(string error, string stack) /// protected Dictionary GetAlgorithmState(DateTime? endTime = null) { - if(Algorithm == null || !string.IsNullOrEmpty(State["RuntimeError"])) + if (Algorithm == null || !string.IsNullOrEmpty(State["RuntimeError"])) { State["Status"] = AlgorithmStatus.RuntimeError.ToStringInvariant(); } diff --git a/Engine/Results/LiveTradingResultHandler.cs b/Engine/Results/LiveTradingResultHandler.cs index 0d42862abb8b..590e1feadeb3 100644 --- a/Engine/Results/LiveTradingResultHandler.cs +++ b/Engine/Results/LiveTradingResultHandler.cs @@ -398,7 +398,7 @@ protected override void StoreOrderEvents(DateTime utcTime, List orde var filename = $"{AlgorithmId}-{utcTime:yyyy-MM-dd}-order-events.json"; var path = GetResultsPath(filename); - var data = JsonConvert.SerializeObject(orderEvents, Formatting.None, OrderEventJsonConverter); + var data = JsonConvert.SerializeObject(orderEvents, Formatting.None, SerializerSettings); File.WriteAllText(path, data); } diff --git a/Optimizer/OptimizationNodePacket.cs b/Optimizer/OptimizationNodePacket.cs index 6593a8977c2e..d399435e5ba9 100644 --- a/Optimizer/OptimizationNodePacket.cs +++ b/Optimizer/OptimizationNodePacket.cs @@ -31,83 +31,70 @@ public class OptimizationNodePacket : Packet /// /// User Id placing request /// - [JsonProperty(PropertyName = "userId")] public int UserId; /// User API Token - [JsonProperty(PropertyName = "userToken")] public string UserToken = ""; /// /// Project Id of the request /// - [JsonProperty(PropertyName = "projectId")] public int ProjectId; /// /// Unique compile id of this optimization /// - [JsonProperty(PropertyName = "compileId")] public string CompileId = ""; /// /// The unique optimization Id of the request /// - [JsonProperty(PropertyName = "optimizationId")] public string OptimizationId = ""; /// /// Organization Id of the request /// - [JsonProperty(PropertyName = "organizationId")] public string OrganizationId = ""; /// /// Limit for the amount of concurrent backtests being run /// - [JsonProperty(PropertyName = "maximumConcurrentBacktests")] public int MaximumConcurrentBacktests; /// /// Optimization strategy name /// - [JsonProperty(PropertyName = "optimizationStrategy")] public string OptimizationStrategy = "QuantConnect.Optimizer.Strategies.GridSearchOptimizationStrategy"; /// /// Objective settings /// - [JsonProperty(PropertyName = "criterion")] public Target Criterion; /// /// Optimization constraints /// - [JsonProperty(PropertyName = "constraints")] public IReadOnlyList Constraints; /// /// The user optimization parameters /// - [JsonProperty(PropertyName = "optimizationParameters")] public HashSet OptimizationParameters; /// /// The user optimization parameters /// - [JsonProperty(PropertyName = "optimizationStrategySettings", TypeNameHandling = TypeNameHandling.All)] + [JsonProperty(TypeNameHandling = TypeNameHandling.All)] public OptimizationStrategySettings OptimizationStrategySettings; /// /// Backtest out of sample maximum end date /// - [JsonProperty(PropertyName = "outOfSampleMaxEndDate")] public DateTime? OutOfSampleMaxEndDate; /// /// The backtest out of sample day count /// - [JsonProperty(PropertyName = "outOfSampleDays")] public int OutOfSampleDays; /// diff --git a/Report/NullResultValueTypeJsonConverter.cs b/Report/NullResultValueTypeJsonConverter.cs index 3e936d9a659d..914f9299cd9c 100644 --- a/Report/NullResultValueTypeJsonConverter.cs +++ b/Report/NullResultValueTypeJsonConverter.cs @@ -65,12 +65,12 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist return null; } - foreach (JProperty property in token["Charts"].Children()) + foreach (JProperty property in GetProperty(token, "Charts").Children()) { - foreach (JProperty seriesProperty in property.Value["Series"]) + foreach (JProperty seriesProperty in GetProperty(property.Value, "Series")) { var newValues = new List(); - foreach (var entry in seriesProperty.Value["Values"]) + foreach (var entry in GetProperty(seriesProperty.Value, "Values")) { if (entry is JObject jobj && (jobj["x"] == null || jobj["x"].Value() == null || @@ -89,7 +89,16 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist newValues.Add(entry); } - token["Charts"][property.Name]["Series"][seriesProperty.Name]["Values"] = JArray.FromObject(newValues); + var chart = GetProperty(token, "Charts")[property.Name]; + var series = GetProperty(chart, "Series")[seriesProperty.Name]; + if (series["Values"] != null) + { + series["Values"] = JArray.FromObject(newValues); + } + else if (series["values"] != null) + { + series["values"] = JArray.FromObject(newValues); + } } } @@ -106,5 +115,10 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s { throw new NotImplementedException(); } + + private static JToken GetProperty(JToken jToken, string name) + { + return jToken[name] ?? jToken[name.ToLower()]; + } } } diff --git a/Report/PortfolioLooper/PortfolioLooperAlgorithm.cs b/Report/PortfolioLooper/PortfolioLooperAlgorithm.cs index 6c28fb7781e5..829f1ffa326f 100644 --- a/Report/PortfolioLooper/PortfolioLooperAlgorithm.cs +++ b/Report/PortfolioLooper/PortfolioLooperAlgorithm.cs @@ -87,7 +87,7 @@ public override void Initialize() if (_algorithmConfiguration != null) { SetAccountCurrency(_algorithmConfiguration.AccountCurrency); - SetBrokerageModel(_algorithmConfiguration.BrokerageName, _algorithmConfiguration.AccountType); + SetBrokerageModel(_algorithmConfiguration.Brokerage, _algorithmConfiguration.AccountType); } SetCash(_startingCash); diff --git a/Tests/Algorithm/Framework/Alphas/Serialization/InsightJsonConverterTests.cs b/Tests/Algorithm/Framework/Alphas/Serialization/InsightJsonConverterTests.cs index 5237aef49b7c..9579f96aeb7c 100644 --- a/Tests/Algorithm/Framework/Alphas/Serialization/InsightJsonConverterTests.cs +++ b/Tests/Algorithm/Framework/Alphas/Serialization/InsightJsonConverterTests.cs @@ -16,6 +16,7 @@ using System; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; using NUnit.Framework; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Alphas.Serialization; @@ -25,11 +26,23 @@ namespace QuantConnect.Tests.Algorithm.Framework.Alphas.Serialization [TestFixture, Parallelizable(ParallelScope.All)] public class InsightJsonConverterTests { + private JsonSerializerSettings _serializerSettings = new() + { + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy + { + ProcessDictionaryKeys = false, + OverrideSpecifiedNames = true + } + } + }; + [Test] public void DeserializesInsightWithoutScore() { - var jObject = JObject.Parse(jsonNoScore); - var result = JsonConvert.DeserializeObject(jsonNoScore); + var jObject = JObject.Parse(jsonNoScoreBackwardsCompatible); + var result = JsonConvert.DeserializeObject(jsonNoScoreBackwardsCompatible); Assert.AreEqual(jObject["id"].Value(), result.Id.ToStringInvariant("N")); Assert.AreEqual(jObject["source-model"].Value(), result.SourceModel); Assert.AreEqual(jObject["group-id"]?.Value(), result.GroupId?.ToStringInvariant("N")); @@ -54,8 +67,8 @@ public void DeserializesInsightWithoutScore() [Test] public void DeserializesInsightWithScore() { - var jObject = JObject.Parse(jsonWithScore); - var result = JsonConvert.DeserializeObject(jsonWithScore); + var jObject = JObject.Parse(jsonWithScoreBackwardsCompatible); + var result = JsonConvert.DeserializeObject(jsonWithScoreBackwardsCompatible); Assert.AreEqual(jObject["id"].Value(), result.Id.ToStringInvariant("N")); Assert.AreEqual(jObject["source-model"].Value(), result.SourceModel); Assert.AreEqual(jObject["group-id"]?.Value(), result.GroupId?.ToStringInvariant("N")); @@ -75,141 +88,79 @@ public void DeserializesInsightWithScore() Assert.AreEqual(jObject["reference-final"].Value(), result.ReferenceValueFinal); } - [Test] - public void SerializesInsightWithoutScore() + [TestCase(true)] + [TestCase(false)] + public void SerializesInsightWithoutScore(bool backwardsCompatible) { - var jObject = JObject.Parse(jsonNoScore); - var insight = Insight.FromSerializedInsight(new SerializedInsight - { - Id = jObject["id"].Value(), - SourceModel = jObject["source-model"].Value(), - GroupId = jObject["group-id"]?.Value(), - CreatedTime = jObject["created-time"].Value(), - CloseTime = jObject["close-time"].Value(), - Symbol = jObject["symbol"].Value(), - Ticker = jObject["ticker"].Value(), - Type = (InsightType)Enum.Parse(typeof(InsightType), jObject["type"].Value(), true), - ReferenceValue = jObject["reference"].Value(), - Direction = (InsightDirection)Enum.Parse(typeof(InsightDirection), jObject["direction"].Value(), true), - Period = jObject["period"].Value(), - Magnitude = jObject["magnitude"].Value() - }); - var result = JsonConvert.SerializeObject(insight, Formatting.None); - Assert.AreEqual(jsonNoScore, result); + var serializedInsight = JsonConvert.DeserializeObject(backwardsCompatible ? jsonNoScoreBackwardsCompatible : jsonNoScore2); + var insight = Insight.FromSerializedInsight(serializedInsight); + var result = JsonConvert.SerializeObject(insight, Formatting.None, _serializerSettings); + Assert.AreEqual(jsonNoScore2, result); } - [Test] - public void SerializesInsightWithScore() + [TestCase(true)] + [TestCase(false)] + public void SerializesInsightWithScore(bool backwardsCompatible) { - var jObject = JObject.Parse(jsonWithScore); - var insight = Insight.FromSerializedInsight(new SerializedInsight - { - Id = jObject["id"].Value(), - SourceModel = jObject["source-model"].Value(), - GroupId = jObject["group-id"]?.Value(), - CreatedTime = jObject["created-time"].Value(), - CloseTime = jObject["close-time"].Value(), - Symbol = jObject["symbol"].Value(), - Ticker = jObject["ticker"].Value(), - Type = (InsightType)Enum.Parse(typeof(InsightType), jObject["type"].Value(), true), - ReferenceValue = jObject["reference"].Value(), - Direction = (InsightDirection)Enum.Parse(typeof(InsightDirection), jObject["direction"].Value(), true), - Period = jObject["period"].Value(), - Magnitude = jObject["magnitude"].Value(), - ScoreIsFinal = jObject["score-final"].Value(), - ScoreMagnitude = jObject["score-magnitude"].Value(), - ScoreDirection = jObject["score-direction"].Value(), - EstimatedValue = jObject["estimated-value"].Value(), - ReferenceValueFinal = jObject["reference-final"].Value() - }); - var result = JsonConvert.SerializeObject(insight, Formatting.None); + var serializedInsight = JsonConvert.DeserializeObject(backwardsCompatible ? jsonWithScoreBackwardsCompatible : jsonWithScore); + var insight = Insight.FromSerializedInsight(serializedInsight); + var result = JsonConvert.SerializeObject(insight, Formatting.None, _serializerSettings); Assert.AreEqual(jsonWithScore, result); } [Test] public void SerializesOldInsightWithMissingCreatedTime() { - var serializedInsight = JsonConvert.DeserializeObject(jsonWithMissingCreatedTime); + var serializedInsight = JsonConvert.DeserializeObject(jsonWithMissingCreatedTimeBackwardsCompatible); var insight = Insight.FromSerializedInsight(serializedInsight); - var result = JsonConvert.SerializeObject(insight, Formatting.None); + var result = JsonConvert.SerializeObject(insight, Formatting.None, _serializerSettings); Assert.AreEqual(serializedInsight.CreatedTime, serializedInsight.GeneratedTime); Assert.AreEqual(jsonWithExpectedOutputFromMissingCreatedTimeValue, result); } - [Test] - public void SerializesInsightWithTag() + + [TestCase(true)] + [TestCase(false)] + public void SerializesInsightWithTag(bool backwardsCompatible) { - var jObject = JObject.Parse(jsonWithTag); - var insight = Insight.FromSerializedInsight(new SerializedInsight - { - Id = jObject["id"].Value(), - GroupId = jObject["group-id"]?.Value(), - SourceModel = jObject["source-model"].Value(), - GeneratedTime = jObject["generated-time"].Value(), - CreatedTime = jObject["created-time"].Value(), - CloseTime = jObject["close-time"].Value(), - Symbol = jObject["symbol"].Value(), - Ticker = jObject["ticker"].Value(), - Type = (InsightType)Enum.Parse(typeof(InsightType), jObject["type"].Value(), true), - ReferenceValue = jObject["reference"].Value(), - ReferenceValueFinal = jObject["reference-final"].Value(), - Direction = (InsightDirection)Enum.Parse(typeof(InsightDirection), jObject["direction"].Value(), true), - Period = jObject["period"].Value(), - ScoreIsFinal = jObject["score-final"].Value(), - ScoreMagnitude = jObject["score-magnitude"].Value(), - ScoreDirection = jObject["score-direction"].Value(), - EstimatedValue = jObject["estimated-value"].Value(), - Tag = jObject["tag"].Value() - }); - var result = JsonConvert.SerializeObject(insight, Formatting.None); + var serializedInsight = JsonConvert.DeserializeObject(backwardsCompatible ? jsonWithTagBackwardsCompatible : jsonWithTag); + var insight = Insight.FromSerializedInsight(serializedInsight); + var result = JsonConvert.SerializeObject(insight, Formatting.None, _serializerSettings); Assert.AreEqual(jsonWithTag, result); } - [Test] - public void SerializesInsightWithoutTag() + [TestCase(true)] + [TestCase(false)] + public void SerializesInsightWithoutTag(bool backwardsCompatible) { - var jObject = JObject.Parse(jsonWithoutTag); - var insight = Insight.FromSerializedInsight(new SerializedInsight - { - Id = jObject["id"].Value(), - GroupId = jObject["group-id"]?.Value(), - SourceModel = jObject["source-model"].Value(), - GeneratedTime = jObject["generated-time"].Value(), - CreatedTime = jObject["created-time"].Value(), - CloseTime = jObject["close-time"].Value(), - Symbol = jObject["symbol"].Value(), - Ticker = jObject["ticker"].Value(), - Type = (InsightType)Enum.Parse(typeof(InsightType), jObject["type"].Value(), true), - ReferenceValue = jObject["reference"].Value(), - ReferenceValueFinal = jObject["reference-final"].Value(), - Direction = (InsightDirection)Enum.Parse(typeof(InsightDirection), jObject["direction"].Value(), true), - Period = jObject["period"].Value(), - ScoreIsFinal = jObject["score-final"].Value(), - ScoreMagnitude = jObject["score-magnitude"].Value(), - ScoreDirection = jObject["score-direction"].Value(), - EstimatedValue = jObject["estimated-value"].Value(), - }); - var result = JsonConvert.SerializeObject(insight, Formatting.None); + var serializedInsight = JsonConvert.DeserializeObject(backwardsCompatible ? jsonWithoutTagBackwardsCompatible : jsonWithoutTag); + var insight = Insight.FromSerializedInsight(serializedInsight); + var result = JsonConvert.SerializeObject(insight, Formatting.None, _serializerSettings); Assert.AreEqual(jsonWithoutTag, result); } [Test] public void DeserializesInsightWithTag() { - var jObject = JObject.Parse(jsonWithTag); - var result = JsonConvert.DeserializeObject(jsonWithTag); + var jObject = JObject.Parse(jsonWithTagBackwardsCompatible); + var result = JsonConvert.DeserializeObject(jsonWithTagBackwardsCompatible); Assert.AreEqual(jObject["tag"].Value(), result.Tag); } [Test] public void DeserializesInsightWithoutTag() { - var result = JsonConvert.DeserializeObject(jsonWithoutTag); + var result = JsonConvert.DeserializeObject(jsonWithoutTagBackwardsCompatible); Assert.IsNull(result.Tag); } - private const string jsonNoScore = + private string jsonNoScore2 = @"{""id"":""e02be50f56a8496b9ba995d19a904ada"",""groupId"":null,""sourceModel"":""mySourceModel-1"",""generatedTime"":1520711961.00055, +""createdTime"":1520711961.00055,""closeTime"":1520711961.00055,""symbol"":""BTCUSD XJ"",""ticker"":""BTCUSD"",""type"":""price"",""reference"":9143.53,""referenceValueFinal"":0.0, +""direction"":""up"",""period"":5.0,""magnitude"":""0.025"",""confidence"":null,""weight"":null,""scoreIsFinal"":false,""scoreMagnitude"":""0"",""scoreDirection"":""0"", +""estimatedValue"":""0"",""tag"":null}".ReplaceLineEndings(string.Empty); + + private const string jsonNoScoreBackwardsCompatible = "{" + "\"id\":\"e02be50f56a8496b9ba995d19a904ada\"," + "\"group-id\":null," + @@ -233,7 +184,11 @@ public void DeserializesInsightWithoutTag() "\"estimated-value\":\"0\"," + "\"tag\":null}"; - private const string jsonWithScore = + private string jsonWithScore = @"{""id"":""e02be50f56a8496b9ba995d19a904ada"",""groupId"":""a02be50f56a8496b9ba995d19a904ada"",""sourceModel"":""mySourceModel-1"", +""generatedTime"":1520711961.00055,""createdTime"":1520711961.00055,""closeTime"":1520711961.00055,""symbol"":""BTCUSD XJ"",""ticker"":""BTCUSD"",""type"":""price"", +""reference"":9143.53,""referenceValueFinal"":9243.53,""direction"":""up"",""period"":5.0,""magnitude"":""0.025"",""confidence"":null,""weight"":null, +""scoreIsFinal"":true,""scoreMagnitude"":""1"",""scoreDirection"":""1"",""estimatedValue"":""1113.2484"",""tag"":null}".ReplaceLineEndings(string.Empty); + private const string jsonWithScoreBackwardsCompatible = "{" + "\"id\":\"e02be50f56a8496b9ba995d19a904ada\"," + "\"group-id\":\"a02be50f56a8496b9ba995d19a904ada\"," + @@ -257,7 +212,7 @@ public void DeserializesInsightWithoutTag() "\"estimated-value\":\"1113.2484\"," + "\"tag\":null}"; - private const string jsonWithMissingCreatedTime = + private const string jsonWithMissingCreatedTimeBackwardsCompatible = "{" + "\"id\":\"e02be50f56a8496b9ba995d19a904ada\"," + "\"group-id\":\"a02be50f56a8496b9ba995d19a904ada\"," + @@ -280,31 +235,16 @@ public void DeserializesInsightWithoutTag() "\"estimated-value\":\"1113.2484\"," + "\"tag\":null}"; - private const string jsonWithExpectedOutputFromMissingCreatedTimeValue = - "{" + - "\"id\":\"e02be50f56a8496b9ba995d19a904ada\"," + - "\"group-id\":\"a02be50f56a8496b9ba995d19a904ada\"," + - "\"source-model\":\"mySourceModel-1\"," + - "\"generated-time\":1520711961.00055," + - "\"created-time\":1520711961.00055," + - "\"close-time\":1520711961.00055," + - "\"symbol\":\"BTCUSD XJ\"," + - "\"ticker\":\"BTCUSD\"," + - "\"type\":\"price\"," + - "\"reference\":9143.53," + - "\"reference-final\":9243.53," + - "\"direction\":\"up\"," + - "\"period\":5.0," + - "\"magnitude\":\"0.025\"," + - "\"confidence\":null," + - "\"weight\":null," + - "\"score-final\":true," + - "\"score-magnitude\":\"1\"," + - "\"score-direction\":\"1\"," + - "\"estimated-value\":\"1113.2484\"," + - "\"tag\":null}"; + private string jsonWithExpectedOutputFromMissingCreatedTimeValue = @"{""id"":""e02be50f56a8496b9ba995d19a904ada"",""groupId"":""a02be50f56a8496b9ba995d19a904ada"", +""sourceModel"":""mySourceModel-1"",""generatedTime"":1520711961.00055,""createdTime"":1520711961.00055,""closeTime"":1520711961.00055,""symbol"":""BTCUSD XJ"",""ticker"": +""BTCUSD"",""type"":""price"",""reference"":9143.53,""referenceValueFinal"":9243.53,""direction"":""up"",""period"":5.0,""magnitude"":""0.025"",""confidence"":null, +""weight"":null,""scoreIsFinal"":true,""scoreMagnitude"":""1"",""scoreDirection"":""1"",""estimatedValue"":""1113.2484"",""tag"":null}".ReplaceLineEndings(string.Empty); - private const string jsonWithTag = + private string jsonWithTag = @"{""id"":""e02be50f56a8496b9ba995d19a904ada"",""groupId"":""a02be50f56a8496b9ba995d19a904ada"",""sourceModel"":""mySourceModel-1"", +""generatedTime"":1520711961.00055,""createdTime"":1520711961.00055,""closeTime"":1520711961.00055,""symbol"":""BTCUSD XJ"",""ticker"":""BTCUSD"",""type"": +""price"",""reference"":9143.53,""referenceValueFinal"":9243.53,""direction"":""up"",""period"":5.0,""magnitude"":null,""confidence"":null,""weight"":null, +""scoreIsFinal"":true,""scoreMagnitude"":""1"",""scoreDirection"":""1"",""estimatedValue"":""1113.2484"",""tag"":""additional information""}".ReplaceLineEndings(string.Empty); + private const string jsonWithTagBackwardsCompatible = "{" + "\"id\":\"e02be50f56a8496b9ba995d19a904ada\"," + "\"group-id\":\"a02be50f56a8496b9ba995d19a904ada\"," + @@ -328,7 +268,12 @@ public void DeserializesInsightWithoutTag() "\"estimated-value\":\"1113.2484\"," + "\"tag\":\"additional information\"}"; - private const string jsonWithoutTag = + private string jsonWithoutTag = @"{""id"":""e02be50f56a8496b9ba995d19a904ada"",""groupId"":""a02be50f56a8496b9ba995d19a904ada"", +""sourceModel"":""mySourceModel-1"",""generatedTime"":1520711961.00055,""createdTime"":1520711961.00055,""closeTime"":1520711961.00055,""symbol"":""BTCUSD XJ"", +""ticker"":""BTCUSD"",""type"":""price"",""reference"":9143.53,""referenceValueFinal"":9243.53,""direction"":""up"",""period"":5.0,""magnitude"":null, +""confidence"":null,""weight"":null,""scoreIsFinal"":true,""scoreMagnitude"":""1"",""scoreDirection"":""1"",""estimatedValue"":""1113.2484"",""tag"":null}".ReplaceLineEndings(string.Empty); + + private const string jsonWithoutTagBackwardsCompatible = "{" + "\"id\":\"e02be50f56a8496b9ba995d19a904ada\"," + "\"group-id\":\"a02be50f56a8496b9ba995d19a904ada\"," + diff --git a/Tests/Common/AlgorithmConfigurationTests.cs b/Tests/Common/AlgorithmConfigurationTests.cs index e7cf93a5caea..da8be85c3901 100644 --- a/Tests/Common/AlgorithmConfigurationTests.cs +++ b/Tests/Common/AlgorithmConfigurationTests.cs @@ -22,6 +22,7 @@ using QuantConnect.Packets; using QuantConnect.Algorithm; using QuantConnect.Brokerages; +using Newtonsoft.Json.Serialization; namespace QuantConnect.Tests.Common { @@ -41,13 +42,14 @@ public void CreatesConfiguration(string currency, BrokerageName brokerageName, A var algorithmConfiguration = AlgorithmConfiguration.Create(algorithm, null); Assert.AreEqual(currency, algorithmConfiguration.AccountCurrency); - Assert.AreEqual(brokerageName, algorithmConfiguration.BrokerageName); + Assert.AreEqual(brokerageName, algorithmConfiguration.Brokerage); Assert.AreEqual(accountType, algorithmConfiguration.AccountType); CollectionAssert.AreEquivalent(parameters, algorithmConfiguration.Parameters); } - [Test] - public void JsonRoundtrip() + [TestCase(true)] + [TestCase(false)] + public void JsonRoundtrip(bool backwardsCompatible) { var algorithm = new QCAlgorithm(); algorithm.SetName("Backtest name"); @@ -64,11 +66,30 @@ public void JsonRoundtrip() }; var algorithmConfiguration = AlgorithmConfiguration.Create(algorithm, backtestNode); - var serialized = JsonConvert.SerializeObject(algorithmConfiguration); - - Assert.AreEqual($"{{\"Name\":\"Backtest name\",\"Tags\":[\"tag1\",\"tag2\"],\"AccountCurrency\":\"GBP\",\"Brokerage\":32," + + var settings = new JsonSerializerSettings() + { + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy + { + ProcessDictionaryKeys = false, + OverrideSpecifiedNames = true + } + } + }; + var serialized = JsonConvert.SerializeObject(algorithmConfiguration, settings); + if (backwardsCompatible) + { + serialized = $"{{\"Name\":\"Backtest name\",\"Tags\":[\"tag1\",\"tag2\"],\"AccountCurrency\":\"GBP\",\"Brokerage\":32," + $"\"AccountType\":1,\"Parameters\":{{\"a\":\"A\",\"b\":\"B\"}},\"OutOfSampleMaxEndDate\":\"2023-01-01T00:00:00\"," + - $"\"OutOfSampleDays\":30,\"StartDate\":\"1998-01-01 00:00:00\",\"EndDate\":\"{algorithm.EndDate.ToString(DateFormat.UI)}\",\"TradingDaysPerYear\":252}}", serialized); + $"\"OutOfSampleDays\":30,\"StartDate\":\"1998-01-01 00:00:00\",\"EndDate\":\"{algorithm.EndDate.ToString(DateFormat.UI)}\",\"TradingDaysPerYear\":252}}"; + } + else + { + Assert.AreEqual($"{{\"name\":\"Backtest name\",\"tags\":[\"tag1\",\"tag2\"],\"accountCurrency\":\"GBP\",\"brokerage\":32," + + $"\"accountType\":1,\"parameters\":{{\"a\":\"A\",\"b\":\"B\"}},\"outOfSampleMaxEndDate\":\"2023-01-01T00:00:00\"," + + $"\"outOfSampleDays\":30,\"startDate\":\"1998-01-01 00:00:00\",\"endDate\":\"{algorithm.EndDate.ToString(DateFormat.UI)}\",\"tradingDaysPerYear\":252}}", serialized); + } var deserialize = JsonConvert.DeserializeObject(serialized); @@ -76,7 +97,7 @@ public void JsonRoundtrip() Assert.AreEqual(algorithmConfiguration.Parameters, deserialize.Parameters); Assert.AreEqual(algorithmConfiguration.AccountCurrency, deserialize.AccountCurrency); Assert.AreEqual(algorithmConfiguration.AccountType, deserialize.AccountType); - Assert.AreEqual(algorithmConfiguration.BrokerageName, deserialize.BrokerageName); + Assert.AreEqual(algorithmConfiguration.Brokerage, deserialize.Brokerage); var expected = new DateTime(algorithm.EndDate.Year, algorithm.EndDate.Month, algorithm.EndDate.Day, algorithm.EndDate.Hour, algorithm.EndDate.Minute, algorithm.EndDate.Second); Assert.AreEqual(expected, deserialize.EndDate); Assert.AreEqual(algorithmConfiguration.OutOfSampleDays, deserialize.OutOfSampleDays); diff --git a/Tests/Common/Util/SeriesJsonConverterTests.cs b/Tests/Common/Util/SeriesJsonConverterTests.cs index b3c02a47257a..625663c76faa 100644 --- a/Tests/Common/Util/SeriesJsonConverterTests.cs +++ b/Tests/Common/Util/SeriesJsonConverterTests.cs @@ -20,6 +20,7 @@ using NUnit.Framework; using QuantConnect.Util; using System.Collections.Generic; +using Newtonsoft.Json.Serialization; namespace QuantConnect.Tests.Common.Util { @@ -148,6 +149,30 @@ public void DeserializeChartPointObject() Assert.AreEqual(series.ScatterMarkerSymbol, result.ScatterMarkerSymbol); } + [Test] + public void DeserializeUpperCaseChartPoint() + { + var date = new DateTime(2050, 1, 1, 1, 1, 1); + var date2 = date.AddSeconds(1); + var series = new Series("Pepito Grillo", SeriesType.Bar, "$", Color.Empty, ScatterMarkerSymbol.Diamond); + series.AddPoint(date, 1); + series.AddPoint(new ChartPoint(date2, null)); + + var result = (Series)JsonConvert.DeserializeObject("{\"Name\":\"Pepito Grillo\",\"Unit\":\"$\",\"Index\":0,\"SeriesType\":3," + + "\"Values\":[[2524611661,1.0],[2524611662,null]],\"Color\":\"\",\"ScatterMarkerSymbol\":\"diamond\"}", typeof(Series)); + + Assert.AreEqual(2, result.Values.Count); + Assert.AreEqual(date, ((ChartPoint)result.Values[0]).Time); + Assert.AreEqual(1, ((ChartPoint)result.Values[0]).y); + Assert.AreEqual(date2, ((ChartPoint)result.Values[1]).Time); + Assert.AreEqual(null, ((ChartPoint)result.Values[1]).y); + Assert.AreEqual(series.Name, result.Name); + Assert.AreEqual(series.Unit, result.Unit); + Assert.AreEqual(series.SeriesType, result.SeriesType); + Assert.AreEqual(series.Color.ToArgb(), result.Color.ToArgb()); + Assert.AreEqual(series.ScatterMarkerSymbol, result.ScatterMarkerSymbol); + } + [Test] public void NullChartPointValue() { @@ -160,8 +185,8 @@ public void NullChartPointValue() var serializedSeries = JsonConvert.SerializeObject(series); var result = (Series)JsonConvert.DeserializeObject(serializedSeries, typeof(Series)); - Assert.AreEqual("{\"Name\":\"Pepito Grillo\",\"Unit\":\"$\",\"Index\":0,\"SeriesType\":3," + - "\"Values\":[[2524611661,1.0],[2524611662,null]],\"Color\":\"\",\"ScatterMarkerSymbol\":\"diamond\"}", serializedSeries); + Assert.AreEqual("{\"name\":\"Pepito Grillo\",\"unit\":\"$\",\"index\":0,\"seriesType\":3," + + "\"values\":[[2524611661,1.0],[2524611662,null]],\"color\":\"\",\"scatterMarkerSymbol\":\"diamond\"}", serializedSeries); Assert.AreEqual(2, result.Values.Count); Assert.AreEqual(date, ((ChartPoint)result.Values[0]).Time); Assert.AreEqual(1, ((ChartPoint)result.Values[0]).y); @@ -174,6 +199,33 @@ public void NullChartPointValue() Assert.AreEqual(series.ScatterMarkerSymbol, result.ScatterMarkerSymbol); } + [Test] + public void DeserializeUpperCaseCandleStick() + { + var date = new DateTime(2050, 1, 1, 1, 1, 1); + var series = new CandlestickSeries("Pepito Grillo"); + series.AddPoint(date, 100, 110, 80, 90); + series.AddPoint(new Candlestick(date.AddSeconds(1), null, null, null, null)); + + var result = (CandlestickSeries)JsonConvert.DeserializeObject("{\"Name\":\"Pepito Grillo\",\"Unit\":\"$\",\"Index\":0,\"SeriesType\":2," + + "\"Values\":[[2524611661,100.0,110.0,80.0,90.0],[2524611662,null,null,null,null]]}", typeof(CandlestickSeries)); + + Assert.AreEqual(series.Values.Count, result.Values.Count); + var values = series.GetValues().ToList(); + var resultValues = result.GetValues().ToList(); + for (var i = 0; i < values.Count; i++) + { + Assert.AreEqual(values[i].Time, resultValues[i].Time); + Assert.AreEqual(values[i].Open, resultValues[i].Open); + Assert.AreEqual(values[i].High, resultValues[i].High); + Assert.AreEqual(values[i].Low, resultValues[i].Low); + Assert.AreEqual(values[i].Close, resultValues[i].Close); + } + Assert.AreEqual(series.Name, result.Name); + Assert.AreEqual(series.Unit, result.Unit); + Assert.AreEqual(series.SeriesType, result.SeriesType); + } + [Test] public void NullCandleStickValue() { @@ -185,7 +237,7 @@ public void NullCandleStickValue() var serializedSeries = JsonConvert.SerializeObject(series); var result = (CandlestickSeries)JsonConvert.DeserializeObject(serializedSeries, typeof(CandlestickSeries)); - Assert.AreEqual("{\"Name\":\"Pepito Grillo\",\"Unit\":\"$\",\"Index\":0,\"SeriesType\":2,\"Values\":[[2524611661,100.0,110.0,80.0,90.0],[2524611662,null,null,null,null]]}", serializedSeries); + Assert.AreEqual("{\"name\":\"Pepito Grillo\",\"unit\":\"$\",\"index\":0,\"seriesType\":2,\"values\":[[2524611661,100.0,110.0,80.0,90.0],[2524611662,null,null,null,null]]}", serializedSeries); Assert.AreEqual(series.Values.Count, result.Values.Count); var values = series.GetValues().ToList(); var resultValues = result.GetValues().ToList(); @@ -211,7 +263,7 @@ public void HandlesAnyBaseSeries() var serializedSeries = JsonConvert.SerializeObject(testSeries); - Assert.AreEqual("{\"Name\":null,\"Unit\":\"$\",\"Index\":0,\"SeriesType\":0,\"Values\":[{\"Time\":\"2050-01-01T01:01:01\",\"Property\":\"Pepe\"}]}", serializedSeries); + Assert.AreEqual("{\"name\":null,\"unit\":\"$\",\"index\":0,\"seriesType\":0,\"values\":[{\"time\":\"2050-01-01T01:01:01\",\"property\":\"Pepe\"}]}", serializedSeries); } private class TestSeries : BaseSeries @@ -238,7 +290,9 @@ public override void AddPoint(DateTime time, List values) private class TestPoint : ISeriesPoint { + [JsonProperty("time")] public DateTime Time { get; set; } + [JsonProperty("property")] public string Property { get; set;} public ISeriesPoint Clone() diff --git a/Tests/Report/ResultDeserializationTests.cs b/Tests/Report/ResultDeserializationTests.cs index 1de8a956f67a..5ce441ec95cd 100644 --- a/Tests/Report/ResultDeserializationTests.cs +++ b/Tests/Report/ResultDeserializationTests.cs @@ -147,10 +147,11 @@ public class ResultDeserializationTests 'OrderIds': [1, 2, 3] } }}"; - [Test] - public void ValidBacktestResultDefaultSerializer() + [TestCase("charts")] + [TestCase("Charts")] + public void ValidBacktestResultDefaultSerializer(string chartKey) { - var result = JsonConvert.DeserializeObject(ValidBacktestResultJson2).Backtest; + var result = JsonConvert.DeserializeObject(ValidBacktestResultJson2.Replace("charts", chartKey, StringComparison.InvariantCulture)).Backtest; Assert.AreEqual(7, result.Charts.Count); Assert.IsTrue(result.Charts.Where(x => x.Key == "Drawdown").All(kvp => !kvp.Value.IsEmpty()));