/// <summary>
        /// Generate the cumulative return of the backtest, benchmark, and live
        /// strategy using the ReportCharts.py python library
        /// </summary>
        public override string Render()
        {
            var backtestReturns = ResultsUtil.EquityPoints(_backtest);
            var benchmark       = ResultsUtil.BenchmarkPoints(_backtest);
            var liveReturns     = ResultsUtil.EquityPoints(_live);
            var liveBenchmark   = ResultsUtil.BenchmarkPoints(_live);

            var backtestTime     = backtestReturns.Keys.ToList();
            var backtestStrategy = backtestReturns.Values.ToList();
            var benchmarkTime    = benchmark.Keys.ToList();
            var benchmarkPoints  = benchmark.Values.ToList();

            var liveTime              = liveReturns.Keys.ToList();
            var liveStrategy          = liveReturns.Values.ToList();
            var liveBenchmarkTime     = liveBenchmark.Keys.ToList();
            var liveBenchmarkStrategy = liveBenchmark.Values.ToList();

            var base64 = "";

            using (Py.GIL())
            {
                var backtestList = new PyList();
                var liveList     = new PyList();

                var backtestSeries          = new Series <DateTime, double>(backtestTime, backtestStrategy);
                var liveSeries              = new Series <DateTime, double>(liveTime, liveStrategy);
                var backtestBenchmarkSeries = new Series <DateTime, double>(benchmarkTime, benchmarkPoints);
                var liveBenchmarkSeries     = new Series <DateTime, double>(liveBenchmarkTime, liveBenchmarkStrategy);

                // Equivalent in python using pandas for the following operations is:
                // --------------------------------------------------
                // >>> # note: [...] denotes the data we're passing in
                // >>> df = pd.Series([...], index=time)
                // >>> df_live = pd.Series([...], index=live_time)
                // >>> df_live = df_live.mul(df.iloc[-1] / df_live.iloc[0]).fillna(method='ffill').dropna()
                // >>> df_final = pd.concat([df, df_live], axis=0)
                // >>> df_cumulative_returns = ((df_final.pct_change().dropna() + 1).cumprod() - 1)
                // --------------------------------------------------
                //
                // We multiply the final value of the backtest and benchmark to have a continuous graph showing the performance out of sample
                // as a continuation of the cumulative returns graph. Otherwise, we start plotting from 0% and not the last value of the backtest data

                var backtestLastValue          = backtestSeries.ValueCount == 0 ? 0 : backtestSeries.LastValue();
                var backtestBenchmarkLastValue = backtestBenchmarkSeries.ValueCount == 0 ? 0 : backtestBenchmarkSeries.LastValue();

                var liveContinuousEquity      = liveSeries;
                var liveBenchContinuousEquity = liveBenchmarkSeries;

                if (liveSeries.ValueCount != 0)
                {
                    liveContinuousEquity = (liveSeries * (backtestLastValue / liveSeries.FirstValue()))
                                           .FillMissing(Direction.Forward)
                                           .DropMissing();
                }
                if (liveBenchmarkSeries.ValueCount != 0)
                {
                    liveBenchContinuousEquity = (liveBenchmarkSeries * (backtestBenchmarkLastValue / liveBenchmarkSeries.FirstValue()))
                                                .FillMissing(Direction.Forward)
                                                .DropMissing();
                }

                var liveStart      = liveContinuousEquity.ValueCount == 0 ? DateTime.MaxValue : liveContinuousEquity.DropMissing().FirstKey();
                var liveBenchStart = liveBenchContinuousEquity.ValueCount == 0 ? DateTime.MaxValue : liveBenchContinuousEquity.DropMissing().FirstKey();

                var finalEquity      = backtestSeries.Where(kvp => kvp.Key < liveStart).Observations.ToList();
                var finalBenchEquity = backtestBenchmarkSeries.Where(kvp => kvp.Key < liveBenchStart).Observations.ToList();

                finalEquity.AddRange(liveContinuousEquity.Observations);
                finalBenchEquity.AddRange(liveBenchContinuousEquity.Observations);

                var finalSeries = (new Series <DateTime, double>(finalEquity).CumulativeReturns() * 100)
                                  .FillMissing(Direction.Forward)
                                  .DropMissing();

                var finalBenchSeries = (new Series <DateTime, double>(finalBenchEquity).CumulativeReturns() * 100)
                                       .FillMissing(Direction.Forward)
                                       .DropMissing();

                var backtestCumulativePercent          = finalSeries.Where(kvp => kvp.Key < liveStart);
                var backtestBenchmarkCumulativePercent = finalBenchSeries.Where(kvp => kvp.Key < liveBenchStart);

                var liveCumulativePercent          = finalSeries.Where(kvp => kvp.Key >= liveStart);
                var liveBenchmarkCumulativePercent = finalBenchSeries.Where(kvp => kvp.Key >= liveBenchStart);

                backtestList.Append(backtestCumulativePercent.Keys.ToList().ToPython());
                backtestList.Append(backtestCumulativePercent.Values.ToList().ToPython());
                backtestList.Append(backtestBenchmarkCumulativePercent.Keys.ToList().ToPython());
                backtestList.Append(backtestBenchmarkCumulativePercent.Values.ToList().ToPython());

                liveList.Append(liveCumulativePercent.Keys.ToList().ToPython());
                liveList.Append(liveCumulativePercent.Values.ToList().ToPython());
                liveList.Append(liveBenchmarkCumulativePercent.Keys.ToList().ToPython());
                liveList.Append(liveBenchmarkCumulativePercent.Values.ToList().ToPython());

                base64 = Charting.GetCumulativeReturns(backtestList, liveList);
            }

            return(base64);
        }
        /// <summary>
        /// Generate the cumulative return of the backtest, benchmark, and live
        /// strategy using the ReportCharts.py python library
        /// </summary>
        public override string Render()
        {
            var backtestReturns = ResultsUtil.EquityPoints(_backtest);
            var benchmark       = ResultsUtil.BenchmarkPoints(_backtest);
            var liveReturns     = ResultsUtil.EquityPoints(_live);
            var liveBenchmark   = ResultsUtil.BenchmarkPoints(_live);

            var backtestTime     = backtestReturns.Keys.ToList();
            var backtestStrategy = backtestReturns.Values.ToList();
            var benchmarkTime    = benchmark.Keys.ToList();
            var benchmarkPoints  = benchmark.Values.ToList();

            var liveTime              = liveReturns.Keys.ToList();
            var liveStrategy          = liveReturns.Values.ToList();
            var liveBenchmarkTime     = liveBenchmark.Keys.ToList();
            var liveBenchmarkStrategy = liveBenchmark.Values.ToList();

            var base64 = "";

            using (Py.GIL())
            {
                var backtestList = new PyList();
                var liveList     = new PyList();

                var backtestSeries          = new Series <DateTime, double>(backtestTime, backtestStrategy);
                var liveSeries              = new Series <DateTime, double>(liveTime, liveStrategy);
                var backtestBenchmarkSeries = new Series <DateTime, double>(benchmarkTime, benchmarkPoints);
                var liveBenchmarkSeries     = new Series <DateTime, double>(liveBenchmarkTime, liveBenchmarkStrategy);

                // Equivalent in python using pandas for the following operations is:
                //
                // df.pct_change().cumsum().mul(100)
                var backtestCumulativePercent          = (backtestSeries.CumulativeReturns() * 100).FillMissing(Direction.Forward).DropMissing();
                var backtestBenchmarkCumulativePercent = (backtestBenchmarkSeries.CumulativeReturns() * 100).FillMissing(Direction.Forward).DropMissing();

                // Equivalent in python using pandas for the following operations is:
                // --------------------------------------------------
                // # note: [...] denotes the data we're passing in
                // bt = pd.Series([...], index=time)
                // df.pct_change().replace([np.inf, -np.inf], np.nan).dropna().cumsum().mul(100).add(bt.iloc[-1])
                // --------------------------------------------------
                //
                // We add the final value of the backtest and benchmark to have a continuous graph showing the performance out of sample
                // as a continuation of the cumulative returns graph. Otherwise, we start plotting from 0% and not the last value of the backtest data

                var backtestLastValue          = backtestCumulativePercent.IsEmpty ? 0 : backtestCumulativePercent.LastValue();
                var backtestBenchmarkLastValue = backtestBenchmarkCumulativePercent.IsEmpty ? 0 : backtestBenchmarkCumulativePercent.LastValue();

                var liveCumulativePercent          = (liveSeries.CumulativeReturns() * 100).FillMissing(Direction.Forward).DropMissing() + backtestLastValue;
                var liveBenchmarkCumulativePercent = (liveBenchmarkSeries.CumulativeReturns() * 100).FillMissing(Direction.Forward).DropMissing() + backtestBenchmarkLastValue;

                backtestList.Append(backtestCumulativePercent.Keys.ToList().ToPython());
                backtestList.Append(backtestCumulativePercent.Values.ToList().ToPython());
                backtestList.Append(backtestBenchmarkCumulativePercent.Keys.ToList().ToPython());
                backtestList.Append(backtestBenchmarkCumulativePercent.Values.ToList().ToPython());

                liveList.Append(liveCumulativePercent.Keys.ToList().ToPython());
                liveList.Append(liveCumulativePercent.Values.ToList().ToPython());
                liveList.Append(liveBenchmarkCumulativePercent.Keys.ToList().ToPython());
                liveList.Append(liveBenchmarkCumulativePercent.Values.ToList().ToPython());

                base64 = Charting.GetCumulativeReturns(backtestList, liveList);
            }

            return(base64);
        }