chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
@@ -0,0 +1,97 @@
/*
* 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.Linq;
using Deedle;
using Python.Runtime;
using QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class AnnualReturnsReportElement : ChartReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// Create a new plot of annual returns
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public AnnualReturnsReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// Generate the annual returns plot using the python libraries.
/// </summary>
public override string Render()
{
var backtestReturns = ResultsUtil.EquityPoints(_backtest);
var liveReturns = ResultsUtil.EquityPoints(_live);
var backtestTime = backtestReturns.Keys.ToList();
var backtestStrategy = backtestReturns.Values.ToList();
var liveTime = liveReturns.Keys.ToList();
var liveStrategy = liveReturns.Values.ToList();
var base64 = "";
using (Py.GIL())
{
var backtestList = new PyList();
var liveList = new PyList();
// We need to set the datetime index first before we resample
//var backtestSeries = Pandas.Series(backtestStrategy.ToPython());
var backtestSeries = new Series<DateTime, double>(backtestTime, backtestStrategy);
// Get the annual returns for the strategy
// ResampleEquivalence works similarly to Pandas' DataFrame.resample(...) method
// Here we transform the series to resample to the year's start, then we get the aggregate return from the year.
// Pandas equivalent:
//
// df.pct_change().resample('AS').sum().mul(100)
var backtestAnnualReturns = backtestSeries.ResampleEquivalence(date => new DateTime(date.Year, 1, 1), agg => agg.TotalReturns() * 100).DropMissing();
// We need to set the datetime index first before we resample
var liveSeries = new Series<DateTime, double>(liveTime, liveStrategy);
// Get the annual returns for the live strategy.
// Same as above, this is equivalent to:
//
// df.pct_change().resample('AS').sum().mul(100)
var liveAnnualReturns = liveSeries.ResampleEquivalence(date => new DateTime(date.Year, 1, 1), agg => agg.TotalReturns() * 100).DropMissing();
// Select only the year number and pass it to the plotting library
backtestList.Append(backtestAnnualReturns.Keys.Select(x => x.Year).ToList().ToPython());
backtestList.Append(backtestAnnualReturns.Values.ToList().ToPython());
liveList.Append(liveAnnualReturns.Keys.Select(x => x.Year).ToList().ToPython());
liveList.Append(liveAnnualReturns.Values.ToList().ToPython());
base64 = Charting.GetAnnualReturns(backtestList, liveList);
}
return base64;
}
}
}
@@ -0,0 +1,91 @@
/*
* 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.Orders;
using QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class AssetAllocationReportElement : ChartReportElement
{
private BacktestResult _backtest;
private List<PointInTimePortfolio> _backtestPortfolios;
private LiveResult _live;
private List<PointInTimePortfolio> _livePortfolios;
/// <summary>
/// Create a new plot of the asset allocation over time
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
/// <param name="backtestPortfolios">Backtest point in time portfolios</param>
/// <param name="livePortfolios">Live point in time portfolios</param>
public AssetAllocationReportElement(
string name,
string key,
BacktestResult backtest,
LiveResult live,
List<PointInTimePortfolio> backtestPortfolios,
List<PointInTimePortfolio> livePortfolios)
{
_backtest = backtest;
_backtestPortfolios = backtestPortfolios;
_live = live;
_livePortfolios = livePortfolios;
Name = name;
Key = key;
}
/// <summary>
/// Generate the asset allocation pie chart using the python libraries.
/// </summary>
public override string Render()
{
var backtestSeries = Metrics.AssetAllocations(_backtestPortfolios);
var liveSeries = Metrics.AssetAllocations(_livePortfolios);
PyObject result;
using (Py.GIL())
{
var data = new PyList();
var liveData = new PyList();
data.Append(backtestSeries.SortBy(x => -x).Where(x => x.Value != 0).Keys.Select(x => x.Value).ToList().ToPython());
data.Append(backtestSeries.SortBy(x => -x).Where(x => x.Value != 0).Values.ToList().ToPython());
liveData.Append(liveSeries.SortBy(x => -x).Where(x => x.Value != 0).Keys.Select(x => x.Value).ToList().ToPython());
liveData.Append(liveSeries.SortBy(x => -x).Where(x => x.Value != 0).Values.ToList().ToPython());
result = Charting.GetAssetAllocation(data, liveData);
}
var base64 = result.ConvertToDictionary<string, string>();
if (base64.ContainsKey("Live Asset Allocation"))
{
return base64["Live Asset Allocation"];
}
return base64["Backtest Asset Allocation"];
}
}
}
@@ -0,0 +1,66 @@
/*
* 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 Deedle;
using QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class CAGRReportElement : ReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// Estimate the CAGR of the strategy.
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public CAGRReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// The generated output string to be injected
/// </summary>
public override string Render()
{
var equityCurve = _live == null
? new Series<DateTime, double>(ResultsUtil.EquityPoints(_backtest))
: DrawdownCollection.NormalizeResults(_backtest, _live);
if (equityCurve.IsEmpty)
{
return "-";
}
var years = (decimal)(equityCurve.LastKey() - equityCurve.FirstKey()).TotalDays / 365m;
Result = Statistics.Statistics.CompoundingAnnualPerformance(
equityCurve.FirstValue().SafeDecimalCast(),
equityCurve.LastValue().SafeDecimalCast(),
years);
return ((decimal?)Result)?.ToString("P1") ?? "-";
}
}
}
@@ -0,0 +1,42 @@
/*
* 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 Python.Runtime;
using QuantConnect.Python;
namespace QuantConnect.Report.ReportElements
{
internal abstract class ChartReportElement : ReportElement
{
internal static dynamic Charting;
/// <summary>
/// Charting base class report element
/// </summary>
protected ChartReportElement()
{
PythonInitializer.Initialize();
using (Py.GIL())
{
dynamic module = Py.Import("ReportCharts");
var classObj = module.ReportCharts;
Charting = classObj.Invoke();
}
}
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,149 @@
/*
* 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.Linq;
using Python.Runtime;
using QuantConnect.Packets;
using System;
using QuantConnect.Util;
using System.Collections.Generic;
namespace QuantConnect.Report.ReportElements
{
internal sealed class CumulativeReturnsReportElement : ChartReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// Create a new array of cumulative percentage return of strategy and benchmark
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public CumulativeReturnsReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// Generate the cumulative return of the backtest, benchmark, and live
/// strategy using the ReportCharts.py python library
/// </summary>
public override string Render()
{
var backtestReturns = ResultsUtil.EquityPoints(_backtest);
var benchmark = ResultsUtil.BenchmarkPoints(_backtest);
var liveReturns = ResultsUtil.EquityPoints(_live);
var liveBenchmark = ResultsUtil.BenchmarkPoints(_live);
var backtestTime = backtestReturns.Keys.ToList();
var backtestStrategy = backtestReturns.Values.ToList();
var benchmarkTime = benchmark.Keys.ToList();
var benchmarkPoints = benchmark.Values.ToList();
var liveTime = liveReturns.Keys.ToList();
var liveStrategy = liveReturns.Values.ToList();
var liveBenchmarkTime = liveBenchmark.Keys.ToList();
var liveBenchmarkStrategy = liveBenchmark.Values.ToList();
var base64 = "";
using (Py.GIL())
{
var backtestList = new PyList();
var liveList = new PyList();
var backtestSeries = new Series<DateTime, double>(backtestTime, backtestStrategy);
var liveSeries = new Series<DateTime, double>(liveTime, liveStrategy);
var backtestBenchmarkSeries = new Series<DateTime, double>(benchmarkTime, benchmarkPoints);
var liveBenchmarkSeries = new Series<DateTime, double>(liveBenchmarkTime, liveBenchmarkStrategy);
// Equivalent in python using pandas for the following operations is:
// --------------------------------------------------
// >>> # note: [...] denotes the data we're passing in
// >>> df = pd.Series([...], index=time)
// >>> df_live = pd.Series([...], index=live_time)
// >>> df_live = df_live.mul(df.iloc[-1] / df_live.iloc[0]).fillna(method='ffill').dropna()
// >>> df_final = pd.concat([df, df_live], axis=0)
// >>> df_cumulative_returns = ((df_final.pct_change().dropna() + 1).cumprod() - 1)
// --------------------------------------------------
//
// We multiply the final value of the backtest and benchmark to have a continuous graph showing the performance out of sample
// as a continuation of the cumulative returns graph. Otherwise, we start plotting from 0% and not the last value of the backtest data
var backtestLastValue = backtestSeries.ValueCount == 0 ? 0 : backtestSeries.LastValue();
var backtestBenchmarkLastValue = backtestBenchmarkSeries.ValueCount == 0 ? 0 : backtestBenchmarkSeries.LastValue();
var liveContinuousEquity = liveSeries;
var liveBenchContinuousEquity = liveBenchmarkSeries;
if (liveSeries.ValueCount != 0)
{
liveContinuousEquity = (liveSeries * (backtestLastValue / liveSeries.FirstValue()))
.FillMissing(Direction.Forward)
.DropMissing();
}
if (liveBenchmarkSeries.ValueCount != 0)
{
liveBenchContinuousEquity = (liveBenchmarkSeries * (backtestBenchmarkLastValue / liveBenchmarkSeries.FirstValue()))
.FillMissing(Direction.Forward)
.DropMissing();
}
var liveStart = liveContinuousEquity.ValueCount == 0 ? DateTime.MaxValue : liveContinuousEquity.DropMissing().FirstKey();
var liveBenchStart = liveBenchContinuousEquity.ValueCount == 0 ? DateTime.MaxValue : liveBenchContinuousEquity.DropMissing().FirstKey();
var finalEquity = backtestSeries.Where(kvp => kvp.Key < liveStart).Observations.ToList();
var finalBenchEquity = backtestBenchmarkSeries.Where(kvp => kvp.Key < liveBenchStart).Observations.ToList();
finalEquity.AddRange(liveContinuousEquity.Observations);
finalBenchEquity.AddRange(liveBenchContinuousEquity.Observations);
var finalSeries = (new Series<DateTime, double>(finalEquity).CumulativeReturns() * 100)
.FillMissing(Direction.Forward)
.DropMissing();
var finalBenchSeries = (new Series<DateTime, double>(finalBenchEquity).CumulativeReturns() * 100)
.FillMissing(Direction.Forward)
.DropMissing();
var backtestCumulativePercent = finalSeries.Where(kvp => kvp.Key < liveStart);
var backtestBenchmarkCumulativePercent = finalBenchSeries.Where(kvp => kvp.Key < liveBenchStart);
var liveCumulativePercent = finalSeries.Where(kvp => kvp.Key >= liveStart);
var liveBenchmarkCumulativePercent = finalBenchSeries.Where(kvp => kvp.Key >= liveBenchStart);
backtestList.Append(backtestCumulativePercent.Keys.ToList().ToPython());
backtestList.Append(backtestCumulativePercent.Values.ToList().ToPython());
backtestList.Append(backtestBenchmarkCumulativePercent.Keys.ToList().ToPython());
backtestList.Append(backtestBenchmarkCumulativePercent.Values.ToList().ToPython());
liveList.Append(liveCumulativePercent.Keys.ToList().ToPython());
liveList.Append(liveCumulativePercent.Values.ToList().ToPython());
liveList.Append(liveBenchmarkCumulativePercent.Keys.ToList().ToPython());
liveList.Append(liveBenchmarkCumulativePercent.Values.ToList().ToPython());
base64 = Charting.GetCumulativeReturns(backtestList, liveList);
}
return base64;
}
}
}
@@ -0,0 +1,79 @@
/*
* 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.Linq;
using Deedle;
using Python.Runtime;
using QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class DailyReturnsReportElement : ChartReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// Create a new plot of the daily returns in bar chart format
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public DailyReturnsReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// Generate the daily returns plot using the python libraries.
/// </summary>
public override string Render()
{
var backtestReturns = ResultsUtil.EquityPoints(_backtest);
var liveReturns = ResultsUtil.EquityPoints(_live);
var backtestSeries = new Series<DateTime, double>(backtestReturns.Keys, backtestReturns.Values);
var liveSeries = new Series<DateTime, double>(liveReturns.Keys, liveReturns.Values);
// The following two operations are equivalent to the Pandas `DataFrame.resample(...)` method
var backtestResampled = backtestSeries.ResampleEquivalence(date => date.Date, s => s.LastValue()).PercentChange().DropMissing() * 100;
var liveResampled = liveSeries.ResampleEquivalence(date => date.Date, s => s.LastValue()).PercentChange().DropMissing() * 100;
var base64 = "";
using (Py.GIL())
{
var backtestList = new PyList();
backtestList.Append(backtestResampled.Keys.ToList().ToPython());
backtestList.Append(backtestResampled.Values.ToList().ToPython());
var liveList = new PyList();
liveList.Append(liveResampled.Keys.ToList().ToPython());
liveList.Append(liveResampled.Values.ToList().ToPython());
base64 = Charting.GetDailyReturns(backtestList, liveList);
backtestList.Dispose();
liveList.Dispose();
}
return base64;
}
}
}
@@ -0,0 +1,107 @@
/*
* 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;
using QuantConnect.Util;
namespace QuantConnect.Report.ReportElements
{
internal sealed class DrawdownReportElement : ChartReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// Create a new plot of the top N worst drawdown durations
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public DrawdownReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// Generate the top N drawdown plot using the python libraries.
/// </summary>
public override string Render()
{
var backtestPoints = ResultsUtil.EquityPoints(_backtest);
var livePoints = ResultsUtil.EquityPoints(_live);
var liveSeries = new Series<DateTime, double>(livePoints.Keys, livePoints.Values);
var strategySeries = DrawdownCollection.NormalizeResults(_backtest, _live);
var seriesUnderwaterPlot = DrawdownCollection.GetUnderwater(strategySeries).DropMissing();
var liveUnderwaterPlot = backtestPoints.Count == 0 ? seriesUnderwaterPlot : seriesUnderwaterPlot.After(backtestPoints.Last().Key);
var drawdownCollection = DrawdownCollection.FromResult(_backtest, _live, periods: 5);
var base64 = "";
using (Py.GIL())
{
var backtestList = new PyList();
if (liveUnderwaterPlot.IsEmpty)
{
backtestList.Append(seriesUnderwaterPlot.Keys.ToList().ToPython());
backtestList.Append(seriesUnderwaterPlot.Values.ToList().ToPython());
}
else
{
backtestList.Append(seriesUnderwaterPlot.Before(liveUnderwaterPlot.FirstKey()).Keys.ToList().ToPython());
backtestList.Append(seriesUnderwaterPlot.Before(liveUnderwaterPlot.FirstKey()).Values.ToList().ToPython());
}
var liveList = new PyList();
liveList.Append(liveUnderwaterPlot.Keys.ToList().ToPython());
liveList.Append(liveUnderwaterPlot.Values.ToList().ToPython());
var worstList = new PyList();
var previousDrawdownPeriods = new List<KeyValuePair<DateTime, DateTime>>();
foreach (var group in drawdownCollection.Drawdowns)
{
// Skip drawdown periods that are overlapping
if (previousDrawdownPeriods.Where(kvp => (group.Start >= kvp.Key && group.Start <= kvp.Value) || (group.End >= kvp.Key && group.End <= kvp.Value)).Any())
{
continue;
}
var worst = new PyDict();
worst.SetItem("Begin", group.Start.ToPython());
worst.SetItem("End", group.End.ToPython());
worst.SetItem("Total", group.PeakToTrough.ToPython());
worstList.Append(worst);
previousDrawdownPeriods.Add(new KeyValuePair<DateTime, DateTime>(group.Start, group.End));
}
base64 = Charting.GetDrawdown(backtestList, liveList, worstList);
}
return base64;
}
}
}
@@ -0,0 +1,69 @@
/*
* 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.Globalization;
using System.Linq;
using QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
/// <summary>
/// Capacity Estimation Report Element
/// </summary>
public sealed class EstimatedCapacityReportElement : ReportElement
{
private readonly BacktestResult _backtest;
private readonly LiveResult _live;
/// <summary>
/// Create a new capacity estimate
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public EstimatedCapacityReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// Render element
/// </summary>
public override string Render()
{
var statistics = _backtest?.Statistics;
string capacityWithCurrency;
if (statistics == null || !statistics.TryGetValue("Estimated Strategy Capacity", out capacityWithCurrency))
{
return "-";
}
var capacity = Currencies.Parse(capacityWithCurrency).RoundToSignificantDigits(2);
Result = capacity;
if (capacity == 0m)
{
return "-";
}
return capacity.ToFinancialFigures();
}
}
}
@@ -0,0 +1,129 @@
/*
* 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.Orders;
using QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class ExposureReportElement : ChartReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
private List<PointInTimePortfolio> _backtestPortfolios;
private List<PointInTimePortfolio> _livePortfolios;
/// <summary>
/// Create a new plot of the exposure
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
/// <param name="backtestPortfolios">Backtest point in time portfolios</param>
/// <param name="livePortfolios">Live point in time portfolios</param>
public ExposureReportElement(
string name,
string key,
BacktestResult backtest,
LiveResult live,
List<PointInTimePortfolio> backtestPortfolios,
List<PointInTimePortfolio> livePortfolios)
{
_backtest = backtest;
_backtestPortfolios = backtestPortfolios;
_live = live;
_livePortfolios = livePortfolios;
Name = name;
Key = key;
}
/// <summary>
/// Generate the exposure plot using the python libraries.
/// </summary>
public override string Render()
{
var longBacktestFrame = Metrics.Exposure(_backtestPortfolios, OrderDirection.Buy);
var shortBacktestFrame = Metrics.Exposure(_backtestPortfolios, OrderDirection.Sell);
var longLiveFrame = Metrics.Exposure(_livePortfolios, OrderDirection.Buy);
var shortLiveFrame = Metrics.Exposure(_livePortfolios, OrderDirection.Sell);
var backtestFrame = longBacktestFrame.Join(shortBacktestFrame)
.FillMissing(Direction.Forward)
.FillMissing(0.0);
var liveFrame = longLiveFrame.Join(shortLiveFrame)
.FillMissing(Direction.Forward)
.FillMissing(0.0);
longBacktestFrame = Frame.CreateEmpty<DateTime, Tuple<SecurityType, OrderDirection>>();
shortBacktestFrame = Frame.CreateEmpty<DateTime, Tuple<SecurityType, OrderDirection>>();
longLiveFrame = Frame.CreateEmpty<DateTime, Tuple<SecurityType, OrderDirection>>();
shortLiveFrame = Frame.CreateEmpty<DateTime, Tuple<SecurityType, OrderDirection>>();
foreach (var key in backtestFrame.ColumnKeys)
{
longBacktestFrame[key] = backtestFrame[key].SelectValues(x => x < 0 ? 0 : x);
shortBacktestFrame[key] = backtestFrame[key].SelectValues(x => x > 0 ? 0 : x);
}
foreach (var key in liveFrame.ColumnKeys)
{
longLiveFrame[key] = liveFrame[key].SelectValues(x => x < 0 ? 0 : x);
shortLiveFrame[key] = liveFrame[key].SelectValues(x => x > 0 ? 0 : x);
}
longBacktestFrame = longBacktestFrame.DropSparseColumnsAll();
shortBacktestFrame = shortBacktestFrame.DropSparseColumnsAll();
longLiveFrame = longLiveFrame.DropSparseColumnsAll();
shortLiveFrame = shortLiveFrame.DropSparseColumnsAll();
var base64 = "";
using (Py.GIL())
{
var time = backtestFrame.RowKeys.ToList().ToPython();
var longSecurities = longBacktestFrame.ColumnKeys.Select(x => x.Item1.ToStringInvariant()).ToList().ToPython();
var shortSecurities = shortBacktestFrame.ColumnKeys.Select(x => x.Item1.ToStringInvariant()).ToList().ToPython();
var longData = longBacktestFrame.ColumnKeys.Select(x => longBacktestFrame[x].Values.ToList().ToPython()).ToPython();
var shortData = shortBacktestFrame.ColumnKeys.Select(x => shortBacktestFrame[x].Values.ToList().ToPython()).ToPython();
var liveTime = liveFrame.RowKeys.ToList().ToPython();
var liveLongSecurities = longLiveFrame.ColumnKeys.Select(x => x.Item1.ToStringInvariant()).ToList().ToPython();
var liveShortSecurities = shortLiveFrame.ColumnKeys.Select(x => x.Item1.ToStringInvariant()).ToList().ToPython();
var liveLongData = longLiveFrame.ColumnKeys.Select(x => longLiveFrame[x].Values.ToList().ToPython()).ToPython();
var liveShortData = shortLiveFrame.ColumnKeys.Select(x => shortLiveFrame[x].Values.ToList().ToPython()).ToPython();
base64 = Charting.GetExposure(
time,
longSecurities,
shortSecurities,
longData,
shortData,
liveTime,
liveLongSecurities,
liveShortSecurities,
liveLongData,
liveShortData
);
}
return base64;
}
}
}
+37
View File
@@ -0,0 +1,37 @@
/*
* 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.
*/
namespace QuantConnect.Report.ReportElements
{
/// <summary>
/// Common interface for template elements of the report
/// </summary>
internal interface IReportElement
{
/// <summary>
/// Name of this report element
/// </summary>
string Name { get; }
/// <summary>
/// Template key code.
/// </summary>
string Key { get; }
/// <summary>
/// The generated output string to be injected
/// </summary>
string Render();
}
}
@@ -0,0 +1,50 @@
/*
* 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 QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class InformationRatioReportElement : ReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// Estimate the information ratio of the strategy.
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public InformationRatioReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// The generated output string to be injected
/// </summary>
public override string Render()
{
var informationRatio = _backtest?.TotalPerformance?.PortfolioStatistics?.InformationRatio;
Result = informationRatio;
return informationRatio?.ToString("F1") ?? "-";
}
}
}
@@ -0,0 +1,84 @@
/*
* 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.Orders;
using QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class LeverageUtilizationReportElement : ChartReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
private List<PointInTimePortfolio> _backtestPortfolios;
private List<PointInTimePortfolio> _livePortfolios;
/// <summary>
/// Create a new plot of the leverage utilization
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
/// <param name="backtestPortfolios">Backtest point in time portfolios</param>
/// <param name="livePortfolios">Live point in time portfolios</param>
public LeverageUtilizationReportElement(
string name,
string key,
BacktestResult backtest,
LiveResult live,
List<PointInTimePortfolio> backtestPortfolios,
List<PointInTimePortfolio> livePortfolios)
{
_backtest = backtest;
_backtestPortfolios = backtestPortfolios;
_live = live;
_livePortfolios = livePortfolios;
Name = name;
Key = key;
}
/// <summary>
/// Generate the leverage utilization plot using the python libraries.
/// </summary>
public override string Render()
{
var backtestSeries = Metrics.LeverageUtilization(_backtestPortfolios).FillMissing(Direction.Forward);
var liveSeries = Metrics.LeverageUtilization(_livePortfolios).FillMissing(Direction.Forward);
var base64 = "";
using (Py.GIL())
{
var backtestList = new PyList();
var liveList = new PyList();
backtestList.Append(backtestSeries.Keys.ToList().ToPython());
backtestList.Append(backtestSeries.Values.ToList().ToPython());
liveList.Append(liveSeries.Keys.ToList().ToPython());
liveList.Append(liveSeries.Values.ToList().ToPython());
base64 = Charting.GetLeverage(backtestList, liveList);
}
return base64;
}
}
}
@@ -0,0 +1,69 @@
/*
* 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.Linq;
using QuantConnect.Orders;
using QuantConnect.Packets;
using System.Collections.Generic;
namespace QuantConnect.Report.ReportElements
{
internal sealed class MarketsReportElement : ReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// Get the markets of the strategy.
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public MarketsReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// The generated output string to be injected
/// </summary>
public override string Render()
{
var liveOrders = _live?.Orders?.Values.ToList();
if (liveOrders == null)
{
liveOrders = new List<Order>();
}
var orders = new List<Order>();
var backtestOrders = _backtest?.Orders?.Values;
if (backtestOrders != null)
{
orders = backtestOrders.ToList();
}
orders = orders.Union(liveOrders).ToList();
var securityTypes = orders.DistinctBy(o => o.SecurityType).Select(s => s.SecurityType.ToString()).ToList();
Result = securityTypes;
return string.Join(",", securityTypes);
}
}
}
@@ -0,0 +1,64 @@
/*
* 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 QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class MaxDrawdownRecoveryReportElement : ReportElement
{
private LiveResult _liveResult;
private BacktestResult _backtestResult;
/// <summary>
/// Estimate the max drawdown of the strategy.
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtestResult">Backtest result object</param>
/// <param name="liveResult">Live result object</param>
public MaxDrawdownRecoveryReportElement(string name, string key, BacktestResult backtestResult, LiveResult liveResult)
{
_liveResult = liveResult;
_backtestResult = backtestResult;
Name = name;
Key = key;
}
/// <summary>
/// The generated output string to be injected
/// </summary>
public override string Render()
{
if (_liveResult == null)
{
var backtestDrawdownRecovery = _backtestResult?.TotalPerformance?.PortfolioStatistics?.DrawdownRecovery;
Result = backtestDrawdownRecovery;
return backtestDrawdownRecovery?.ToStringInvariant() ?? "-";
}
var equityCurve = new SortedDictionary<DateTime, decimal>(DrawdownCollection.NormalizeResults(_backtestResult, _liveResult)
.Observations
.ToDictionary(kvp => kvp.Key, kvp => (decimal)kvp.Value));
var maxDrawdownRecovery = Statistics.Statistics.CalculateDrawdownMetrics(equityCurve).DrawdownRecovery;
Result = maxDrawdownRecovery;
return $"{maxDrawdownRecovery}";
}
}
}
@@ -0,0 +1,66 @@
/*
* 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 QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class MaxDrawdownReportElement : ReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// Estimate the max drawdown of the strategy.
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public MaxDrawdownReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// The generated output string to be injected
/// </summary>
public override string Render()
{
if (_live == null)
{
var backtestDrawdown = _backtest?.TotalPerformance?.PortfolioStatistics?.Drawdown;
Result = backtestDrawdown;
return backtestDrawdown?.ToString("P1") ?? "-";
}
var equityCurve = new SortedDictionary<DateTime, decimal>(DrawdownCollection.NormalizeResults(_backtest, _live)
.Observations
.ToDictionary(kvp => kvp.Key, kvp => (decimal)kvp.Value));
var maxDrawdown = Statistics.Statistics.CalculateDrawdownMetrics(equityCurve).Drawdown;
Result = maxDrawdown;
return $"{maxDrawdown:P1}";
}
}
}
@@ -0,0 +1,116 @@
/*
* 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;
/// <summary>
/// Create a monthly returns plot
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public MonthlyReturnsReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// Generate the monthly returns plot using the python libraries.
/// </summary>
public override string Render()
{
var backtestPoints = ResultsUtil.EquityPoints(_backtest);
var livePoints = ResultsUtil.EquityPoints(_live);
var backtestSeries = new Series<DateTime, double>(backtestPoints.Keys, backtestPoints.Values);
var liveSeries = new Series<DateTime, double>(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<double>();
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<double>();
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;
}
}
}
+100
View File
@@ -0,0 +1,100 @@
/*
* 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.Linq;
using Deedle;
using QuantConnect.Data;
using QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class PSRReportElement : ReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// The number of trading days per year to get better result of statistics
/// </summary>
private int _tradingDaysPerYear;
/// <summary>
/// Estimate the PSR of the strategy.
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
/// <param name="tradingDaysPerYear">The number of trading days per year to get better result of statistics</param>
public PSRReportElement(string name, string key, BacktestResult backtest, LiveResult live, int tradingDaysPerYear)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
_tradingDaysPerYear = tradingDaysPerYear;
}
/// <summary>
/// The generated output string to be injected
/// </summary>
public override string Render()
{
decimal? psr;
if (_live == null)
{
psr = _backtest?.TotalPerformance?.PortfolioStatistics?.ProbabilisticSharpeRatio;
Result = psr;
if (psr == null)
{
return "-";
}
return $"{psr:P0}";
}
var equityCurvePerformance = DrawdownCollection.NormalizeResults(_backtest, _live)
.ResampleEquivalence(date => date.Date, s => s.LastValue())
.PercentChange();
if (equityCurvePerformance.IsEmpty || equityCurvePerformance.KeyCount < 180)
{
return "-";
}
var sixMonthsBefore = equityCurvePerformance.LastKey() - TimeSpan.FromDays(180);
// Skip weekends so we stay on a trading-day basis. The risk-free rate below is deannualized by
// tradingDaysPerYear, so leaving calendar days in would deduct it over ~365 days a year and over-deduct the rate
var lastSixMonthsPerformance = equityCurvePerformance.Where(kvp => kvp.Key >= sixMonthsBefore
&& kvp.Key.DayOfWeek != DayOfWeek.Saturday
&& kvp.Key.DayOfWeek != DayOfWeek.Sunday);
var benchmarkSharpeRatio = 1.0d / Math.Sqrt(_tradingDaysPerYear);
// Use the same excess-return basis as the reported PSR by subtracting the deannualized risk-free rate
var riskFreeRate = new InterestRateProvider().GetAverageRiskFreeRate(lastSixMonthsPerformance.Keys);
psr = Statistics.Statistics.ProbabilisticSharpeRatio(
lastSixMonthsPerformance
.Values
.ToList(),
benchmarkSharpeRatio,
(double)riskFreeRate / _tradingDaysPerYear)
.SafeDecimalCast();
Result = psr;
return $"{psr:P0}";
}
}
}
@@ -0,0 +1,115 @@
/*
* 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.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace QuantConnect.Report.ReportElements
{
/// <summary>
/// Class for creating a two column table for the Algorithm's Parameters in a report
/// </summary>
public class ParametersReportElement : ReportElement
{
private IReadOnlyDictionary<string, string> _parameters;
private readonly string _template;
/// <summary>
/// Creates a two column table for the Algorithm's Parameters
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtestConfiguration">The configuration of the backtest algorithm</param>
/// <param name="liveConfiguration">The configuration of the live algorithm</param>
/// <param name="template">HTML template to use</param>
public ParametersReportElement(string name, string key, AlgorithmConfiguration backtestConfiguration, AlgorithmConfiguration liveConfiguration, string template)
{
Name = name;
Key = key;
_template = template;
if (liveConfiguration != null)
{
_parameters = liveConfiguration.Parameters;
}
else if (backtestConfiguration != null)
{
_parameters = backtestConfiguration.Parameters;
}
else
{
// This case only happens for the unit tests, then we just create an empty dictionary
_parameters = new Dictionary<string, string>();
}
}
/// <summary>
/// Generates a HTML two column table for the Algorithm's Parameters
/// </summary>
/// <returns>Returns a string representing a HTML two column table</returns>
public override string Render()
{
var items = new List<string>();
int parameterIndex;
var columns = (new Regex(@"{{\$KEY(\d+?)}}")).Matches(_template).Count;
for (parameterIndex = 0; parameterIndex < _parameters.Count;)
{
var template = _template;
int column;
for (column = 0; column < columns; column++)
{
var currTemplateKey = "{{$KEY" + column.ToString() + "}}";
var currTemplateValue = "{{$VALUE" + column.ToString() + "}}";
if (parameterIndex < _parameters.Count)
{
var parameter = _parameters.ElementAt(parameterIndex);
template = template.Replace(currTemplateKey, parameter.Key);
template = template.Replace(currTemplateValue, parameter.Value);
}
else
{
template = template.Replace(currTemplateKey, string.Empty);
template = template.Replace(currTemplateValue, string.Empty);
}
parameterIndex++;
}
if (column == 0)
{
parameterIndex++;
}
items.Add(template);
}
if (Key == ReportKey.ParametersPageStyle)
{
if (items.Count == 0)
{
return "display: none;";
}
return string.Empty;
}
var parameters= string.Join("\n", items);
return parameters;
}
}
}
+48
View File
@@ -0,0 +1,48 @@
/*
* 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.
*/
namespace QuantConnect.Report.ReportElements
{
/// <summary>
/// Common interface for template elements of the report
/// </summary>
public abstract class ReportElement : IReportElement
{
/// <summary>
/// Name of this report element
/// </summary>
public virtual string Name { get; protected set; }
/// <summary>
/// Template key code.
/// </summary>
public virtual string Key { get; protected set; }
/// <summary>
/// Normalizes the key into a JSON-friendly key
/// </summary>
public string JsonKey => Key.Replace("KPI-", "").Replace("$", "").Replace("{", "").Replace("}", "") .ToLowerInvariant();
/// <summary>
/// Result of the render as an object for serialization to JSON
/// </summary>
public virtual object Result { get; protected set; }
/// <summary>
/// The generated output string to be injected
/// </summary>
public abstract string Render();
}
}
@@ -0,0 +1,78 @@
/*
* 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 Python.Runtime;
using QuantConnect.Logging;
using QuantConnect.Packets;
using QuantConnect.Statistics;
namespace QuantConnect.Report.ReportElements
{
internal sealed class ReturnsPerTradeReportElement : ChartReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// Create a new distribution plot of returns per trade
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public ReturnsPerTradeReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// Generate the returns per trade plot using the python libraries.
/// </summary>
public override string Render()
{
var backtestPercentagePerTrade = new List<double>();
if (_backtest?.TotalPerformance?.ClosedTrades != null)
{
foreach (var trade in _backtest.TotalPerformance.ClosedTrades)
{
if (trade.EntryPrice == 0m)
{
Log.Error($"ReturnsPerTradeReportElement.Render(): Encountered entry price of 0 in trade with entry time: {trade.EntryTime:yyyy-MM-dd HH:mm:ss} - Exit time: {trade.ExitTime:yyyy-MM-dd HH::mm:ss}");
continue;
}
var sideMultiplier = trade.Direction == TradeDirection.Long ? 1 : -1;
backtestPercentagePerTrade.Add(sideMultiplier * (Convert.ToDouble(trade.ExitPrice) - Convert.ToDouble(trade.EntryPrice)) / Convert.ToDouble(trade.EntryPrice));
}
}
// TODO: LiveResult does not contain a TotalPerformance field, so skip live mode for now
var base64 = "";
using (Py.GIL())
{
// Charting library does not expect values to be in whole percentage values (i.e. not 1% == 1.0, but rather 1% == 0.01),
base64 = Charting.GetReturnsPerTrade(backtestPercentagePerTrade.ToPython());
}
return base64;
}
}
}
@@ -0,0 +1,101 @@
/*
* 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.Linq;
using Python.Runtime;
using QuantConnect.Packets;
using System.Collections.Generic;
using QuantConnect.Lean.Engine.Results;
namespace QuantConnect.Report.ReportElements
{
internal sealed class RollingPortfolioBetaReportElement : ChartReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// The number of trading days per year to get better result of statistics
/// </summary>
private int _tradingDaysPerYear;
/// <summary>
/// Create a new plot of the rolling portfolio beta to equities
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
/// <param name="tradingDaysPerYear">The number of trading days per year to get better result of statistics</param>
public RollingPortfolioBetaReportElement(string name, string key, BacktestResult backtest, LiveResult live, int tradingDaysPerYear)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
_tradingDaysPerYear = tradingDaysPerYear;
}
/// <summary>
/// Generate the rolling portfolio beta to equities plot using the python libraries.
/// </summary>
public override string Render()
{
var backtestPoints = GetReturnSeries(_backtest);
var backtestBenchmarkPoints = ResultsUtil.BenchmarkPoints(_backtest);
var livePoints = GetReturnSeries(_live);
var liveBenchmarkPoints = ResultsUtil.BenchmarkPoints(_live);
var base64 = "";
using (Py.GIL())
{
var backtestList = new PyList();
var liveList = new PyList();
var backtestRollingBetaSixMonths = Rolling.Beta(backtestPoints, backtestBenchmarkPoints, windowSize: 22 * 6);
var backtestRollingBetaTwelveMonths = Rolling.Beta(backtestPoints, backtestBenchmarkPoints, windowSize: _tradingDaysPerYear);
backtestList.Append(backtestRollingBetaSixMonths.Keys.ToList().ToPython());
backtestList.Append(backtestRollingBetaSixMonths.Values.ToList().ToPython());
backtestList.Append(backtestRollingBetaTwelveMonths.Keys.ToList().ToPython());
backtestList.Append(backtestRollingBetaTwelveMonths.Values.ToList().ToPython());
var liveRollingBetaSixMonths = Rolling.Beta(livePoints, liveBenchmarkPoints, windowSize: 22 * 6);
var liveRollingBetaTwelveMonths = Rolling.Beta(livePoints, liveBenchmarkPoints, windowSize: _tradingDaysPerYear);
liveList.Append(liveRollingBetaSixMonths.Keys.ToList().ToPython());
liveList.Append(liveRollingBetaSixMonths.Values.ToList().ToPython());
liveList.Append(liveRollingBetaTwelveMonths.Keys.ToList().ToPython());
liveList.Append(liveRollingBetaTwelveMonths.Values.ToList().ToPython());
base64 = Charting.GetRollingBeta(backtestList, liveList);
}
return base64;
}
private static SortedList<DateTime, double> GetReturnSeries(Result leanResult)
{
var returnSeries = ResultsUtil.EquityPoints(leanResult, BaseResultsHandler.ReturnKey);
if (returnSeries == null || returnSeries.Count == 0)
{
// for backwards compatibility
returnSeries = ResultsUtil.EquityPoints(leanResult, "Daily Performance");
}
return returnSeries;
}
}
}
@@ -0,0 +1,89 @@
/*
* 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.Linq;
using Deedle;
using Python.Runtime;
using QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class RollingSharpeReportElement : ChartReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// The number of trading days per year to get better result of statistics
/// </summary>
private int _tradingDaysPerYear;
/// <summary>
/// Create a new plot of the rolling sharpe ratio
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
/// <param name="tradingDaysPerYear">The number of trading days per year to get better result of statistics</param>
public RollingSharpeReportElement(string name, string key, BacktestResult backtest, LiveResult live, int tradingDaysPerYear)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
_tradingDaysPerYear = tradingDaysPerYear;
}
/// <summary>
/// Generate the rolling sharpe using the python libraries.
/// </summary>
public override string Render()
{
var backtestPoints = ResultsUtil.EquityPoints(_backtest);
var livePoints = ResultsUtil.EquityPoints(_live);
var backtestSeries = new Series<DateTime, double>(backtestPoints);
var liveSeries = new Series<DateTime, double>(livePoints);
var backtestRollingSharpeSixMonths = Rolling.Sharpe(backtestSeries, 6, _tradingDaysPerYear).DropMissing();
var backtestRollingSharpeTwelveMonths = Rolling.Sharpe(backtestSeries, 12, _tradingDaysPerYear).DropMissing();
var liveRollingSharpeSixMonths = Rolling.Sharpe(liveSeries, 6, _tradingDaysPerYear).DropMissing();
var liveRollingSharpeTwelveMonths = Rolling.Sharpe(liveSeries, 12, _tradingDaysPerYear).DropMissing();
var base64 = "";
using (Py.GIL())
{
var backtestList = new PyList();
var liveList = new PyList();
backtestList.Append(backtestRollingSharpeSixMonths.Keys.ToList().ToPython());
backtestList.Append(backtestRollingSharpeSixMonths.Values.ToList().ToPython());
backtestList.Append(backtestRollingSharpeTwelveMonths.Keys.ToList().ToPython());
backtestList.Append(backtestRollingSharpeTwelveMonths.Values.ToList().ToPython());
liveList.Append(liveRollingSharpeSixMonths.Keys.ToList().ToPython());
liveList.Append(liveRollingSharpeSixMonths.Values.ToList().ToPython());
liveList.Append(liveRollingSharpeTwelveMonths.Keys.ToList().ToPython());
liveList.Append(liveRollingSharpeTwelveMonths.Values.ToList().ToPython());
base64 = Charting.GetRollingSharpeRatio(backtestList, liveList);
}
return base64;
}
}
}
@@ -0,0 +1,66 @@
/*
* 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.Linq;
using QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal class RuntimeDaysReportElement : ReportElement
{
private BacktestResult _backtest;
private LiveResult _live;
/// <summary>
/// Create a new metric describing the number of days an algorithm ran for.
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public RuntimeDaysReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_backtest = backtest;
_live = live;
Name = name;
Key = key;
}
/// <summary>
/// The generated output string to be injected
/// </summary>
public override string Render()
{
var result = (Result) _live ?? _backtest;
if (result == null)
{
return "-";
}
var equityPoints = ResultsUtil.EquityPoints(result);
if (equityPoints.Count == 0)
{
Result = 0;
return "0";
}
var days = (equityPoints.Last().Key - equityPoints.First().Key).Days;
Result = days;
return days.ToStringInvariant();
}
}
}
@@ -0,0 +1,117 @@
/*
* 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 QuantConnect.Data;
using QuantConnect.Packets;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace QuantConnect.Report.ReportElements
{
/// <summary>
/// Class for render the Sharpe Ratio statistic for a report
/// </summary>
public class SharpeRatioReportElement : ReportElement
{
/// <summary>
/// The number of trading days per year to get better result of statistics
/// </summary>
private double _tradingDaysPerYear;
/// <summary>
/// Live result object
/// </summary>
protected LiveResult LiveResult { get; }
/// <summary>
/// Backtest result object
/// </summary>
protected BacktestResult BacktestResult { get; }
/// <summary>
/// Sharpe Ratio from a backtest
/// </summary>
public virtual decimal? BacktestResultValue => BacktestResult?.TotalPerformance?.PortfolioStatistics?.SharpeRatio;
/// <summary>
/// Estimate the sharpe ratio of the strategy.
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
/// <param name="tradingDaysPerYear">The number of trading days per year to get better result of statistics</param>
public SharpeRatioReportElement(string name, string key, BacktestResult backtest, LiveResult live, int tradingDaysPerYear)
{
LiveResult = live;
BacktestResult = backtest;
Name = name;
Key = key;
_tradingDaysPerYear = Convert.ToDouble(tradingDaysPerYear, CultureInfo.InvariantCulture);
}
/// <summary>
/// The generated output string to be injected
/// </summary>
public override string Render()
{
if (LiveResult == null)
{
Result = BacktestResultValue;
return BacktestResultValue?.ToString("F1") ?? "-";
}
var equityPoints = ResultsUtil.EquityPoints(LiveResult);
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 trailingSeries = performance.Where(series => series.Key >= sixMonthsAgo && series.Key.DayOfWeek != DayOfWeek.Saturday && series.Key.DayOfWeek != DayOfWeek.Sunday);
var trailingPerformance = trailingSeries
.Values
.ToList();
var annualStandardDeviation = trailingPerformance.Count < 7 ? 0 : GetAnnualStandardDeviation(trailingPerformance, _tradingDaysPerYear);
if (annualStandardDeviation <= 0)
{
return "-";
}
// Use excess returns to stay consistent with the reported PSR and backtest Sharpe ratio
var riskFreeRate = new InterestRateProvider().GetAverageRiskFreeRate(trailingSeries.Keys);
var annualPerformance = Statistics.Statistics.AnnualPerformance(trailingPerformance, _tradingDaysPerYear);
var liveResultValue = Statistics.Statistics.SharpeRatio(annualPerformance, annualStandardDeviation, (double)riskFreeRate);
Result = liveResultValue;
return liveResultValue.ToString("F2");
}
/// <summary>
/// Get annual standard deviation
/// </summary>
/// <param name="trailingPerformance">The performance for the last period</param>
/// <param name="tradingDaysPerYear">The number of trading days per year to get better result of statistics</param>
/// <returns>Annual standard deviation.</returns>
public virtual double GetAnnualStandardDeviation(List<double> trailingPerformance, double tradingDaysPerYear)
{
return Statistics.Statistics.AnnualStandardDeviation(trailingPerformance, tradingDaysPerYear);
}
}
}
@@ -0,0 +1,54 @@
/*
* 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.Globalization;
using QuantConnect.Packets;
using System.Collections.Generic;
namespace QuantConnect.Report.ReportElements
{
internal sealed class SortinoRatioReportElement : SharpeRatioReportElement
{
/// <summary>
/// Sortino ratio from a backtest
/// </summary>
public override decimal? BacktestResultValue => BacktestResult?.TotalPerformance?.PortfolioStatistics?.SortinoRatio;
/// <summary>
/// Estimate the Sortino ratio of the strategy.
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
/// <param name="tradingDaysPerYear">The number of trading days per year to get better result of statistics</param>
public SortinoRatioReportElement(string name, string key, BacktestResult backtest, LiveResult live, int tradingDaysPerYear)
: base(name, key, backtest, live, tradingDaysPerYear)
{
}
/// <summary>
/// Get annual standard deviation
/// </summary>
/// <param name="trailingPerformance">The performance for the last period</param>
/// <param name="tradingDaysPerYear">The number of trading days per year to get better result of statistics</param>
/// <returns>Annual downside standard deviation.</returns>
public override double GetAnnualStandardDeviation(List<double> trailingPerformance, double tradingDaysPerYear)
{
return Statistics.Statistics.AnnualDownsideStandardDeviation(trailingPerformance, tradingDaysPerYear);
}
}
}
@@ -0,0 +1,44 @@
/*
* 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.
*/
namespace QuantConnect.Report.ReportElements
{
internal sealed class TextReportElement : ReportElement
{
private readonly string _content;
/// <summary>
/// Text place holder report element
/// </summary>
/// <param name="name">Name of this text field</param>
/// <param name="key">Report injection point</param>
/// <param name="content">Content for injection</param>
public TextReportElement(string name, string key, string content)
{
Name = name;
Key = key;
_content = content;
}
/// <summary>
/// Render the element contents
/// </summary>
/// <returns></returns>
public override string Render()
{
return _content;
}
}
}
@@ -0,0 +1,85 @@
/*
* 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.Collections.Generic;
using System.Linq;
using QuantConnect.Orders;
using QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class TradesPerDayReportElement : ReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// Estimate the trades per day of the strategy.
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public TradesPerDayReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// Generate trades per day
/// </summary>
public override string Render()
{
var liveOrders = _live?.Orders?.Values.ToList();
if (liveOrders == null)
{
liveOrders = new List<Order>();
}
var orders = _backtest?.Orders?.Values.Concat(liveOrders).OrderBy(x => x.Time);
if (orders == null)
{
return "-";
}
if (!orders.Any())
{
return "-";
}
var days = orders.Last().Time
.Subtract(orders.First().Time)
.TotalDays;
if (days == 0)
{
days = 1;
}
var tradesPerDay = orders.Count() / days;
Result = tradesPerDay;
if (tradesPerDay > 9)
{
return $"{tradesPerDay:F0}";
}
return $"{tradesPerDay:F1}";
}
}
}
@@ -0,0 +1,55 @@
/*
* 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 QuantConnect.Packets;
namespace QuantConnect.Report.ReportElements
{
internal sealed class TurnoverReportElement : ReportElement
{
private LiveResult _live;
private BacktestResult _backtest;
/// <summary>
/// Estimate the turnover of the strategy.
/// </summary>
/// <param name="name">Name of the widget</param>
/// <param name="key">Location of injection</param>
/// <param name="backtest">Backtest result object</param>
/// <param name="live">Live result object</param>
public TurnoverReportElement(string name, string key, BacktestResult backtest, LiveResult live)
{
_live = live;
_backtest = backtest;
Name = name;
Key = key;
}
/// <summary>
/// The generated output string to be injected
/// </summary>
public override string Render()
{
var turnover = _backtest?.TotalPerformance?.PortfolioStatistics?.PortfolioTurnover;
Result = turnover;
if (turnover == null)
{
return "-";
}
return $"{turnover:P0}";
}
}
}