19 using System.Collections.Generic;
32 public DateTime
Start {
get;
private set; }
37 public DateTime
End {
get;
private set; }
48 public List<DrawdownPeriod>
Drawdowns {
get;
private set; }
70 Start = strategySeries.IsEmpty ? DateTime.MinValue : strategySeries.FirstKey();
71 End = strategySeries.IsEmpty ? DateTime.MaxValue : strategySeries.LastKey();
72 Drawdowns = drawdowns.OrderByDescending(x => x.PeakToTrough)
100 if (backtestPoints.Count < 2 && livePoints.Count < 2)
105 var startingEquity = backtestPoints.Count == 0 ? livePoints.First().Value : backtestPoints.First().Value;
110 var backtestSeries =
new Series<DateTime, double>(backtestPoints).PercentChange().Where(kvp => !
double.IsInfinity(kvp.Value)).CumulativeSum();
111 var liveSeries =
new Series<DateTime, double>(livePoints).PercentChange().Where(kvp => !
double.IsInfinity(kvp.Value)).CumulativeSum();
114 var firstLiveKey = liveSeries.IsEmpty ? backtestSeries.LastKey().AddDays(1) : liveSeries.FirstKey();
117 if (!backtestSeries.IsEmpty)
119 var filtered = backtestSeries.Where(kvp => kvp.Key < firstLiveKey);
120 liveSeries = filtered.IsEmpty ? liveSeries : liveSeries + filtered.LastValue();
133 return backtestSeries.Merge(liveSeries, UnionBehavior.PreferRight)
134 .FillMissing(Direction.Forward)
137 .SelectValues(x => x + 1)
139 .SelectValues(x => x * startingEquity);
155 var returns = curve / curve.FirstValue();
156 var cumulativeMax = returns.CumulativeMax();
158 return (1 - (returns / cumulativeMax)) * -1;
170 var frame = Frame.CreateEmpty<DateTime,
string>();
176 var returns = curve / curve.FirstValue();
177 var cumulativeMax = returns.CumulativeMax();
178 var drawdown = 1 - (returns / cumulativeMax);
180 frame.AddColumn(
"returns", returns);
181 frame.AddColumn(
"cumulativeMax", cumulativeMax);
182 frame.AddColumn(
"drawdown", drawdown);
196 var frame = Frame.CreateEmpty<DateTime,
string>();
202 var returns = curve / curve.FirstValue();
203 var cumulativeMax = returns.CumulativeMax();
204 var drawdown = 1 - (returns / cumulativeMax);
206 var groups = cumulativeMax.GroupBy(kvp => kvp.Value);
208 var drawdownGroups =
new List<Tuple<DateTime, double, double, double>>();
210 foreach (var group
in groups.Values)
212 var firstDate = group.SortByKey().FirstKey();
213 var lastDate = group.SortByKey().LastKey();
215 var cumulativeMaxGroup = cumulativeMax.Between(firstDate, lastDate);
216 var drawdownGroup = drawdown.Between(firstDate, lastDate);
217 var drawdownGroupMax = drawdownGroup.
Values.Max();
219 var drawdownMax = drawdownGroup.Where(kvp => kvp.Value == drawdownGroupMax);
221 drawdownGroups.Add(
new Tuple<DateTime, double, double, double>(
222 drawdownMax.FirstKey(),
224 cumulativeMaxGroup.FirstValue(),
225 drawdownMax.FirstValue()
229 var drawdowns =
new Series<DateTime, double>(drawdownGroups.Select(x => x.Item1), drawdownGroups.Select(x => x.Item4));
231 var sortedDrawdowns = drawdowns.SortBy(x => -x);
233 var periodsToTake = periods < sortedDrawdowns.ValueCount ? periods : sortedDrawdowns.ValueCount;
236 var topDrawdowns =
new Series<DateTime, double>(sortedDrawdowns.Keys.Take(periodsToTake), sortedDrawdowns.Values.Take(periodsToTake));
237 var topDurations =
new Series<DateTime, double>(topDrawdowns.Keys.OrderBy(x => x), drawdownGroups.Where(t => topDrawdowns.Keys.Contains(t.Item1)).OrderBy(x => x.Item1).Select(x => x.Item2));
238 var topCumulativeMax =
new Series<DateTime, double>(topDrawdowns.Keys.OrderBy(x => x), drawdownGroups.Where(t => topDrawdowns.Keys.Contains(t.Item1)).OrderBy(x => x.Item1).Select(x => x.Item3));
240 frame.AddColumn(
"duration", topDurations);
241 frame.AddColumn(
"cumulativeMax", topCumulativeMax);
242 frame.AddColumn(
"drawdown", topDrawdowns);
258 for (var i = 1; i <= topDrawdowns.RowCount; i++)
260 var data = DrawdownGroup(frame, topDrawdowns[
"cumulativeMax"].GetAt(i - 1));
263 yield
return new DrawdownPeriod(data.Item1, data.Item2, data.Item3);
267 private static Tuple<DateTime, DateTime, double> DrawdownGroup(Frame<DateTime, string> frame,
double groupMax)
269 var drawdownAfter = frame[
"cumulativeMax"].Where(kvp => kvp.Value > groupMax);
270 var drawdownGroup = frame[
"cumulativeMax"].Where(kvp => kvp.Value == groupMax);
271 var groupDrawdown = frame[
"drawdown"].Realign(drawdownGroup.Keys).Max();
273 var groupStart = drawdownGroup.FirstKey();
276 var groupEnd = drawdownAfter.IsEmpty ? drawdownGroup.LastKey() : drawdownAfter.FirstKey();
278 return new Tuple<DateTime, DateTime, double>(groupStart, groupEnd, groupDrawdown);