/* * 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.Data; using System.Drawing; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using QuantConnect.Logging; namespace QuantConnect { /// /// Single Parent Chart Object for Custom Charting /// public class Chart { /// /// Name of the Chart /// public string Name { get; set; } = string.Empty; /// Type of the Chart, Overlayed or Stacked. [Obsolete("ChartType is now obsolete. Please use Series indexes instead by setting index in the series constructor.")] public ChartType ChartType { get; set; } = ChartType.Overlay; /// List of Series Objects for this Chart: [JsonConverter(typeof(ChartSeriesJsonConverter))] public Dictionary Series { get; set; } = new(); /// /// Associated symbol if any, making this an asset plot /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public Symbol Symbol { get; set; } /// /// True to hide this series legend from the chart /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public bool LegendDisabled { get; set; } /// /// Default constructor for chart: /// public Chart() { } /// /// Chart Constructor: /// /// Name of the Chart /// Type of the chart [Obsolete("ChartType is now obsolete and ignored in charting. Please use Series indexes instead by setting index in the series constructor.")] public Chart(string name, ChartType type = ChartType.Overlay) { Name = name; Series = new Dictionary(); ChartType = type; } /// /// Constructor for a chart /// /// String name of the chart public Chart(string name) : this(name, null) { } /// /// Constructor for a chart /// /// String name of the chart /// Associated symbol if any public Chart(string name, Symbol symbol) { Name = name; Symbol = symbol; Series = new Dictionary(); } /// /// Add a reference to this chart series: /// /// Chart series class object public void AddSeries(BaseSeries series) { //If we dont already have this series, add to the chrt: if (!Series.ContainsKey(series.Name)) { Series.Add(series.Name, series); } else { throw new DuplicateNameException($"Chart.AddSeries(): ${Messages.Chart.ChartSeriesAlreadyExists}"); } } /// /// Gets Series if already present in chart, else will add a new series and return it /// /// Name of the series /// Type of the series /// Index position on the chart of the series /// Unit for the series axis /// Color of the series /// Symbol for the marker in a scatter plot series /// True will always add a new Series instance, stepping on existing if any public Series TryAddAndGetSeries(string name, SeriesType type, int index, string unit, Color color, ScatterMarkerSymbol symbol, bool forceAddNew = false) { BaseSeries series; if (forceAddNew || !Series.TryGetValue(name, out series)) { series = new Series(name, type, index, unit) { Color = color, ScatterMarkerSymbol = symbol }; Series[name] = series; } return (Series)series; } /// /// Gets Series if already present in chart, else will add a new series and return it /// /// Name of the series /// Series to be used as a template. It will be clone without values if the series is added to the chart /// True will always add a new Series instance, stepping on existing if any public BaseSeries TryAddAndGetSeries(string name, BaseSeries templateSeries, bool forceAddNew = false) { BaseSeries chartSeries; if (forceAddNew || !Series.TryGetValue(name, out chartSeries)) { Series[name] = chartSeries = templateSeries.Clone(empty: true); } return chartSeries; } /// /// Fetch a chart with only the updates since the last request, /// Underlying series will save the index position. /// /// public Chart GetUpdates() { var copy = CloneEmpty(); try { foreach (var series in Series.Values) { copy.AddSeries(series.GetUpdates()); } } catch (Exception err) { Log.Error(err); } return copy; } /// /// Return a new instance clone of this object /// /// public virtual Chart Clone() { var chart = CloneEmpty(); foreach (var kvp in Series) { chart.Series.Add(kvp.Key, kvp.Value.Clone()); } return chart; } /// /// Return a new empty instance clone of this object /// public virtual Chart CloneEmpty() { return new Chart(Name) { LegendDisabled = LegendDisabled, Symbol = Symbol }; } } /// /// Type of chart - should we draw the series as overlayed or stacked /// public enum ChartType { /// Overlayed stacked (0) Overlay, /// Stacked series on top of each other. (1) Stacked } }