/* * 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 System.Reflection; using QuantConnect.Data; using System.Collections; using System.Collections.Generic; namespace QuantConnect.Indicators { /// /// Collection of indicator data points for a given time /// public class IndicatorDataPoints : DynamicData { /// /// The indicator value at a given point /// public IndicatorDataPoint Current => (IndicatorDataPoint)GetProperty("Current"); /// /// The indicator value at a given point /// public override decimal Value => Current.Value; /// /// Access the historical indicator values per indicator property name /// public IndicatorDataPoint this[string name] { get { return GetProperty(name) as IndicatorDataPoint; } } /// /// String representation /// public override string ToString() { return $"{EndTime} {string.Join(", ", GetStorageDictionary().OrderBy(x => x.Key).Select(x => $"{x.Key}: {HandleObjectStorage(x.Value)}"))}"; } /// /// Returns the current data value held within the instance /// /// The DataPoint instance /// The current data value held within the instance public static implicit operator decimal(IndicatorDataPoints instance) { return instance.Value; } private static string HandleObjectStorage(object storedObject) { if (storedObject is IndicatorDataPoint point) { return point.Value.SmartRounding().ToStringInvariant(); } return storedObject?.ToString() ?? string.Empty; } } /// /// Internal carrier of an indicator values by property name /// public class InternalIndicatorValues : IEnumerable { /// /// The name of the values associated to this dto /// public string Name { get; } /// /// The indicator values /// public List Values { get; } /// /// The target indicator /// protected IIndicator Indicator { get; } /// /// Creates a new instance /// public InternalIndicatorValues(IIndicator indicator, string name) { Name = name; Values = new(); Indicator = indicator; } /// /// Update with a new indicator point /// public virtual IndicatorDataPoint UpdateValue() { Values.Add(Indicator.Current); return Indicator.Current; } /// /// Creates a new instance /// public static InternalIndicatorValues Create(IIndicator indicator, string name) { return new InternalIndicatorValues(indicator, name); } /// /// Creates a new instance /// public static InternalIndicatorValues Create(IIndicator indicator, PropertyInfo propertyInfo) { return new IndicatorPropertyValues(indicator, propertyInfo); } /// /// String representation /// public override string ToString() { return $"{Name} {Values.Count} indicator values"; } /// /// Returns an enumerator for the indicator values /// public IEnumerator GetEnumerator() { return ((IEnumerable)Values).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)Values).GetEnumerator(); } private class IndicatorPropertyValues : InternalIndicatorValues { private readonly PropertyInfo _currentInfo; private readonly PropertyInfo _propertyInfo; public IndicatorPropertyValues(IIndicator indicator, PropertyInfo propertyInfo) : base(indicator, propertyInfo.Name) { _propertyInfo = propertyInfo; _currentInfo = _propertyInfo.PropertyType.GetProperty("Current"); } public override IndicatorDataPoint UpdateValue() { var value = _propertyInfo.GetValue(Indicator); if (value == null) { return null; } if (_currentInfo != null) { value = _currentInfo.GetValue(value); } var point = value as IndicatorDataPoint; if (Values.Count == 0 || point.EndTime != Values[^1].EndTime) { // If the list is empty or the new point has a different EndTime, add it to the list Values.Add(point); } else { // If the new point has the same EndTime as the last point, update the last point Values[^1] = point; } return point; } } } }