/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using Deedle; using System; using System.Collections.Generic; using System.Linq; namespace QuantConnect.Report { /// /// Utility extension methods for Deedle series/frames /// public static class DeedleUtil { /// /// Calculates the cumulative sum for the given series /// /// Series to calculate cumulative sum for /// Cumulative sum in series form public static Series CumulativeSum(this Series input) { if (input.IsEmpty) { return input; } var prev = 0.0; return input.SelectValues(current => { var sum = prev + current; prev = sum; return sum; }); } /// /// Calculates the cumulative product of the series. This is equal to the python pandas method: `df.cumprod()` /// /// Input series /// Cumulative product public static Series CumulativeProduct(this Series input) { if (input.IsEmpty) { return input; } var prev = 1.0; return input.SelectValues(current => { var product = prev * current; prev = product; return product; }); } /// /// Calculates the cumulative max of the series. This is equal to the python pandas method: `df.cummax()`. /// /// /// public static Series CumulativeMax(this Series input) { if (input.IsEmpty) { return input; } var prevMax = double.NegativeInfinity; var values = new List(); foreach (var point in input.Values) { if (point > prevMax) { prevMax = point; } values.Add(prevMax); } return new Series(input.Keys, values); } /// /// Calculates the percentage change from the previous value to the current /// /// Series to calculate percentage change for /// Percentage change in series form /// Equivalent to `df.pct_change()` public static Series PercentChange(this Series input) { if (input.IsEmpty) { return input; } var inputShifted = input.Shift(1); return (input - inputShifted) / inputShifted; } /// /// Calculates the cumulative returns series of the given input equity curve /// /// Equity curve series /// Cumulative returns over time public static Series CumulativeReturns(this Series input) { if (input.IsEmpty) { return input; } return (input.PercentChange() .Where(kvp => !double.IsInfinity(kvp.Value)) + 1) .CumulativeProduct() - 1; } /// /// Calculates the total returns over a period of time for the given input /// /// Equity curve series /// Total returns over time public static double TotalReturns(this Series input) { var returns = input.CumulativeReturns(); if (returns.IsEmpty) { return double.NaN; } return returns.LastValue(); } /// /// Drops sparse columns only if every value is `missing` in the column /// /// Frame row key /// Frame column key /// Data Frame /// new Frame with sparse columns dropped /// Equivalent to `df.dropna(axis=1, how='all')` public static Frame DropSparseColumnsAll(this Frame frame) { var newFrame = frame.Clone(); foreach (var key in frame.ColumnKeys) { if (newFrame[key].DropMissing().ValueCount == 0) { newFrame.DropColumn(key); } } return newFrame; } /// /// Drops sparse rows if and only if every value is `missing` in the Frame /// /// Frame row key /// Frame column key /// Data Frame /// new Frame with sparse rows dropped /// Equivalent to `df.dropna(how='all')` public static Frame DropSparseRowsAll(this Frame frame) { if (frame.ColumnKeys.Count() == 0) { return Frame.CreateEmpty(); } var newFrame = frame.Clone().Transpose(); foreach (var key in frame.RowKeys) { if (newFrame[key].DropMissing().ValueCount == 0) { newFrame.DropColumn(key); } } return newFrame.Transpose(); } } }