Files
2026-07-13 13:02:50 +08:00

213 lines
7.9 KiB
C#

/*
* 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;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Python.Runtime;
using QuantConnect.Data;
namespace QuantConnect.Securities
{
/// <summary>
/// Provides access to a security's data via it's type. This implementation supports dynamic access
/// by type name.
/// </summary>
public class DynamicSecurityData : IDynamicMetaObjectProvider
{
private static readonly MethodInfo SetPropertyMethodInfo = typeof(DynamicSecurityData).GetMethod("SetProperty");
private static readonly MethodInfo GetPropertyMethodInfo = typeof(DynamicSecurityData).GetMethod("GetProperty");
private readonly IRegisteredSecurityDataTypesProvider _registeredTypes;
private readonly ConcurrentDictionary<Type, Type> _genericTypes = new ConcurrentDictionary<Type, Type>();
private readonly SecurityCache _cache;
/// <summary>
/// Initializes a new instance of the <see cref="DynamicSecurityData"/> class
/// </summary>
/// <param name="registeredTypes">Provides all the registered data types for the algorithm</param>
/// <param name="cache">The security cache</param>
public DynamicSecurityData(IRegisteredSecurityDataTypesProvider registeredTypes, SecurityCache cache)
{
_registeredTypes = registeredTypes;
_cache = cache;
}
/// <summary>Returns the <see cref="T:System.Dynamic.DynamicMetaObject" /> responsible for binding operations performed on this object.</summary>
/// <returns>The <see cref="T:System.Dynamic.DynamicMetaObject" /> to bind this object.</returns>
/// <param name="parameter">The expression tree representation of the runtime value.</param>
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new GetSetPropertyDynamicMetaObject(parameter, this, SetPropertyMethodInfo, GetPropertyMethodInfo);
}
/// <summary>
/// Gets whether or not this dynamic data instance has data stored for the specified type
/// </summary>
public bool HasData<T>()
{
return _cache.HasData(typeof(T));
}
/// <summary>
/// Gets whether or not this dynamic data instance has a property with the specified name.
/// This is a case-insensitive search.
/// </summary>
/// <param name="name">The property name to check for</param>
/// <returns>True if the property exists, false otherwise</returns>
public bool HasProperty(string name)
{
Type type;
if (_registeredTypes.TryGetType(name, out type))
{
return _cache.HasData(type);
}
return false;
}
/// <summary>
/// Gets the last item in the data list for the specified type
/// </summary>
public T Get<T>()
{
var list = GetAll<T>();
return list.LastOrDefault();
}
/// <summary>
/// Gets the data list for the specified type
/// </summary>
public IReadOnlyList<T> GetAll<T>()
{
return GetAllImpl(typeof(T));
}
/// <summary>
/// Get the matching cached object in a python friendly accessor
/// </summary>
/// <param name="type">Type to search for</param>
/// <returns>Matching object</returns>
public PyObject Get(Type type)
{
var list = GetAll(type);
if (list.Count == 0)
{
return null;
}
using (Py.GIL())
{
return list[list.Count - 1].ToPython();
}
}
/// <summary>
/// Get all the matching types with a python friendly overload.
/// </summary>
/// <param name="type">Search type</param>
/// <returns>List of matching objects cached</returns>
public IList GetAll(Type type)
{
return GetAllImpl(type);
}
/// <summary>
/// Sets the property with the specified name to the value. This is a case-insensitve search.
/// </summary>
/// <param name="name">The property name to set</param>
/// <param name="value">The new property value</param>
/// <returns>Returns the input value back to the caller</returns>
[Obsolete("DynamicSecurityData is a view of the SecurityCache. It is readonly, properties can not be set")]
public object SetProperty(string name, object value)
{
throw new InvalidOperationException(Messages.DynamicSecurityData.PropertiesCannotBeSet);
}
/// <summary>
/// Gets the property's value with the specified name. This is a case-insensitve search.
/// </summary>
/// <param name="name">The property name to access</param>
/// <returns>object value of BaseData</returns>
public object GetProperty(string name)
{
// check to see if the requested name matches one of the algorithm registered data types and if
// so, we'll return a new empty list. this precludes us from always needing to check HasData<T>
Type type;
if (_registeredTypes.TryGetType(name, out type))
{
IReadOnlyList<BaseData> data;
if (_cache.TryGetValue(type, out data))
{
return data;
}
var listType = GetGenericListType(type);
return Activator.CreateInstance(listType);
}
throw new KeyNotFoundException(Messages.DynamicSecurityData.PropertyNotFound(name));
}
/// <summary>
/// Get all implementation that covers both Python and C#
/// </summary>
private dynamic GetAllImpl(Type type)
{
var data = GetProperty(type.Name);
var dataType = data.GetType();
if (dataType.GetElementType() == type // covers arrays
// covers lists
|| dataType.GenericTypeArguments.Length == 1
&& dataType.GenericTypeArguments[0] == type)
{
return data;
}
var baseDataList = data as IReadOnlyList<BaseData>;
if (baseDataList != null)
{
var listType = GetGenericListType(type);
var list = (IList)Activator.CreateInstance(listType);
foreach (var baseData in baseDataList)
{
list.Add(baseData);
}
return list;
}
throw new InvalidOperationException(Messages.DynamicSecurityData.UnexpectedTypesForGetAll(type, data));
}
private Type GetGenericListType(Type type)
{
Type containerType;
if (!_genericTypes.TryGetValue(type, out containerType))
{
// for performance we keep the generic type
_genericTypes[type] = containerType = typeof(List<>).MakeGenericType(type);
}
return containerType;
}
}
}