/* * 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 System; using System.Collections.Generic; using System.Linq; using Deedle; using Python.Runtime; using QuantConnect.Packets; namespace QuantConnect.Report.ReportElements { internal sealed class MonthlyReturnsReportElement : ChartReportElement { private LiveResult _live; private BacktestResult _backtest; /// /// Create a monthly returns plot /// /// Name of the widget /// Location of injection /// Backtest result object /// Live result object public MonthlyReturnsReportElement(string name, string key, BacktestResult backtest, LiveResult live) { _live = live; _backtest = backtest; Name = name; Key = key; } /// /// Generate the monthly returns plot using the python libraries. /// public override string Render() { var backtestPoints = ResultsUtil.EquityPoints(_backtest); var livePoints = ResultsUtil.EquityPoints(_live); var backtestSeries = new Series(backtestPoints.Keys, backtestPoints.Values); var liveSeries = new Series(livePoints.Keys, livePoints.Values); // Equivalent to python pandas line: `backtestSeries.resample('M').apply(lambda x: x.pct_change().sum())` var backtestMonthlyReturns = backtestSeries.ResampleEquivalence(date => new DateTime(date.Year, date.Month, 1).AddMonths(1).AddDays(-1)) .Select(kvp => kvp.Value.TotalReturns()); var liveMonthlyReturns = liveSeries.ResampleEquivalence(date => new DateTime(date.Year, date.Month, 1).AddMonths(1).AddDays(-1)) .Select(kvp => kvp.Value.TotalReturns()); var base64 = ""; using (Py.GIL()) { var backtestResults = new PyDict(); foreach (var kvp in backtestMonthlyReturns.GroupBy(kvp => kvp.Key.Year).GetObservations()) { var key = kvp.Key.ToStringInvariant(); var monthlyReturns = kvp.Value * 100; var values = new List(); for (var i = 1; i <= 12; i++) { var returns = monthlyReturns.Where(row => row.Key.Month == i); if (!returns.IsEmpty) { values.Add(returns.FirstValue()); continue; } values.Add(double.NaN); } backtestResults.SetItem(key.ToPython(), values.ToPython()); } var liveResults = new PyDict(); foreach (var kvp in liveMonthlyReturns.GroupBy(kvp => kvp.Key.Year).GetObservations()) { var key = kvp.Key.ToStringInvariant(); var monthlyReturns = kvp.Value * 100; var values = new List(); for (var i = 1; i <= 12; i++) { var returns = monthlyReturns.Where(row => row.Key.Month == i); if (!returns.IsEmpty) { values.Add(returns.FirstValue()); continue; } values.Add(double.NaN); } liveResults.SetItem(key.ToPython(), values.ToPython()); } base64 = Charting.GetMonthlyReturns(backtestResults, liveResults); } return base64; } } }