/* * 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.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; using QuantConnect.Data.Market; using QuantConnect.Indicators; namespace QuantConnect.Algorithm { public partial class QCAlgorithm { private bool _isEmitWarmupPlotWarningSet; private readonly ConcurrentDictionary _charts = new ConcurrentDictionary(); private static readonly Dictionary> ReservedChartSeriesNames = new Dictionary> { { "Strategy Equity", new List { "Equity", "Return" } }, { "Capacity", new List { "Strategy Capacity" } }, { "Drawdown", new List { "Equity Drawdown" } }, { "Benchmark", new List() { "Benchmark" } }, { "Assets Sales Volume", new List() }, { "Exposure", new List() }, { "Portfolio Margin", new List() }, { "Portfolio Turnover", new List { "Portfolio Turnover" } } }; /// /// Access to the runtime statistics property. User provided statistics. /// /// RuntimeStatistics are displayed in the head banner in live trading [DocumentationAttribute(Charting)] public ConcurrentDictionary RuntimeStatistics { get; } = new ConcurrentDictionary(); /// /// Add a Chart object to algorithm collection /// /// Chart object to add to collection. /// [DocumentationAttribute(Charting)] public void AddChart(Chart chart) { _charts.TryAdd(chart.Name, chart); } /// /// Plot a chart using string series name, with value. /// /// Name of the plot series /// Value to plot /// [DocumentationAttribute(Charting)] public void Plot(string series, decimal value) { //By default plot to the primary chart: Plot("Strategy Equity", series, value); } /// /// Plot a chart using string series name, with int value. Alias of Plot(); /// /// Record(string series, int value) /// [DocumentationAttribute(Charting)] public void Record(string series, int value) { Plot(series, value); } /// /// Plot a chart using string series name, with double value. Alias of Plot(); /// /// [DocumentationAttribute(Charting)] public void Record(string series, double value) { Plot(series, value); } /// /// Plot a chart using string series name, with decimal value. Alias of Plot(); /// /// /// /// [DocumentationAttribute(Charting)] public void Record(string series, decimal value) { //By default plot to the primary chart: Plot(series, value); } /// /// Plot a chart using string series name, with double value. /// /// [DocumentationAttribute(Charting)] public void Plot(string series, double value) { Plot(series, value.SafeDecimalCast()); } /// /// Plot a chart using string series name, with int value. /// /// [DocumentationAttribute(Charting)] public void Plot(string series, int value) { Plot(series, (decimal)value); } /// ///Plot a chart using string series name, with float value. /// /// [DocumentationAttribute(Charting)] public void Plot(string series, float value) { Plot(series, (double)value); } /// /// Plot a chart to string chart name, using string series name, with double value. /// /// [DocumentationAttribute(Charting)] public void Plot(string chart, string series, double value) { Plot(chart, series, value.SafeDecimalCast()); } /// /// Plot a chart to string chart name, using string series name, with int value /// /// [DocumentationAttribute(Charting)] public void Plot(string chart, string series, int value) { Plot(chart, series, (decimal)value); } /// /// Plot a chart to string chart name, using string series name, with float value /// /// [DocumentationAttribute(Charting)] public void Plot(string chart, string series, float value) { Plot(chart, series, (double)value); } /// /// Plot a value to a chart of string-chart name, with string series name, and decimal value. If chart does not exist, create it. /// /// Chart name /// Series name /// Value of the point [DocumentationAttribute(Charting)] public void Plot(string chart, string series, decimal value) { if (TryGetChartSeries(chart, series, out Series chartSeries)) { chartSeries.AddPoint(UtcTime, value); } } /// /// Plot a candlestick to the default/primary chart series by the given series name. /// /// Series name /// The candlestick open value /// The candlestick high value /// The candlestick low value /// The candlestick close value /// [DocumentationAttribute(Charting)] public void Plot(string series, double open, double high, double low, double close) { Plot(series, open.SafeDecimalCast(), high.SafeDecimalCast(), low.SafeDecimalCast(), close.SafeDecimalCast()); } /// /// Plot a candlestick to the default/primary chart series by the given series name. /// /// Series name /// The candlestick open value /// The candlestick high value /// The candlestick low value /// The candlestick close value /// [DocumentationAttribute(Charting)] public void Plot(string series, float open, float high, float low, float close) { Plot(series, (double)open, (double)high, (double)low, (double)close); } /// /// Plot a candlestick to the default/primary chart series by the given series name. /// /// Series name /// The candlestick open value /// The candlestick high value /// The candlestick low value /// The candlestick close value /// [DocumentationAttribute(Charting)] public void Plot(string series, int open, int high, int low, int close) { Plot(series, (decimal)open, (decimal)high, (decimal)low, (decimal)close); } /// /// Plot a candlestick to the default/primary chart series by the given series name. /// /// Name of the plot series /// The candlestick open value /// The candlestick high value /// The candlestick low value /// The candlestick close value /// [DocumentationAttribute(Charting)] public void Plot(string series, decimal open, decimal high, decimal low, decimal close) { //By default plot to the primary chart: Plot("Strategy Equity", series, open, high, low, close); } /// /// Plot a candlestick to the given series of the given chart. /// /// Chart name /// Series name /// The candlestick open value /// The candlestick high value /// The candlestick low value /// The candlestick close value /// [DocumentationAttribute(Charting)] public void Plot(string chart, string series, double open, double high, double low, double close) { Plot(chart, series, open.SafeDecimalCast(), high.SafeDecimalCast(), low.SafeDecimalCast(), close.SafeDecimalCast()); } /// /// Plot a candlestick to the given series of the given chart. /// /// Chart name /// Series name /// The candlestick open value /// The candlestick high value /// The candlestick low value /// The candlestick close value /// [DocumentationAttribute(Charting)] public void Plot(string chart, string series, float open, float high, float low, float close) { Plot(chart, series, (double)open, (double)high, (double)low, (double)close); } /// /// Plot a candlestick to the given series of the given chart. /// /// Chart name /// Series name /// The candlestick open value /// The candlestick high value /// The candlestick low value /// The candlestick close value /// [DocumentationAttribute(Charting)] public void Plot(string chart, string series, int open, int high, int low, int close) { Plot(chart, series, (decimal)open, (decimal)high, (decimal)low, (decimal)close); } /// /// Plot a candlestick to a chart of string-chart name, with string series name, and decimal value. If chart does not exist, create it. /// /// Chart name /// Series name /// The candlestick open value /// The candlestick high value /// The candlestick low value /// The candlestick close value [DocumentationAttribute(Charting)] public void Plot(string chart, string series, decimal open, decimal high, decimal low, decimal close) { if (TryGetChartSeries(chart, series, out CandlestickSeries candlestickSeries)) { candlestickSeries.AddPoint(UtcTime, open, high, low, close); } } /// /// Plot a candlestick to the given series of the given chart. /// /// Name of the plot series /// The trade bar to be plotted to the candlestick series /// [DocumentationAttribute(Charting)] public void Plot(string series, TradeBar bar) { Plot(series, bar.Open, bar.High, bar.Low, bar.Close); } /// /// Plot a candlestick to the given series of the given chart. /// /// Chart name /// Name of the plot series /// The trade bar to be plotted to the candlestick series /// [DocumentationAttribute(Charting)] public void Plot(string chart, string series, TradeBar bar) { Plot(chart, series, bar.Open, bar.High, bar.Low, bar.Close); } private bool TryGetChartSeries(string chartName, string seriesName, out T series) where T : BaseSeries, new() { series = null; // Check if chart/series names are reserved if (ReservedChartSeriesNames.TryGetValue(chartName, out var reservedSeriesNames)) { if (reservedSeriesNames.Count == 0) { throw new ArgumentException($"'{chartName}' is a reserved chart name."); } if (reservedSeriesNames.Contains(seriesName)) { throw new ArgumentException($"'{seriesName}' is a reserved series name for chart '{chartName}'."); } } if(!_charts.TryGetValue(chartName, out var chart)) { // If we don't have the chart, create it _charts[chartName] = chart = new Chart(chartName); } if (!chart.Series.TryGetValue(seriesName, out var chartSeries)) { chartSeries = new T() { Name = seriesName }; chart.AddSeries(chartSeries); } if (LiveMode && IsWarmingUp) { if (!_isEmitWarmupPlotWarningSet) { _isEmitWarmupPlotWarningSet = true; Debug("Plotting is disabled during algorithm warmup in live trading."); } return false; } series = (T)chartSeries; return true; } /// /// Add a series object for charting. This is useful when initializing charts with /// series other than type = line. If a series exists in the chart with the same name, /// then it is replaced. /// /// The chart name /// The series name /// The type of series, i.e, Scatter /// The unit of the y axis, usually $ [DocumentationAttribute(Charting)] public void AddSeries(string chart, string series, SeriesType seriesType, string unit = "$") { Chart c; if (!_charts.TryGetValue(chart, out c)) { _charts[chart] = c = new Chart(chart); } c.Series[series] = BaseSeries.Create(seriesType, series, unit: unit); } /// /// Plots the value of each indicator on the chart /// /// The chart's name /// The indicators to plot /// [DocumentationAttribute(Charting)] public void Plot(string chart, params IndicatorBase[] indicators) { foreach (var indicator in indicators) { Plot(chart, indicator.Name, indicator.Current.Value); } } /// /// Automatically plots each indicator when a new value is available /// [DocumentationAttribute(Charting)] [DocumentationAttribute(Indicators)] public void PlotIndicator(string chart, params IndicatorBase[] indicators) { PlotIndicator(chart, false, indicators); } /// /// Automatically plots each indicator when a new value is available, optionally waiting for indicator.IsReady to return true /// [DocumentationAttribute(Charting)] [DocumentationAttribute(Indicators)] public void PlotIndicator(string chart, bool waitForReady, params IndicatorBase[] indicators) { foreach (var i in indicators) { if (i == null) continue; // copy loop variable for usage in closure var ilocal = i; i.Updated += (sender, args) => { if (!waitForReady || ilocal.IsReady) { Plot(chart, ilocal); } }; } } /// /// Set a runtime statistic for the algorithm. Runtime statistics are shown in the top banner of a live algorithm GUI. /// /// Name of your runtime statistic /// String value of your runtime statistic /// [DocumentationAttribute(Charting)] public void SetRuntimeStatistic(string name, string value) { RuntimeStatistics.AddOrUpdate(name, value); } /// /// Set a runtime statistic for the algorithm. Runtime statistics are shown in the top banner of a live algorithm GUI. /// /// Name of your runtime statistic /// Decimal value of your runtime statistic [DocumentationAttribute(Charting)] public void SetRuntimeStatistic(string name, decimal value) { SetRuntimeStatistic(name, value.ToString(CultureInfo.InvariantCulture)); } /// /// Set a runtime statistic for the algorithm. Runtime statistics are shown in the top banner of a live algorithm GUI. /// /// Name of your runtime statistic /// Int value of your runtime statistic [DocumentationAttribute(Charting)] public void SetRuntimeStatistic(string name, int value) { SetRuntimeStatistic(name, value.ToStringInvariant()); } /// /// Set a runtime statistic for the algorithm. Runtime statistics are shown in the top banner of a live algorithm GUI. /// /// Name of your runtime statistic /// Double value of your runtime statistic [DocumentationAttribute(Charting)] public void SetRuntimeStatistic(string name, double value) { SetRuntimeStatistic(name, value.ToString(CultureInfo.InvariantCulture)); } /// /// Set a custom summary statistic for the algorithm. /// /// Name of the custom summary statistic /// Value of the custom summary statistic [DocumentationAttribute(StatisticsTag)] public void SetSummaryStatistic(string name, string value) { _statisticsService.SetSummaryStatistic(name, value); } /// /// Set a custom summary statistic for the algorithm. /// /// Name of the custom summary statistic /// Value of the custom summary statistic [DocumentationAttribute(StatisticsTag)] public void SetSummaryStatistic(string name, int value) { _statisticsService.SetSummaryStatistic(name, value.ToStringInvariant()); } /// /// Set a custom summary statistic for the algorithm. /// /// Name of the custom summary statistic /// Value of the custom summary statistic [DocumentationAttribute(StatisticsTag)] public void SetSummaryStatistic(string name, double value) { _statisticsService.SetSummaryStatistic(name, value.ToStringInvariant()); } /// /// Set a custom summary statistic for the algorithm. /// /// Name of the custom summary statistic /// Value of the custom summary statistic [DocumentationAttribute(StatisticsTag)] public void SetSummaryStatistic(string name, decimal value) { _statisticsService.SetSummaryStatistic(name, value.ToStringInvariant()); } /// /// Get the chart updates by fetch the recent points added and return for dynamic Charting. /// /// /// List of chart updates since the last request /// GetChartUpdates returns the latest updates since previous request. [DocumentationAttribute(Charting)] public IEnumerable GetChartUpdates(bool clearChartData = false) { foreach (var chart in _charts.Values) { yield return chart.GetUpdates(); if (clearChartData) { // we can clear this data out after getting updates to prevent unnecessary memory usage foreach (var series in chart.Series) { series.Value.Purge(); } } } } } }