/* * 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.Linq; using System.Dynamic; using Newtonsoft.Json; using QuantConnect.Data; using System.Reflection; using Newtonsoft.Json.Linq; using System.Linq.Expressions; using QuantConnect.Interfaces; using System.Collections.Generic; namespace QuantConnect.Commands { /// /// Base generic dynamic command class /// public class Command : DynamicObject { private static readonly MethodInfo SetPropertyMethodInfo = typeof(Command).GetMethod("SetProperty"); private static readonly MethodInfo GetPropertyMethodInfo = typeof(Command).GetMethod("GetProperty"); private readonly Dictionary _storage = new(StringComparer.InvariantCultureIgnoreCase); /// /// Useful to string representation in python /// protected string PayloadData { get; set; } /// /// Get the metaObject required for Dynamism. /// public sealed override DynamicMetaObject GetMetaObject(Expression parameter) { return new SerializableDynamicMetaObject(parameter, this, SetPropertyMethodInfo, GetPropertyMethodInfo); } /// /// Sets the property with the specified name to the value. This is a case-insensitve search. /// /// The property name to set /// The new property value /// Returns the input value back to the caller public object SetProperty(string name, object value) { if (value is JArray jArray) { return _storage[name] = jArray.ToObject>(); } else if (value is JObject jobject) { return _storage[name] = jobject.ToObject>(); } else { return _storage[name] = value; } } /// /// Gets the property's value with the specified name. This is a case-insensitve search. /// /// The property name to access /// object value of BaseData public object GetProperty(string name) { if (!_storage.TryGetValue(name, out var value)) { var type = GetType(); if (type != typeof(Command)) { var propertyInfo = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); if (propertyInfo != null) { return propertyInfo.GetValue(this, null); } var fieldInfo = type.GetField(name, BindingFlags.Public | BindingFlags.Instance); if (fieldInfo != null) { return fieldInfo.GetValue(this); } } return null; } return value; } /// /// Run this command using the target algorithm /// /// The algorithm instance /// True if success, false otherwise. Returning null will disable command feedback public virtual bool? Run(IAlgorithm algorithm) { throw new NotImplementedException($"Please implement the 'def run(algorithm) -> bool | None:' method"); } /// /// The string representation of this command /// public override string ToString() { if (!string.IsNullOrEmpty(PayloadData)) { return PayloadData; } return JsonConvert.SerializeObject(this); } /// /// Helper class so we can serialize a command /// private class SerializableDynamicMetaObject : GetSetPropertyDynamicMetaObject { private readonly Command _object; public SerializableDynamicMetaObject(Expression expression, object value, MethodInfo setPropertyMethodInfo, MethodInfo getPropertyMethodInfo) : base(expression, value, setPropertyMethodInfo, getPropertyMethodInfo) { _object = (Command)value; } public override IEnumerable GetDynamicMemberNames() { return _object._storage.Keys.Concat(_object.GetType() .GetMembers(BindingFlags.Public | BindingFlags.Instance) .Where(x => x.MemberType == MemberTypes.Field || x.MemberType == MemberTypes.Property).Select(x => x.Name)); } } } }