/// <summary>
        /// Generate the top N drawdown plot using the python libraries.
        /// </summary>
        public override string Render()
        {
            var backtestPoints = ResultsUtil.EquityPoints(_backtest);
            var livePoints     = ResultsUtil.EquityPoints(_live);

            var liveSeries     = new Series <DateTime, double>(livePoints.Keys, livePoints.Values);
            var strategySeries = DrawdownCollection.NormalizeResults(_backtest, _live);

            var seriesUnderwaterPlot = DrawdownCollection.GetUnderwater(strategySeries).DropMissing();
            var liveUnderwaterPlot   = backtestPoints.Count == 0 ? seriesUnderwaterPlot : seriesUnderwaterPlot.After(backtestPoints.Last().Key);
            var drawdownCollection   = DrawdownCollection.FromResult(_backtest, _live, periods: 5);

            var base64 = "";

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

                if (liveUnderwaterPlot.IsEmpty)
                {
                    backtestList.Append(seriesUnderwaterPlot.Keys.ToList().ToPython());
                    backtestList.Append(seriesUnderwaterPlot.Values.ToList().ToPython());
                }
                else
                {
                    backtestList.Append(seriesUnderwaterPlot.Before(liveUnderwaterPlot.FirstKey()).Keys.ToList().ToPython());
                    backtestList.Append(seriesUnderwaterPlot.Before(liveUnderwaterPlot.FirstKey()).Values.ToList().ToPython());
                }

                var liveList = new PyList();
                liveList.Append(liveUnderwaterPlot.Keys.ToList().ToPython());
                liveList.Append(liveUnderwaterPlot.Values.ToList().ToPython());

                var worstList = new PyList();
                var previousDrawdownPeriods = new List <KeyValuePair <DateTime, DateTime> >();

                foreach (var group in drawdownCollection.Drawdowns)
                {
                    // Skip drawdown periods that are overlapping
                    if (previousDrawdownPeriods.Where(kvp => (group.Start >= kvp.Key && group.Start <= kvp.Value) || (group.End >= kvp.Key && group.End <= kvp.Value)).Any())
                    {
                        continue;
                    }

                    var worst = new PyDict();
                    worst.SetItem("Begin", group.Start.ToPython());
                    worst.SetItem("End", group.End.ToPython());
                    worst.SetItem("Total", group.PeakToTrough.ToPython());

                    worstList.Append(worst);
                    previousDrawdownPeriods.Add(new KeyValuePair <DateTime, DateTime>(group.Start, group.End));
                }

                base64 = Charting.GetDrawdown(backtestList, liveList, worstList);
            }

            return(base64);
        }
        public void MaxDrawdown()
        {
            var series = new Deedle.Series <DateTime, double>(new []
            {
                new KeyValuePair <DateTime, double>(new DateTime(2020, 1, 1), 100000),
                new KeyValuePair <DateTime, double>(new DateTime(2020, 1, 2), 90000),
                new KeyValuePair <DateTime, double>(new DateTime(2020, 1, 3), 100000),
                new KeyValuePair <DateTime, double>(new DateTime(2020, 1, 4), 100000),
                new KeyValuePair <DateTime, double>(new DateTime(2020, 1, 5), 80000)
            });

            var collection = DrawdownCollection.GetDrawdownPeriods(series, 1).ToList();

            Assert.AreEqual(1, collection.Count);
            Assert.AreEqual(0.2, collection.First().Drawdown, 0.0001);
        }
示例#3
0
        /// <summary>
        /// The generated output string to be injected
        /// </summary>
        public override string Render()
        {
            if (_live == null)
            {
                var backtestDrawdown = _backtest?.TotalPerformance?.PortfolioStatistics?.Drawdown;
                Result = backtestDrawdown;
                return(backtestDrawdown?.ToString("P1") ?? "-");
            }

            var equityCurve = new SortedDictionary <DateTime, decimal>(DrawdownCollection.NormalizeResults(_backtest, _live)
                                                                       .Observations
                                                                       .ToDictionary(kvp => kvp.Key, kvp => (decimal)kvp.Value));

            var maxDrawdown = Statistics.Statistics.DrawdownPercent(equityCurve);

            Result = maxDrawdown;

            return($"{maxDrawdown:P1}");
        }
示例#4
0
        /// <summary>
        /// The generated output string to be injected
        /// </summary>
        public override string Render()
        {
            var equityCurve = _live == null
                ? new Series <DateTime, double>(ResultsUtil.EquityPoints(_backtest))
                : DrawdownCollection.NormalizeResults(_backtest, _live);

            if (equityCurve.IsEmpty)
            {
                return("-");
            }

            var years = (decimal)(equityCurve.LastKey() - equityCurve.FirstKey()).TotalDays / 365m;

            Result = Statistics.Statistics.CompoundingAnnualPerformance(
                equityCurve.FirstValue().SafeDecimalCast(),
                equityCurve.LastValue().SafeDecimalCast(),
                years);

            return(((decimal?)Result)?.ToString("P1") ?? "-");
        }
示例#5
0
        /// <summary>
        /// The generated output string to be injected
        /// </summary>
        public override string Render()
        {
            if (_live == null)
            {
                return(_backtest?.TotalPerformance?.PortfolioStatistics?.Drawdown.ToString("P1") ?? "-");
            }

            var backtestEquityPoints = new Series <DateTime, double>(ResultsUtil.EquityPoints(_backtest));
            var liveEquityPoints     = new Series <DateTime, double>(ResultsUtil.EquityPoints(_live));

            var backtestDrawdownGroups = new DrawdownCollection(backtestEquityPoints, 1);
            var liveDrawdownGroups     = new DrawdownCollection(liveEquityPoints, 1);

            var separateResultsMaxDrawdown = backtestDrawdownGroups.Drawdowns
                                             .Concat(liveDrawdownGroups.Drawdowns)
                                             .Select(x => x.PeakToTrough)
                                             .OrderByDescending(x => x)
                                             .FirstOrDefault();

            return($"{separateResultsMaxDrawdown:P1}");
        }
示例#6
0
        /// <summary>
        /// The generated output string to be injected
        /// </summary>
        public override string Render()
        {
            decimal?psr;

            if (_live == null)
            {
                psr    = _backtest?.TotalPerformance?.PortfolioStatistics?.ProbabilisticSharpeRatio;
                Result = psr;
                if (psr == null)
                {
                    return("-");
                }

                return($"{psr:P0}");
            }

            var equityCurvePerformance = DrawdownCollection.NormalizeResults(_backtest, _live)
                                         .ResampleEquivalence(date => date.Date, s => s.LastValue())
                                         .PercentChange();

            if (equityCurvePerformance.IsEmpty || equityCurvePerformance.KeyCount < 180)
            {
                return("-");
            }

            var sixMonthsBefore = equityCurvePerformance.LastKey() - TimeSpan.FromDays(180);

            var benchmarkSharpeRatio = 1.0d / Math.Sqrt(252);

            psr = Statistics.Statistics.ProbabilisticSharpeRatio(
                equityCurvePerformance
                .Where(kvp => kvp.Key >= sixMonthsBefore)
                .Values
                .ToList(),
                benchmarkSharpeRatio)
                  .SafeDecimalCast();

            Result = psr;
            return($"{psr:P0}");
        }
        public void NoDrawdown(bool hasEquityPoint)
        {
            var strategyEquityChart = new Chart("Strategy Equity");
            var equitySeries        = new Series("Equity");

            strategyEquityChart.AddSeries(equitySeries);

            if (hasEquityPoint)
            {
                equitySeries.AddPoint(new DateTime(2020, 1, 1), 100000);
            }

            var backtest = new BacktestResult
            {
                Charts = new Dictionary <string, Chart> {
                    [strategyEquityChart.Name] = strategyEquityChart
                }
            };

            var normalizedResults = DrawdownCollection.NormalizeResults(backtest, null);

            Assert.AreEqual(0, normalizedResults.KeyCount);
            Assert.AreEqual(0, normalizedResults.ValueCount);
        }