Lean  $LEAN_TAG$
CumulativeReturnsReportElement.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using Deedle;
17 using System.Linq;
18 using Python.Runtime;
19 using QuantConnect.Packets;
20 using System;
21 using QuantConnect.Util;
22 using System.Collections.Generic;
23 
25 {
26  internal sealed class CumulativeReturnsReportElement : ChartReportElement
27  {
28  private LiveResult _live;
29  private BacktestResult _backtest;
30 
31  /// <summary>
32  /// Create a new array of cumulative percentage return of strategy and benchmark
33  /// </summary>
34  /// <param name="name">Name of the widget</param>
35  /// <param name="key">Location of injection</param>
36  /// <param name="backtest">Backtest result object</param>
37  /// <param name="live">Live result object</param>
38  public CumulativeReturnsReportElement(string name, string key, BacktestResult backtest, LiveResult live)
39  {
40  _live = live;
41  _backtest = backtest;
42  Name = name;
43  Key = key;
44  }
45 
46  /// <summary>
47  /// Generate the cumulative return of the backtest, benchmark, and live
48  /// strategy using the ReportCharts.py python library
49  /// </summary>
50  public override string Render()
51  {
52  var backtestReturns = ResultsUtil.EquityPoints(_backtest);
53  var benchmark = ResultsUtil.BenchmarkPoints(_backtest);
54  var liveReturns = ResultsUtil.EquityPoints(_live);
55  var liveBenchmark = ResultsUtil.BenchmarkPoints(_live);
56 
57  var backtestTime = backtestReturns.Keys.ToList();
58  var backtestStrategy = backtestReturns.Values.ToList();
59  var benchmarkTime = benchmark.Keys.ToList();
60  var benchmarkPoints = benchmark.Values.ToList();
61 
62  var liveTime = liveReturns.Keys.ToList();
63  var liveStrategy = liveReturns.Values.ToList();
64  var liveBenchmarkTime = liveBenchmark.Keys.ToList();
65  var liveBenchmarkStrategy = liveBenchmark.Values.ToList();
66 
67  var base64 = "";
68  using (Py.GIL())
69  {
70  var backtestList = new PyList();
71  var liveList = new PyList();
72 
73  var backtestSeries = new Series<DateTime, double>(backtestTime, backtestStrategy);
74  var liveSeries = new Series<DateTime, double>(liveTime, liveStrategy);
75  var backtestBenchmarkSeries = new Series<DateTime, double>(benchmarkTime, benchmarkPoints);
76  var liveBenchmarkSeries = new Series<DateTime, double>(liveBenchmarkTime, liveBenchmarkStrategy);
77 
78  // Equivalent in python using pandas for the following operations is:
79  // --------------------------------------------------
80  // >>> # note: [...] denotes the data we're passing in
81  // >>> df = pd.Series([...], index=time)
82  // >>> df_live = pd.Series([...], index=live_time)
83  // >>> df_live = df_live.mul(df.iloc[-1] / df_live.iloc[0]).fillna(method='ffill').dropna()
84  // >>> df_final = pd.concat([df, df_live], axis=0)
85  // >>> df_cumulative_returns = ((df_final.pct_change().dropna() + 1).cumprod() - 1)
86  // --------------------------------------------------
87  //
88  // We multiply the final value of the backtest and benchmark to have a continuous graph showing the performance out of sample
89  // as a continuation of the cumulative returns graph. Otherwise, we start plotting from 0% and not the last value of the backtest data
90 
91  var backtestLastValue = backtestSeries.ValueCount == 0 ? 0 : backtestSeries.LastValue();
92  var backtestBenchmarkLastValue = backtestBenchmarkSeries.ValueCount == 0 ? 0 : backtestBenchmarkSeries.LastValue();
93 
94  var liveContinuousEquity = liveSeries;
95  var liveBenchContinuousEquity = liveBenchmarkSeries;
96 
97  if (liveSeries.ValueCount != 0)
98  {
99  liveContinuousEquity = (liveSeries * (backtestLastValue / liveSeries.FirstValue()))
100  .FillMissing(Direction.Forward)
101  .DropMissing();
102  }
103  if (liveBenchmarkSeries.ValueCount != 0)
104  {
105  liveBenchContinuousEquity = (liveBenchmarkSeries * (backtestBenchmarkLastValue / liveBenchmarkSeries.FirstValue()))
106  .FillMissing(Direction.Forward)
107  .DropMissing();
108  }
109 
110  var liveStart = liveContinuousEquity.ValueCount == 0 ? DateTime.MaxValue : liveContinuousEquity.DropMissing().FirstKey();
111  var liveBenchStart = liveBenchContinuousEquity.ValueCount == 0 ? DateTime.MaxValue : liveBenchContinuousEquity.DropMissing().FirstKey();
112 
113  var finalEquity = backtestSeries.Where(kvp => kvp.Key < liveStart).Observations.ToList();
114  var finalBenchEquity = backtestBenchmarkSeries.Where(kvp => kvp.Key < liveBenchStart).Observations.ToList();
115 
116  finalEquity.AddRange(liveContinuousEquity.Observations);
117  finalBenchEquity.AddRange(liveBenchContinuousEquity.Observations);
118 
119  var finalSeries = (new Series<DateTime, double>(finalEquity).CumulativeReturns() * 100)
120  .FillMissing(Direction.Forward)
121  .DropMissing();
122 
123  var finalBenchSeries = (new Series<DateTime, double>(finalBenchEquity).CumulativeReturns() * 100)
124  .FillMissing(Direction.Forward)
125  .DropMissing();
126 
127  var backtestCumulativePercent = finalSeries.Where(kvp => kvp.Key < liveStart);
128  var backtestBenchmarkCumulativePercent = finalBenchSeries.Where(kvp => kvp.Key < liveBenchStart);
129 
130  var liveCumulativePercent = finalSeries.Where(kvp => kvp.Key >= liveStart);
131  var liveBenchmarkCumulativePercent = finalBenchSeries.Where(kvp => kvp.Key >= liveBenchStart);
132 
133  backtestList.Append(backtestCumulativePercent.Keys.ToList().ToPython());
134  backtestList.Append(backtestCumulativePercent.Values.ToList().ToPython());
135  backtestList.Append(backtestBenchmarkCumulativePercent.Keys.ToList().ToPython());
136  backtestList.Append(backtestBenchmarkCumulativePercent.Values.ToList().ToPython());
137 
138  liveList.Append(liveCumulativePercent.Keys.ToList().ToPython());
139  liveList.Append(liveCumulativePercent.Values.ToList().ToPython());
140  liveList.Append(liveBenchmarkCumulativePercent.Keys.ToList().ToPython());
141  liveList.Append(liveBenchmarkCumulativePercent.Values.ToList().ToPython());
142 
143  base64 = Charting.GetCumulativeReturns(backtestList, liveList);
144  }
145 
146  return base64;
147  }
148  }
149 }