/* * 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 NUnit.Framework; using QuantConnect.Api; using QuantConnect.Orders; using QuantConnect.Packets; using QuantConnect.Report; using QuantConnect.Report.ReportElements; using System; using System.Collections.Generic; using System.Linq; using static QuantConnect.Report.Report; namespace QuantConnect.Tests.Report { [TestFixture] public class ResultDeserializationTests { private const string OrderStringReplace = "{{orderStringReplace}}"; private const string OrderTypeStringReplace = "{{marketOrderType}}"; private const string EmptyJson = "{}"; private const string ValidBacktestResultJson2 = "{\"backtest\":{\"note\":null,\"name\":\"Emotional Black Mosquito\",\"organizationId\":99568,\"projectId\":15863659,\"completed\":true,\"optimizationId\":null,\"backtestId\"" + ":\"2d5e2342b73ffb04bfa6ff4a505e3cad\",\"tradeableDates\":381,\"researchGuide\":{\"minutes\":1,\"backtestCount\":1,\"parameters\":0},\"backtestStart\":\"2022-03-10 00:00:00\",\"backtestEnd\":\"2023-09-14 23:59:59\"," + "\"created\":\"2023-09-15 16:23:31\",\"snapshotId\":15863663,\"status\":\"Completed.\",\"error\":null,\"stacktrace\":null,\"progress\":1,\"profitLoss\":[],\"hasInitializeError\":false,\"charts\":{\"Portfolio Turnover\":" + "{\"Name\":\"Portfolio Turnover\"},\"Drawdown\":{\"Name\":\"Drawdown\",\"ChartType\":0,\"Series\":{\"Equity Drawdown\":{\"Name\":\"Equity Drawdown\",\"Unit\":\"%\",\"Index\":0,\"SeriesType\":0,\"Values\":[{\"x\":1646888400,\"y\":0}" + ",{\"x\":1646974800,\"y\":0}],\"Color\":\"\",\"ScatterMarkerSymbol\":\"none\"}}},\"Exposure\":{\"Name\":\"Exposure\"},\"Universe Analytics\":{\"Name\":\"Universe Analytics\"},\"Strategy Equity\":{\"Name\":\"Strategy Equity\"" + ",\"LastValue\":null},\"Capacity\":{\"Name\":\"Capacity\"},\"Benchmark\":{\"Name\":\"Benchmark\"}},\"parameterSet\":[],\"alphaRuntimeStatistics\":null,\"runtimeStatistics\":{\"Equity\":\"$100,000.00\",\"Fees\":\"-$0.00\"," + "\"Holdings\":\"$0.00\",\"Net Profit\":\"$0.00\",\"Probabilistic Sharpe Ratio\":\"0%\",\"Return\":\"0.00 %\",\"Unrealized\":\"$0.00\",\"Volume\":\"$0.00\"},\"statistics\":{\"Total Trades\":\"0\",\"Average Win\":\"0%\"," + "\"Average Loss\":\"0%\",\"Compounding Annual Return\":\"0%\",\"Drawdown\":\"0%\",\"Expectancy\":\"0\",\"Net Profit\":\"0%\",\"Sharpe Ratio\":\"0\",\"Probabilistic Sharpe Ratio\":\"0%\",\"Loss Rate\":\"0%\",\"Win Rate\"" + ":\"0%\",\"Profit-Loss Ratio\":\"0\",\"Alpha\":\"0\",\"Beta\":\"0\",\"Annual Standard Deviation\":\"0\",\"Annual Variance\":\"0\",\"Information Ratio\":\"-0.309\",\"Tracking Error\":\"0.169\",\"Treynor Ratio\":\"0\"" + ",\"Total Fees\":\"$0.00\",\"Estimated Strategy Capacity\":\"$0\",\"Lowest Capacity Asset\":\"\",\"Portfolio Turnover\":\"0%\"},\"totalPerformance\":{\"TradeStatistics\":{\"StartDateTime\":null,\"EndDateTime\":null," + "\"TotalNumberOfTrades\":0,\"NumberOfWinningTrades\":0,\"NumberOfLosingTrades\":0,\"TotalProfitLoss\":\"0\",\"TotalProfit\":\"0\",\"TotalLoss\":\"0\",\"LargestProfit\":\"0\",\"LargestLoss\":\"0\",\"AverageProfitLoss\"" + ":\"0\",\"AverageProfit\":\"0\",\"AverageLoss\":\"0\",\"AverageTradeDuration\":\"00:00:00\",\"AverageWinningTradeDuration\":\"00:00:00\",\"AverageLosingTradeDuration\":\"00:00:00\",\"MedianTradeDuration\":\"00:00:00\"," + "\"MedianWinningTradeDuration\":\"00:00:00\",\"MedianLosingTradeDuration\":\"00:00:00\",\"MaxConsecutiveWinningTrades\":0,\"MaxConsecutiveLosingTrades\":0,\"ProfitLossRatio\":\"0\",\"WinLossRatio\":\"0\",\"WinRate\":\"0\"," + "\"LossRate\":\"0\",\"AverageMAE\":\"0\",\"AverageMFE\":\"0\",\"LargestMAE\":\"0\",\"LargestMFE\":\"0\",\"MaximumClosedTradeDrawdown\":\"0\",\"MaximumIntraTradeDrawdown\":\"0\",\"ProfitLossStandardDeviation\":\"0\"," + "\"ProfitLossDownsideDeviation\":\"0\",\"ProfitFactor\":\"0\",\"SharpeRatio\":\"0\",\"SortinoRatio\":\"0\",\"ProfitToMaxDrawdownRatio\":\"0\",\"MaximumEndTradeDrawdown\":\"0\",\"AverageEndTradeDrawdown\":\"0\",\"MaximumDrawdownDuration\"" + ":\"00:00:00\",\"TotalFees\":\"0\"},\"PortfolioStatistics\":{\"AverageWinRate\":\"0\",\"AverageLossRate\":\"0\",\"ProfitLossRatio\":\"0\",\"WinRate\":\"0\",\"LossRate\":\"0\",\"Expectancy\":\"0\",\"CompoundingAnnualReturn\":\"0\"" + ",\"Drawdown\":\"0\",\"TotalNetProfit\":\"0\",\"SharpeRatio\":\"0\",\"ProbabilisticSharpeRatio\":\"0\",\"Alpha\":\"0\",\"Beta\":\"0\",\"AnnualStandardDeviation\":\"0\",\"AnnualVariance\":\"0\",\"InformationRatio\":\"-0.3094\"," + "\"TrackingError\":\"0.1686\",\"TreynorRatio\":\"0\",\"PortfolioTurnover\":\"0\"},\"ClosedTrades\":[]},\"signals\":null,\"nodeName\":\"B8-16 node 1ed393c8\"},\"success\":true}"; private const string ValidBacktestResultJson = "{\"RollingWindow\":{\"M1_20131011\":{\"TradeStatistics\":{\"StartDateTime\":null,\"EndDateTime\":null,\"TotalNumberOfTrades\":0,\"NumberOfWinningTrades\":0,\"NumberOfLosingTrades\":0,\"TotalProfitLoss\":\"0\",\"TotalProfit\":\"0\",\"TotalLoss\":\"0\",\"LargestProfit\":\"0\",\"LargestLoss\":\"0\",\"AverageProfitLoss\":\"0\"," + "\"AverageProfit\":\"0\",\"AverageLoss\":\"0\",\"AverageTradeDuration\":\"00:00:00\",\"AverageWinningTradeDuration\":\"00:00:00\",\"AverageLosingTradeDuration\":\"00:00:00\",\"MedianTradeDuration\":\"00:00:00\",\"MedianWinningTradeDuration\":\"00:00:00\",\"MedianLosingTradeDuration\":\"00:00:00\",\"MaxConsecutiveWinningTrades\":0,\"MaxConsecutiveLosingTrades\":0," + "\"ProfitLossRatio\":\"0\",\"WinLossRatio\":\"0\",\"WinRate\":\"0\",\"LossRate\":\"0\",\"AverageMAE\":\"0\",\"AverageMFE\":\"0\",\"LargestMAE\":\"0\",\"LargestMFE\":\"0\",\"MaximumClosedTradeDrawdown\":\"0\",\"MaximumIntraTradeDrawdown\":\"0\",\"ProfitLossStandardDeviation\":\"0\",\"ProfitLossDownsideDeviation\":\"0\",\"ProfitFactor\":\"0\",\"SharpeRatio\":\"0\"," + "\"SortinoRatio\":\"0\",\"ProfitToMaxDrawdownRatio\":\"0\",\"MaximumEndTradeDrawdown\":\"0\",\"AverageEndTradeDrawdown\":\"0\",\"MaximumDrawdownDuration\":\"00:00:00\",\"TotalFees\":\"0\"},\"PortfolioStatistics\":{\"AverageWinRate\":\"0\",\"AverageLossRate\":\"0\",\"ProfitLossRatio\":\"0\",\"WinRate\":\"0\",\"LossRate\":\"0\",\"Expectancy\":\"0\",\"CompoundingAnnualReturn\":" + "\"2.7145\",\"Drawdown\":\"0.022\",\"TotalNetProfit\":\"0.0169\",\"SharpeRatio\":\"8.8881\",\"ProbabilisticSharpeRatio\":\"0.6761\",\"Alpha\":\"-0.0049\",\"Beta\":\"0.9961\",\"AnnualStandardDeviation\":\"0.2217\",\"AnnualVariance\":\"0.0491\",\"InformationRatio\":\"-14.5651\",\"TrackingError\":\"0.0009\",\"TreynorRatio\":\"1.9780\",\"PortfolioTurnover\":\"0.1993\"}," + "\"ClosedTrades\":[]},\"M3_20131011\":{\"TradeStatistics\":{\"StartDateTime\":null,\"EndDateTime\":null,\"TotalNumberOfTrades\":0,\"NumberOfWinningTrades\":0,\"NumberOfLosingTrades\":0,\"TotalProfitLoss\":\"0\",\"TotalProfit\":\"0\",\"TotalLoss\":\"0\",\"LargestProfit\":\"0\",\"LargestLoss\":\"0\",\"AverageProfitLoss\":\"0\",\"AverageProfit\":\"0\",\"AverageLoss\":" + "\"0\",\"AverageTradeDuration\":\"00:00:00\",\"AverageWinningTradeDuration\":\"00:00:00\",\"AverageLosingTradeDuration\":\"00:00:00\",\"MedianTradeDuration\":\"00:00:00\",\"MedianWinningTradeDuration\":\"00:00:00\",\"MedianLosingTradeDuration\":\"00:00:00\",\"MaxConsecutiveWinningTrades\":0,\"MaxConsecutiveLosingTrades\":0,\"ProfitLossRatio\":\"0\",\"WinLossRatio\":" + "\"0\",\"WinRate\":\"0\",\"LossRate\":\"0\",\"AverageMAE\":\"0\",\"AverageMFE\":\"0\",\"LargestMAE\":\"0\",\"LargestMFE\":\"0\",\"MaximumClosedTradeDrawdown\":\"0\",\"MaximumIntraTradeDrawdown\":\"0\",\"ProfitLossStandardDeviation\":\"0\",\"ProfitLossDownsideDeviation\":\"0\",\"ProfitFactor\":\"0\",\"SharpeRatio\":\"0\",\"SortinoRatio\":\"0\",\"ProfitToMaxDrawdownRatio\":" + "\"0\",\"MaximumEndTradeDrawdown\":\"0\",\"AverageEndTradeDrawdown\":\"0\",\"MaximumDrawdownDuration\":\"00:00:00\",\"TotalFees\":\"0\"},\"PortfolioStatistics\":{\"AverageWinRate\":\"0\",\"AverageLossRate\":\"0\",\"ProfitLossRatio\":\"0\",\"WinRate\":\"0\",\"LossRate\":\"0\",\"Expectancy\":\"0\",\"CompoundingAnnualReturn\":\"2.7145\",\"Drawdown\":\"0.022\",\"TotalNetProfit\"" + ":\"0.0169\",\"SharpeRatio\":\"8.8881\",\"ProbabilisticSharpeRatio\":\"0.6761\",\"Alpha\":\"-0.0049\",\"Beta\":\"0.9961\",\"AnnualStandardDeviation\":\"0.2217\",\"AnnualVariance\":\"0.0491\",\"InformationRatio\":\"-14.5651\",\"TrackingError\":\"0.0009\",\"TreynorRatio\":\"1.9780\",\"PortfolioTurnover\":\"0.1993\"},\"ClosedTrades\":[]},\"M6_20131011\":{\"TradeStatistics\"" + ":{\"StartDateTime\":null,\"EndDateTime\":null,\"TotalNumberOfTrades\":0,\"NumberOfWinningTrades\":0,\"NumberOfLosingTrades\":0,\"TotalProfitLoss\":\"0\",\"TotalProfit\":\"0\",\"TotalLoss\":\"0\",\"LargestProfit\":\"0\",\"LargestLoss\":\"0\",\"AverageProfitLoss\":\"0\",\"AverageProfit\":\"0\",\"AverageLoss\":\"0\",\"AverageTradeDuration\":\"00:00:00\"," + "\"AverageWinningTradeDuration\":\"00:00:00\",\"AverageLosingTradeDuration\":\"00:00:00\",\"MedianTradeDuration\":\"00:00:00\",\"MedianWinningTradeDuration\":\"00:00:00\",\"MedianLosingTradeDuration\":\"00:00:00\",\"MaxConsecutiveWinningTrades\":0,\"MaxConsecutiveLosingTrades\":0,\"ProfitLossRatio\":\"0\",\"WinLossRatio\":\"0\",\"WinRate\":\"0\",\"LossRate\":\"0\"," + "\"AverageMAE\":\"0\",\"AverageMFE\":\"0\",\"LargestMAE\":\"0\",\"LargestMFE\":\"0\",\"MaximumClosedTradeDrawdown\":\"0\",\"MaximumIntraTradeDrawdown\":\"0\",\"ProfitLossStandardDeviation\":\"0\",\"ProfitLossDownsideDeviation\":\"0\",\"ProfitFactor\":\"0\",\"SharpeRatio\":\"0\",\"SortinoRatio\":\"0\",\"ProfitToMaxDrawdownRatio\":\"0\",\"MaximumEndTradeDrawdown\"" + ":\"0\",\"AverageEndTradeDrawdown\":\"0\",\"MaximumDrawdownDuration\":\"00:00:00\",\"TotalFees\":\"0\"},\"PortfolioStatistics\":{\"AverageWinRate\":\"0\",\"AverageLossRate\":\"0\",\"ProfitLossRatio\":\"0\",\"WinRate\":\"0\",\"LossRate\":\"0\",\"Expectancy\":\"0\",\"CompoundingAnnualReturn\":\"2.7145\",\"Drawdown\":\"0.022\",\"TotalNetProfit\":\"0.0169\",\"SharpeRatio\":" + "\"8.8881\",\"ProbabilisticSharpeRatio\":\"0.6761\",\"Alpha\":\"-0.0049\",\"Beta\":\"0.9961\",\"AnnualStandardDeviation\":\"0.2217\",\"AnnualVariance\":\"0.0491\",\"InformationRatio\":\"-14.5651\",\"TrackingError\":\"0.0009\",\"TreynorRatio\":\"1.9780\",\"PortfolioTurnover\":\"0.1993\"},\"ClosedTrades\":[]},\"M12_20131011\":{\"TradeStatistics\":{\"StartDateTime\"" + ":null,\"EndDateTime\":null,\"TotalNumberOfTrades\":0,\"NumberOfWinningTrades\":0,\"NumberOfLosingTrades\":0,\"TotalProfitLoss\":\"0\",\"TotalProfit\":\"0\",\"TotalLoss\":\"0\",\"LargestProfit\":\"0\",\"LargestLoss\":\"0\",\"AverageProfitLoss\":\"0\",\"AverageProfit\":\"0\",\"AverageLoss\":\"0\",\"AverageTradeDuration\":\"00:00:00\",\"AverageWinningTradeDuration\":" + "\"00:00:00\",\"AverageLosingTradeDuration\":\"00:00:00\",\"MedianTradeDuration\":\"00:00:00\",\"MedianWinningTradeDuration\":\"00:00:00\",\"MedianLosingTradeDuration\":\"00:00:00\",\"MaxConsecutiveWinningTrades\":0,\"MaxConsecutiveLosingTrades\":0,\"ProfitLossRatio\":\"0\",\"WinLossRatio\":\"0\",\"WinRate\":\"0\",\"LossRate\":\"0\",\"AverageMAE\":\"0\",\"AverageMFE\"" + ":\"0\",\"LargestMAE\":\"0\",\"LargestMFE\":\"0\",\"MaximumClosedTradeDrawdown\":\"0\",\"MaximumIntraTradeDrawdown\":\"0\",\"ProfitLossStandardDeviation\":\"0\",\"ProfitLossDownsideDeviation\":\"0\",\"ProfitFactor\":\"0\",\"SharpeRatio\":\"0\",\"SortinoRatio\":\"0\",\"ProfitToMaxDrawdownRatio\":\"0\",\"MaximumEndTradeDrawdown\":\"0\",\"AverageEndTradeDrawdown\":\"0\"," + "\"MaximumDrawdownDuration\":\"00:00:00\",\"TotalFees\":\"0\"},\"PortfolioStatistics\":{\"AverageWinRate\":\"0\",\"AverageLossRate\":\"0\",\"ProfitLossRatio\":\"0\",\"WinRate\":\"0\",\"LossRate\":\"0\",\"Expectancy\":\"0\",\"CompoundingAnnualReturn\":\"2.7145\",\"Drawdown\":\"0.022\",\"TotalNetProfit\":\"0.0169\",\"SharpeRatio\":\"8.8881\",\"ProbabilisticSharpeRatio\"" + ":\"0.6761\",\"Alpha\":\"-0.0049\",\"Beta\":\"0.9961\",\"AnnualStandardDeviation\":\"0.2217\",\"AnnualVariance\":\"0.0491\",\"InformationRatio\":\"-14.5651\",\"TrackingError\":\"0.0009\",\"TreynorRatio\":\"1.9780\",\"PortfolioTurnover\":\"0.1993\"},\"ClosedTrades\":[]}},\"TotalPerformance\":{\"TradeStatistics\":{\"StartDateTime\":null,\"EndDateTime\":null," + "\"TotalNumberOfTrades\":0,\"NumberOfWinningTrades\":0,\"NumberOfLosingTrades\":0,\"TotalProfitLoss\":\"0\",\"TotalProfit\":\"0\",\"TotalLoss\":\"0\",\"LargestProfit\":\"0\",\"LargestLoss\":\"0\",\"AverageProfitLoss\":\"0\",\"AverageProfit\":\"0\",\"AverageLoss\":\"0\",\"AverageTradeDuration\":\"00:00:00\",\"AverageWinningTradeDuration\":\"00:00:00\",\"AverageLosingTradeDuration\"" + ":\"00:00:00\",\"MedianTradeDuration\":\"00:00:00\",\"MedianWinningTradeDuration\":\"00:00:00\",\"MedianLosingTradeDuration\":\"00:00:00\",\"MaxConsecutiveWinningTrades\":0,\"MaxConsecutiveLosingTrades\":0,\"ProfitLossRatio\":\"0\",\"WinLossRatio\":\"0\",\"WinRate\":\"0\",\"LossRate\":\"0\",\"AverageMAE\":\"0\",\"AverageMFE\":\"0\",\"LargestMAE\":\"0\",\"LargestMFE\":\"0\"" + ",\"MaximumClosedTradeDrawdown\":\"0\",\"MaximumIntraTradeDrawdown\":\"0\",\"ProfitLossStandardDeviation\":\"0\",\"ProfitLossDownsideDeviation\":\"0\",\"ProfitFactor\":\"0\",\"SharpeRatio\":\"0\",\"SortinoRatio\":\"0\",\"ProfitToMaxDrawdownRatio\":\"0\",\"MaximumEndTradeDrawdown\":\"0\",\"AverageEndTradeDrawdown\":\"0\",\"MaximumDrawdownDuration\":\"00:00:00\",\"TotalFees\":\"0\"}" + ",\"PortfolioStatistics\":{\"AverageWinRate\":\"0\",\"AverageLossRate\":\"0\",\"ProfitLossRatio\":\"0\",\"WinRate\":\"0\",\"LossRate\":\"0\",\"Expectancy\":\"0\",\"CompoundingAnnualReturn\":\"2.7145\",\"Drawdown\":\"0.022\",\"TotalNetProfit\":\"0.0169\",\"SharpeRatio\":\"8.8881\",\"ProbabilisticSharpeRatio\":\"0.6761\",\"Alpha\":\"-0.0049\",\"Beta\":\"0.9961\",\"AnnualStandardDeviation\"" + ":\"0.2217\",\"AnnualVariance\":\"0.0491\",\"InformationRatio\":\"-14.5651\",\"TrackingError\":\"0.0009\",\"TreynorRatio\":\"1.9780\",\"PortfolioTurnover\":\"0.1993\"},\"ClosedTrades\":[]},\"Charts\":{\"Drawdown\":{\"Name\":\"Drawdown\",\"ChartType\":0,\"Series\":{\"Equity Drawdown\":{\"Name\":\"Equity Drawdown\",\"Unit\":\"%\",\"Index\":0,\"SeriesType\":0,\"Values\":[{\"x\":1381118400" + ",\"y\":0.0},{\"x\":1381204800,\"y\":-0.02},{\"x\":1381291200,\"y\":-1.18},{\"x\":1381377600,\"y\":-1.12},{\"x\":1381464000,\"y\":0.0},{\"x\":1381521600,\"y\":0.0}],\"Color\":\"\",\"ScatterMarkerSymbol\":\"none\"}}},\"Strategy Equity\":{\"Name\":\"Strategy Equity\",\"ChartType\":0,\"Series\":{\"Equity\":{\"Name\":\"Equity\",\"Unit\":\"$\",\"Index\":0,\"SeriesType\":2,\"Values\":" + "[[1381118400,100000.0,100000.0,100000.0,100000.0],[1381152660,99990.6114,99990.6114,99990.6114,99990.6114],[1381152960,99948.971,100056.0463,99895.4333,100056.0463],[1381153260,99984.6627,100020.3545,99984.6627,100008.4573],[1381153560,100073.8922,100127.4299,100073.8922,100097.6867],[1381153860,100139.3271,100234.5052,100139.3271,100234.5052],[1381154160,100204.7621,100204.7621,100157.173,100157.173]," + "[1381154460,100139.3271,100180.9676,100139.3271,100169.0703],[1381154760,100115.5326,100145.2758,100103.6354,100145.2758],[1381155060,100210.7107,100371.3238,100210.7107,100365.3751],[1381155360,100359.4265,100424.8614,100359.4265,100401.0669],[1381155660,100395.1183,100442.7073,100377.2724,100442.7073],[1381155960,100436.7587,100490.2964,100407.0155,100466.5018],[1381156260,100496.245,100514.0909" + ",100460.5532,100514.0909],[1381156560,100537.8854,100537.8854,100430.8101,100430.8101],[1381156860,100407.0155,100424.8614,100407.0155,100418.9128],[1381157160,100407.0155,100407.0155,100383.221,100383.221],[1381157460,100424.8614,100454.6046,100418.9128,100424.8614],[1381157760,100442.7073,100484.3477,100442.7073,100460.5532],[1381158060,100424.8614,100436.7587,100424.8614,100424.8614]," + "[1381158360,100341.5806,100353.4779,100323.7347,100341.5806],[1381158660,100347.5292,100395.1183,100335.632,100395.1183],[1381158960,100418.9128,100418.9128,100329.6833,100329.6833],[1381159260,100341.5806,100341.5806,100288.0429,100311.8374],[1381159560,100341.5806,100359.4265,100341.5806,100359.4265],[1381159860,100347.5292,100359.4265,100329.6833,100341.5806],[1381160160,100329.6833,100472.4505," + "100329.6833,100472.4505],[1381160460,100466.5018,100466.5018,100424.8614,100436.7587],[1381160760,100424.8614,100484.3477,100407.0155,100484.3477],[1381161060,100496.245,100537.8854,100484.3477,100537.8854],[1381161360,100525.9882,100525.9882,100472.4505,100484.3477],[1381161660,100484.3477,100490.2964,100430.8101,100430.8101],[1381161960,100401.0669,100424.8614,100395.1183,100407.0155],[1381162260," + "100430.8101,100460.5532,100418.9128,100436.7587],[1381162560,100424.8614,100430.8101,100418.9128,100430.8101],[1381162860,100436.7587,100436.7587,100424.8614,100430.8101],[1381163160,100377.2724,100395.1183,100377.2724,100377.2724],[1381163460,100359.4265,100359.4265,100228.5566,100228.5566],[1381163760,100222.608,100293.9916,100222.608,100264.2484],[1381164060,100282.0943,100282.0943,100258.2998," + "100258.2998],[1381164360,100282.0943,100323.7347,100276.1457,100323.7347],[1381164660,100335.632,100335.632,100299.9402,100323.7347],[1381164960,100341.5806,100341.5806,100317.7861,100317.7861],[1381165260,100323.7347,100323.7347,100216.6593,100276.1457],[1381165560,100317.7861,100317.7861,100264.2484,100264.2484],[1381165860,100246.4025,100299.9402,100240.4539,100299.9402],[1381166160,100365.3751" + ",100365.3751,100341.5806,100365.3751],[1381166460,100395.1183,100395.1183,100377.2724,100383.221],[1381166760,100383.221,100383.221,100365.3751,100377.2724],[1381167060,100365.3751,100407.0155,100335.632,100407.0155],[1381167360,100395.1183,100395.1183,100359.4265,100389.1696],[1381167660,100365.3751,100365.3751,100293.9916,100317.7861],[1381167960,100311.8374,100335.632,100299.9402,100323.7347]" + ",[1381168260,100347.5292,100359.4265,100329.6833,100329.6833],[1381168560,100353.4779,100383.221,100347.5292,100383.221],[1381168860,100418.9128,100514.0909,100418.9128,100514.0909],[1381169160,100514.0909,100543.8341,100496.245,100496.245],[1381169460,100430.8101,100430.8101,100389.1696,100424.8614],[1381169760,100454.6046,100454.6046,100407.0155,100418.9128],[1381170060,100418.9128,100454.6046" + ",100383.221,100436.7587],[1381170360,100412.9642,100430.8101,100412.9642,100424.8614],[1381170660,100412.9642,100448.656,100383.221,100436.7587],[1381170960,100448.656,100454.6046,100442.7073,100454.6046],[1381171260,100496.245,100496.245,100442.7073,100442.7073],[1381171560,100430.8101,100430.8101,100359.4265,100383.221],[1381171860,100395.1183,100442.7073,100395.1183,100442.7073],[1381172160" + ",100418.9128,100472.4505,100401.0669,100472.4505],[1381172460,100484.3477,100484.3477,100454.6046,100454.6046],[1381172760,100430.8101,100472.4505,100430.8101,100436.7587],[1381173060,100436.7587,100454.6046,100418.9128,100454.6046],[1381173360,100448.656,100448.656,100430.8101,100448.656],[1381173660,100418.9128,100436.7587,100383.221,100383.221],[1381173960,100341.5806,100341.5806,100299.9402" + ",100299.9402],[1381174260,100288.0429,100305.8888,100288.0429,100293.9916],[1381174560,100276.1457,100293.9916,100228.5566,100228.5566],[1381174860,100240.4539,100246.4025,100198.8135,100198.8135],[1381175160,100169.0703,100228.5566,100169.0703,100180.9676],[1381175460,100121.4813,100145.2758,100103.6354,100139.3271],[1381175760,100121.4813,100127.4299,100044.149,100044.149],[1381204800,100109.584" + ",100127.4299,99978.7141,99978.7141],[1381239060,100026.3032,100026.3032,100026.3032,100026.3032],[1381239360,99978.7141,100008.4573,99919.2278,99919.2278],[1381239660,99895.4333,99895.4333,99776.4607,99776.4607],[1381239960,99806.2038,100014.4059,99806.2038,100014.4059],[1381240260,100014.4059,100014.4059,99895.4333,99895.4333],[1381240560,99853.7929,100061.9949,99853.7929,100061.9949],[1381240860" + ",100038.2004,100050.0977,100020.3545,100020.3545],[1381241160,99907.3305,99978.7141,99877.5874,99978.7141],[1381241460,99943.0223,99943.0223,99829.9983,99829.9983],[1381241760,99883.536,99931.1251,99883.536,99931.1251],[1381242060,99937.0737,100014.4059,99937.0737,100014.4059],[1381242360,99978.7141,99996.56,99966.8168,99966.8168],[1381242660,99960.8682,100014.4059,99954.9196,99954.9196],[1381242960" + ",99895.4333,99907.3305,99877.5874,99895.4333],[1381243260,99913.2792,99913.2792,99841.8956,99841.8956],[1381243560,99853.7929,99907.3305,99853.7929,99907.3305],[1381243860,99806.2038,99806.2038,99586.1045,99586.1045],[1381244160,99633.6935,99633.6935,99574.2072,99574.2072],[1381244460,99568.2586,99586.1045,99532.5668,99532.5668],[1381244760,99520.6695,99520.6695,99419.5428,99490.9264],[1381245060" + ",99508.7723,99609.899,99508.7723,99609.899],[1381245360,99592.0531,99627.7449,99562.3099,99562.3099],[1381245660,99580.1558,99580.1558,99532.5668,99550.4127],[1381245960,99562.3099,99598.0017,99520.6695,99598.0017],[1381246260,99550.4127,99550.4127,99490.9264,99490.9264],[1381246560,99461.1832,99473.0805,99366.0051,99366.0051],[1381246860,99395.7483,99395.7483,99312.4674,99342.2106],[1381247160" + ",99294.6215,99360.0565,99294.6215,99318.4161],[1381247460,99252.9811,99258.9298,99205.3921,99258.9298],[1381247760,99282.7243,99306.5188,99270.827,99300.5702],[1381248060,99300.5702,99342.2106,99270.827,99270.827],[1381248360,99258.9298,99306.5188,99258.9298,99282.7243],[1381248660,99264.8784,99306.5188,99223.238,99270.827],[1381248960,99229.1866,99270.827,99223.238,99223.238],[1381249260,99235.1352," + "99235.1352,99193.4948,99193.4948],[1381249560,99181.5976,99193.4948,99134.0085,99134.0085],[1381249860,99145.9058,99199.4434,99128.0599,99128.0599],[1381250160,99116.1626,99116.1626,99074.5222,99104.2653],[1381250460,99169.7003,99175.6489,99151.8544,99151.8544],[1381250760,99169.7003,99318.4161,99169.7003,99318.4161],[1381251060,99312.4674,99324.3647,99288.6729,99288.6729],[1381251360,99247.0325" + ",99247.0325,99211.3407,99229.1866],[1381251660,99247.0325,99312.4674,99247.0325,99312.4674],[1381251960,99348.1592,99348.1592,99258.9298,99258.9298],[1381252260,99247.0325,99247.0325,99199.4434,99211.3407],[1381252560,99229.1866,99270.827,99181.5976,99181.5976],[1381252860,99187.5462,99193.4948,99122.1112,99122.1112],[1381253160,99122.1112,99193.4948,99122.1112,99187.5462],[1381253460,99235.1352" + ",99288.6729,99169.7003,99288.6729],[1381253760,99306.5188,99306.5188,99241.0839,99241.0839],[1381254060,99187.5462,99217.2893,99187.5462,99193.4948],[1381254360,99199.4434,99211.3407,99157.803,99169.7003],[1381254660,99151.8544,99151.8544,99092.3681,99134.0085],[1381254960,99122.1112,99151.8544,99110.214,99151.8544],[1381255260,99157.803,99199.4434,99062.6249,99062.6249],[1381255560,99044.779" + ",99044.779,99003.1386,99032.8818],[1381255860,99015.0359,99026.9331,98967.4468,99026.9331],[1381256160,99009.0873,99050.7277,99003.1386,99003.1386],[1381256460,98955.5496,99009.0873,98937.7037,98937.7037],[1381256760,98937.7037,99116.1626,98937.7037,99098.3167],[1381257060,99157.803,99169.7003,99116.1626,99163.7517],[1381257360,99169.7003,99169.7003,99038.8304,99038.8304],[1381257660,99032.8818" + ",99080.4708,99026.9331,99074.5222],[1381257960,99032.8818,99086.4195,99020.9845,99020.9845],[1381258260,98991.2414,99020.9845,98967.4468,98967.4468],[1381258560,99020.9845,99020.9845,98955.5496,98955.5496],[1381258860,98979.3441,99086.4195,98979.3441,99086.4195],[1381259160,99092.3681,99128.0599,99092.3681,99092.3681],[1381259460,99116.1626,99116.1626,99009.0873,99026.9331],[1381259760,98973.3955" + ",99020.9845,98967.4468,98973.3955],[1381260060,99003.1386,99086.4195,99003.1386,99086.4195],[1381260360,99139.9571,99235.1352,99139.9571,99217.2893],[1381260660,99247.0325,99264.8784,99217.2893,99264.8784],[1381260960,99270.827,99270.827,99092.3681,99092.3681],[1381261260,99080.4708,99169.7003,99080.4708,99169.7003],[1381261560,99151.8544,99163.7517,99104.2653,99104.2653],[1381261860,99038.8304,99050.7277" + ",98979.3441,99050.7277],[1381262160,99003.1386,99020.9845,98890.1146,98890.1146],[1381291200,98836.577,98919.8578,98824.6797,98824.6797],[1381325460,98985.2927,98985.2927,98985.2927,98985.2927],[1381325760,99015.0359,99015.0359,98878.2174,98890.1146],[1381326060,98937.7037,98973.3955,98907.9605,98907.9605],[1381326360,98842.5256,98842.5256,98753.2961,98794.9365],[1381326660,98866.3201,98866.3201,98658.118,98658.118]" + ",[1381326960,98723.553,98777.0906,98699.7584,98753.2961],[1381327260,98854.4228,98907.9605,98854.4228,98872.2687],[1381327560,98818.7311,98830.6283,98729.5016,98729.5016],[1381327860,98729.5016,98836.577,98729.5016,98777.0906],[1381328160,98741.3989,98818.7311,98717.6043,98818.7311],[1381328460,98800.8852,98800.8852,98741.3989,98794.9365],[1381328760,98747.3475,98985.2927,98747.3475,98985.2927],[1381329060," + "98907.9605,98943.6523,98907.9605,98919.8578],[1381329360,98931.755,99009.0873,98878.2174,98878.2174],[1381329660,98836.577,98890.1146,98836.577,98890.1146],[1381329960,98907.9605,98907.9605,98818.7311,98818.7311],[1381330260,98687.8612,98759.2448,98687.8612,98759.2448],[1381330560,98753.2961,98753.2961,98580.7858,98592.6831],[1381330860,98616.4776,98634.3235,98539.1454,98574.8372],[1381331160,98634.3235," + "98664.0667,98622.4262,98628.3749],[1381331460,98604.5803,98604.5803,98449.9159,98449.9159],[1381331760,98485.6077,98485.6077,98402.3269,98402.3269],[1381332060,98390.4296,98420.1728,98342.8406,98342.8406],[1381332360,98283.3543,98336.8919,98283.3543,98336.8919],[1381332660,98390.4296,98426.1214,98390.4296,98426.1214],[1381332960,98473.7105,98551.0427,98473.7105,98491.5564]" + ",[1381333260,98503.4536,98539.1454,98461.8132,98485.6077]]},\"Daily Performance\":{\"Name\":\"Daily Performance\",\"Unit\":\"%\",\"Index\":1,\"SeriesType\":3,\"Values\":[{\"x\":1381118400,\"y\":0.0},{\"x\":1381204800,\"y\":-0.02128589},{\"x\":1381291200,\"y\":-1.15428},{\"x\":1381377600,\"y\":0.0541744},{\"x\":1381464000,\"y\":2.207916},{\"x\":1381521600,\"y\":0.6239327}],\"Color\"" + ":\"\",\"ScatterMarkerSymbol\":\"none\"}}},\"Benchmark\":{\"Name\":\"Benchmark\",\"ChartType\":0,\"Series\":{\"Benchmark\":{\"Name\":\"Benchmark\",\"Unit\":\"$\",\"Index\":0,\"SeriesType\":0,\"Values\":[{\"x\":1381118400,\"y\":146.0268},{\"x\":1381204800,\"y\":144.7558},{\"x\":1381291200,\"y\":143.0784},{\"x\":1381377600,\"y\":143.1562},{\"x\":1381464000,\"y\":146.3294},{\"x\":1381521600," + "\"y\":147.2459}],\"Color\":\"\",\"ScatterMarkerSymbol\":\"none\"}}},\"Capacity\":{\"Name\":\"Capacity\",\"ChartType\":0,\"Series\":{\"Strategy Capacity\":{\"Name\":\"Strategy Capacity\",\"Unit\":\"$\",\"Index\":0,\"SeriesType\":0,\"Values\":[{\"x\":1381118400,\"y\":0.0},{\"x\":1381204800,\"y\":0.0},{\"x\":1381291200,\"y\":0.0},{\"x\":1381377600,\"y\":0.0},{\"x\":1381464000,\"y\":0.0}," + "{\"x\":1381521600,\"y\":56181000.0}],\"Color\":\"\",\"ScatterMarkerSymbol\":\"none\"}}},\"Portfolio Turnover\":{\"Name\":\"Portfolio Turnover\",\"ChartType\":0,\"Series\":{\"Portfolio Turnover\":{\"Name\":\"Portfolio Turnover\",\"Unit\":\"%\",\"Index\":0,\"SeriesType\":0,\"Values\":[{\"x\":1381204800,\"y\":0.9963103},{\"x\":1381291200,\"y\":0.0},{\"x\":1381377600,\"y\":0.0},{\"x\":1381464000,\"y\":0.0}" + ",{\"x\":1381521600,\"y\":0.0}],\"Color\":\"\",\"ScatterMarkerSymbol\":\"none\"}}},\"Exposure\":{\"Name\":\"Exposure\",\"ChartType\":0,\"Series\":{\"Equity - Long Ratio\":{\"Name\":\"Equity - Long Ratio\",\"Unit\":\"\",\"Index\":0,\"SeriesType\":0,\"Values\":[{\"x\":1381118400,\"y\":0.0},{\"x\":1381204800,\"y\":0.9961},{\"x\":1381291200,\"y\":0.9961},{\"x\":1381377600,\"y\":0.9961},{\"x\":1381464000,\"y\":0.9962}" + ",{\"x\":1381521600,\"y\":0.9962}],\"Color\":\"\",\"ScatterMarkerSymbol\":\"none\"},\"Equity - Short Ratio\":{\"Name\":\"Equity - Short Ratio\",\"Unit\":\"\",\"Index\":0,\"SeriesType\":0,\"Values\":[{\"x\":1381118400,\"y\":0.0},{\"x\":1381204800,\"y\":0.0},{\"x\":1381291200,\"y\":0.0},{\"x\":1381377600,\"y\":0.0},{\"x\":1381464000,\"y\":0.0},{\"x\":1381521600,\"y\":0.0}],\"Color\":\"\"" + ",\"ScatterMarkerSymbol\":\"none\"}}},\"Assets Sales Volume\":{\"Name\":\"Assets Sales Volume\",\"ChartType\":0,\"Series\":{\"SPY\":{\"Name\":\"SPY\",\"Unit\":\"$\",\"Index\":0,\"SeriesType\":7,\"Values\":[{\"x\":1381204800,\"y\":99609.8262},{\"x\":1381291200,\"y\":99609.8262},{\"x\":1381377600,\"y\":99609.8262},{\"x\":1381464000,\"y\":99609.8262},{\"x\":1381521600,\"y\":99609.8262}],\"Color\":\"\"" + ",\"ScatterMarkerSymbol\":\"none\"}}}},\"Orders\":{\"1\":{\"Type\":0,\"Id\":1,\"ContingentId\":0,\"BrokerId\":[\"1\"],\"Symbol\":{\"Value\":\"SPY\",\"ID\":\"SPY R735QTJ8XC9X\",\"Permtick\":\"SPY\"},\"Price\":144.78172417,\"PriceCurrency\":\"USD\",\"Time\":\"2013-10-07T13:31:00Z\",\"CreatedTime\":\"2013-10-07T13:31:00Z\",\"LastFillTime\":\"2013-10-07T13:31:00Z\",\"Quantity\":688.0,\"Status\":3,\"Properties\"" + ":{\"TimeInForce\":{}},\"SecurityType\":1,\"Direction\":0,\"Value\":99609.82622896,\"OrderSubmissionData\":{\"BidPrice\":144.77307790400,\"AskPrice\":144.78172417000,\"LastPrice\":144.77307790400},\"IsMarketable\":true}},\"ProfitLoss\":{},\"Statistics\":{\"Total Trades\":\"1\",\"Average Win\":\"0%\",\"Average Loss\":\"0%\",\"Compounding Annual Return\":\"271.453%\",\"Drawdown\":\"2.200%\",\"Expectancy\":\"0\"" + ",\"Net Profit\":\"1.692%\",\"Sharpe Ratio\":\"8.888\",\"Probabilistic Sharpe Ratio\":\"67.609%\",\"Loss Rate\":\"0%\",\"Win Rate\":\"0%\",\"Profit-Loss Ratio\":\"0\",\"Alpha\":\"-0.005\",\"Beta\":\"0.996\",\"Annual Standard Deviation\":\"0.222\",\"Annual Variance\":\"0.049\",\"Information Ratio\":\"-14.565\",\"Tracking Error\":\"0.001\",\"Treynor Ratio\":\"1.978\",\"Total Fees\":\"$3.44\"" + ",\"Estimated Strategy Capacity\":\"$56000000.00\",\"Lowest Capacity Asset\":\"SPY R735QTJ8XC9X\",\"Portfolio Turnover\":\"19.93%\"},\"RuntimeStatistics\":{\"Equity\":\"$101,691.92\",\"Fees\":\"-$3.44\",\"Holdings\":\"$101,305.19\",\"Net Profit\":\"$-3.44\",\"Probabilistic Sharpe Ratio\":\"67.609%\",\"Return\":\"1.69 %\",\"Unrealized\":\"$1,656.23\",\"Volume\":\"$99,609.83\"},\"State\":{\"StartTime\":" + "\"2023-08-09 17:14:06\",\"EndTime\":\"2023-08-09 17:14:07\",\"RuntimeError\":\"\",\"StackTrace\":\"\",\"LogCount\":\"4\",\"OrderCount\":\"1\",\"InsightCount\":\"0\",\"Name\":\"local\",\"Hostname\":\"MM\",\"Status\":\"Completed\"},\"AlgorithmConfiguration\":{\"AccountCurrency\":\"USD\",\"Brokerage\":0,\"AccountType\":0,\"Parameters\":{\"intrinio-username\":\"\",\"intrinio-password\":\"\",\"ema-fast\":\"10\"," + "\"ema-slow\":\"20\"}}}"; private const string InvalidBacktestResultJson = "{\"RollingWindow\":{},\"TotalPerformance\":null,\"Charts\":{\"Equity\":{\"Name\":\"Equity\",\"ChartType\":0,\"Series\":{\"Performance\":{\"Name\":\"Performance\",\"Unit\":\"$\",\"Index\":0,\"Values\":[{\"x\":1583704925,\"y\":5.0},{\"x\":1583791325,\"y\":null},{\"x\":1583877725,\"y\":7.0},{\"x\":1583964125,\"y\":8.0},{\"x\":1584050525,\"y\":9.0}],\"SeriesType\":0,\"Color\":\"\",\"ScatterMarkerSymbol\":\"none\"}}}},\"Orders\":" + OrderStringReplace + ",\"ProfitLoss\":{},\"Statistics\":{},\"RuntimeStatistics\":{}}"; private const string InvalidLiveResultJson = "{\"Holdings\":{},\"Cash\":{\"USD\":{\"SecuritySymbol\":{\"Value\":\"\",\"ID\":\" 0\",\"Permtick\":\"\"},\"Symbol\":\"USD\",\"Amount\":0.0,\"ConversionRate\":1.0,\"CurrencySymbol\":\"$\",\"ValueInAccountCurrency\":0.0}},\"ServerStatistics\":{\"CPU Usage\":\"0.0%\",\"Used RAM (MB)\":\"68\",\"Total RAM (MB)\":\"\",\"Used Disk Space (MB)\":\"1\",\"Total Disk Space (MB)\":\"5\",\"Hostname\":\"LEAN\",\"LEAN Version\":\"v2.4.0.0\"},\"Charts\":{\"Equity\":{\"Name\":\"Equity\",\"ChartType\":0,\"Series\":{\"Performance\":{\"Name\":\"Performance\",\"Unit\":\"$\",\"Index\":0,\"Values\":[{\"x\":1583705127,\"y\":5.0},{\"x\":1583791527,\"y\":null},{\"x\":1583877927,\"y\":7.0},{\"x\":1583964327,\"y\":8.0},{\"x\":1584050727,\"y\":9.0}],\"SeriesType\":0,\"Color\":\"\",\"ScatterMarkerSymbol\":\"none\"}}}},\"Orders\":" + OrderStringReplace + ",\"ProfitLoss\":{},\"Statistics\":{},\"RuntimeStatistics\":{}}"; private const string OrderJson = @"{'1': { 'Type':" + OrderTypeStringReplace + @", 'Value':99986.827413672, 'Id':1, 'ContingentId':0, 'BrokerId':[1], 'Symbol':{'Value':'SPY', 'Permtick':'SPY'}, 'Price':100.086914328, 'Time':'2010-03-04T14:31:00Z', 'Quantity':999, 'Status':3, 'Duration':2, 'DurationValue':'2010-04-04T14:31:00Z', 'Tag':'', 'SecurityType':1, 'Direction':0, 'AbsoluteQuantity':999, 'GroupOrderManager': { 'Id': 1, 'Count': 3, 'Quantity': 10, 'LimitPrice': 123.456, 'OrderIds': [1, 2, 3] } }}"; [TestCase("charts")] [TestCase("Charts")] public void ValidBacktestResultDefaultSerializer(string chartKey) { 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())); } [Test] public void ValidBacktestResult() { var settings = new JsonSerializerSettings { Converters = new List { new NullResultValueTypeJsonConverter() }, FloatParseHandling = FloatParseHandling.Decimal }; var result = JsonConvert.DeserializeObject(ValidBacktestResultJson, settings); Assert.AreEqual(7, result.Charts.Count); Assert.IsTrue(result.Charts.All(kvp => !kvp.Value.IsEmpty())); } [Test] public void BacktestResult_NullChartPoint_IsSkipped() { var converter = new NullResultValueTypeJsonConverter(); var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; var deWithoutConverter = JsonConvert.DeserializeObject(InvalidBacktestResultJson.Replace(OrderStringReplace, EmptyJson), settings); var deWithConverter = JsonConvert.DeserializeObject(InvalidBacktestResultJson.Replace(OrderStringReplace, EmptyJson), converter); var noConverterPoints = GetChartPoints(deWithoutConverter).ToList(); var withConverterPoints = GetChartPoints(deWithConverter).ToList(); Assert.IsTrue(withConverterPoints.All(kvp => kvp.Value > 0)); Assert.AreEqual(4, withConverterPoints.Count); Assert.AreEqual(1, noConverterPoints.Count(kvp => !kvp.Value.HasValue)); Assert.AreEqual(5, noConverterPoints.Count); var convertedSerialized = JsonConvert.SerializeObject(deWithConverter); var roundtripDeserialization = JsonConvert.DeserializeObject(convertedSerialized); Assert.IsTrue(withConverterPoints.SequenceEqual(GetChartPoints(roundtripDeserialization).ToList())); } [Test] public void LiveResult_NullChartPoint_IsSkipped() { var converter = new NullResultValueTypeJsonConverter(); var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; var deWithoutConverter = JsonConvert.DeserializeObject(InvalidLiveResultJson.Replace(OrderStringReplace, EmptyJson), settings); var deWithConverter = JsonConvert.DeserializeObject(InvalidLiveResultJson.Replace(OrderStringReplace, EmptyJson), converter); var noConverterPoints = GetChartPoints(deWithoutConverter).ToList(); var withConverterPoints = GetChartPoints(deWithConverter).ToList(); Assert.IsTrue(withConverterPoints.All(kvp => kvp.Value > 0)); Assert.AreEqual(4, withConverterPoints.Count); Assert.AreEqual(1, noConverterPoints.Count(kvp => !kvp.Value.HasValue)); Assert.AreEqual(5, noConverterPoints.Count); var convertedSerialized = JsonConvert.SerializeObject(deWithConverter); var roundtripDeserialization = JsonConvert.DeserializeObject(convertedSerialized); Assert.IsTrue(withConverterPoints.SequenceEqual(GetChartPoints(roundtripDeserialization).ToList())); } [Test] public void NullCandleStickPoint_IsSkipped() { var converter = new NullResultValueTypeJsonConverter(); var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; var nullCandlestick = "{\"Charts\":{\"Strategy Equity\":{\"Name\":\"Strategy Equity\",\"ChartType\":0,\"Series\":{\"Equity\":{\"Name\":\"Equity\",\"Values\":[[1695991900,null,null,null,null],[1695992500,10000000,10000000,10000000,10000000]],\"SeriesType\":2,\"Index\":2,\"Unit\":\"\"}}}}}"; var deWithoutConverter = JsonConvert.DeserializeObject(nullCandlestick, settings); var deWithConverter = JsonConvert.DeserializeObject(nullCandlestick, converter); var noConverterPoints = deWithoutConverter.Charts["Strategy Equity"].Series["Equity"].Values.Cast().ToList(); var withConverterPoints = deWithConverter.Charts["Strategy Equity"].Series["Equity"].Values.Cast().ToList(); Assert.IsTrue(withConverterPoints.All(kvp => kvp.Close > 0)); Assert.AreEqual(1, withConverterPoints.Count); Assert.AreEqual(1, noConverterPoints.Count(kvp => !kvp.Close.HasValue)); Assert.AreEqual(2, noConverterPoints.Count); var convertedSerialized = JsonConvert.SerializeObject(deWithConverter); var roundtripDeserialization = JsonConvert.DeserializeObject(convertedSerialized); var roundTripCandlestick = roundtripDeserialization.Charts["Strategy Equity"].Series["Equity"].Values.Cast(); Assert.IsTrue(withConverterPoints.Select(x => x.Close).SequenceEqual(roundTripCandlestick.Select(x => x.Close))); } [Test] public void OrderTypeEnumStringAndValueDeserialization() { var settings = new JsonSerializerSettings { Converters = new[] { new NullResultValueTypeJsonConverter() } }; foreach (var orderType in (OrderType[])Enum.GetValues(typeof(OrderType))) { //var orderObjectType = OrderTypeNormalizingJsonConverter.TypeFromOrderTypeEnum(orderType); var intValueJson = OrderJson.Replace(OrderTypeStringReplace, ((int)orderType).ToStringInvariant()); var upperCaseJson = OrderJson.Replace(OrderTypeStringReplace, $"'{orderType.ToStringInvariant().ToUpperInvariant()}'"); var camelCaseJson = OrderJson.Replace(OrderTypeStringReplace, $"'{orderType.ToStringInvariant().ToCamelCase()}'"); var intValueLiveResult = InvalidLiveResultJson.Replace(OrderStringReplace, intValueJson); var upperCaseLiveResult = InvalidLiveResultJson.Replace(OrderStringReplace, upperCaseJson); var camelCaseLiveResult = InvalidLiveResultJson.Replace(OrderStringReplace, camelCaseJson); var intInstance = JsonConvert.DeserializeObject(intValueLiveResult, settings).Orders.Values.Single(); var upperCaseInstance = JsonConvert.DeserializeObject(upperCaseLiveResult, settings).Orders.Values.Single(); var camelCaseInstance = JsonConvert.DeserializeObject(camelCaseLiveResult, settings).Orders.Values.Single(); CollectionAssert.AreEqual(intInstance.BrokerId, upperCaseInstance.BrokerId); Assert.AreEqual(intInstance.ContingentId, upperCaseInstance.ContingentId); Assert.AreEqual(intInstance.Direction, upperCaseInstance.Direction); Assert.AreEqual(intInstance.TimeInForce.GetType(), upperCaseInstance.TimeInForce.GetType()); Assert.AreEqual(intInstance.Id, upperCaseInstance.Id); Assert.AreEqual(intInstance.Price, upperCaseInstance.Price); Assert.AreEqual(intInstance.PriceCurrency, upperCaseInstance.PriceCurrency); Assert.AreEqual(intInstance.SecurityType, upperCaseInstance.SecurityType); Assert.AreEqual(intInstance.Status, upperCaseInstance.Status); Assert.AreEqual(intInstance.Symbol, upperCaseInstance.Symbol); Assert.AreEqual(intInstance.Tag, upperCaseInstance.Tag); Assert.AreEqual(intInstance.Time, upperCaseInstance.Time); Assert.AreEqual(intInstance.CreatedTime, upperCaseInstance.CreatedTime); Assert.AreEqual(intInstance.LastFillTime, upperCaseInstance.LastFillTime); Assert.AreEqual(intInstance.LastUpdateTime, upperCaseInstance.LastUpdateTime); Assert.AreEqual(intInstance.CanceledTime, upperCaseInstance.CanceledTime); Assert.AreEqual(intInstance.Type, upperCaseInstance.Type); Assert.AreEqual(intInstance.Value, upperCaseInstance.Value); Assert.AreEqual(intInstance.Quantity, upperCaseInstance.Quantity); Assert.AreEqual(intInstance.TimeInForce.GetType(), upperCaseInstance.TimeInForce.GetType()); Assert.AreEqual(intInstance.Symbol.ID.Market, upperCaseInstance.Symbol.ID.Market); Assert.AreEqual(intInstance.OrderSubmissionData?.AskPrice, upperCaseInstance.OrderSubmissionData?.AskPrice); Assert.AreEqual(intInstance.OrderSubmissionData?.BidPrice, upperCaseInstance.OrderSubmissionData?.BidPrice); Assert.AreEqual(intInstance.OrderSubmissionData?.LastPrice, upperCaseInstance.OrderSubmissionData?.LastPrice); CollectionAssert.AreEqual(intInstance.BrokerId, camelCaseInstance.BrokerId); Assert.AreEqual(intInstance.ContingentId, camelCaseInstance.ContingentId); Assert.AreEqual(intInstance.Direction, camelCaseInstance.Direction); Assert.AreEqual(intInstance.TimeInForce.GetType(), camelCaseInstance.TimeInForce.GetType()); Assert.AreEqual(intInstance.Id, camelCaseInstance.Id); Assert.AreEqual(intInstance.Price, camelCaseInstance.Price); Assert.AreEqual(intInstance.PriceCurrency, camelCaseInstance.PriceCurrency); Assert.AreEqual(intInstance.SecurityType, camelCaseInstance.SecurityType); Assert.AreEqual(intInstance.Status, camelCaseInstance.Status); Assert.AreEqual(intInstance.Symbol, camelCaseInstance.Symbol); Assert.AreEqual(intInstance.Tag, camelCaseInstance.Tag); Assert.AreEqual(intInstance.Time, camelCaseInstance.Time); Assert.AreEqual(intInstance.CreatedTime, camelCaseInstance.CreatedTime); Assert.AreEqual(intInstance.LastFillTime, camelCaseInstance.LastFillTime); Assert.AreEqual(intInstance.LastUpdateTime, camelCaseInstance.LastUpdateTime); Assert.AreEqual(intInstance.CanceledTime, camelCaseInstance.CanceledTime); Assert.AreEqual(intInstance.Type, camelCaseInstance.Type); Assert.AreEqual(intInstance.Value, camelCaseInstance.Value); Assert.AreEqual(intInstance.Quantity, camelCaseInstance.Quantity); Assert.AreEqual(intInstance.TimeInForce.GetType(), camelCaseInstance.TimeInForce.GetType()); Assert.AreEqual(intInstance.Symbol.ID.Market, camelCaseInstance.Symbol.ID.Market); Assert.AreEqual(intInstance.OrderSubmissionData?.AskPrice, camelCaseInstance.OrderSubmissionData?.AskPrice); Assert.AreEqual(intInstance.OrderSubmissionData?.BidPrice, camelCaseInstance.OrderSubmissionData?.BidPrice); Assert.AreEqual(intInstance.OrderSubmissionData?.LastPrice, camelCaseInstance.OrderSubmissionData?.LastPrice); } } [TestCaseSource(nameof(CreatesReportParametersTableCorrectlyTestCases))] public void CreatesReportParametersTableCorrectly(string parametersTemplate, Dictionary parameters, string expectedParametersTable) { parametersTemplate = parametersTemplate.Replace("\r", string.Empty); var algorithmConfiguration = new AlgorithmConfiguration { Parameters = parameters }; var parametersReportElment = new ParametersReportElement("parameters", "", algorithmConfiguration, null, parametersTemplate); var parametersTable = parametersReportElment.Render(); expectedParametersTable = expectedParametersTable.Replace("\r", string.Empty); Assert.AreEqual(expectedParametersTable, parametersTable); } [TestCase(htmlExampleCode + @" ", @"", @"
{{$TEXT-CRISIS-TITLE}}
")] [TestCase(htmlExampleCode + @" ", @"", @" {{$FIRST-KPI-NAME}} {{$FIRST-KPI-VALUE}} {{$SECOND-KPI-NAME}} {{$SECOND-KPI-VALUE}} ")] [TestCase(@"", @"", @"
{{$TEXT-CRISIS-TITLE}}
")] [TestCase(@"", @"", @" {{$FIRST-KPI-NAME}} {{$FIRST-KPI-VALUE}} ")] public void GetsExpectedCrisisAndParametersHTMLCodes(string input, string pattern, string expected) { var htmlCode = GetRegexInInput(pattern, input); Assert.IsNotNull(htmlCode); Assert.AreEqual(expected, htmlCode); } [TestCase(htmlExampleCode + @" ", @"")] [TestCase(htmlExampleCode + @" ", @"")] [TestCase(@"", @"")] [TestCase(@"", @"")] [TestCase(@"
{{$TEXT-CRISIS-TITLE}}
crisis-->", @"")] [TestCase(@"")] [TestCase(@"
{{$TEXT-CRISIS-TITLE}}
", @"")] [TestCase(@" {{$FIRST-KPI-NAME}} {{$FIRST-KPI-VALUE}} parameters-->", @"")] [TestCase(@"")] [TestCase(@" {{$FIRST-KPI-NAME}} {{$FIRST-KPI-VALUE}} ", @"")] public void FindsNoMatchingForCrisisAndParametersInGivenInput(string input, string pattern) { var matching = GetRegexInInput(pattern, input); Assert.IsNull(matching); } public IEnumerable> GetChartPoints(Result result) { return result.Charts["Equity"].Series["Performance"].Values.Cast().Select(point => new KeyValuePair(point.x, point.y)); } private const string htmlExampleCode = @"
Strategy Report Summary: {{$TEXT-STRATEGY-NAME}} {{$TEXT-STRATEGY-VERSION}}
{{$HTML-CRISIS-PLOTS}}
Strategy Report Summary: {{$TEXT-STRATEGY-NAME}} {{$TEXT-STRATEGY-VERSION}}
{{$PARAMETERS}}
Parameters
"; public static object[] CreatesReportParametersTableCorrectlyTestCases = new object[] { // Happy test cases new object[] { @" {{$KEY0}} {{$VALUE0}} ", new Dictionary() { { "test-key-one", "1" }, { "test-key-two", "2" }, { "test-key-three", "three" } }, @" test-key-one 1 test-key-two 2 test-key-three three "}, new object[] { @" {{$KEY0}} {{$VALUE0}} {{$KEY1}} {{$VALUE1}} ", new Dictionary() { { "test-key-one", "1" }, { "test-key-two", "2" }, { "test-key-three", "three" } }, @" test-key-one 1 test-key-two 2 test-key-three three "}, new object[] { @" {{$KEY0}} {{$VALUE0}} {{$KEY1}} {{$VALUE1}} ", new Dictionary() { { "test-key-one", "1" }, { "test-key-two", "2" } }, @" test-key-one 1 test-key-two 2 "}, new object[] { @" {{$KEY0}} {{$VALUE0}} {{$KEY1}} {{$VALUE1}} {{$KEY2}} {{$VALUE2}} ", new Dictionary() { { "test-key-one", "1" }, { "test-key-two", "2" }, { "test-key-three", "three" }, { "test-key-four", "4"} }, @" test-key-one 1 test-key-two 2 test-key-three three test-key-four 4 "}, new object[] { @" {{$KEY0}} {{$VALUE0}} {{$KEY1}} {{$VALUE1}} {{$KEY2}} {{$VALUE2}} ", new Dictionary() { { "test-key-one", "1" }, { "test-key-two", "2" }, { "test-key-three", "three" }, { "test-key-four", "4"}, { "test-key-five", "5"} }, @" test-key-one 1 test-key-two 2 test-key-three three test-key-four 4 test-key-five 5 "}, new object[] { @" {{$KEY0}} {{$VALUE0}} {{$KEY1}} {{$VALUE1}} {{$KEY2}} {{$VALUE2}} {{$KEY3}} {{$VALUE3}} {{$KEY4}} {{$VALUE4}} {{$KEY5}} {{$VALUE5}} {{$KEY6}} {{$VALUE6}} {{$KEY7}} {{$VALUE7}} {{$KEY8}} {{$VALUE8}} {{$KEY9}} {{$VALUE9}} {{$KEY10}} {{$VALUE10}} {{$KEY11}} {{$VALUE11}} ", new Dictionary() { { "test-key-one", "1" }, { "test-key-two", "2" }, { "test-key-three", "three" }, { "test-key-four", "4" }, { "test-key-five", "5" }, { "test-key-six", "6" }, { "test-key-seven", "7" }, { "test-key-eight", "8" }, { "test-key-nine", "9" }, { "test-key-ten", "10"}, { "test-key-eleven", "11"}}, @" test-key-one 1 test-key-two 2 test-key-three three test-key-four 4 test-key-five 5 test-key-six 6 test-key-seven 7 test-key-eight 8 test-key-nine 9 test-key-ten 10 test-key-eleven 11 "}, // Sad test cases new object[] { @" {{$KEY1}} {{$VALUE1}} ", new Dictionary() { { "test-key-one", "1" }, { "test-key-two", "2" }, { "test-key-three", "three" } }, @" {{$KEY1}} {{$VALUE1}} {{$KEY1}} {{$VALUE1}} {{$KEY1}} {{$VALUE1}} "}, new object[] { @" {{$KEY0}} {{$VALUE}} ", new Dictionary() { { "test-key-one", "1" }, { "test-key-two", "2" }, { "test-key-three", "three" } }, @" test-key-one {{$VALUE}} test-key-two {{$VALUE}} test-key-three {{$VALUE}} "}, new object[] { @" {{$KEY1}} {{$VALUE0}} ", new Dictionary() { { "test-key-one", "1" }, { "test-key-two", "2" }, { "test-key-three", "three" } }, @" {{$KEY1}} 1 {{$KEY1}} 2 {{$KEY1}} three "}, new object[] { @" {{$KEY1}} {{$VALUE1}} {{$KEY2}} {{$VALUE2}} {{$KEY3}} {{$VALUE3}} ", new Dictionary() { { "test-key-one", "1" }, { "test-key-two", "2" }, { "test-key-three", "three" }, { "test-key-four", "4"} }, @" test-key-two 2 test-key-three three {{$KEY3}} {{$VALUE3}} {{$KEY3}} {{$VALUE3}} "}, }; } }