/*
* 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;
}
}
}
}