Skip to content

Commit

Permalink
Fixes issue where PortfolioLooper would crash generating certain reports
Browse files Browse the repository at this point in the history
 * Updates SharpeRatioReportElement calculation
  • Loading branch information
gsalaz98 committed Mar 26, 2020
1 parent ba7f39f commit e5ed489
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 6 deletions.
19 changes: 14 additions & 5 deletions Report/PortfolioLooper/PortfolioLooper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
using QuantConnect.Lean.Engine.Setup;
using QuantConnect.Lean.Engine.TransactionHandlers;
using QuantConnect.Logging;
using QuantConnect.Orders.Fills;
using QuantConnect.Packets;
using QuantConnect.Securities;
using QuantConnect.Util;
Expand Down Expand Up @@ -270,11 +269,21 @@ public static IEnumerable<PointInTimePortfolio> FromOrders(Series<DateTime, doub
PointInTimePortfolio prev = null;
foreach (var deploymentOrders in portfolioDeployments)
{
if (deploymentOrders.Count == 0)
{
Log.Trace($"PortfolioLooper.FromOrders(): Deployment contains no orders");
continue;
}
var startTime = deploymentOrders.First().Time;
var deployment = equityCurve.Where(kvp => kvp.Key <= startTime);
if (deployment.IsEmpty)
{
Log.Trace($"PortfolioLooper.FromOrders(): Equity series is empty after filtering with upper bound: {startTime}");
continue;
}

// For every deployment, we want to start fresh.
var looper = new PortfolioLooper(
equityCurve.Where(kvp => kvp.Key <= deploymentOrders.First().Time).LastValue(),
deploymentOrders
);
var looper = new PortfolioLooper(deployment.LastValue(), deploymentOrders);

foreach (var portfolio in looper.ProcessOrders(deploymentOrders))
{
Expand Down
28 changes: 27 additions & 1 deletion Report/ReportElements/SharpeRatioReportElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
* limitations under the License.
*/

using Deedle;
using QuantConnect.Packets;
using System;
using System.Linq;

namespace QuantConnect.Report.ReportElements
{
Expand Down Expand Up @@ -42,7 +45,30 @@ public SharpeRatioReportElement(string name, string key, BacktestResult backtest
/// </summary>
public override string Render()
{
return _backtest?.TotalPerformance?.PortfolioStatistics?.SharpeRatio.ToString("F1") ?? "-";
var result = _live ?? (Result)_backtest;
if (result == null)
{
return "-";
}

var equityPoints = ResultsUtil.EquityPoints(result);
var performance = DeedleUtil.PercentChange(new Series<DateTime, double>(equityPoints).ResampleEquivalence(date => date.Date, s => s.LastValue()));
if (performance.ValueCount == 0)
{
return "-";
}

var sixMonthsAgo = performance.LastKey().AddDays(-180);
var trailingPerformance = performance.Where(series => series.Key >= sixMonthsAgo && series.Key.DayOfWeek != DayOfWeek.Saturday && series.Key.DayOfWeek != DayOfWeek.Sunday)
.Values
.ToList();

if (trailingPerformance.Count < 7 || Statistics.Statistics.AnnualStandardDeviation(trailingPerformance) == 0)
{
return "-";
}

return Statistics.Statistics.SharpeRatio(trailingPerformance, 0.0).ToString("F2");
}
}
}
1 change: 1 addition & 0 deletions Tests/QuantConnect.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@
<Compile Include="Python\PandasConverterTests.cs" />
<Compile Include="RegressionTests.cs" />
<Compile Include="Report\CalculationTests.cs" />
<Compile Include="Report\PortfolioLooperTests.cs" />
<Compile Include="Report\ResultDeserializationTests.cs" />
<Compile Include="Report\PortfolioLooperAlgorithmTests.cs" />
<Compile Include="Symbols.cs" />
Expand Down
51 changes: 51 additions & 0 deletions Tests/Report/PortfolioLooperTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 NUnit.Framework;
using QuantConnect.Orders;
using QuantConnect.Report;
using System;
using System.Collections.Generic;
using System.Linq;

namespace QuantConnect.Tests.Report
{
[TestFixture]
public class PortfolioLooperTests
{
[Test]
public void EmptyEquitySeriesDoesNotCrash()
{
var equityPoints = new SortedList<DateTime, double>
{
{ new DateTime(2019, 1, 3, 5, 0, 5), 100000 }
};
var series = new Series<DateTime, double>(equityPoints);
var order = new MarketOrder(Symbols.SPY, 1m, new DateTime(2019, 1, 3, 5, 0, 0));

// Force an order ID >= 1 on the order, otherwise the test will fail
// because the order will be filtered out.
order.GetType().GetProperty("Id").SetValue(order, 1);

var orders = new List<Order>
{
order
};

Assert.DoesNotThrow(() => PortfolioLooper.FromOrders(series, orders).ToList());
}
}
}

0 comments on commit e5ed489

Please sign in to comment.