/// <summary>
        /// Generate the rolling portfolio beta to equities plot using the python libraries.
        /// </summary>
        public override string Render()
        {
            var backtestPoints          = ResultsUtil.EquityPoints(_backtest);
            var backtestBenchmarkPoints = ResultsUtil.BenchmarkPoints(_backtest);
            var livePoints          = ResultsUtil.EquityPoints(_live);
            var liveBenchmarkPoints = ResultsUtil.BenchmarkPoints(_live);

            var backtestSeries          = new Series <DateTime, double>(backtestPoints);
            var backtestBenchmarkSeries = new Series <DateTime, double>(backtestBenchmarkPoints);
            var liveSeries          = new Series <DateTime, double>(livePoints);
            var liveBenchmarkSeries = new Series <DateTime, double>(liveBenchmarkPoints);

            var base64 = "";

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

                var backtestRollingBetaSixMonths    = Rolling.Beta(backtestSeries, backtestBenchmarkSeries, windowSize: 22 * 6);
                var backtestRollingBetaTwelveMonths = Rolling.Beta(backtestSeries, backtestBenchmarkSeries, windowSize: 252);

                backtestList.Append(backtestRollingBetaSixMonths.Keys.ToList().ToPython());
                backtestList.Append(backtestRollingBetaSixMonths.Values.ToList().ToPython());
                backtestList.Append(backtestRollingBetaTwelveMonths.Keys.ToList().ToPython());
                backtestList.Append(backtestRollingBetaTwelveMonths.Values.ToList().ToPython());

                var liveRollingBetaSixMonths    = Rolling.Beta(liveSeries, liveBenchmarkSeries, windowSize: 22 * 6);
                var liveRollingBetaTwelveMonths = Rolling.Beta(liveSeries, liveBenchmarkSeries, windowSize: 252);

                liveList.Append(liveRollingBetaSixMonths.Keys.ToList().ToPython());
                liveList.Append(liveRollingBetaSixMonths.Values.ToList().ToPython());
                liveList.Append(liveRollingBetaTwelveMonths.Keys.ToList().ToPython());
                liveList.Append(liveRollingBetaTwelveMonths.Values.ToList().ToPython());

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

            return(base64);
        }
        /// <summary>
        /// The generated output string to be injected
        /// </summary>
        public override string Render()
        {
            var backtestPoints          = ResultsUtil.EquityPoints(_backtest);
            var backtestBenchmarkPoints = ResultsUtil.BenchmarkPoints(_backtest);

            var backtestSeries          = new Series <DateTime, double>(backtestPoints.Keys, backtestPoints.Values);
            var backtestBenchmarkSeries = new Series <DateTime, double>(backtestBenchmarkPoints.Keys, backtestBenchmarkPoints.Values);

            var html = new List <string>();

            foreach (var crisisEvent in Crisis.Events)
            {
                using (Py.GIL())
                {
                    var crisis = crisisEvent.Value;
                    var data   = new PyList();
                    var frame  = Frame.CreateEmpty <DateTime, string>();

                    // The two following operations are equivalent to Pandas' `df.resample("D").sum()`
                    frame["Backtest"]  = backtestSeries.ResampleEquivalence(date => date.Date, s => s.LastValue());
                    frame["Benchmark"] = backtestBenchmarkSeries.ResampleEquivalence(date => date.Date, s => s.LastValue());

                    var crisisFrame = frame.Where(kvp => kvp.Key >= crisis.Start && kvp.Key <= crisis.End);
                    crisisFrame = crisisFrame.Join("BacktestPercent", crisisFrame["Backtest"].CumulativeReturns());
                    crisisFrame = crisisFrame.Join("BenchmarkPercent", crisisFrame["Benchmark"].CumulativeReturns());

                    // Pad out all missing values to start from 0 for nice plots
                    crisisFrame = crisisFrame.FillMissing(Direction.Forward).FillMissing(0.0);

                    data.Append(crisisFrame.RowKeys.ToList().ToPython());
                    data.Append(crisisFrame["BacktestPercent"].Values.ToList().ToPython());
                    data.Append(crisisFrame["BenchmarkPercent"].Values.ToList().ToPython());

                    var base64 = (string)Charting.GetCrisisEventsPlots(data, crisis.Name.Replace("/", "").Replace(".", "").Replace(" ", ""));

                    if (base64 == _emptyChart)
                    {
                        continue;
                    }

                    if (!crisisFrame.IsEmpty)
                    {
                        var contents = _template.Replace(ReportKey.CrisisTitle, crisis.ToString(crisisFrame.GetRowKeyAt(0), crisisFrame.GetRowKeyAt(crisisFrame.RowCount - 1)));
                        contents = contents.Replace(ReportKey.CrisisContents, base64);

                        html.Add(contents);
                    }
                }
            }

            if (Key == ReportKey.CrisisPageStyle)
            {
                if (html.Count == 0)
                {
                    return("display: none;");
                }

                return(string.Empty);
            }

            return(string.Join("\n", html));
        }
        /// <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);
        }