Lean  $LEAN_TAG$
DeedleUtil.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;
18 using System.Collections.Generic;
19 using System.Linq;
20 
21 namespace QuantConnect.Report
22 {
23  /// <summary>
24  /// Utility extension methods for Deedle series/frames
25  /// </summary>
26  public static class DeedleUtil
27  {
28  /// <summary>
29  /// Calculates the cumulative sum for the given series
30  /// </summary>
31  /// <param name="input">Series to calculate cumulative sum for</param>
32  /// <returns>Cumulative sum in series form</returns>
34  {
35  if (input.IsEmpty)
36  {
37  return input;
38  }
39 
40  var prev = 0.0;
41 
42  return input.SelectValues(current =>
43  {
44  var sum = prev + current;
45  prev = sum;
46 
47  return sum;
48  });
49  }
50 
51  /// <summary>
52  /// Calculates the cumulative product of the series. This is equal to the python pandas method: `df.cumprod()`
53  /// </summary>
54  /// <param name="input">Input series</param>
55  /// <returns>Cumulative product</returns>
57  {
58  if (input.IsEmpty)
59  {
60  return input;
61  }
62 
63  var prev = 1.0;
64 
65  return input.SelectValues(current =>
66  {
67  var product = prev * current;
68  prev = product;
69 
70  return product;
71  });
72  }
73 
74  /// <summary>
75  /// Calculates the cumulative max of the series. This is equal to the python pandas method: `df.cummax()`.
76  /// </summary>
77  /// <param name="input"></param>
78  /// <returns></returns>
80  {
81  if (input.IsEmpty)
82  {
83  return input;
84  }
85 
86  var prevMax = double.NegativeInfinity;
87  var values = new List<double>();
88 
89  foreach (var point in input.Values)
90  {
91  if (point > prevMax)
92  {
93  prevMax = point;
94  }
95 
96  values.Add(prevMax);
97  }
98 
99  return new Series<DateTime, double>(input.Keys, values);
100  }
101 
102  /// <summary>
103  /// Calculates the percentage change from the previous value to the current
104  /// </summary>
105  /// <param name="input">Series to calculate percentage change for</param>
106  /// <returns>Percentage change in series form</returns>
107  /// <remarks>Equivalent to `df.pct_change()`</remarks>
109  {
110  if (input.IsEmpty)
111  {
112  return input;
113  }
114 
115  var inputShifted = input.Shift(1);
116 
117  return (input - inputShifted) / inputShifted;
118  }
119 
120  /// <summary>
121  /// Calculates the cumulative returns series of the given input equity curve
122  /// </summary>
123  /// <param name="input">Equity curve series</param>
124  /// <returns>Cumulative returns over time</returns>
126  {
127  if (input.IsEmpty)
128  {
129  return input;
130  }
131 
132  return (input.PercentChange()
133  .Where(kvp => !double.IsInfinity(kvp.Value)) + 1)
134  .CumulativeProduct() - 1;
135  }
136 
137  /// <summary>
138  /// Calculates the total returns over a period of time for the given input
139  /// </summary>
140  /// <param name="input">Equity curve series</param>
141  /// <returns>Total returns over time</returns>
142  public static double TotalReturns(this Series<DateTime, double> input)
143  {
144  var returns = input.CumulativeReturns();
145 
146  if (returns.IsEmpty)
147  {
148  return double.NaN;
149  }
150 
151  return returns.LastValue();
152  }
153 
154  /// <summary>
155  /// Drops sparse columns only if every value is `missing` in the column
156  /// </summary>
157  /// <typeparam name="TRowKey">Frame row key</typeparam>
158  /// <typeparam name="TColumnKey">Frame column key</typeparam>
159  /// <param name="frame">Data Frame</param>
160  /// <returns>new Frame with sparse columns dropped</returns>
161  /// <remarks>Equivalent to `df.dropna(axis=1, how='all')`</remarks>
162  public static Frame<TRowKey, TColumnKey> DropSparseColumnsAll<TRowKey, TColumnKey>(this Frame<TRowKey, TColumnKey> frame)
163  {
164  var newFrame = frame.Clone();
165 
166  foreach (var key in frame.ColumnKeys)
167  {
168  if (newFrame[key].DropMissing().ValueCount == 0)
169  {
170  newFrame.DropColumn(key);
171  }
172  }
173 
174  return newFrame;
175  }
176 
177  /// <summary>
178  /// Drops sparse rows if and only if every value is `missing` in the Frame
179  /// </summary>
180  /// <typeparam name="TRowKey">Frame row key</typeparam>
181  /// <typeparam name="TColumnKey">Frame column key</typeparam>
182  /// <param name="frame">Data Frame</param>
183  /// <returns>new Frame with sparse rows dropped</returns>
184  /// <remarks>Equivalent to `df.dropna(how='all')`</remarks>
185  public static Frame<TRowKey, TColumnKey> DropSparseRowsAll<TRowKey, TColumnKey>(this Frame<TRowKey, TColumnKey> frame)
186  {
187  if (frame.ColumnKeys.Count() == 0)
188  {
189  return Frame.CreateEmpty<TRowKey, TColumnKey>();
190  }
191 
192  var newFrame = frame.Clone().Transpose();
193 
194  foreach (var key in frame.RowKeys)
195  {
196  if (newFrame[key].DropMissing().ValueCount == 0)
197  {
198  newFrame.DropColumn(key);
199  }
200  }
201 
202  return newFrame.Transpose();
203  }
204  }
205 }