Lean  $LEAN_TAG$
Command.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Linq;
18 using System.Dynamic;
19 using Newtonsoft.Json;
20 using QuantConnect.Data;
21 using System.Reflection;
22 using Newtonsoft.Json.Linq;
23 using System.Linq.Expressions;
25 using System.Collections.Generic;
26 
27 namespace QuantConnect.Commands
28 {
29  /// <summary>
30  /// Base generic dynamic command class
31  /// </summary>
32  public class Command : DynamicObject
33  {
34  private static readonly MethodInfo SetPropertyMethodInfo = typeof(Command).GetMethod("SetProperty");
35  private static readonly MethodInfo GetPropertyMethodInfo = typeof(Command).GetMethod("GetProperty");
36 
37  private readonly Dictionary<string, object> _storage = new(StringComparer.InvariantCultureIgnoreCase);
38 
39  /// <summary>
40  /// Useful to string representation in python
41  /// </summary>
42  protected string PayloadData { get; set; }
43 
44  /// <summary>
45  /// Get the metaObject required for Dynamism.
46  /// </summary>
47  public sealed override DynamicMetaObject GetMetaObject(Expression parameter)
48  {
49  return new SerializableDynamicMetaObject(parameter, this, SetPropertyMethodInfo, GetPropertyMethodInfo);
50  }
51 
52  /// <summary>
53  /// Sets the property with the specified name to the value. This is a case-insensitve search.
54  /// </summary>
55  /// <param name="name">The property name to set</param>
56  /// <param name="value">The new property value</param>
57  /// <returns>Returns the input value back to the caller</returns>
58  public object SetProperty(string name, object value)
59  {
60  if (value is JArray jArray)
61  {
62  return _storage[name] = jArray.ToObject<List<object>>();
63  }
64  else if (value is JObject jobject)
65  {
66  return _storage[name] = jobject.ToObject<Dictionary<string, object>>();
67  }
68  else
69  {
70  return _storage[name] = value;
71  }
72  }
73 
74  /// <summary>
75  /// Gets the property's value with the specified name. This is a case-insensitve search.
76  /// </summary>
77  /// <param name="name">The property name to access</param>
78  /// <returns>object value of BaseData</returns>
79  public object GetProperty(string name)
80  {
81  if (!_storage.TryGetValue(name, out var value))
82  {
83  var type = GetType();
84  if (type != typeof(Command))
85  {
86  var propertyInfo = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
87  if (propertyInfo != null)
88  {
89  return propertyInfo.GetValue(this, null);
90  }
91  var fieldInfo = type.GetField(name, BindingFlags.Public | BindingFlags.Instance);
92  if (fieldInfo != null)
93  {
94  return fieldInfo.GetValue(this);
95  }
96  }
97  return null;
98  }
99  return value;
100  }
101 
102  /// <summary>
103  /// Run this command using the target algorithm
104  /// </summary>
105  /// <param name="algorithm">The algorithm instance</param>
106  /// <returns>True if success, false otherwise. Returning null will disable command feedback</returns>
107  public virtual bool? Run(IAlgorithm algorithm)
108  {
109  throw new NotImplementedException($"Please implement the 'def run(algorithm) -> bool | None:' method");
110  }
111 
112  /// <summary>
113  /// The string representation of this command
114  /// </summary>
115  public override string ToString()
116  {
117  if (!string.IsNullOrEmpty(PayloadData))
118  {
119  return PayloadData;
120  }
121  return JsonConvert.SerializeObject(this);
122  }
123 
124  /// <summary>
125  /// Helper class so we can serialize a command
126  /// </summary>
127  private class SerializableDynamicMetaObject : GetSetPropertyDynamicMetaObject
128  {
129  private readonly Command _object;
130  public SerializableDynamicMetaObject(Expression expression, object value, MethodInfo setPropertyMethodInfo, MethodInfo getPropertyMethodInfo)
131  : base(expression, value, setPropertyMethodInfo, getPropertyMethodInfo)
132  {
133  _object = (Command)value;
134  }
135  public override IEnumerable<string> GetDynamicMemberNames()
136  {
137  return _object._storage.Keys.Concat(_object.GetType()
138  .GetMembers(BindingFlags.Public | BindingFlags.Instance)
139  .Where(x => x.MemberType == MemberTypes.Field || x.MemberType == MemberTypes.Property).Select(x => x.Name));
140  }
141  }
142  }
143 }