Lean  $LEAN_TAG$
QCAlgorithm.Plotting.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Collections.Concurrent;
18 using System.Collections.Generic;
19 using System.Globalization;
20 using System.Linq;
23 
24 namespace QuantConnect.Algorithm
25 {
26  public partial class QCAlgorithm
27  {
28  private bool _isEmitWarmupPlotWarningSet;
29  private readonly ConcurrentDictionary<string, Chart> _charts = new ConcurrentDictionary<string, Chart>();
30 
31  private static readonly Dictionary<string, List<string>> ReservedChartSeriesNames = new Dictionary<string, List<string>>
32  {
33  { "Strategy Equity", new List<string> { "Equity", "Return" } },
34  { "Capacity", new List<string> { "Strategy Capacity" } },
35  { "Drawdown", new List<string> { "Equity Drawdown" } },
36  { "Benchmark", new List<string>() { "Benchmark" } },
37  { "Assets Sales Volume", new List<string>() },
38  { "Exposure", new List<string>() },
39  { "Portfolio Margin", new List<string>() },
40  { "Portfolio Turnover", new List<string> { "Portfolio Turnover" } }
41  };
42 
43  /// <summary>
44  /// Access to the runtime statistics property. User provided statistics.
45  /// </summary>
46  /// <remarks> RuntimeStatistics are displayed in the head banner in live trading</remarks>
47  [DocumentationAttribute(Charting)]
48  public ConcurrentDictionary<string, string> RuntimeStatistics { get; } = new ConcurrentDictionary<string, string>();
49 
50  /// <summary>
51  /// Add a Chart object to algorithm collection
52  /// </summary>
53  /// <param name="chart">Chart object to add to collection.</param>
54  /// <seealso cref="Plot(string,string,decimal)"/>
55  [DocumentationAttribute(Charting)]
56  public void AddChart(Chart chart)
57  {
58  _charts.TryAdd(chart.Name, chart);
59  }
60 
61  /// <summary>
62  /// Plot a chart using string series name, with value.
63  /// </summary>
64  /// <param name="series">Name of the plot series</param>
65  /// <param name="value">Value to plot</param>
66  /// <seealso cref="Plot(string,string,decimal)"/>
67  [DocumentationAttribute(Charting)]
68  public void Plot(string series, decimal value)
69  {
70  //By default plot to the primary chart:
71  Plot("Strategy Equity", series, value);
72  }
73 
74  /// <summary>
75  /// Plot a chart using string series name, with int value. Alias of Plot();
76  /// </summary>
77  /// <remarks> Record(string series, int value)</remarks>
78  /// <seealso cref="Plot(string,string,decimal)"/>
79  [DocumentationAttribute(Charting)]
80  public void Record(string series, int value)
81  {
82  Plot(series, value);
83  }
84 
85  /// <summary>
86  /// Plot a chart using string series name, with double value. Alias of Plot();
87  /// </summary>
88  /// <seealso cref="Plot(string,string,decimal)"/>
89  [DocumentationAttribute(Charting)]
90  public void Record(string series, double value)
91  {
92  Plot(series, value);
93  }
94 
95  /// <summary>
96  /// Plot a chart using string series name, with decimal value. Alias of Plot();
97  /// </summary>
98  /// <param name="series"></param>
99  /// <param name="value"></param>
100  /// <seealso cref="Plot(string,string,decimal)"/>
101  [DocumentationAttribute(Charting)]
102  public void Record(string series, decimal value)
103  {
104  //By default plot to the primary chart:
105  Plot(series, value);
106  }
107 
108  /// <summary>
109  /// Plot a chart using string series name, with double value.
110  /// </summary>
111  /// <seealso cref="Plot(string,string,decimal)"/>
112  [DocumentationAttribute(Charting)]
113  public void Plot(string series, double value) {
114  Plot(series, value.SafeDecimalCast());
115  }
116 
117  /// <summary>
118  /// Plot a chart using string series name, with int value.
119  /// </summary>
120  /// <seealso cref="Plot(string,string,decimal)"/>
121  [DocumentationAttribute(Charting)]
122  public void Plot(string series, int value)
123  {
124  Plot(series, (decimal)value);
125  }
126 
127  /// <summary>
128  ///Plot a chart using string series name, with float value.
129  /// </summary>
130  /// <seealso cref="Plot(string,string,decimal)"/>
131  [DocumentationAttribute(Charting)]
132  public void Plot(string series, float value)
133  {
134  Plot(series, (double)value);
135  }
136 
137  /// <summary>
138  /// Plot a chart to string chart name, using string series name, with double value.
139  /// </summary>
140  /// <seealso cref="Plot(string,string,decimal)"/>
141  [DocumentationAttribute(Charting)]
142  public void Plot(string chart, string series, double value)
143  {
144  Plot(chart, series, value.SafeDecimalCast());
145  }
146 
147  /// <summary>
148  /// Plot a chart to string chart name, using string series name, with int value
149  /// </summary>
150  /// <seealso cref="Plot(string,string,decimal)"/>
151  [DocumentationAttribute(Charting)]
152  public void Plot(string chart, string series, int value)
153  {
154  Plot(chart, series, (decimal)value);
155  }
156 
157  /// <summary>
158  /// Plot a chart to string chart name, using string series name, with float value
159  /// </summary>
160  /// <seealso cref="Plot(string,string,decimal)"/>
161  [DocumentationAttribute(Charting)]
162  public void Plot(string chart, string series, float value)
163  {
164  Plot(chart, series, (double)value);
165  }
166 
167  /// <summary>
168  /// Plot a value to a chart of string-chart name, with string series name, and decimal value. If chart does not exist, create it.
169  /// </summary>
170  /// <param name="chart">Chart name</param>
171  /// <param name="series">Series name</param>
172  /// <param name="value">Value of the point</param>
173  [DocumentationAttribute(Charting)]
174  public void Plot(string chart, string series, decimal value)
175  {
176  if (TryGetChartSeries(chart, series, out Series chartSeries))
177  {
178  chartSeries.AddPoint(UtcTime, value);
179  }
180  }
181 
182  /// <summary>
183  /// Plot a candlestick to the default/primary chart series by the given series name.
184  /// </summary>
185  /// <param name="series">Series name</param>
186  /// <param name="open">The candlestick open value</param>
187  /// <param name="high">The candlestick high value</param>
188  /// <param name="low">The candlestick low value</param>
189  /// <param name="close">The candlestick close value</param>
190  /// <seealso cref="Plot(string,string,decimal,decimal,decimal,decimal)"/>
191  [DocumentationAttribute(Charting)]
192  public void Plot(string series, double open, double high, double low, double close)
193  {
194  Plot(series, open.SafeDecimalCast(), high.SafeDecimalCast(), low.SafeDecimalCast(), close.SafeDecimalCast());
195  }
196 
197  /// <summary>
198  /// Plot a candlestick to the default/primary chart series by the given series name.
199  /// </summary>
200  /// <param name="series">Series name</param>
201  /// <param name="open">The candlestick open value</param>
202  /// <param name="high">The candlestick high value</param>
203  /// <param name="low">The candlestick low value</param>
204  /// <param name="close">The candlestick close value</param>
205  /// <seealso cref="Plot(string,string,decimal,decimal,decimal,decimal)"/>
206  [DocumentationAttribute(Charting)]
207  public void Plot(string series, float open, float high, float low, float close)
208  {
209  Plot(series, (double)open, (double)high, (double)low, (double)close);
210  }
211 
212  /// <summary>
213  /// Plot a candlestick to the default/primary chart series by the given series name.
214  /// </summary>
215  /// <param name="series">Series name</param>
216  /// <param name="open">The candlestick open value</param>
217  /// <param name="high">The candlestick high value</param>
218  /// <param name="low">The candlestick low value</param>
219  /// <param name="close">The candlestick close value</param>
220  /// <seealso cref="Plot(string,string,decimal,decimal,decimal,decimal)"/>
221  [DocumentationAttribute(Charting)]
222  public void Plot(string series, int open, int high, int low, int close)
223  {
224  Plot(series, (decimal)open, (decimal)high, (decimal)low, (decimal)close);
225  }
226 
227  /// <summary>
228  /// Plot a candlestick to the default/primary chart series by the given series name.
229  /// </summary>
230  /// <param name="series">Name of the plot series</param>
231  /// <param name="open">The candlestick open value</param>
232  /// <param name="high">The candlestick high value</param>
233  /// <param name="low">The candlestick low value</param>
234  /// <param name="close">The candlestick close value</param>
235  /// <seealso cref="Plot(string,string,decimal,decimal,decimal,decimal)"/>
236  [DocumentationAttribute(Charting)]
237  public void Plot(string series, decimal open, decimal high, decimal low, decimal close)
238  {
239  //By default plot to the primary chart:
240  Plot("Strategy Equity", series, open, high, low, close);
241  }
242 
243  /// <summary>
244  /// Plot a candlestick to the given series of the given chart.
245  /// </summary>
246  /// <param name="chart">Chart name</param>
247  /// <param name="series">Series name</param>
248  /// <param name="open">The candlestick open value</param>
249  /// <param name="high">The candlestick high value</param>
250  /// <param name="low">The candlestick low value</param>
251  /// <param name="close">The candlestick close value</param>
252  /// <seealso cref="Plot(string,string,decimal,decimal,decimal,decimal)"/>
253  [DocumentationAttribute(Charting)]
254  public void Plot(string chart, string series, double open, double high, double low, double close)
255  {
256  Plot(chart, series, open.SafeDecimalCast(), high.SafeDecimalCast(), low.SafeDecimalCast(), close.SafeDecimalCast());
257  }
258 
259  /// <summary>
260  /// Plot a candlestick to the given series of the given chart.
261  /// </summary>
262  /// <param name="chart">Chart name</param>
263  /// <param name="series">Series name</param>
264  /// <param name="open">The candlestick open value</param>
265  /// <param name="high">The candlestick high value</param>
266  /// <param name="low">The candlestick low value</param>
267  /// <param name="close">The candlestick close value</param>
268  /// <seealso cref="Plot(string,string,decimal,decimal,decimal,decimal)"/>
269  [DocumentationAttribute(Charting)]
270  public void Plot(string chart, string series, float open, float high, float low, float close)
271  {
272  Plot(chart, series, (double)open, (double)high, (double)low, (double)close);
273  }
274 
275  /// <summary>
276  /// Plot a candlestick to the given series of the given chart.
277  /// </summary>
278  /// <param name="chart">Chart name</param>
279  /// <param name="series">Series name</param>
280  /// <param name="open">The candlestick open value</param>
281  /// <param name="high">The candlestick high value</param>
282  /// <param name="low">The candlestick low value</param>
283  /// <param name="close">The candlestick close value</param>
284  /// <seealso cref="Plot(string,string,decimal,decimal,decimal,decimal)"/>
285  [DocumentationAttribute(Charting)]
286  public void Plot(string chart, string series, int open, int high, int low, int close)
287  {
288  Plot(chart, series, (decimal)open, (decimal)high, (decimal)low, (decimal)close);
289  }
290 
291  /// <summary>
292  /// Plot a candlestick to a chart of string-chart name, with string series name, and decimal value. If chart does not exist, create it.
293  /// </summary>
294  /// <param name="chart">Chart name</param>
295  /// <param name="series">Series name</param>
296  /// <param name="open">The candlestick open value</param>
297  /// <param name="high">The candlestick high value</param>
298  /// <param name="low">The candlestick low value</param>
299  /// <param name="close">The candlestick close value</param>
300  [DocumentationAttribute(Charting)]
301  public void Plot(string chart, string series, decimal open, decimal high, decimal low, decimal close)
302  {
303  if (TryGetChartSeries(chart, series, out CandlestickSeries candlestickSeries))
304  {
305  candlestickSeries.AddPoint(UtcTime, open, high, low, close);
306  }
307  }
308 
309  /// <summary>
310  /// Plot a candlestick to the given series of the given chart.
311  /// </summary>
312  /// <param name="series">Name of the plot series</param>
313  /// <param name="bar">The trade bar to be plotted to the candlestick series</param>
314  /// <seealso cref="Plot(string,string,decimal,decimal,decimal,decimal)"/>
315  [DocumentationAttribute(Charting)]
316  public void Plot(string series, TradeBar bar)
317  {
318  Plot(series, bar.Open, bar.High, bar.Low, bar.Close);
319  }
320 
321  /// <summary>
322  /// Plot a candlestick to the given series of the given chart.
323  /// </summary>
324  /// <param name="chart">Chart name</param>
325  /// <param name="series">Name of the plot series</param>
326  /// <param name="bar">The trade bar to be plotted to the candlestick series</param>
327  /// <seealso cref="Plot(string,string,decimal,decimal,decimal,decimal)"/>
328  [DocumentationAttribute(Charting)]
329  public void Plot(string chart, string series, TradeBar bar)
330  {
331  Plot(chart, series, bar.Open, bar.High, bar.Low, bar.Close);
332  }
333 
334  private bool TryGetChartSeries<T>(string chartName, string seriesName, out T series)
335  where T : BaseSeries, new()
336  {
337  series = null;
338 
339  // Check if chart/series names are reserved
340  if (ReservedChartSeriesNames.TryGetValue(chartName, out var reservedSeriesNames))
341  {
342  if (reservedSeriesNames.Count == 0)
343  {
344  throw new ArgumentException($"'{chartName}' is a reserved chart name.");
345  }
346  if (reservedSeriesNames.Contains(seriesName))
347  {
348  throw new ArgumentException($"'{seriesName}' is a reserved series name for chart '{chartName}'.");
349  }
350  }
351 
352  if(!_charts.TryGetValue(chartName, out var chart))
353  {
354  // If we don't have the chart, create it
355  _charts[chartName] = chart = new Chart(chartName);
356  }
357 
358  if (!chart.Series.TryGetValue(seriesName, out var chartSeries))
359  {
360  chartSeries = new T() { Name = seriesName };
361  chart.AddSeries(chartSeries);
362  }
363 
364  if (LiveMode && IsWarmingUp)
365  {
366  if (!_isEmitWarmupPlotWarningSet)
367  {
368  _isEmitWarmupPlotWarningSet = true;
369  Debug("Plotting is disabled during algorithm warmup in live trading.");
370  }
371  return false;
372  }
373 
374  series = (T)chartSeries;
375  return true;
376  }
377 
378  /// <summary>
379  /// Add a series object for charting. This is useful when initializing charts with
380  /// series other than type = line. If a series exists in the chart with the same name,
381  /// then it is replaced.
382  /// </summary>
383  /// <param name="chart">The chart name</param>
384  /// <param name="series">The series name</param>
385  /// <param name="seriesType">The type of series, i.e, Scatter</param>
386  /// <param name="unit">The unit of the y axis, usually $</param>
387  [DocumentationAttribute(Charting)]
388  public void AddSeries(string chart, string series, SeriesType seriesType, string unit = "$")
389  {
390  Chart c;
391  if (!_charts.TryGetValue(chart, out c))
392  {
393  _charts[chart] = c = new Chart(chart);
394  }
395 
396  c.Series[series] = BaseSeries.Create(seriesType, series, unit: unit);
397  }
398 
399  /// <summary>
400  /// Plots the value of each indicator on the chart
401  /// </summary>
402  /// <param name="chart">The chart's name</param>
403  /// <param name="indicators">The indicators to plot</param>
404  /// <seealso cref="Plot(string,string,decimal)"/>
405  [DocumentationAttribute(Charting)]
406  public void Plot(string chart, params IndicatorBase[] indicators)
407  {
408  foreach (var indicator in indicators)
409  {
410  Plot(chart, indicator.Name, indicator.Current.Value);
411  }
412  }
413 
414  /// <summary>
415  /// Automatically plots each indicator when a new value is available
416  /// </summary>
417  [DocumentationAttribute(Charting)]
418  [DocumentationAttribute(Indicators)]
419  public void PlotIndicator(string chart, params IndicatorBase[] indicators)
420  {
421  PlotIndicator(chart, false, indicators);
422  }
423 
424  /// <summary>
425  /// Automatically plots each indicator when a new value is available, optionally waiting for indicator.IsReady to return true
426  /// </summary>
427  [DocumentationAttribute(Charting)]
428  [DocumentationAttribute(Indicators)]
429  public void PlotIndicator(string chart, bool waitForReady, params IndicatorBase[] indicators)
430  {
431  foreach (var i in indicators)
432  {
433  if (i == null) continue;
434 
435  // copy loop variable for usage in closure
436  var ilocal = i;
437  i.Updated += (sender, args) =>
438  {
439  if (!waitForReady || ilocal.IsReady)
440  {
441  Plot(chart, ilocal);
442  }
443  };
444  }
445  }
446 
447  /// <summary>
448  /// Set a runtime statistic for the algorithm. Runtime statistics are shown in the top banner of a live algorithm GUI.
449  /// </summary>
450  /// <param name="name">Name of your runtime statistic</param>
451  /// <param name="value">String value of your runtime statistic</param>
452  /// <seealso cref="LiveMode"/>
453  [DocumentationAttribute(Charting)]
454  public void SetRuntimeStatistic(string name, string value)
455  {
456  RuntimeStatistics.AddOrUpdate(name, value);
457  }
458 
459  /// <summary>
460  /// Set a runtime statistic for the algorithm. Runtime statistics are shown in the top banner of a live algorithm GUI.
461  /// </summary>
462  /// <param name="name">Name of your runtime statistic</param>
463  /// <param name="value">Decimal value of your runtime statistic</param>
464  [DocumentationAttribute(Charting)]
465  public void SetRuntimeStatistic(string name, decimal value)
466  {
467  SetRuntimeStatistic(name, value.ToString(CultureInfo.InvariantCulture));
468  }
469 
470  /// <summary>
471  /// Set a runtime statistic for the algorithm. Runtime statistics are shown in the top banner of a live algorithm GUI.
472  /// </summary>
473  /// <param name="name">Name of your runtime statistic</param>
474  /// <param name="value">Int value of your runtime statistic</param>
475  [DocumentationAttribute(Charting)]
476  public void SetRuntimeStatistic(string name, int value)
477  {
478  SetRuntimeStatistic(name, value.ToStringInvariant());
479  }
480 
481  /// <summary>
482  /// Set a runtime statistic for the algorithm. Runtime statistics are shown in the top banner of a live algorithm GUI.
483  /// </summary>
484  /// <param name="name">Name of your runtime statistic</param>
485  /// <param name="value">Double value of your runtime statistic</param>
486  [DocumentationAttribute(Charting)]
487  public void SetRuntimeStatistic(string name, double value)
488  {
489  SetRuntimeStatistic(name, value.ToString(CultureInfo.InvariantCulture));
490  }
491 
492  /// <summary>
493  /// Set a custom summary statistic for the algorithm.
494  /// </summary>
495  /// <param name="name">Name of the custom summary statistic</param>
496  /// <param name="value">Value of the custom summary statistic</param>
497  [DocumentationAttribute(StatisticsTag)]
498  public void SetSummaryStatistic(string name, string value)
499  {
500  _statisticsService.SetSummaryStatistic(name, value);
501  }
502 
503  /// <summary>
504  /// Set a custom summary statistic for the algorithm.
505  /// </summary>
506  /// <param name="name">Name of the custom summary statistic</param>
507  /// <param name="value">Value of the custom summary statistic</param>
508  [DocumentationAttribute(StatisticsTag)]
509  public void SetSummaryStatistic(string name, int value)
510  {
511  _statisticsService.SetSummaryStatistic(name, value.ToStringInvariant());
512  }
513 
514  /// <summary>
515  /// Set a custom summary statistic for the algorithm.
516  /// </summary>
517  /// <param name="name">Name of the custom summary statistic</param>
518  /// <param name="value">Value of the custom summary statistic</param>
519  [DocumentationAttribute(StatisticsTag)]
520  public void SetSummaryStatistic(string name, double value)
521  {
522  _statisticsService.SetSummaryStatistic(name, value.ToStringInvariant());
523  }
524 
525  /// <summary>
526  /// Set a custom summary statistic for the algorithm.
527  /// </summary>
528  /// <param name="name">Name of the custom summary statistic</param>
529  /// <param name="value">Value of the custom summary statistic</param>
530  [DocumentationAttribute(StatisticsTag)]
531  public void SetSummaryStatistic(string name, decimal value)
532  {
533  _statisticsService.SetSummaryStatistic(name, value.ToStringInvariant());
534  }
535 
536  /// <summary>
537  /// Get the chart updates by fetch the recent points added and return for dynamic Charting.
538  /// </summary>
539  /// <param name="clearChartData"></param>
540  /// <returns>List of chart updates since the last request</returns>
541  /// <remarks>GetChartUpdates returns the latest updates since previous request.</remarks>
542  [DocumentationAttribute(Charting)]
543  public IEnumerable<Chart> GetChartUpdates(bool clearChartData = false)
544  {
545  foreach (var chart in _charts.Values)
546  {
547  yield return chart.GetUpdates();
548  if (clearChartData)
549  {
550  // we can clear this data out after getting updates to prevent unnecessary memory usage
551  foreach (var series in chart.Series)
552  {
553  series.Value.Purge();
554  }
555  }
556  }
557  }
558  }
559 }