Lean  $LEAN_TAG$
Extensions.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.Collections;
18 using System.Collections.Concurrent;
19 using System.Collections.Generic;
20 using System.Collections.Immutable;
21 using System.Globalization;
22 using System.IO;
23 using System.Linq;
24 using System.Net;
25 using System.Net.Http;
26 using System.Reflection;
27 using System.Reflection.Emit;
28 using System.Runtime.CompilerServices;
29 using System.Security.Cryptography;
30 using System.Text;
31 using System.Text.RegularExpressions;
32 using System.Threading;
33 using System.Threading.Tasks;
34 using Newtonsoft.Json;
35 using NodaTime;
36 using ProtoBuf;
37 using Python.Runtime;
41 using QuantConnect.Data;
44 using QuantConnect.Logging;
45 using QuantConnect.Orders;
46 using QuantConnect.Packets;
47 using QuantConnect.Python;
50 using QuantConnect.Util;
51 using Timer = System.Timers.Timer;
52 using Microsoft.IO;
53 using NodaTime.TimeZones;
61 using Newtonsoft.Json.Linq;
63 
64 namespace QuantConnect
65 {
66  /// <summary>
67  /// Extensions function collections - group all static extensions functions here.
68  /// </summary>
69  public static class Extensions
70  {
71  private static readonly Dictionary<string, bool> _emptyDirectories = new ();
72  private static readonly HashSet<string> InvalidSecurityTypes = new HashSet<string>();
73  private static readonly Regex DateCheck = new Regex(@"\d{8}", RegexOptions.Compiled);
74  private static RecyclableMemoryStreamManager MemoryManager = new RecyclableMemoryStreamManager();
75  private static readonly int DataUpdatePeriod = Config.GetInt("downloader-data-update-period", 7);
76 
77  private static readonly Dictionary<IntPtr, PythonActivator> PythonActivators
78  = new Dictionary<IntPtr, PythonActivator>();
79 
80  /// <summary>
81  /// Maintains old behavior of NodaTime's (&lt; 2.0) daylight savings mapping.
82  /// We keep the old behavior to ensure the FillForwardEnumerator does not get stuck on an infinite loop.
83  /// The test `ConvertToSkipsDiscontinuitiesBecauseOfDaylightSavingsStart_AddingOneHour` and other related tests
84  /// assert the expected behavior, which is to ignore discontinuities in daylight savings resolving.
85  ///
86  /// More info can be found in the summary of the <see cref="Resolvers.LenientResolver"/> delegate.
87  /// </summary>
88  private static readonly ZoneLocalMappingResolver _mappingResolver = Resolvers.CreateMappingResolver(Resolvers.ReturnLater, Resolvers.ReturnStartOfIntervalAfter);
89 
90  /// <summary>
91  /// The offset span from the market close to liquidate or exercise a security on the delisting date
92  /// </summary>
93  /// <remarks>Will no be used in live trading</remarks>
94  /// <remarks>By default span is negative 15 minutes. We want to liquidate before market closes if not, in some cases
95  /// like future options the market close would match the delisted event time and would cancel all orders and mark the security
96  /// as non tradable and delisted.</remarks>
97  public static TimeSpan DelistingMarketCloseOffsetSpan { get; set; } = TimeSpan.FromMinutes(-15);
98 
99  /// <summary>
100  /// Helper method to get a property in a jobject if available
101  /// </summary>
102  /// <typeparam name="T">The property type</typeparam>
103  /// <param name="jObject">The jobject source</param>
104  /// <param name="name">The property name</param>
105  /// <returns>The property value if present or it's default value</returns>
106  public static T TryGetPropertyValue<T>(this JObject jObject, string name)
107  {
108  T result = default;
109  if (jObject == null)
110  {
111  return result;
112  }
113 
114  var jValue = jObject[name];
115  if (jValue != null && jValue.Type != JTokenType.Null)
116  {
117  result = jValue.Value<T>();
118  }
119  return result;
120  }
121 
122  /// <summary>
123  /// Determine if the file is out of date according to our download period.
124  /// Date based files are never out of date (Files with YYYYMMDD)
125  /// </summary>
126  /// <param name="filepath">Path to the file</param>
127  /// <returns>True if the file is out of date</returns>
128  public static bool IsOutOfDate(this string filepath)
129  {
130  var fileName = Path.GetFileName(filepath);
131  // helper to determine if file is date based using regex, matches a 8 digit value because we expect YYYYMMDD
132  return !DateCheck.IsMatch(fileName) && DateTime.Now - TimeSpan.FromDays(DataUpdatePeriod) > File.GetLastWriteTime(filepath);
133  }
134 
135  /// <summary>
136  /// Helper method to check if a directory exists and is not empty
137  /// </summary>
138  /// <param name="directoryPath">The path to check</param>
139  /// <returns>True if the directory does not exist or is empty</returns>
140  /// <remarks>Will cache results</remarks>
141  public static bool IsDirectoryEmpty(this string directoryPath)
142  {
143  lock (_emptyDirectories)
144  {
145  if(!_emptyDirectories.TryGetValue(directoryPath, out var result))
146  {
147  // is empty unless it exists and it has at least 1 file or directory in it
148  result = true;
149  if (Directory.Exists(directoryPath))
150  {
151  try
152  {
153  result = !Directory.EnumerateFileSystemEntries(directoryPath).Any();
154  }
155  catch (Exception exception)
156  {
157  Log.Error(exception);
158  }
159  }
160 
161  _emptyDirectories[directoryPath] = result;
162  if (result)
163  {
164  Log.Trace($"Extensions.IsDirectoryEmpty(): directory '{directoryPath}' not found or empty");
165  }
166  }
167 
168  return result;
169  }
170  }
171 
172  /// <summary>
173  /// Helper method to get a market hours entry
174  /// </summary>
175  /// <param name="marketHoursDatabase">The market hours data base instance</param>
176  /// <param name="symbol">The symbol to get the entry for</param>
177  /// <param name="dataTypes">For custom data types can optionally provide data type so that a new entry is added</param>
178  public static MarketHoursDatabase.Entry GetEntry(this MarketHoursDatabase marketHoursDatabase, Symbol symbol, IEnumerable<Type> dataTypes)
179  {
180  if (symbol.SecurityType == SecurityType.Base)
181  {
182  if (!marketHoursDatabase.TryGetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType, out var entry))
183  {
184  var type = dataTypes.Single();
185  var baseInstance = type.GetBaseDataInstance();
186  baseInstance.Symbol = symbol;
187  SecurityIdentifier.TryGetCustomDataType(symbol.ID.Symbol, out var customType);
188  // for custom types we will add an entry for that type
189  entry = marketHoursDatabase.SetEntryAlwaysOpen(symbol.ID.Market, customType != null ? $"TYPE.{customType}" : null, SecurityType.Base, baseInstance.DataTimeZone());
190  }
191  return entry;
192  }
193 
194  var result = marketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType);
195 
196  // For the OptionUniverse and FutureUniverse types, the exchange and data time zones are set to the same value (exchange tz).
197  // This is not actual options/futures data, just chains/universe selection, so we don't want any offsets
198  // between the exchange and data time zones.
199  // If the MHDB were data type dependent as well, this would be taken care in there.
200  if (result != null && dataTypes.Any(dataType => dataType.IsAssignableTo(typeof(BaseChainUniverseData))))
201  {
202  result = new MarketHoursDatabase.Entry(result.ExchangeHours.TimeZone, result.ExchangeHours);
203  }
204 
205  return result;
206  }
207 
208  /// <summary>
209  /// Helper method to deserialize a json array into a list also handling single json values
210  /// </summary>
211  /// <param name="jsonArray">The value to deserialize</param>
212  public static List<string> DeserializeList(this string jsonArray)
213  {
214  return DeserializeList<string>(jsonArray);
215  }
216 
217  /// <summary>
218  /// Helper method to deserialize a json array into a list also handling single json values
219  /// </summary>
220  /// <param name="jsonArray">The value to deserialize</param>
221  public static List<T> DeserializeList<T>(this string jsonArray)
222  {
223  try
224  {
225  if (string.IsNullOrEmpty(jsonArray))
226  {
227  return new();
228  }
229  return JsonConvert.DeserializeObject<List<T>>(jsonArray);
230  }
231  catch (Exception ex)
232  {
233  if (ex is not JsonReaderException && ex is not JsonSerializationException)
234  {
235  throw;
236  }
237 
238  if (typeof(T) == typeof(string))
239  {
240  return new List<T> { (T)Convert.ChangeType(jsonArray, typeof(T), CultureInfo.InvariantCulture) };
241  }
242  return new List<T> { JsonConvert.DeserializeObject<T>(jsonArray) };
243  }
244  }
245 
246  /// <summary>
247  /// Helper method to download a provided url as a string
248  /// </summary>
249  /// <param name="client">The http client to use</param>
250  /// <param name="url">The url to download data from</param>
251  /// <param name="headers">Add custom headers for the request</param>
252  public static string DownloadData(this HttpClient client, string url, Dictionary<string, string> headers = null)
253  {
254  if (headers != null)
255  {
256  foreach (var kvp in headers)
257  {
258  client.DefaultRequestHeaders.Add(kvp.Key, kvp.Value);
259  }
260  }
261  try
262  {
263  using (var response = client.GetAsync(url).Result)
264  {
265  using (var content = response.Content)
266  {
267  return content.ReadAsStringAsync().Result;
268  }
269  }
270  }
271  catch (WebException ex)
272  {
273  Log.Error(ex, $"DownloadData(): {Messages.Extensions.DownloadDataFailed(url)}");
274  return null;
275  }
276  }
277 
278  /// <summary>
279  /// Helper method to download a provided url as a string
280  /// </summary>
281  /// <param name="url">The url to download data from</param>
282  /// <param name="headers">Add custom headers for the request</param>
283  public static string DownloadData(this string url, Dictionary<string, string> headers = null)
284  {
285  using var client = new HttpClient();
286  return client.DownloadData(url, headers);
287  }
288 
289  /// <summary>
290  /// Helper method to download a provided url as a byte array
291  /// </summary>
292  /// <param name="url">The url to download data from</param>
293  public static byte[] DownloadByteArray(this string url)
294  {
295  using (var wc = new HttpClient())
296  {
297  try
298  {
299  return wc.GetByteArrayAsync(url).Result;
300  }
301  catch (Exception ex)
302  {
303  Log.Error(ex, $"DownloadByteArray(): {Messages.Extensions.DownloadDataFailed(url)}");
304  return null;
305  }
306  }
307  }
308 
309  /// <summary>
310  /// Safe multiplies a decimal by 100
311  /// </summary>
312  /// <param name="value">The decimal to multiply</param>
313  /// <returns>The result, maxed out at decimal.MaxValue</returns>
314  public static decimal SafeMultiply100(this decimal value)
315  {
316  const decimal max = decimal.MaxValue / 100m;
317  if (value >= max) return decimal.MaxValue;
318  return value * 100m;
319  }
320 
321  /// <summary>
322  /// Will return a memory stream using the <see cref="RecyclableMemoryStreamManager"/> instance.
323  /// </summary>
324  /// <param name="guid">Unique guid</param>
325  /// <returns>A memory stream</returns>
326  [MethodImpl(MethodImplOptions.AggressiveInlining)]
327  public static MemoryStream GetMemoryStream(Guid guid)
328  {
329  return MemoryManager.GetStream(guid);
330  }
331 
332  /// <summary>
333  /// Serialize a list of ticks using protobuf
334  /// </summary>
335  /// <param name="ticks">The list of ticks to serialize</param>
336  /// <param name="guid">Unique guid</param>
337  /// <returns>The resulting byte array</returns>
338  public static byte[] ProtobufSerialize(this List<Tick> ticks, Guid guid)
339  {
340  byte[] result;
341  using (var stream = GetMemoryStream(guid))
342  {
343  Serializer.Serialize(stream, ticks);
344  result = stream.ToArray();
345  }
346  return result;
347  }
348 
349  /// <summary>
350  /// Serialize a base data instance using protobuf
351  /// </summary>
352  /// <param name="baseData">The data point to serialize</param>
353  /// <param name="guid">Unique guid</param>
354  /// <returns>The resulting byte array</returns>
355  public static byte[] ProtobufSerialize(this IBaseData baseData, Guid guid)
356  {
357  byte[] result;
358  using (var stream = GetMemoryStream(guid))
359  {
360  baseData.ProtobufSerialize(stream);
361  result = stream.ToArray();
362  }
363 
364  return result;
365  }
366 
367  /// <summary>
368  /// Serialize a base data instance using protobuf
369  /// </summary>
370  /// <param name="baseData">The data point to serialize</param>
371  /// <param name="stream">The destination stream</param>
372  public static void ProtobufSerialize(this IBaseData baseData, Stream stream)
373  {
374  switch (baseData.DataType)
375  {
376  case MarketDataType.Tick:
377  Serializer.SerializeWithLengthPrefix(stream, baseData as Tick, PrefixStyle.Base128, 1);
378  break;
379  case MarketDataType.QuoteBar:
380  Serializer.SerializeWithLengthPrefix(stream, baseData as QuoteBar, PrefixStyle.Base128, 1);
381  break;
382  case MarketDataType.TradeBar:
383  Serializer.SerializeWithLengthPrefix(stream, baseData as TradeBar, PrefixStyle.Base128, 1);
384  break;
385  default:
386  Serializer.SerializeWithLengthPrefix(stream, baseData as BaseData, PrefixStyle.Base128, 1);
387  break;
388  }
389  }
390 
391  /// <summary>
392  /// Extension method to get security price is 0 messages for users
393  /// </summary>
394  /// <remarks>The value of this method is normalization</remarks>
395  public static string GetZeroPriceMessage(this Symbol symbol)
396  {
398  }
399 
400  /// <summary>
401  /// Converts the provided string into camel case notation
402  /// </summary>
403  public static string ToCamelCase(this string value)
404  {
405  if (string.IsNullOrEmpty(value))
406  {
407  return value;
408  }
409 
410  if (value.Length == 1)
411  {
412  return value.ToLowerInvariant();
413  }
414  return char.ToLowerInvariant(value[0]) + value.Substring(1);
415  }
416 
417  /// <summary>
418  /// Helper method to batch a collection of <see cref="AlphaResultPacket"/> into 1 single instance.
419  /// Will return null if the provided list is empty. Will keep the last Order instance per order id,
420  /// which is the latest. Implementations trusts the provided 'resultPackets' list to batch is in order
421  /// </summary>
422  public static AlphaResultPacket Batch(this List<AlphaResultPacket> resultPackets)
423  {
424  AlphaResultPacket resultPacket = null;
425 
426  // batch result packets into a single packet
427  if (resultPackets.Count > 0)
428  {
429  // we will batch results into the first packet
430  resultPacket = resultPackets[0];
431  for (var i = 1; i < resultPackets.Count; i++)
432  {
433  var newerPacket = resultPackets[i];
434 
435  // only batch current packet if there actually is data
436  if (newerPacket.Insights != null)
437  {
438  if (resultPacket.Insights == null)
439  {
440  // initialize the collection if it isn't there
441  resultPacket.Insights = new List<Insight>();
442  }
443  resultPacket.Insights.AddRange(newerPacket.Insights);
444  }
445 
446  // only batch current packet if there actually is data
447  if (newerPacket.OrderEvents != null)
448  {
449  if (resultPacket.OrderEvents == null)
450  {
451  // initialize the collection if it isn't there
452  resultPacket.OrderEvents = new List<OrderEvent>();
453  }
454  resultPacket.OrderEvents.AddRange(newerPacket.OrderEvents);
455  }
456 
457  // only batch current packet if there actually is data
458  if (newerPacket.Orders != null)
459  {
460  if (resultPacket.Orders == null)
461  {
462  // initialize the collection if it isn't there
463  resultPacket.Orders = new List<Order>();
464  }
465  resultPacket.Orders.AddRange(newerPacket.Orders);
466 
467  // GroupBy guarantees to respect original order, so we want to get the last order instance per order id
468  // this way we only keep the most updated version
469  resultPacket.Orders = resultPacket.Orders.GroupBy(order => order.Id)
470  .Select(ordersGroup => ordersGroup.Last()).ToList();
471  }
472  }
473  }
474  return resultPacket;
475  }
476 
477  /// <summary>
478  /// Helper method to safely stop a running thread
479  /// </summary>
480  /// <param name="thread">The thread to stop</param>
481  /// <param name="timeout">The timeout to wait till the thread ends after which abort will be called</param>
482  /// <param name="token">Cancellation token source to use if any</param>
483  public static void StopSafely(this Thread thread, TimeSpan timeout, CancellationTokenSource token = null)
484  {
485  if (thread != null)
486  {
487  try
488  {
489  if (token != null && !token.IsCancellationRequested)
490  {
491  token.Cancel(false);
492  }
493  Log.Trace($"StopSafely(): {Messages.Extensions.WaitingForThreadToStopSafely(thread.Name)}");
494  // just in case we add a time out
495  if (!thread.Join(timeout))
496  {
497  Log.Error($"StopSafely(): {Messages.Extensions.TimeoutWaitingForThreadToStopSafely(thread.Name)}");
498  }
499  }
500  catch (Exception exception)
501  {
502  // just in case catch any exceptions
503  Log.Error(exception);
504  }
505  }
506  }
507 
508  /// <summary>
509  /// Generates a hash code from a given collection of orders
510  /// </summary>
511  /// <param name="orders">The order collection</param>
512  /// <returns>The hash value</returns>
513  public static string GetHash(this IDictionary<int, Order> orders)
514  {
515  var joinedOrders = string.Join(
516  ",",
517  orders
518  .OrderBy(pair => pair.Key)
519  .Select(pair =>
520  {
521  // this is required to avoid any small differences between python and C#
522  var order = pair.Value;
523  order.Price = order.Price.SmartRounding();
524  var limit = order as LimitOrder;
525  if (limit != null)
526  {
527  limit.LimitPrice = limit.LimitPrice.SmartRounding();
528  }
529  var stopLimit = order as StopLimitOrder;
530  if (stopLimit != null)
531  {
532  stopLimit.LimitPrice = stopLimit.LimitPrice.SmartRounding();
533  stopLimit.StopPrice = stopLimit.StopPrice.SmartRounding();
534  }
535  var trailingStop = order as TrailingStopOrder;
536  if (trailingStop != null)
537  {
538  trailingStop.TrailingAmount = trailingStop.TrailingAmount.SmartRounding();
539  }
540  var stopMarket = order as StopMarketOrder;
541  if (stopMarket != null)
542  {
543  stopMarket.StopPrice = stopMarket.StopPrice.SmartRounding();
544  }
545  var limitIfTouched = order as LimitIfTouchedOrder;
546  if (limitIfTouched != null)
547  {
548  limitIfTouched.LimitPrice = limitIfTouched.LimitPrice.SmartRounding();
549  limitIfTouched.TriggerPrice = limitIfTouched.TriggerPrice.SmartRounding();
550  }
551  return JsonConvert.SerializeObject(pair.Value, Formatting.None);
552  }
553  )
554  );
555 
556  return joinedOrders.ToMD5();
557  }
558 
559  /// <summary>
560  /// Converts a date rule into a function that receives current time
561  /// and returns the next date.
562  /// </summary>
563  /// <param name="dateRule">The date rule to convert</param>
564  /// <returns>A function that will enumerate the provided date rules</returns>
565  public static Func<DateTime, DateTime?> ToFunc(this IDateRule dateRule)
566  {
567  IEnumerator<DateTime> dates = null;
568  return timeUtc =>
569  {
570  if (dates == null)
571  {
572  dates = dateRule.GetDates(timeUtc, Time.EndOfTime).GetEnumerator();
573  if (!dates.MoveNext())
574  {
575  return Time.EndOfTime;
576  }
577  }
578 
579  try
580  {
581  // only advance enumerator if provided time is past or at our current
582  if (timeUtc >= dates.Current)
583  {
584  if (!dates.MoveNext())
585  {
586  return Time.EndOfTime;
587  }
588  }
589  return dates.Current;
590  }
591  catch (InvalidOperationException)
592  {
593  // enumeration ended
594  return Time.EndOfTime;
595  }
596  };
597  }
598 
599  /// <summary>
600  /// Returns true if the specified <see cref="BaseSeries"/> instance holds no <see cref="ISeriesPoint"/>
601  /// </summary>
602  public static bool IsEmpty(this BaseSeries series)
603  {
604  return series.Values.Count == 0;
605  }
606 
607  /// <summary>
608  /// Returns if the specified <see cref="Chart"/> instance holds no <see cref="Series"/>
609  /// or they are all empty <see cref="Extensions.IsEmpty(BaseSeries)"/>
610  /// </summary>
611  public static bool IsEmpty(this Chart chart)
612  {
613  return chart.Series.Values.All(IsEmpty);
614  }
615 
616  /// <summary>
617  /// Gets a python method by name
618  /// </summary>
619  /// <param name="instance">The object instance to search the method in</param>
620  /// <param name="name">The name of the method</param>
621  /// <returns>The python method or null if not defined or CSharp implemented</returns>
622  public static dynamic GetPythonMethod(this PyObject instance, string name)
623  {
624  using (Py.GIL())
625  {
626  PyObject method;
627 
628  // Let's try first with snake-case style in case the user is using it
629  var snakeCasedNamed = name.ToSnakeCase();
630  if (snakeCasedNamed != name)
631  {
632  method = instance.GetPythonMethodWithChecks(snakeCasedNamed);
633  if (method != null)
634  {
635  return method;
636  }
637  }
638 
639  method = instance.GetAttr(name);
640  var pythonType = method.GetPythonType();
641  var isPythonDefined = pythonType.Repr().Equals("<class \'method\'>", StringComparison.Ordinal);
642 
643  if (isPythonDefined)
644  {
645  return method;
646  }
647 
648  return null;
649  }
650  }
651 
652  /// <summary>
653  /// Gets a python property by name
654  /// </summary>
655  /// <param name="instance">The object instance to search the property in</param>
656  /// <param name="name">The name of the property</param>
657  /// <returns>The python property or null if not defined or CSharp implemented</returns>
658  public static dynamic GetPythonBoolProperty(this PyObject instance, string name)
659  {
660  using (Py.GIL())
661  {
662  var objectType = instance.GetPythonType();
663  if (!objectType.HasAttr(name))
664  {
665  return null;
666  }
667 
668  var property = instance.GetAttr(name);
669  var pythonType = property.GetPythonType();
670  var isPythonDefined = pythonType.Repr().Equals("<class \'bool\'>", StringComparison.Ordinal);
671 
672  if (isPythonDefined)
673  {
674  return property;
675  }
676 
677  return null;
678  }
679  }
680 
681  /// <summary>
682  /// Gets a python property by name
683  /// </summary>
684  /// <param name="instance">The object instance to search the property in</param>
685  /// <param name="name">The name of the method</param>
686  /// <returns>The python property or null if not defined or CSharp implemented</returns>
687  public static dynamic GetPythonBoolPropertyWithChecks(this PyObject instance, string name)
688  {
689  using (Py.GIL())
690  {
691  if (!instance.HasAttr(name))
692  {
693  return null;
694  }
695 
696  return instance.GetPythonBoolProperty(name);
697  }
698  }
699 
700  /// <summary>
701  /// Gets a python method by name
702  /// </summary>
703  /// <param name="instance">The object instance to search the method in</param>
704  /// <param name="name">The name of the method</param>
705  /// <returns>The python method or null if not defined or CSharp implemented</returns>
706  public static dynamic GetPythonMethodWithChecks(this PyObject instance, string name)
707  {
708  using (Py.GIL())
709  {
710  if (!instance.HasAttr(name))
711  {
712  return null;
713  }
714 
715  return instance.GetPythonMethod(name);
716  }
717  }
718 
719  /// <summary>
720  /// Gets a method from a <see cref="PyObject"/> instance by name.
721  /// First, it tries to get the snake-case version of the method name, in case the user is using that style.
722  /// Else, it tries to get the method with the original name, regardless of whether the class has a Python overload or not.
723  /// </summary>
724  /// <param name="instance">The object instance to search the method in</param>
725  /// <param name="name">The name of the method</param>
726  /// <returns>The method matching the name</returns>
727  public static dynamic GetMethod(this PyObject instance, string name)
728  {
729  using var _ = Py.GIL();
730  return instance.GetPythonMethodWithChecks(name.ToSnakeCase()) ?? instance.GetAttr(name);
731  }
732 
733  /// <summary>
734  /// Get a python methods arg count
735  /// </summary>
736  /// <param name="method">The Python method</param>
737  /// <returns>Count of arguments</returns>
738  public static int GetPythonArgCount(this PyObject method)
739  {
740  using (Py.GIL())
741  {
742  int argCount;
743  var pyArgCount = PyModule.FromString(Guid.NewGuid().ToString(),
744  "from inspect import signature\n" +
745  "def GetArgCount(method):\n" +
746  " return len(signature(method).parameters)\n"
747  ).GetAttr("GetArgCount").Invoke(method);
748  pyArgCount.TryConvert(out argCount);
749 
750  return argCount;
751  }
752  }
753 
754  /// <summary>
755  /// Returns an ordered enumerable where position reducing orders are executed first
756  /// and the remaining orders are executed in decreasing order value.
757  /// Will NOT return targets during algorithm warmup.
758  /// Will NOT return targets for securities that have no data yet.
759  /// Will NOT return targets for which current holdings + open orders quantity, sum up to the target quantity
760  /// </summary>
761  /// <param name="targets">The portfolio targets to order by margin</param>
762  /// <param name="algorithm">The algorithm instance</param>
763  /// <param name="targetIsDelta">True if the target quantity is the delta between the
764  /// desired and existing quantity</param>
765  public static IEnumerable<IPortfolioTarget> OrderTargetsByMarginImpact(
766  this IEnumerable<IPortfolioTarget> targets,
767  IAlgorithm algorithm,
768  bool targetIsDelta = false)
769  {
770  if (algorithm.IsWarmingUp)
771  {
772  return Enumerable.Empty<IPortfolioTarget>();
773  }
774 
775  return targets.Select(x =>
776  {
777  var security = algorithm.Securities[x.Symbol];
778  return new
779  {
780  PortfolioTarget = x,
781  TargetQuantity = OrderSizing.AdjustByLotSize(security, x.Quantity),
782  ExistingQuantity = security.Holdings.Quantity
783  + algorithm.Transactions.GetOpenOrderTickets(x.Symbol)
784  .Aggregate(0m, (d, t) => d + t.Quantity - t.QuantityFilled),
785  Security = security
786  };
787  })
788  .Where(x => x.Security.HasData
789  && x.Security.IsTradable
790  && (targetIsDelta ? Math.Abs(x.TargetQuantity) : Math.Abs(x.TargetQuantity - x.ExistingQuantity))
791  >= x.Security.SymbolProperties.LotSize
792  )
793  .Select(x => new {
794  x.PortfolioTarget,
795  OrderValue = Math.Abs((targetIsDelta ? x.TargetQuantity : (x.TargetQuantity - x.ExistingQuantity)) * x.Security.Price),
796  IsReducingPosition = x.ExistingQuantity != 0
797  && Math.Abs((targetIsDelta ? (x.TargetQuantity + x.ExistingQuantity) : x.TargetQuantity)) < Math.Abs(x.ExistingQuantity)
798  })
799  .OrderByDescending(x => x.IsReducingPosition)
800  .ThenByDescending(x => x.OrderValue)
801  .Select(x => x.PortfolioTarget);
802  }
803 
804  /// <summary>
805  /// Given a type will create a new instance using the parameterless constructor
806  /// and assert the type implements <see cref="BaseData"/>
807  /// </summary>
808  /// <remarks>One of the objectives of this method is to normalize the creation of the
809  /// BaseData instances while reducing code duplication</remarks>
810  public static BaseData GetBaseDataInstance(this Type type)
811  {
812  var objectActivator = ObjectActivator.GetActivator(type);
813  if (objectActivator == null)
814  {
815  throw new ArgumentException(Messages.Extensions.DataTypeMissingParameterlessConstructor(type));
816  }
817 
818  var instance = objectActivator.Invoke(new object[] { type });
819  if(instance == null)
820  {
821  // shouldn't happen but just in case...
822  throw new ArgumentException(Messages.Extensions.FailedToCreateInstanceOfType(type));
823  }
824 
825  // we expect 'instance' to inherit BaseData in most cases so we use 'as' versus 'IsAssignableFrom'
826  // since it is slightly cheaper
827  var result = instance as BaseData;
828  if (result == null)
829  {
830  throw new ArgumentException(Messages.Extensions.TypeIsNotBaseData(type));
831  }
832  return result;
833  }
834 
835  /// <summary>
836  /// Helper method that will cast the provided <see cref="PyObject"/>
837  /// to a T type and dispose of it.
838  /// </summary>
839  /// <typeparam name="T">The target type</typeparam>
840  /// <param name="instance">The <see cref="PyObject"/> instance to cast and dispose</param>
841  /// <returns>The instance of type T. Will return default value if
842  /// provided instance is null</returns>
843  public static T GetAndDispose<T>(this PyObject instance)
844  {
845  if (instance == null)
846  {
847  return default(T);
848  }
849  var returnInstance = instance.As<T>();
850  // will reduce ref count
851  instance.Dispose();
852  return returnInstance;
853  }
854 
855  /// <summary>
856  /// Extension to move one element from list from A to position B.
857  /// </summary>
858  /// <typeparam name="T">Type of list</typeparam>
859  /// <param name="list">List we're operating on.</param>
860  /// <param name="oldIndex">Index of variable we want to move.</param>
861  /// <param name="newIndex">New location for the variable</param>
862  public static void Move<T>(this List<T> list, int oldIndex, int newIndex)
863  {
864  var oItem = list[oldIndex];
865  list.RemoveAt(oldIndex);
866  if (newIndex > oldIndex) newIndex--;
867  list.Insert(newIndex, oItem);
868  }
869 
870  /// <summary>
871  /// Extension method to convert a string into a byte array
872  /// </summary>
873  /// <param name="str">String to convert to bytes.</param>
874  /// <returns>Byte array</returns>
875  public static byte[] GetBytes(this string str)
876  {
877  var bytes = new byte[str.Length * sizeof(char)];
878  Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
879  return bytes;
880  }
881 
882  /// <summary>
883  /// Reads the entire content of a stream and returns it as a byte array.
884  /// </summary>
885  /// <param name="stream">Stream to read bytes from</param>
886  /// <returns>The bytes read from the stream</returns>
887  public static byte[] GetBytes(this Stream stream)
888  {
889  using var memoryStream = new MemoryStream();
890  stream.CopyTo(memoryStream);
891  return memoryStream.ToArray();
892  }
893 
894  /// <summary>
895  /// Extentsion method to clear all items from a thread safe queue
896  /// </summary>
897  /// <remarks>Small risk of race condition if a producer is adding to the list.</remarks>
898  /// <typeparam name="T">Queue type</typeparam>
899  /// <param name="queue">queue object</param>
900  public static void Clear<T>(this ConcurrentQueue<T> queue)
901  {
902  T item;
903  while (queue.TryDequeue(out item)) {
904  // NOP
905  }
906  }
907 
908  /// <summary>
909  /// Extension method to convert a byte array into a string.
910  /// </summary>
911  /// <param name="bytes">Byte array to convert.</param>
912  /// <param name="encoding">The encoding to use for the conversion. Defaults to Encoding.ASCII</param>
913  /// <returns>String from bytes.</returns>
914  public static string GetString(this byte[] bytes, Encoding encoding = null)
915  {
916  if (encoding == null) encoding = Encoding.ASCII;
917 
918  return encoding.GetString(bytes);
919  }
920 
921  /// <summary>
922  /// Extension method to convert a string to a MD5 hash.
923  /// </summary>
924  /// <param name="str">String we want to MD5 encode.</param>
925  /// <returns>MD5 hash of a string</returns>
926  public static string ToMD5(this string str)
927  {
928  var builder = new StringBuilder(32);
929  var data = MD5.HashData(Encoding.UTF8.GetBytes(str));
930  for (var i = 0; i < 16; i++)
931  {
932  builder.Append(data[i].ToStringInvariant("x2"));
933  }
934  return builder.ToString();
935  }
936 
937  /// <summary>
938  /// Encrypt the token:time data to make our API hash.
939  /// </summary>
940  /// <param name="data">Data to be hashed by SHA256</param>
941  /// <returns>Hashed string.</returns>
942  public static string ToSHA256(this string data)
943  {
944  var hash = new StringBuilder(64);
945  var crypto = SHA256.HashData(Encoding.UTF8.GetBytes(data));
946  for (var i = 0; i < 32; i++)
947  {
948  hash.Append(crypto[i].ToStringInvariant("x2"));
949  }
950  return hash.ToString();
951  }
952 
953  /// <summary>
954  /// Converts a long to an uppercase alpha numeric string
955  /// </summary>
956  public static string EncodeBase36(this ulong data)
957  {
958  var stack = new Stack<char>(15);
959  while (data != 0)
960  {
961  var value = data % 36;
962  var c = value < 10
963  ? (char)(value + '0')
964  : (char)(value - 10 + 'A');
965 
966  stack.Push(c);
967  data /= 36;
968  }
969  return new string(stack.ToArray());
970  }
971 
972  /// <summary>
973  /// Converts an upper case alpha numeric string into a long
974  /// </summary>
975  public static ulong DecodeBase36(this string symbol)
976  {
977  var result = 0ul;
978  var baseValue = 1ul;
979  for (var i = symbol.Length - 1; i > -1; i--)
980  {
981  var c = symbol[i];
982 
983  // assumes alpha numeric upper case only strings
984  var value = (uint)(c <= 57
985  ? c - '0'
986  : c - 'A' + 10);
987 
988  result += baseValue * value;
989  baseValue *= 36;
990  }
991 
992  return result;
993  }
994 
995  /// <summary>
996  /// Convert a string to Base64 Encoding
997  /// </summary>
998  /// <param name="text">Text to encode</param>
999  /// <returns>Encoded result</returns>
1000  public static string EncodeBase64(this string text)
1001  {
1002  if (string.IsNullOrEmpty(text))
1003  {
1004  return text;
1005  }
1006 
1007  byte[] textBytes = Encoding.UTF8.GetBytes(text);
1008  return Convert.ToBase64String(textBytes);
1009  }
1010 
1011  /// <summary>
1012  /// Decode a Base64 Encoded string
1013  /// </summary>
1014  /// <param name="base64EncodedText">Text to decode</param>
1015  /// <returns>Decoded result</returns>
1016  public static string DecodeBase64(this string base64EncodedText)
1017  {
1018  if (string.IsNullOrEmpty(base64EncodedText))
1019  {
1020  return base64EncodedText;
1021  }
1022 
1023  byte[] base64EncodedBytes = Convert.FromBase64String(base64EncodedText);
1024  return Encoding.UTF8.GetString(base64EncodedBytes);
1025  }
1026 
1027  /// <summary>
1028  /// Lazy string to upper implementation.
1029  /// Will first verify the string is not already upper and avoid
1030  /// the call to <see cref="string.ToUpperInvariant()"/> if possible.
1031  /// </summary>
1032  /// <param name="data">The string to upper</param>
1033  /// <returns>The upper string</returns>
1034  public static string LazyToUpper(this string data)
1035  {
1036  // for performance only call to upper if required
1037  var alreadyUpper = true;
1038  for (int i = 0; i < data.Length && alreadyUpper; i++)
1039  {
1040  alreadyUpper = char.IsUpper(data[i]);
1041  }
1042  return alreadyUpper ? data : data.ToUpperInvariant();
1043  }
1044 
1045  /// <summary>
1046  /// Lazy string to lower implementation.
1047  /// Will first verify the string is not already lower and avoid
1048  /// the call to <see cref="string.ToLowerInvariant()"/> if possible.
1049  /// </summary>
1050  /// <param name="data">The string to lower</param>
1051  /// <returns>The lower string</returns>
1052  public static string LazyToLower(this string data)
1053  {
1054  // for performance only call to lower if required
1055  var alreadyLower = true;
1056  for (int i = 0; i < data.Length && alreadyLower; i++)
1057  {
1058  alreadyLower = char.IsLower(data[i]);
1059  }
1060  return alreadyLower ? data : data.ToLowerInvariant();
1061  }
1062 
1063  /// <summary>
1064  /// Extension method to automatically set the update value to same as "add" value for TryAddUpdate.
1065  /// This makes the API similar for traditional and concurrent dictionaries.
1066  /// </summary>
1067  /// <typeparam name="K">Key type for dictionary</typeparam>
1068  /// <typeparam name="V">Value type for dictonary</typeparam>
1069  /// <param name="dictionary">Dictionary object we're operating on</param>
1070  /// <param name="key">Key we want to add or update.</param>
1071  /// <param name="value">Value we want to set.</param>
1072  public static void AddOrUpdate<K, V>(this ConcurrentDictionary<K, V> dictionary, K key, V value)
1073  {
1074  dictionary.AddOrUpdate(key, value, (oldkey, oldvalue) => value);
1075  }
1076 
1077  /// <summary>
1078  /// Extension method to automatically add/update lazy values in concurrent dictionary.
1079  /// </summary>
1080  /// <typeparam name="TKey">Key type for dictionary</typeparam>
1081  /// <typeparam name="TValue">Value type for dictonary</typeparam>
1082  /// <param name="dictionary">Dictionary object we're operating on</param>
1083  /// <param name="key">Key we want to add or update.</param>
1084  /// <param name="addValueFactory">The function used to generate a value for an absent key</param>
1085  /// <param name="updateValueFactory">The function used to generate a new value for an existing key based on the key's existing value</param>
1086  public static TValue AddOrUpdate<TKey, TValue>(this ConcurrentDictionary<TKey, Lazy<TValue>> dictionary, TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
1087  {
1088  var result = dictionary.AddOrUpdate(key, new Lazy<TValue>(() => addValueFactory(key)), (key2, old) => new Lazy<TValue>(() => updateValueFactory(key2, old.Value)));
1089  return result.Value;
1090  }
1091 
1092  /// <summary>
1093  /// Adds the specified element to the collection with the specified key. If an entry does not exist for the
1094  /// specified key then one will be created.
1095  /// </summary>
1096  /// <typeparam name="TKey">The key type</typeparam>
1097  /// <typeparam name="TElement">The collection element type</typeparam>
1098  /// <typeparam name="TCollection">The collection type</typeparam>
1099  /// <param name="dictionary">The source dictionary to be added to</param>
1100  /// <param name="key">The key</param>
1101  /// <param name="element">The element to be added</param>
1102  public static void Add<TKey, TElement, TCollection>(this IDictionary<TKey, TCollection> dictionary, TKey key, TElement element)
1103  where TCollection : ICollection<TElement>, new()
1104  {
1105  TCollection list;
1106  if (!dictionary.TryGetValue(key, out list))
1107  {
1108  list = new TCollection();
1109  dictionary.Add(key, list);
1110  }
1111  list.Add(element);
1112  }
1113 
1114  /// <summary>
1115  /// Adds the specified element to the collection with the specified key. If an entry does not exist for the
1116  /// specified key then one will be created.
1117  /// </summary>
1118  /// <typeparam name="TKey">The key type</typeparam>
1119  /// <typeparam name="TElement">The collection element type</typeparam>
1120  /// <param name="dictionary">The source dictionary to be added to</param>
1121  /// <param name="key">The key</param>
1122  /// <param name="element">The element to be added</param>
1123  public static ImmutableDictionary<TKey, ImmutableHashSet<TElement>> Add<TKey, TElement>(
1124  this ImmutableDictionary<TKey, ImmutableHashSet<TElement>> dictionary,
1125  TKey key,
1126  TElement element
1127  )
1128  {
1129  ImmutableHashSet<TElement> set;
1130  if (!dictionary.TryGetValue(key, out set))
1131  {
1132  set = ImmutableHashSet<TElement>.Empty.Add(element);
1133  return dictionary.Add(key, set);
1134  }
1135 
1136  return dictionary.SetItem(key, set.Add(element));
1137  }
1138 
1139  /// <summary>
1140  /// Adds the specified element to the collection with the specified key. If an entry does not exist for the
1141  /// specified key then one will be created.
1142  /// </summary>
1143  /// <typeparam name="TKey">The key type</typeparam>
1144  /// <typeparam name="TElement">The collection element type</typeparam>
1145  /// <param name="dictionary">The source dictionary to be added to</param>
1146  /// <param name="key">The key</param>
1147  /// <param name="element">The element to be added</param>
1148  public static ImmutableSortedDictionary<TKey, ImmutableHashSet<TElement>> Add<TKey, TElement>(
1149  this ImmutableSortedDictionary<TKey, ImmutableHashSet<TElement>> dictionary,
1150  TKey key,
1151  TElement element
1152  )
1153  {
1154  ImmutableHashSet<TElement> set;
1155  if (!dictionary.TryGetValue(key, out set))
1156  {
1157  set = ImmutableHashSet<TElement>.Empty.Add(element);
1158  return dictionary.Add(key, set);
1159  }
1160 
1161  return dictionary.SetItem(key, set.Add(element));
1162  }
1163 
1164  /// <summary>
1165  /// Adds the specified Tick to the Ticks collection. If an entry does not exist for the specified key then one will be created.
1166  /// </summary>
1167  /// <param name="dictionary">The ticks dictionary</param>
1168  /// <param name="key">The symbol</param>
1169  /// <param name="tick">The tick to add</param>
1170  /// <remarks>For performance we implement this method based on <see cref="Add{TKey,TElement,TCollection}"/></remarks>
1171  public static void Add(this Ticks dictionary, Symbol key, Tick tick)
1172  {
1173  List<Tick> list;
1174  if (!dictionary.TryGetValue(key, out list))
1175  {
1176  dictionary[key] = list = new List<Tick>(1);
1177  }
1178  list.Add(tick);
1179  }
1180 
1181  /// <summary>
1182  /// Extension method to round a double value to a fixed number of significant figures instead of a fixed decimal places.
1183  /// </summary>
1184  /// <param name="d">Double we're rounding</param>
1185  /// <param name="digits">Number of significant figures</param>
1186  /// <returns>New double rounded to digits-significant figures</returns>
1187  public static decimal RoundToSignificantDigits(this decimal d, int digits)
1188  {
1189  if (d == 0) return 0;
1190  var scale = (decimal)Math.Pow(10, Math.Floor(Math.Log10((double) Math.Abs(d))) + 1);
1191  return scale * Math.Round(d / scale, digits);
1192  }
1193 
1194  /// <summary>
1195  /// Converts a decimal into a rounded number ending with K (thousands), M (millions), B (billions), etc.
1196  /// </summary>
1197  /// <param name="number">Number to convert</param>
1198  /// <returns>Formatted number with figures written in shorthand form</returns>
1199  public static string ToFinancialFigures(this decimal number)
1200  {
1201  if (number < 1000)
1202  {
1203  return number.ToStringInvariant();
1204  }
1205 
1206  // Subtract by multiples of 5 to round down to nearest round number
1207  if (number < 10000)
1208  {
1209  return (number - 5m).ToString("#,.##", CultureInfo.InvariantCulture) + "K";
1210  }
1211 
1212  if (number < 100000)
1213  {
1214  return (number - 50m).ToString("#,.#", CultureInfo.InvariantCulture) + "K";
1215  }
1216 
1217  if (number < 1000000)
1218  {
1219  return (number - 500m).ToString("#,.", CultureInfo.InvariantCulture) + "K";
1220  }
1221 
1222  if (number < 10000000)
1223  {
1224  return (number - 5000m).ToString("#,,.##", CultureInfo.InvariantCulture) + "M";
1225  }
1226 
1227  if (number < 100000000)
1228  {
1229  return (number - 50000m).ToString("#,,.#", CultureInfo.InvariantCulture) + "M";
1230  }
1231 
1232  if (number < 1000000000)
1233  {
1234  return (number - 500000m).ToString("#,,.", CultureInfo.InvariantCulture) + "M";
1235  }
1236 
1237  return (number - 5000000m).ToString("#,,,.##", CultureInfo.InvariantCulture) + "B";
1238  }
1239 
1240  /// <summary>
1241  /// Discretizes the <paramref name="value"/> to a maximum precision specified by <paramref name="quanta"/>. Quanta
1242  /// can be an arbitrary positive number and represents the step size. Consider a quanta equal to 0.15 and rounding
1243  /// a value of 1.0. Valid values would be 0.9 (6 quanta) and 1.05 (7 quanta) which would be rounded up to 1.05.
1244  /// </summary>
1245  /// <param name="value">The value to be rounded by discretization</param>
1246  /// <param name="quanta">The maximum precision allowed by the value</param>
1247  /// <param name="mode">Specifies how to handle the rounding of half value, defaulting to away from zero.</param>
1248  /// <returns></returns>
1249  public static decimal DiscretelyRoundBy(this decimal value, decimal quanta, MidpointRounding mode = MidpointRounding.AwayFromZero)
1250  {
1251  if (quanta == 0m)
1252  {
1253  return value;
1254  }
1255 
1256  // away from zero is the 'common sense' rounding.
1257  // +0.5 rounded by 1 yields +1
1258  // -0.5 rounded by 1 yields -1
1259  var multiplicand = Math.Round(value / quanta, mode);
1260  return quanta * multiplicand;
1261  }
1262 
1263  /// <summary>
1264  /// Will truncate the provided decimal, without rounding, to 3 decimal places
1265  /// </summary>
1266  /// <param name="value">The value to truncate</param>
1267  /// <returns>New instance with just 3 decimal places</returns>
1268  public static decimal TruncateTo3DecimalPlaces(this decimal value)
1269  {
1270  // we will multiply by 1k bellow, if its bigger it will stack overflow
1271  if (value >= decimal.MaxValue / 1000
1272  || value <= decimal.MinValue / 1000
1273  || value == 0)
1274  {
1275  return value;
1276  }
1277 
1278  return Math.Truncate(1000 * value) / 1000;
1279  }
1280 
1281  /// <summary>
1282  /// Provides global smart rounding, numbers larger than 1000 will round to 4 decimal places,
1283  /// while numbers smaller will round to 7 significant digits
1284  /// </summary>
1285  public static decimal? SmartRounding(this decimal? input)
1286  {
1287  if (!input.HasValue)
1288  {
1289  return null;
1290  }
1291  return input.Value.SmartRounding();
1292  }
1293 
1294  /// <summary>
1295  /// Provides global smart rounding, numbers larger than 1000 will round to 4 decimal places,
1296  /// while numbers smaller will round to 7 significant digits
1297  /// </summary>
1298  public static decimal SmartRounding(this decimal input)
1299  {
1300  input = Normalize(input);
1301 
1302  // any larger numbers we still want some decimal places
1303  if (input > 1000)
1304  {
1305  return Math.Round(input, 4);
1306  }
1307 
1308  // this is good for forex and other small numbers
1309  return input.RoundToSignificantDigits(7).Normalize();
1310  }
1311 
1312  /// <summary>
1313  /// Provides global smart rounding to a shorter version
1314  /// </summary>
1315  public static decimal SmartRoundingShort(this decimal input)
1316  {
1317  input = Normalize(input);
1318  if (input <= 1)
1319  {
1320  // 0.99 > input
1321  return input;
1322  }
1323  else if (input <= 10)
1324  {
1325  // 1.01 to 9.99
1326  return Math.Round(input, 2);
1327  }
1328  else if (input <= 100)
1329  {
1330  // 99.9 to 10.1
1331  return Math.Round(input, 1);
1332  }
1333  // 100 to inf
1334  return Math.Truncate(input);
1335  }
1336 
1337  /// <summary>
1338  /// Casts the specified input value to a decimal while acknowledging the overflow conditions
1339  /// </summary>
1340  /// <param name="input">The value to be cast</param>
1341  /// <returns>The input value as a decimal, if the value is too large or to small to be represented
1342  /// as a decimal, then the closest decimal value will be returned</returns>
1343  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1344  public static decimal SafeDecimalCast(this double input)
1345  {
1346  if (input.IsNaNOrInfinity())
1347  {
1348  throw new ArgumentException(
1350  nameof(input),
1351  new NotFiniteNumberException(input)
1352  );
1353  }
1354 
1355  if (input <= (double) decimal.MinValue) return decimal.MinValue;
1356  if (input >= (double) decimal.MaxValue) return decimal.MaxValue;
1357  return (decimal) input;
1358  }
1359 
1360  /// <summary>
1361  /// Will remove any trailing zeros for the provided decimal input
1362  /// </summary>
1363  /// <param name="input">The <see cref="decimal"/> to remove trailing zeros from</param>
1364  /// <returns>Provided input with no trailing zeros</returns>
1365  /// <remarks>Will not have the expected behavior when called from Python,
1366  /// since the returned <see cref="decimal"/> will be converted to python float,
1367  /// <see cref="NormalizeToStr"/></remarks>
1368  public static decimal Normalize(this decimal input)
1369  {
1370  // http://stackoverflow.com/a/7983330/1582922
1371  return input / 1.000000000000000000000000000000000m;
1372  }
1373 
1374  /// <summary>
1375  /// Will remove any trailing zeros for the provided decimal and convert to string.
1376  /// Uses <see cref="Normalize(decimal)"/>.
1377  /// </summary>
1378  /// <param name="input">The <see cref="decimal"/> to convert to <see cref="string"/></param>
1379  /// <returns>Input converted to <see cref="string"/> with no trailing zeros</returns>
1380  public static string NormalizeToStr(this decimal input)
1381  {
1382  return Normalize(input).ToString(CultureInfo.InvariantCulture);
1383  }
1384 
1385  /// <summary>
1386  /// Helper method to determine the amount of decimal places associated with the given decimal
1387  /// </summary>
1388  /// <param name="input">The value to get the decimal count from</param>
1389  /// <returns>The quantity of decimal places</returns>
1390  public static int GetDecimalPlaces(this decimal input)
1391  {
1392  return BitConverter.GetBytes(decimal.GetBits(input)[3])[2];
1393  }
1394 
1395  /// <summary>
1396  /// Extension method for faster string to decimal conversion.
1397  /// </summary>
1398  /// <param name="str">String to be converted to positive decimal value</param>
1399  /// <remarks>
1400  /// Leading and trailing whitespace chars are ignored
1401  /// </remarks>
1402  /// <returns>Decimal value of the string</returns>
1403  public static decimal ToDecimal(this string str)
1404  {
1405  long value = 0;
1406  var decimalPlaces = 0;
1407  var hasDecimals = false;
1408  var index = 0;
1409  var length = str.Length;
1410 
1411  while (index < length && char.IsWhiteSpace(str[index]))
1412  {
1413  index++;
1414  }
1415 
1416  var isNegative = index < length && str[index] == '-';
1417  if (isNegative)
1418  {
1419  index++;
1420  }
1421 
1422  while (index < length)
1423  {
1424  var ch = str[index++];
1425  if (ch == '.')
1426  {
1427  hasDecimals = true;
1428  decimalPlaces = 0;
1429  }
1430  else if (char.IsWhiteSpace(ch))
1431  {
1432  break;
1433  }
1434  else
1435  {
1436  value = value * 10 + (ch - '0');
1437  decimalPlaces++;
1438  }
1439  }
1440 
1441  var lo = (int)value;
1442  var mid = (int)(value >> 32);
1443  return new decimal(lo, mid, 0, isNegative, (byte)(hasDecimals ? decimalPlaces : 0));
1444  }
1445 
1446  /// <summary>
1447  /// Extension method for faster string to normalized decimal conversion, i.e. 20.0% should be parsed into 0.2
1448  /// </summary>
1449  /// <param name="str">String to be converted to positive decimal value</param>
1450  /// <remarks>
1451  /// Leading and trailing whitespace chars are ignored
1452  /// </remarks>
1453  /// <returns>Decimal value of the string</returns>
1454  public static decimal ToNormalizedDecimal(this string str)
1455  {
1456  var trimmed = str.Trim();
1457  var value = str.TrimEnd('%').ToDecimal();
1458  if (trimmed.EndsWith("%"))
1459  {
1460  value /= 100;
1461  }
1462 
1463  return value;
1464  }
1465 
1466  /// <summary>
1467  /// Extension method for string to decimal conversion where string can represent a number with exponent xe-y
1468  /// </summary>
1469  /// <param name="str">String to be converted to decimal value</param>
1470  /// <returns>Decimal value of the string</returns>
1471  public static decimal ToDecimalAllowExponent(this string str)
1472  {
1473  return decimal.Parse(str, NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture);
1474  }
1475 
1476  /// <summary>
1477  /// Extension method for faster string to Int32 conversion.
1478  /// </summary>
1479  /// <param name="str">String to be converted to positive Int32 value</param>
1480  /// <remarks>Method makes some assuptions - always numbers, no "signs" +,- etc.</remarks>
1481  /// <returns>Int32 value of the string</returns>
1482  public static int ToInt32(this string str)
1483  {
1484  int value = 0;
1485  for (var i = 0; i < str.Length; i++)
1486  {
1487  if (str[i] == '.')
1488  break;
1489 
1490  value = value * 10 + (str[i] - '0');
1491  }
1492  return value;
1493  }
1494 
1495  /// <summary>
1496  /// Extension method for faster string to Int64 conversion.
1497  /// </summary>
1498  /// <param name="str">String to be converted to positive Int64 value</param>
1499  /// <remarks>Method makes some assuptions - always numbers, no "signs" +,- etc.</remarks>
1500  /// <returns>Int32 value of the string</returns>
1501  public static long ToInt64(this string str)
1502  {
1503  long value = 0;
1504  for (var i = 0; i < str.Length; i++)
1505  {
1506  if (str[i] == '.')
1507  break;
1508 
1509  value = value * 10 + (str[i] - '0');
1510  }
1511  return value;
1512  }
1513 
1514  /// <summary>
1515  /// Helper method to determine if a data type implements the Stream reader method
1516  /// </summary>
1517  public static bool ImplementsStreamReader(this Type baseDataType)
1518  {
1519  // we know these type implement the streamReader interface lets avoid dynamic reflection call to figure it out
1520  if (baseDataType == typeof(TradeBar) || baseDataType == typeof(QuoteBar) || baseDataType == typeof(Tick))
1521  {
1522  return true;
1523  }
1524 
1525  var method = baseDataType.GetMethod("Reader",
1526  new[] { typeof(SubscriptionDataConfig), typeof(StreamReader), typeof(DateTime), typeof(bool) });
1527  if (method != null && method.DeclaringType == baseDataType)
1528  {
1529  return true;
1530  }
1531  return false;
1532  }
1533 
1534  /// <summary>
1535  /// Breaks the specified string into csv components, all commas are considered separators
1536  /// </summary>
1537  /// <param name="str">The string to be broken into csv</param>
1538  /// <param name="size">The expected size of the output list</param>
1539  /// <returns>A list of the csv pieces</returns>
1540  public static List<string> ToCsv(this string str, int size = 4)
1541  {
1542  int last = 0;
1543  var csv = new List<string>(size);
1544  for (int i = 0; i < str.Length; i++)
1545  {
1546  if (str[i] == ',')
1547  {
1548  if (last != 0) last = last + 1;
1549  csv.Add(str.Substring(last, i - last));
1550  last = i;
1551  }
1552  }
1553  if (last != 0) last = last + 1;
1554  csv.Add(str.Substring(last));
1555  return csv;
1556  }
1557 
1558  /// <summary>
1559  /// Breaks the specified string into csv components, works correctly with commas in data fields
1560  /// </summary>
1561  /// <param name="str">The string to be broken into csv</param>
1562  /// <param name="size">The expected size of the output list</param>
1563  /// <param name="delimiter">The delimiter used to separate entries in the line</param>
1564  /// <returns>A list of the csv pieces</returns>
1565  public static List<string> ToCsvData(this string str, int size = 4, char delimiter = ',')
1566  {
1567  var csv = new List<string>(size);
1568 
1569  var last = -1;
1570  var count = 0;
1571  var textDataField = false;
1572 
1573  for (var i = 0; i < str.Length; i++)
1574  {
1575  var current = str[i];
1576  if (current == '"')
1577  {
1578  textDataField = !textDataField;
1579  }
1580  else if (!textDataField && current == delimiter)
1581  {
1582  csv.Add(str.Substring(last + 1, (i - last)).Trim(' ', ','));
1583  last = i;
1584  count++;
1585  }
1586  }
1587 
1588  if (last != 0)
1589  {
1590  csv.Add(str.Substring(last + 1).Trim());
1591  }
1592 
1593  return csv;
1594  }
1595 
1596  /// <summary>
1597  /// Gets the value at the specified index from a CSV line.
1598  /// </summary>
1599  /// <param name="csvLine">The CSV line</param>
1600  /// <param name="index">The index of the value to be extracted from the CSV line</param>
1601  /// <param name="result">The value at the given index</param>
1602  /// <returns>Whether there was a value at the given index and could be extracted</returns>
1603  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1604  public static bool TryGetFromCsv(this string csvLine, int index, out ReadOnlySpan<char> result)
1605  {
1606  result = ReadOnlySpan<char>.Empty;
1607  if (string.IsNullOrEmpty(csvLine) || index < 0)
1608  {
1609  return false;
1610  }
1611 
1612  var span = csvLine.AsSpan();
1613  for (int i = 0; i < index; i++)
1614  {
1615  var commaIndex = span.IndexOf(',');
1616  if (commaIndex == -1)
1617  {
1618  return false;
1619  }
1620  span = span.Slice(commaIndex + 1);
1621  }
1622 
1623  var nextCommaIndex = span.IndexOf(',');
1624  if (nextCommaIndex == -1)
1625  {
1626  nextCommaIndex = span.Length;
1627  }
1628 
1629  result = span.Slice(0, nextCommaIndex);
1630  return true;
1631  }
1632 
1633  /// <summary>
1634  /// Gets the value at the specified index from a CSV line, converted into a decimal.
1635  /// </summary>
1636  /// <param name="csvLine">The CSV line</param>
1637  /// <param name="index">The index of the value to be extracted from the CSV line</param>
1638  /// <param name="value">The decimal value at the given index</param>
1639  /// <returns>Whether there was a value at the given index and could be extracted and converted into a decimal</returns>
1640  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1641  public static bool TryGetDecimalFromCsv(this string csvLine, int index, out decimal value)
1642  {
1643  value = decimal.Zero;
1644  if (!csvLine.TryGetFromCsv(index, out var csvValue))
1645  {
1646  return false;
1647  }
1648 
1649  return decimal.TryParse(csvValue, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
1650  }
1651 
1652  /// <summary>
1653  /// Gets the value at the specified index from a CSV line, converted into a decimal.
1654  /// </summary>
1655  /// <param name="csvLine">The CSV line</param>
1656  /// <param name="index">The index of the value to be extracted from the CSV line</param>
1657  /// <returns>The decimal value at the given index. If the index is invalid or conversion fails, it will return zero</returns>
1658  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1659  public static decimal GetDecimalFromCsv(this string csvLine, int index)
1660  {
1661  csvLine.TryGetDecimalFromCsv(index, out var value);
1662  return value;
1663  }
1664 
1665  /// <summary>
1666  /// Check if a number is NaN or infinity
1667  /// </summary>
1668  /// <param name="value">The double value to check</param>
1669  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1670  public static bool IsNaNOrInfinity(this double value)
1671  {
1672  return double.IsNaN(value) || double.IsInfinity(value);
1673  }
1674 
1675  /// <summary>
1676  /// Check if a number is NaN or equal to zero
1677  /// </summary>
1678  /// <param name="value">The double value to check</param>
1679  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1680  public static bool IsNaNOrZero(this double value)
1681  {
1682  return double.IsNaN(value) || Math.Abs(value) < double.Epsilon;
1683  }
1684 
1685  /// <summary>
1686  /// Gets the smallest positive number that can be added to a decimal instance and return
1687  /// a new value that does not == the old value
1688  /// </summary>
1689  public static decimal GetDecimalEpsilon()
1690  {
1691  return new decimal(1, 0, 0, false, 27); //1e-27m;
1692  }
1693 
1694  /// <summary>
1695  /// Extension method to extract the extension part of this file name if it matches a safe list, or return a ".custom" extension for ones which do not match.
1696  /// </summary>
1697  /// <param name="str">String we're looking for the extension for.</param>
1698  /// <returns>Last 4 character string of string.</returns>
1699  public static string GetExtension(this string str) {
1700  var ext = str.Substring(Math.Max(0, str.Length - 4));
1701  var allowedExt = new List<string> { ".zip", ".csv", ".json", ".tsv" };
1702  if (!allowedExt.Contains(ext))
1703  {
1704  ext = ".custom";
1705  }
1706  return ext;
1707  }
1708 
1709  /// <summary>
1710  /// Extension method to convert strings to stream to be read.
1711  /// </summary>
1712  /// <param name="str">String to convert to stream</param>
1713  /// <returns>Stream instance</returns>
1714  public static Stream ToStream(this string str)
1715  {
1716  var stream = new MemoryStream();
1717  var writer = new StreamWriter(stream);
1718  writer.Write(str);
1719  writer.Flush();
1720  stream.Position = 0;
1721  return stream;
1722  }
1723 
1724  /// <summary>
1725  /// Extension method to round a timeSpan to nearest timespan period.
1726  /// </summary>
1727  /// <param name="time">TimeSpan To Round</param>
1728  /// <param name="roundingInterval">Rounding Unit</param>
1729  /// <param name="roundingType">Rounding method</param>
1730  /// <returns>Rounded timespan</returns>
1731  public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval, MidpointRounding roundingType)
1732  {
1733  if (roundingInterval == TimeSpan.Zero)
1734  {
1735  // divide by zero exception
1736  return time;
1737  }
1738 
1739  return new TimeSpan(
1740  Convert.ToInt64(Math.Round(
1741  time.Ticks / (decimal)roundingInterval.Ticks,
1742  roundingType
1743  )) * roundingInterval.Ticks
1744  );
1745  }
1746 
1747 
1748  /// <summary>
1749  /// Extension method to round timespan to nearest timespan period.
1750  /// </summary>
1751  /// <param name="time">Base timespan we're looking to round.</param>
1752  /// <param name="roundingInterval">Timespan period we're rounding.</param>
1753  /// <returns>Rounded timespan period</returns>
1754  public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval)
1755  {
1756  return Round(time, roundingInterval, MidpointRounding.ToEven);
1757  }
1758 
1759  /// <summary>
1760  /// Extension method to round a datetime down by a timespan interval.
1761  /// </summary>
1762  /// <param name="dateTime">Base DateTime object we're rounding down.</param>
1763  /// <param name="interval">Timespan interval to round to</param>
1764  /// <returns>Rounded datetime</returns>
1765  /// <remarks>Using this with timespans greater than 1 day may have unintended
1766  /// consequences. Be aware that rounding occurs against ALL time, so when using
1767  /// timespan such as 30 days we will see 30 day increments but it will be based
1768  /// on 30 day increments from the beginning of time.</remarks>
1769  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1770  public static DateTime RoundDown(this DateTime dateTime, TimeSpan interval)
1771  {
1772  if (interval == TimeSpan.Zero)
1773  {
1774  // divide by zero exception
1775  return dateTime;
1776  }
1777 
1778  var amount = dateTime.Ticks % interval.Ticks;
1779  if (amount > 0)
1780  {
1781  return dateTime.AddTicks(-amount);
1782  }
1783  return dateTime;
1784  }
1785 
1786  /// <summary>
1787  /// Rounds the specified date time in the specified time zone. Careful with calling this method in a loop while modifying dateTime, check unit tests.
1788  /// </summary>
1789  /// <param name="dateTime">Date time to be rounded</param>
1790  /// <param name="roundingInterval">Timespan rounding period</param>
1791  /// <param name="sourceTimeZone">Time zone of the date time</param>
1792  /// <param name="roundingTimeZone">Time zone in which the rounding is performed</param>
1793  /// <returns>The rounded date time in the source time zone</returns>
1794  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1795  public static DateTime RoundDownInTimeZone(this DateTime dateTime, TimeSpan roundingInterval, DateTimeZone sourceTimeZone, DateTimeZone roundingTimeZone)
1796  {
1797  var dateTimeInRoundingTimeZone = dateTime.ConvertTo(sourceTimeZone, roundingTimeZone);
1798  var roundedDateTimeInRoundingTimeZone = dateTimeInRoundingTimeZone.RoundDown(roundingInterval);
1799  return roundedDateTimeInRoundingTimeZone.ConvertTo(roundingTimeZone, sourceTimeZone);
1800  }
1801 
1802  /// <summary>
1803  /// Extension method to round a datetime down by a timespan interval until it's
1804  /// within the specified exchange's open hours. This works by first rounding down
1805  /// the specified time using the interval, then producing a bar between that
1806  /// rounded time and the interval plus the rounded time and incrementally walking
1807  /// backwards until the exchange is open
1808  /// </summary>
1809  /// <param name="dateTime">Time to be rounded down</param>
1810  /// <param name="interval">Timespan interval to round to.</param>
1811  /// <param name="exchangeHours">The exchange hours to determine open times</param>
1812  /// <param name="extendedMarketHours">True for extended market hours, otherwise false</param>
1813  /// <returns>Rounded datetime</returns>
1814  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1815  public static DateTime ExchangeRoundDown(this DateTime dateTime, TimeSpan interval, SecurityExchangeHours exchangeHours, bool extendedMarketHours)
1816  {
1817  // can't round against a zero interval
1818  if (interval == TimeSpan.Zero) return dateTime;
1819 
1820  var rounded = dateTime.RoundDown(interval);
1821  while (!exchangeHours.IsOpen(rounded, rounded + interval, extendedMarketHours))
1822  {
1823  rounded -= interval;
1824  }
1825  return rounded;
1826  }
1827 
1828  /// <summary>
1829  /// Extension method to round a datetime down by a timespan interval until it's
1830  /// within the specified exchange's open hours. The rounding is performed in the
1831  /// specified time zone
1832  /// </summary>
1833  /// <param name="dateTime">Time to be rounded down</param>
1834  /// <param name="interval">Timespan interval to round to.</param>
1835  /// <param name="exchangeHours">The exchange hours to determine open times</param>
1836  /// <param name="roundingTimeZone">The time zone to perform the rounding in</param>
1837  /// <param name="extendedMarketHours">True for extended market hours, otherwise false</param>
1838  /// <returns>Rounded datetime</returns>
1839  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1840  public static DateTime ExchangeRoundDownInTimeZone(this DateTime dateTime, TimeSpan interval, SecurityExchangeHours exchangeHours, DateTimeZone roundingTimeZone, bool extendedMarketHours)
1841  {
1842  // can't round against a zero interval
1843  if (interval == TimeSpan.Zero) return dateTime;
1844 
1845  var dateTimeInRoundingTimeZone = dateTime.ConvertTo(exchangeHours.TimeZone, roundingTimeZone);
1846  var roundedDateTimeInRoundingTimeZone = dateTimeInRoundingTimeZone.RoundDown(interval);
1847  var rounded = roundedDateTimeInRoundingTimeZone.ConvertTo(roundingTimeZone, exchangeHours.TimeZone);
1848 
1849  while (!exchangeHours.IsOpen(rounded, rounded + interval, extendedMarketHours))
1850  {
1851  // Will subtract interval to 'dateTime' in the roundingTimeZone (using the same value type instance) to avoid issues with daylight saving time changes.
1852  // GH issue 2368: subtracting interval to 'dateTime' in exchangeHours.TimeZone and converting back to roundingTimeZone
1853  // caused the substraction to be neutralized by daylight saving time change, which caused an infinite loop situation in this loop.
1854  // The issue also happens if substracting in roundingTimeZone and converting back to exchangeHours.TimeZone.
1855 
1856  dateTimeInRoundingTimeZone -= interval;
1857  roundedDateTimeInRoundingTimeZone = dateTimeInRoundingTimeZone.RoundDown(interval);
1858  rounded = roundedDateTimeInRoundingTimeZone.ConvertTo(roundingTimeZone, exchangeHours.TimeZone);
1859  }
1860  return rounded;
1861  }
1862 
1863  /// <summary>
1864  /// Helper method to determine if a specific market is open
1865  /// </summary>
1866  /// <param name="security">The target security</param>
1867  /// <param name="extendedMarketHours">True if should consider extended market hours</param>
1868  /// <returns>True if the market is open</returns>
1869  public static bool IsMarketOpen(this Security security, bool extendedMarketHours)
1870  {
1871  return security.Exchange.Hours.IsOpen(security.LocalTime, extendedMarketHours);
1872  }
1873 
1874  /// <summary>
1875  /// Helper method to determine if a specific market is open
1876  /// </summary>
1877  /// <param name="symbol">The target symbol</param>
1878  /// <param name="utcTime">The current UTC time</param>
1879  /// <param name="extendedMarketHours">True if should consider extended market hours</param>
1880  /// <returns>True if the market is open</returns>
1881  public static bool IsMarketOpen(this Symbol symbol, DateTime utcTime, bool extendedMarketHours)
1882  {
1883  var exchangeHours = MarketHoursDatabase.FromDataFolder()
1884  .GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType);
1885 
1886  var time = utcTime.ConvertFromUtc(exchangeHours.TimeZone);
1887 
1888  return exchangeHours.IsOpen(time, extendedMarketHours);
1889  }
1890 
1891  /// <summary>
1892  /// Extension method to round a datetime to the nearest unit timespan.
1893  /// </summary>
1894  /// <param name="datetime">Datetime object we're rounding.</param>
1895  /// <param name="roundingInterval">Timespan rounding period.</param>
1896  /// <returns>Rounded datetime</returns>
1897  public static DateTime Round(this DateTime datetime, TimeSpan roundingInterval)
1898  {
1899  return new DateTime((datetime - DateTime.MinValue).Round(roundingInterval).Ticks);
1900  }
1901 
1902  /// <summary>
1903  /// Extension method to explicitly round up to the nearest timespan interval.
1904  /// </summary>
1905  /// <param name="time">Base datetime object to round up.</param>
1906  /// <param name="interval">Timespan interval to round to</param>
1907  /// <returns>Rounded datetime</returns>
1908  /// <remarks>Using this with timespans greater than 1 day may have unintended
1909  /// consequences. Be aware that rounding occurs against ALL time, so when using
1910  /// timespan such as 30 days we will see 30 day increments but it will be based
1911  /// on 30 day increments from the beginning of time.</remarks>
1912  public static DateTime RoundUp(this DateTime time, TimeSpan interval)
1913  {
1914  if (interval == TimeSpan.Zero)
1915  {
1916  // divide by zero exception
1917  return time;
1918  }
1919 
1920  return new DateTime(((time.Ticks + interval.Ticks - 1) / interval.Ticks) * interval.Ticks);
1921  }
1922 
1923  /// <summary>
1924  /// Converts the specified time from the <paramref name="from"/> time zone to the <paramref name="to"/> time zone
1925  /// </summary>
1926  /// <param name="time">The time to be converted in terms of the <paramref name="from"/> time zone</param>
1927  /// <param name="from">The time zone the specified <paramref name="time"/> is in</param>
1928  /// <param name="to">The time zone to be converted to</param>
1929  /// <param name="strict">True for strict conversion, this will throw during ambiguitities, false for lenient conversion</param>
1930  /// <returns>The time in terms of the to time zone</returns>
1931  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1932  public static DateTime ConvertTo(this DateTime time, DateTimeZone from, DateTimeZone to, bool strict = false)
1933  {
1934  if (strict)
1935  {
1936  return from.AtStrictly(LocalDateTime.FromDateTime(time)).WithZone(to).ToDateTimeUnspecified();
1937  }
1938 
1939  // `InZone` sets the LocalDateTime's timezone, `WithZone` is the tz the time will be converted into.
1940  return LocalDateTime.FromDateTime(time)
1941  .InZone(from, _mappingResolver)
1942  .WithZone(to)
1943  .ToDateTimeUnspecified();
1944  }
1945 
1946  /// <summary>
1947  /// Converts the specified time from UTC to the <paramref name="to"/> time zone
1948  /// </summary>
1949  /// <param name="time">The time to be converted expressed in UTC</param>
1950  /// <param name="to">The destinatio time zone</param>
1951  /// <param name="strict">True for strict conversion, this will throw during ambiguitities, false for lenient conversion</param>
1952  /// <returns>The time in terms of the <paramref name="to"/> time zone</returns>
1953  public static DateTime ConvertFromUtc(this DateTime time, DateTimeZone to, bool strict = false)
1954  {
1955  return time.ConvertTo(TimeZones.Utc, to, strict);
1956  }
1957 
1958  /// <summary>
1959  /// Converts the specified time from the <paramref name="from"/> time zone to <see cref="TimeZones.Utc"/>
1960  /// </summary>
1961  /// <param name="time">The time to be converted in terms of the <paramref name="from"/> time zone</param>
1962  /// <param name="from">The time zone the specified <paramref name="time"/> is in</param>
1963  /// <param name="strict">True for strict conversion, this will throw during ambiguitities, false for lenient conversion</param>
1964  /// <returns>The time in terms of the to time zone</returns>
1965  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1966  public static DateTime ConvertToUtc(this DateTime time, DateTimeZone from, bool strict = false)
1967  {
1968  if (strict)
1969  {
1970  return from.AtStrictly(LocalDateTime.FromDateTime(time)).ToDateTimeUtc();
1971  }
1972 
1973  // Set the local timezone with `InZone` and convert to UTC
1974  return LocalDateTime.FromDateTime(time)
1975  .InZone(from, _mappingResolver)
1976  .ToDateTimeUtc();
1977  }
1978 
1979  /// <summary>
1980  /// Business day here is defined as any day of the week that is not saturday or sunday
1981  /// </summary>
1982  /// <param name="date">The date to be examined</param>
1983  /// <returns>A bool indicating wether the datetime is a weekday or not</returns>
1984  public static bool IsCommonBusinessDay(this DateTime date)
1985  {
1986  return (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday);
1987  }
1988 
1989  /// <summary>
1990  /// Add the reset method to the System.Timer class.
1991  /// </summary>
1992  /// <param name="timer">System.timer object</param>
1993  public static void Reset(this Timer timer)
1994  {
1995  timer.Stop();
1996  timer.Start();
1997  }
1998 
1999  /// <summary>
2000  /// Function used to match a type against a string type name. This function compares on the AssemblyQualfiedName,
2001  /// the FullName, and then just the Name of the type.
2002  /// </summary>
2003  /// <param name="type">The type to test for a match</param>
2004  /// <param name="typeName">The name of the type to match</param>
2005  /// <returns>True if the specified type matches the type name, false otherwise</returns>
2006  public static bool MatchesTypeName(this Type type, string typeName)
2007  {
2008  if (type.AssemblyQualifiedName == typeName)
2009  {
2010  return true;
2011  }
2012  if (type.FullName == typeName)
2013  {
2014  return true;
2015  }
2016  if (type.Name == typeName)
2017  {
2018  return true;
2019  }
2020  return false;
2021  }
2022 
2023  /// <summary>
2024  /// Checks the specified type to see if it is a subclass of the <paramref name="possibleSuperType"/>. This method will
2025  /// crawl up the inheritance heirarchy to check for equality using generic type definitions (if exists)
2026  /// </summary>
2027  /// <param name="type">The type to be checked as a subclass of <paramref name="possibleSuperType"/></param>
2028  /// <param name="possibleSuperType">The possible superclass of <paramref name="type"/></param>
2029  /// <returns>True if <paramref name="type"/> is a subclass of the generic type definition <paramref name="possibleSuperType"/></returns>
2030  public static bool IsSubclassOfGeneric(this Type type, Type possibleSuperType)
2031  {
2032  while (type != null && type != typeof(object))
2033  {
2034  Type cur;
2035  if (type.IsGenericType && possibleSuperType.IsGenericTypeDefinition)
2036  {
2037  cur = type.GetGenericTypeDefinition();
2038  }
2039  else
2040  {
2041  cur = type;
2042  }
2043  if (possibleSuperType == cur)
2044  {
2045  return true;
2046  }
2047  type = type.BaseType;
2048  }
2049  return false;
2050  }
2051 
2052  /// <summary>
2053  /// Gets a type's name with the generic parameters filled in the way they would look when
2054  /// defined in code, such as converting Dictionary&lt;`1,`2&gt; to Dictionary&lt;string,int&gt;
2055  /// </summary>
2056  /// <param name="type">The type who's name we seek</param>
2057  /// <returns>A better type name</returns>
2058  public static string GetBetterTypeName(this Type type)
2059  {
2060  string name = type.Name;
2061  if (type.IsGenericType)
2062  {
2063  var genericArguments = type.GetGenericArguments();
2064  var toBeReplaced = "`" + (genericArguments.Length);
2065  name = name.Replace(toBeReplaced, $"<{string.Join(", ", genericArguments.Select(x => x.GetBetterTypeName()))}>");
2066  }
2067  return name;
2068  }
2069 
2070  /// <summary>
2071  /// Converts the Resolution instance into a TimeSpan instance
2072  /// </summary>
2073  /// <param name="resolution">The resolution to be converted</param>
2074  /// <returns>A TimeSpan instance that represents the resolution specified</returns>
2075  [MethodImpl(MethodImplOptions.AggressiveInlining)]
2076  public static TimeSpan ToTimeSpan(this Resolution resolution)
2077  {
2078  switch (resolution)
2079  {
2080  case Resolution.Tick:
2081  // ticks can be instantaneous
2082  return TimeSpan.Zero;
2083  case Resolution.Second:
2084  return Time.OneSecond;
2085  case Resolution.Minute:
2086  return Time.OneMinute;
2087  case Resolution.Hour:
2088  return Time.OneHour;
2089  case Resolution.Daily:
2090  return Time.OneDay;
2091  default:
2092  throw new ArgumentOutOfRangeException(nameof(resolution));
2093  }
2094  }
2095 
2096  /// <summary>
2097  /// Converts the specified time span into a resolution enum value. If an exact match
2098  /// is not found and `requireExactMatch` is false, then the higher resoluion will be
2099  /// returned. For example, timeSpan=5min will return Minute resolution.
2100  /// </summary>
2101  /// <param name="timeSpan">The time span to convert to resolution</param>
2102  /// <param name="requireExactMatch">True to throw an exception if an exact match is not found</param>
2103  /// <returns>The resolution</returns>
2104  public static Resolution ToHigherResolutionEquivalent(this TimeSpan timeSpan, bool requireExactMatch)
2105  {
2106  if (requireExactMatch)
2107  {
2108  if (TimeSpan.Zero == timeSpan) return Resolution.Tick;
2109  if (Time.OneSecond == timeSpan) return Resolution.Second;
2110  if (Time.OneMinute == timeSpan) return Resolution.Minute;
2111  if (Time.OneHour == timeSpan) return Resolution.Hour;
2112  if (Time.OneDay == timeSpan) return Resolution.Daily;
2113  throw new InvalidOperationException(Messages.Extensions.UnableToConvertTimeSpanToResolution(timeSpan));
2114  }
2115 
2116  // for non-perfect matches
2117  if (Time.OneSecond > timeSpan) return Resolution.Tick;
2118  if (Time.OneMinute > timeSpan) return Resolution.Second;
2119  if (Time.OneHour > timeSpan) return Resolution.Minute;
2120  if (Time.OneDay > timeSpan) return Resolution.Hour;
2121 
2122  return Resolution.Daily;
2123  }
2124 
2125  /// <summary>
2126  /// Attempts to convert the string into a <see cref="SecurityType"/> enum value
2127  /// </summary>
2128  /// <param name="value">string value to convert to SecurityType</param>
2129  /// <param name="securityType">SecurityType output</param>
2130  /// <param name="ignoreCase">Ignore casing</param>
2131  /// <returns>true if parsed into a SecurityType successfully, false otherwise</returns>
2132  /// <remarks>
2133  /// Logs once if we've encountered an invalid SecurityType
2134  /// </remarks>
2135  public static bool TryParseSecurityType(this string value, out SecurityType securityType, bool ignoreCase = true)
2136  {
2137  if (Enum.TryParse(value, ignoreCase, out securityType))
2138  {
2139  return true;
2140  }
2141 
2142  if (InvalidSecurityTypes.Add(value))
2143  {
2144  Log.Error($"Extensions.TryParseSecurityType(): {Messages.Extensions.UnableToParseUnknownSecurityType(value)}");
2145  }
2146 
2147  return false;
2148 
2149  }
2150 
2151  /// <summary>
2152  /// Converts the specified string value into the specified type
2153  /// </summary>
2154  /// <typeparam name="T">The output type</typeparam>
2155  /// <param name="value">The string value to be converted</param>
2156  /// <returns>The converted value</returns>
2157  public static T ConvertTo<T>(this string value)
2158  {
2159  return (T) value.ConvertTo(typeof (T));
2160  }
2161 
2162  /// <summary>
2163  /// Converts the specified string value into the specified type
2164  /// </summary>
2165  /// <param name="value">The string value to be converted</param>
2166  /// <param name="type">The output type</param>
2167  /// <returns>The converted value</returns>
2168  public static object ConvertTo(this string value, Type type)
2169  {
2170  if (type.IsEnum)
2171  {
2172  return Enum.Parse(type, value, true);
2173  }
2174 
2175  if (typeof (IConvertible).IsAssignableFrom(type))
2176  {
2177  return Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
2178  }
2179 
2180  // try and find a static parse method
2181  var parse = type.GetMethod("Parse", new[] {typeof (string)});
2182  if (parse != null)
2183  {
2184  var result = parse.Invoke(null, new object[] {value});
2185  return result;
2186  }
2187 
2188  return JsonConvert.DeserializeObject(value, type);
2189  }
2190 
2191  /// <summary>
2192  /// Blocks the current thread until the current <see cref="T:System.Threading.WaitHandle"/> receives a signal, while observing a <see cref="T:System.Threading.CancellationToken"/>.
2193  /// </summary>
2194  /// <param name="waitHandle">The wait handle to wait on</param>
2195  /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
2196  /// <exception cref="T:System.InvalidOperationException">The maximum number of waiters has been exceeded.</exception>
2197  /// <exception cref="T:System.OperationCanceledExcepton"><paramref name="cancellationToken"/> was canceled.</exception>
2198  /// <exception cref="T:System.ObjectDisposedException">The object has already been disposed or the <see cref="T:System.Threading.CancellationTokenSource"/> that created <paramref name="cancellationToken"/> has been disposed.</exception>
2199  public static bool WaitOne(this WaitHandle waitHandle, CancellationToken cancellationToken)
2200  {
2201  return waitHandle.WaitOne(Timeout.Infinite, cancellationToken);
2202  }
2203 
2204  /// <summary>
2205  /// Blocks the current thread until the current <see cref="T:System.Threading.WaitHandle"/> is set, using a <see cref="T:System.TimeSpan"/> to measure the time interval, while observing a <see cref="T:System.Threading.CancellationToken"/>.
2206  /// </summary>
2207  ///
2208  /// <returns>
2209  /// true if the <see cref="T:System.Threading.WaitHandle"/> was set; otherwise, false.
2210  /// </returns>
2211  /// <param name="waitHandle">The wait handle to wait on</param>
2212  /// <param name="timeout">A <see cref="T:System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see cref="T:System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.</param>
2213  /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
2214  /// <exception cref="T:System.Threading.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
2215  /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than <see cref="F:System.Int32.MaxValue"/>.</exception>
2216  /// <exception cref="T:System.InvalidOperationException">The maximum number of waiters has been exceeded. </exception><exception cref="T:System.ObjectDisposedException">The object has already been disposed or the <see cref="T:System.Threading.CancellationTokenSource"/> that created <paramref name="cancellationToken"/> has been disposed.</exception>
2217  public static bool WaitOne(this WaitHandle waitHandle, TimeSpan timeout, CancellationToken cancellationToken)
2218  {
2219  return waitHandle.WaitOne((int) timeout.TotalMilliseconds, cancellationToken);
2220  }
2221 
2222  /// <summary>
2223  /// Blocks the current thread until the current <see cref="T:System.Threading.WaitHandle"/> is set, using a 32-bit signed integer to measure the time interval, while observing a <see cref="T:System.Threading.CancellationToken"/>.
2224  /// </summary>
2225  ///
2226  /// <returns>
2227  /// true if the <see cref="T:System.Threading.WaitHandle"/> was set; otherwise, false.
2228  /// </returns>
2229  /// <param name="waitHandle">The wait handle to wait on</param>
2230  /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="F:System.Threading.Timeout.Infinite"/>(-1) to wait indefinitely.</param>
2231  /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
2232  /// <exception cref="T:System.Threading.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
2233  /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an infinite time-out.</exception>
2234  /// <exception cref="T:System.InvalidOperationException">The maximum number of waiters has been exceeded.</exception>
2235  /// <exception cref="T:System.ObjectDisposedException">The object has already been disposed or the <see cref="T:System.Threading.CancellationTokenSource"/> that created <paramref name="cancellationToken"/> has been disposed.</exception>
2236  public static bool WaitOne(this WaitHandle waitHandle, int millisecondsTimeout, CancellationToken cancellationToken)
2237  {
2238  return WaitHandle.WaitAny(new[] { waitHandle, cancellationToken.WaitHandle }, millisecondsTimeout) == 0;
2239  }
2240 
2241  /// <summary>
2242  /// Gets the MD5 hash from a stream
2243  /// </summary>
2244  /// <param name="stream">The stream to compute a hash for</param>
2245  /// <returns>The MD5 hash</returns>
2246  public static byte[] GetMD5Hash(this Stream stream)
2247  {
2248  using (var md5 = MD5.Create())
2249  {
2250  return md5.ComputeHash(stream);
2251  }
2252  }
2253 
2254  /// <summary>
2255  /// Convert a string into the same string with a URL! :)
2256  /// </summary>
2257  /// <param name="source">The source string to be converted</param>
2258  /// <returns>The same source string but with anchor tags around substrings matching a link regex</returns>
2259  public static string WithEmbeddedHtmlAnchors(this string source)
2260  {
2261  var regx = new Regex("http(s)?://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*([a-zA-Z0-9\\?\\#\\=\\/]){1})?", RegexOptions.IgnoreCase);
2262  var matches = regx.Matches(source);
2263  foreach (Match match in matches)
2264  {
2265  source = source.Replace(match.Value, $"<a href=\'{match.Value}\' target=\'blank\'>{match.Value}</a>");
2266  }
2267  return source;
2268  }
2269 
2270  /// <summary>
2271  /// Get the first occurence of a string between two characters from another string
2272  /// </summary>
2273  /// <param name="value">The original string</param>
2274  /// <param name="left">Left bound of the substring</param>
2275  /// <param name="right">Right bound of the substring</param>
2276  /// <returns>Substring from original string bounded by the two characters</returns>
2277  public static string GetStringBetweenChars(this string value, char left, char right)
2278  {
2279  var startIndex = 1 + value.IndexOf(left);
2280  var length = value.IndexOf(right, startIndex) - startIndex;
2281  if (length > 0)
2282  {
2283  value = value.Substring(startIndex, length);
2284  startIndex = 1 + value.IndexOf(left);
2285  return value.Substring(startIndex).Trim();
2286  }
2287  return string.Empty;
2288  }
2289 
2290  /// <summary>
2291  /// Return the first in the series of names, or find the one that matches the configured algorithmTypeName
2292  /// </summary>
2293  /// <param name="names">The list of class names</param>
2294  /// <param name="algorithmTypeName">The configured algorithm type name from the config</param>
2295  /// <returns>The name of the class being run</returns>
2296  public static string SingleOrAlgorithmTypeName(this List<string> names, string algorithmTypeName)
2297  {
2298  // If there's only one name use that guy
2299  if (names.Count == 1) { return names.Single(); }
2300 
2301  // If we have multiple names we need to search the names based on the given algorithmTypeName
2302  // If the given name already contains dots (fully named) use it as it is
2303  // otherwise add a dot to the beginning to avoid matching any subsets of other names
2304  var searchName = algorithmTypeName.Contains('.', StringComparison.InvariantCulture) ? algorithmTypeName : "." + algorithmTypeName;
2305  return names.SingleOrDefault(x => x.EndsWith(searchName));
2306  }
2307 
2308  /// <summary>
2309  /// Converts the specified <paramref name="enum"/> value to its corresponding lower-case string representation
2310  /// </summary>
2311  /// <param name="enum">The enumeration value</param>
2312  /// <returns>A lower-case string representation of the specified enumeration value</returns>
2313  public static string ToLower(this Enum @enum)
2314  {
2315  return @enum.ToString().ToLowerInvariant();
2316  }
2317 
2318  /// <summary>
2319  /// Asserts the specified <paramref name="securityType"/> value is valid
2320  /// </summary>
2321  /// <remarks>This method provides faster performance than <see cref="Enum.IsDefined"/> which uses reflection</remarks>
2322  /// <param name="securityType">The SecurityType value</param>
2323  /// <returns>True if valid security type value</returns>
2324  public static bool IsValid(this SecurityType securityType)
2325  {
2326  switch (securityType)
2327  {
2328  case SecurityType.Base:
2329  case SecurityType.Equity:
2330  case SecurityType.Option:
2331  case SecurityType.FutureOption:
2332  case SecurityType.Commodity:
2333  case SecurityType.Forex:
2334  case SecurityType.Future:
2335  case SecurityType.Cfd:
2336  case SecurityType.Crypto:
2337  case SecurityType.CryptoFuture:
2338  case SecurityType.Index:
2339  case SecurityType.IndexOption:
2340  return true;
2341  default:
2342  return false;
2343  }
2344  }
2345 
2346  /// <summary>
2347  /// Determines if the provided SecurityType is a type of Option.
2348  /// Valid option types are: Equity Options, Futures Options, and Index Options.
2349  /// </summary>
2350  /// <param name="securityType">The SecurityType to check if it's an option asset</param>
2351  /// <returns>
2352  /// true if the asset has the makings of an option (exercisable, expires, and is a derivative of some underlying),
2353  /// false otherwise.
2354  /// </returns>
2355  public static bool IsOption(this SecurityType securityType)
2356  {
2357  switch (securityType)
2358  {
2359  case SecurityType.Option:
2360  case SecurityType.FutureOption:
2361  case SecurityType.IndexOption:
2362  return true;
2363 
2364  default:
2365  return false;
2366  }
2367  }
2368 
2369  /// <summary>
2370  /// Determines if the provided SecurityType has a matching option SecurityType, used to represent
2371  /// the current SecurityType as a derivative.
2372  /// </summary>
2373  /// <param name="securityType">The SecurityType to check if it has options available</param>
2374  /// <returns>true if there are options for the SecurityType, false otherwise</returns>
2375  public static bool HasOptions(this SecurityType securityType)
2376  {
2377  switch (securityType)
2378  {
2379  case SecurityType.Equity:
2380  case SecurityType.Future:
2381  case SecurityType.Index:
2382  return true;
2383 
2384  default:
2385  return false;
2386  }
2387  }
2388 
2389  /// <summary>
2390  /// Gets the default <see cref="OptionStyle"/> for the provided <see cref="SecurityType"/>
2391  /// </summary>
2392  /// <param name="securityType">SecurityType to get default OptionStyle for</param>
2393  /// <returns>Default OptionStyle for the SecurityType</returns>
2394  /// <exception cref="ArgumentException">The SecurityType has no options available for it or it is not an option</exception>
2395  public static OptionStyle DefaultOptionStyle(this SecurityType securityType)
2396  {
2397  if (!securityType.HasOptions() && !securityType.IsOption())
2398  {
2399  throw new ArgumentException(Messages.Extensions.NoDefaultOptionStyleForSecurityType(securityType));
2400  }
2401 
2402  switch (securityType)
2403  {
2404  case SecurityType.Index:
2405  case SecurityType.IndexOption:
2406  return OptionStyle.European;
2407 
2408  default:
2409  return OptionStyle.American;
2410  }
2411  }
2412 
2413  /// <summary>
2414  /// Converts the specified string to its corresponding OptionStyle
2415  /// </summary>
2416  /// <remarks>This method provides faster performance than enum parse</remarks>
2417  /// <param name="optionStyle">The OptionStyle string value</param>
2418  /// <returns>The OptionStyle value</returns>
2419  public static OptionStyle ParseOptionStyle(this string optionStyle)
2420  {
2421  switch (optionStyle.LazyToLower())
2422  {
2423  case "american":
2424  return OptionStyle.American;
2425  case "european":
2426  return OptionStyle.European;
2427  default:
2428  throw new ArgumentException(Messages.Extensions.UnknownOptionStyle(optionStyle));
2429  }
2430  }
2431 
2432  /// <summary>
2433  /// Converts the specified string to its corresponding OptionRight
2434  /// </summary>
2435  /// <remarks>This method provides faster performance than enum parse</remarks>
2436  /// <param name="optionRight">The optionRight string value</param>
2437  /// <returns>The OptionRight value</returns>
2438  public static OptionRight ParseOptionRight(this string optionRight)
2439  {
2440  switch (optionRight.LazyToLower())
2441  {
2442  case "call":
2443  return OptionRight.Call;
2444  case "put":
2445  return OptionRight.Put;
2446  default:
2447  throw new ArgumentException(Messages.Extensions.UnknownOptionRight(optionRight));
2448  }
2449  }
2450 
2451  /// <summary>
2452  /// Converts the specified <paramref name="optionRight"/> value to its corresponding string representation
2453  /// </summary>
2454  /// <remarks>This method provides faster performance than enum <see cref="Object.ToString"/></remarks>
2455  /// <param name="optionRight">The optionRight value</param>
2456  /// <returns>A string representation of the specified OptionRight value</returns>
2457  public static string ToStringPerformance(this OptionRight optionRight)
2458  {
2459  switch (optionRight)
2460  {
2461  case OptionRight.Call:
2462  return "Call";
2463  case OptionRight.Put:
2464  return "Put";
2465  default:
2466  // just in case
2467  return optionRight.ToString();
2468  }
2469  }
2470 
2471  /// <summary>
2472  /// Converts the specified <paramref name="optionRight"/> value to its corresponding lower-case string representation
2473  /// </summary>
2474  /// <remarks>This method provides faster performance than <see cref="ToLower"/></remarks>
2475  /// <param name="optionRight">The optionRight value</param>
2476  /// <returns>A lower case string representation of the specified OptionRight value</returns>
2477  public static string OptionRightToLower(this OptionRight optionRight)
2478  {
2479  switch (optionRight)
2480  {
2481  case OptionRight.Call:
2482  return "call";
2483  case OptionRight.Put:
2484  return "put";
2485  default:
2486  throw new ArgumentException(Messages.Extensions.UnknownOptionRight(optionRight));
2487  }
2488  }
2489 
2490  /// <summary>
2491  /// Converts the specified <paramref name="optionStyle"/> value to its corresponding lower-case string representation
2492  /// </summary>
2493  /// <remarks>This method provides faster performance than <see cref="ToLower"/></remarks>
2494  /// <param name="optionStyle">The optionStyle value</param>
2495  /// <returns>A lower case string representation of the specified optionStyle value</returns>
2496  public static string OptionStyleToLower(this OptionStyle optionStyle)
2497  {
2498  switch (optionStyle)
2499  {
2500  case OptionStyle.American:
2501  return "american";
2502  case OptionStyle.European:
2503  return "european";
2504  default:
2505  throw new ArgumentException(Messages.Extensions.UnknownOptionStyle(optionStyle));
2506  }
2507  }
2508 
2509  /// <summary>
2510  /// Converts the specified string to its corresponding DataMappingMode
2511  /// </summary>
2512  /// <remarks>This method provides faster performance than enum parse</remarks>
2513  /// <param name="dataMappingMode">The dataMappingMode string value</param>
2514  /// <returns>The DataMappingMode value</returns>
2515  public static DataMappingMode? ParseDataMappingMode(this string dataMappingMode)
2516  {
2517  if (string.IsNullOrEmpty(dataMappingMode))
2518  {
2519  return null;
2520  }
2521  switch (dataMappingMode.LazyToLower())
2522  {
2523  case "0":
2524  case "lasttradingday":
2525  return DataMappingMode.LastTradingDay;
2526  case "1":
2527  case "firstdaymonth":
2528  return DataMappingMode.FirstDayMonth;
2529  case "2":
2530  case "openinterest":
2531  return DataMappingMode.OpenInterest;
2532  case "3":
2533  case "openinterestannual":
2534  return DataMappingMode.OpenInterestAnnual;
2535  default:
2536  throw new ArgumentException(Messages.Extensions.UnknownDataMappingMode(dataMappingMode));
2537  }
2538  }
2539 
2540  /// <summary>
2541  /// Converts the specified <paramref name="securityType"/> value to its corresponding lower-case string representation
2542  /// </summary>
2543  /// <remarks>This method provides faster performance than <see cref="ToLower"/></remarks>
2544  /// <param name="securityType">The SecurityType value</param>
2545  /// <returns>A lower-case string representation of the specified SecurityType value</returns>
2546  public static string SecurityTypeToLower(this SecurityType securityType)
2547  {
2548  switch (securityType)
2549  {
2550  case SecurityType.Base:
2551  return "base";
2552  case SecurityType.Equity:
2553  return "equity";
2554  case SecurityType.Option:
2555  return "option";
2556  case SecurityType.FutureOption:
2557  return "futureoption";
2558  case SecurityType.IndexOption:
2559  return "indexoption";
2560  case SecurityType.Commodity:
2561  return "commodity";
2562  case SecurityType.Forex:
2563  return "forex";
2564  case SecurityType.Future:
2565  return "future";
2566  case SecurityType.Index:
2567  return "index";
2568  case SecurityType.Cfd:
2569  return "cfd";
2570  case SecurityType.Crypto:
2571  return "crypto";
2572  case SecurityType.CryptoFuture:
2573  return "cryptofuture";
2574  default:
2575  // just in case
2576  return securityType.ToLower();
2577  }
2578  }
2579 
2580  /// <summary>
2581  /// Converts the specified <paramref name="tickType"/> value to its corresponding lower-case string representation
2582  /// </summary>
2583  /// <remarks>This method provides faster performance than <see cref="ToLower"/></remarks>
2584  /// <param name="tickType">The tickType value</param>
2585  /// <returns>A lower-case string representation of the specified tickType value</returns>
2586  public static string TickTypeToLower(this TickType tickType)
2587  {
2588  switch (tickType)
2589  {
2590  case TickType.Trade:
2591  return "trade";
2592  case TickType.Quote:
2593  return "quote";
2594  case TickType.OpenInterest:
2595  return "openinterest";
2596  default:
2597  // just in case
2598  return tickType.ToLower();
2599  }
2600  }
2601 
2602  /// <summary>
2603  /// Converts the specified <paramref name="resolution"/> value to its corresponding lower-case string representation
2604  /// </summary>
2605  /// <remarks>This method provides faster performance than <see cref="ToLower"/></remarks>
2606  /// <param name="resolution">The resolution value</param>
2607  /// <returns>A lower-case string representation of the specified resolution value</returns>
2608  public static string ResolutionToLower(this Resolution resolution)
2609  {
2610  switch (resolution)
2611  {
2612  case Resolution.Tick:
2613  return "tick";
2614  case Resolution.Second:
2615  return "second";
2616  case Resolution.Minute:
2617  return "minute";
2618  case Resolution.Hour:
2619  return "hour";
2620  case Resolution.Daily:
2621  return "daily";
2622  default:
2623  // just in case
2624  return resolution.ToLower();
2625  }
2626  }
2627 
2628  /// <summary>
2629  /// Turn order into an order ticket
2630  /// </summary>
2631  /// <param name="order">The <see cref="Order"/> being converted</param>
2632  /// <param name="transactionManager">The transaction manager, <see cref="SecurityTransactionManager"/></param>
2633  /// <returns></returns>
2634  public static OrderTicket ToOrderTicket(this Order order, SecurityTransactionManager transactionManager)
2635  {
2636  var limitPrice = 0m;
2637  var stopPrice = 0m;
2638  var triggerPrice = 0m;
2639  var trailingAmount = 0m;
2640  var trailingAsPercentage = false;
2641 
2642  switch (order.Type)
2643  {
2644  case OrderType.Limit:
2645  var limitOrder = order as LimitOrder;
2646  limitPrice = limitOrder.LimitPrice;
2647  break;
2648  case OrderType.StopMarket:
2649  var stopMarketOrder = order as StopMarketOrder;
2650  stopPrice = stopMarketOrder.StopPrice;
2651  break;
2652  case OrderType.StopLimit:
2653  var stopLimitOrder = order as StopLimitOrder;
2654  stopPrice = stopLimitOrder.StopPrice;
2655  limitPrice = stopLimitOrder.LimitPrice;
2656  break;
2657  case OrderType.TrailingStop:
2658  var trailingStopOrder = order as TrailingStopOrder;
2659  stopPrice = trailingStopOrder.StopPrice;
2660  trailingAmount = trailingStopOrder.TrailingAmount;
2661  trailingAsPercentage = trailingStopOrder.TrailingAsPercentage;
2662  break;
2663  case OrderType.LimitIfTouched:
2664  var limitIfTouched = order as LimitIfTouchedOrder;
2665  triggerPrice = limitIfTouched.TriggerPrice;
2666  limitPrice = limitIfTouched.LimitPrice;
2667  break;
2668  case OrderType.OptionExercise:
2669  case OrderType.Market:
2670  case OrderType.MarketOnOpen:
2671  case OrderType.MarketOnClose:
2672  case OrderType.ComboMarket:
2673  limitPrice = order.Price;
2674  stopPrice = order.Price;
2675  break;
2676  case OrderType.ComboLimit:
2677  limitPrice = order.GroupOrderManager.LimitPrice;
2678  break;
2679  case OrderType.ComboLegLimit:
2680  var legLimitOrder = order as ComboLegLimitOrder;
2681  limitPrice = legLimitOrder.LimitPrice;
2682  break;
2683  default:
2684  throw new ArgumentOutOfRangeException();
2685  }
2686 
2687  var submitOrderRequest = new SubmitOrderRequest(order.Type,
2688  order.SecurityType,
2689  order.Symbol,
2690  order.Quantity,
2691  stopPrice,
2692  limitPrice,
2693  triggerPrice,
2694  trailingAmount,
2695  trailingAsPercentage,
2696  order.Time,
2697  order.Tag,
2698  order.Properties,
2699  order.GroupOrderManager);
2700 
2701  submitOrderRequest.SetOrderId(order.Id);
2702  var orderTicket = new OrderTicket(transactionManager, submitOrderRequest);
2703  orderTicket.SetOrder(order);
2704  return orderTicket;
2705  }
2706 
2707  /// <summary>
2708  /// Process all items in collection through given handler
2709  /// </summary>
2710  /// <typeparam name="T"></typeparam>
2711  /// <param name="collection">Collection to process</param>
2712  /// <param name="handler">Handler to process those items with</param>
2713  public static void ProcessUntilEmpty<T>(this IProducerConsumerCollection<T> collection, Action<T> handler)
2714  {
2715  T item;
2716  while (collection.TryTake(out item))
2717  {
2718  handler(item);
2719  }
2720  }
2721 
2722  /// <summary>
2723  /// Returns a <see cref="string"/> that represents the current <see cref="PyObject"/>
2724  /// </summary>
2725  /// <param name="pyObject">The <see cref="PyObject"/> being converted</param>
2726  /// <returns>string that represents the current PyObject</returns>
2727  public static string ToSafeString(this PyObject pyObject)
2728  {
2729  using (Py.GIL())
2730  {
2731  var value = "";
2732  // PyObject objects that have the to_string method, like some pandas objects,
2733  // can use this method to convert them into string objects
2734  if (pyObject.HasAttr("to_string"))
2735  {
2736  var pyValue = pyObject.InvokeMethod("to_string");
2737  value = Environment.NewLine + pyValue;
2738  pyValue.Dispose();
2739  }
2740  else
2741  {
2742  value = pyObject.ToString();
2743  if (string.IsNullOrWhiteSpace(value))
2744  {
2745  var pythonType = pyObject.GetPythonType();
2746  if (pythonType.GetType() == typeof(PyObject))
2747  {
2748  value = pythonType.ToString();
2749  }
2750  else
2751  {
2752  var type = pythonType.As<Type>();
2753  value = pyObject.AsManagedObject(type).ToString();
2754  }
2755  pythonType.Dispose();
2756  }
2757  }
2758  return value;
2759  }
2760  }
2761 
2762  /// <summary>
2763  /// Tries to convert a <see cref="PyObject"/> into a managed object
2764  /// </summary>
2765  /// <remarks>This method is not working correctly for a wrapped <see cref="TimeSpan"/> instance,
2766  /// probably because it is a struct, using <see cref="PyObject.As{T}"/> is a valid work around.
2767  /// Not used here because it caused errors
2768  /// </remarks>
2769  /// <typeparam name="T">Target type of the resulting managed object</typeparam>
2770  /// <param name="pyObject">PyObject to be converted</param>
2771  /// <param name="result">Managed object </param>
2772  /// <param name="allowPythonDerivative">True will convert python subclasses of T</param>
2773  /// <returns>True if successful conversion</returns>
2774  public static bool TryConvert<T>(this PyObject pyObject, out T result, bool allowPythonDerivative = false)
2775  {
2776  result = default(T);
2777  var type = typeof(T);
2778 
2779  if (pyObject == null)
2780  {
2781  return true;
2782  }
2783 
2784  using (Py.GIL())
2785  {
2786  try
2787  {
2788  // We must first check if allowPythonDerivative is true to then only return true
2789  // when the PyObject is assignable from Type or IEnumerable and is a C# type
2790  // wrapped in PyObject
2791  if (allowPythonDerivative)
2792  {
2793  result = (T)pyObject.AsManagedObject(type);
2794  return true;
2795  }
2796 
2797  // Special case: Type
2798  if (typeof(Type).IsAssignableFrom(type))
2799  {
2800  result = (T)pyObject.AsManagedObject(type);
2801  // pyObject is a C# object wrapped in PyObject, in this case return true
2802  if(!pyObject.HasAttr("__name__"))
2803  {
2804  return true;
2805  }
2806  // Otherwise, pyObject is a python object that subclass a C# class, only return true if 'allowPythonDerivative'
2807  var castedResult = (Type)pyObject.AsManagedObject(type);
2808  var pythonName = pyObject.GetAttr("__name__").GetAndDispose<string>();
2809  return pythonName == castedResult.Name;
2810  }
2811 
2812  // Special case: IEnumerable
2813  if (typeof(IEnumerable).IsAssignableFrom(type))
2814  {
2815  result = (T)pyObject.AsManagedObject(type);
2816  return true;
2817  }
2818 
2819  using var pythonType = pyObject.GetPythonType();
2820  var csharpType = pythonType.As<Type>();
2821 
2822  if (!type.IsAssignableFrom(csharpType))
2823  {
2824  return false;
2825  }
2826 
2827  result = (T)pyObject.AsManagedObject(type);
2828 
2829  // The PyObject is a Python object of a Python class that is a subclass of a C# class.
2830  // In this case, we return false just because we want the actual Python object
2831  // so it gets wrapped in a python wrapper, not the C# object.
2832  if (result is IPythonDerivedType)
2833  {
2834  return false;
2835  }
2836 
2837  // If the python type object is just a representation of the C# type, the conversion is direct,
2838  // the python object is an instance of the C# class.
2839  // We can compare by reference because pythonnet caches the PyTypes and because the behavior of
2840  // PyObject.Equals is not exactly what we want:
2841  // e.g. type(class PyClass(CSharpClass)) == type(CSharpClass) is true in Python
2842  if (PythonReferenceComparer.Instance.Equals(PyType.Get(csharpType), pythonType))
2843  {
2844  return true;
2845  }
2846 
2847  // If the PyObject type and the managed object names are the same,
2848  // pyObject is a C# object wrapped in PyObject, in this case return true
2849  // Otherwise, pyObject is a python object that subclass a C# class, only return true if 'allowPythonDerivative'
2850  var name = (((dynamic)pythonType).__name__ as PyObject).GetAndDispose<string>();
2851  return name == result.GetType().Name;
2852  }
2853  catch
2854  {
2855  // Do not throw or log the exception.
2856  // Return false as an exception means that the conversion could not be made.
2857  }
2858  }
2859 
2860  return false;
2861  }
2862 
2863  /// <summary>
2864  /// Tries to convert a <see cref="PyObject"/> into a managed object
2865  /// </summary>
2866  /// <typeparam name="T">Target type of the resulting managed object</typeparam>
2867  /// <param name="pyObject">PyObject to be converted</param>
2868  /// <param name="result">Managed object </param>
2869  /// <returns>True if successful conversion</returns>
2870  public static bool TryConvertToDelegate<T>(this PyObject pyObject, out T result)
2871  {
2872  var type = typeof(T);
2873 
2874  // The PyObject is a C# object wrapped
2875  if (TryConvert<T>(pyObject, out result))
2876  {
2877  return true;
2878  }
2879 
2880  if (!typeof(MulticastDelegate).IsAssignableFrom(type))
2881  {
2882  throw new ArgumentException(Messages.Extensions.ConvertToDelegateCannotConverPyObjectToType("TryConvertToDelegate", type));
2883  }
2884 
2885  result = default(T);
2886 
2887  if (pyObject == null)
2888  {
2889  return true;
2890  }
2891 
2892  var code = string.Empty;
2893  var types = type.GetGenericArguments();
2894 
2895  using (Py.GIL())
2896  {
2897  var locals = new PyDict();
2898  try
2899  {
2900  for (var i = 0; i < types.Length; i++)
2901  {
2902  var iString = i.ToStringInvariant();
2903  code += $",t{iString}";
2904  locals.SetItem($"t{iString}", types[i].ToPython());
2905  }
2906 
2907  locals.SetItem("pyObject", pyObject);
2908 
2909  var name = type.FullName.Substring(0, type.FullName.IndexOf('`'));
2910  code = $"import System; delegate = {name}[{code.Substring(1)}](pyObject)";
2911 
2912  PythonEngine.Exec(code, null, locals);
2913  result = (T)locals.GetItem("delegate").AsManagedObject(typeof(T));
2914  locals.Dispose();
2915  return true;
2916  }
2917  catch
2918  {
2919  // Do not throw or log the exception.
2920  // Return false as an exception means that the conversion could not be made.
2921  }
2922  locals.Dispose();
2923  }
2924  return false;
2925  }
2926 
2927  /// <summary>
2928  /// Safely convert PyObject to ManagedObject using Py.GIL Lock
2929  /// If no type is given it will convert the PyObject's Python Type to a ManagedObject Type
2930  /// in a attempt to resolve the target type to convert to.
2931  /// </summary>
2932  /// <param name="pyObject">PyObject to convert to managed</param>
2933  /// <param name="typeToConvertTo">The target type to convert to</param>
2934  /// <returns>The resulting ManagedObject</returns>
2935  public static dynamic SafeAsManagedObject(this PyObject pyObject, Type typeToConvertTo = null)
2936  {
2937  using (Py.GIL())
2938  {
2939  if (typeToConvertTo == null)
2940  {
2941  typeToConvertTo = pyObject.GetPythonType().AsManagedObject(typeof(Type)) as Type;
2942  }
2943 
2944  return pyObject.AsManagedObject(typeToConvertTo);
2945  }
2946  }
2947 
2948  /// <summary>
2949  /// Converts a Python function to a managed function returning a Symbol
2950  /// </summary>
2951  /// <param name="universeFilterFunc">Universe filter function from Python</param>
2952  /// <returns>Function that provides <typeparamref name="T"/> and returns an enumerable of Symbols</returns>
2953  public static Func<IEnumerable<T>, IEnumerable<Symbol>> ConvertPythonUniverseFilterFunction<T>(this PyObject universeFilterFunc) where T : BaseData
2954  {
2955  Func<IEnumerable<T>, object> convertedFunc;
2956  Func<IEnumerable<T>, IEnumerable<Symbol>> filterFunc = null;
2957 
2958  if (universeFilterFunc != null && universeFilterFunc.TryConvertToDelegate(out convertedFunc))
2959  {
2960  filterFunc = convertedFunc.ConvertToUniverseSelectionSymbolDelegate();
2961  }
2962 
2963  return filterFunc;
2964  }
2965 
2966  /// <summary>
2967  /// Wraps the provided universe selection selector checking if it returned <see cref="Universe.Unchanged"/>
2968  /// and returns it instead, else enumerates result as <see cref="IEnumerable{Symbol}"/>
2969  /// </summary>
2970  /// <remarks>This method is a work around for the fact that currently we can not create a delegate which returns
2971  /// an <see cref="IEnumerable{Symbol}"/> from a python method returning an array, plus the fact that
2972  /// <see cref="Universe.Unchanged"/> can not be cast to an array</remarks>
2973  public static Func<IEnumerable<T>, IEnumerable<Symbol>> ConvertToUniverseSelectionSymbolDelegate<T>(this Func<IEnumerable<T>, object> selector) where T : BaseData
2974  {
2975  if (selector == null)
2976  {
2977  return (dataPoints) => dataPoints.Select(x => x.Symbol);
2978  }
2979  return selector.ConvertSelectionSymbolDelegate();
2980  }
2981 
2982  /// <summary>
2983  /// Wraps the provided universe selection selector checking if it returned <see cref="Universe.Unchanged"/>
2984  /// and returns it instead, else enumerates result as <see cref="IEnumerable{Symbol}"/>
2985  /// </summary>
2986  /// <remarks>This method is a work around for the fact that currently we can not create a delegate which returns
2987  /// an <see cref="IEnumerable{Symbol}"/> from a python method returning an array, plus the fact that
2988  /// <see cref="Universe.Unchanged"/> can not be cast to an array</remarks>
2989  public static Func<T, IEnumerable<Symbol>> ConvertSelectionSymbolDelegate<T>(this Func<T, object> selector)
2990  {
2991  return data =>
2992  {
2993  var result = selector(data);
2994  return ReferenceEquals(result, Universe.Unchanged)
2996  : ((object[])result).Select(x =>
2997  {
2998  if (x is Symbol castedSymbol)
2999  {
3000  return castedSymbol;
3001  }
3002  return SymbolCache.TryGetSymbol((string)x, out var symbol) ? symbol : null;
3003  });
3004  };
3005  }
3006 
3007  /// <summary>
3008  /// Wraps the provided universe selection selector checking if it returned <see cref="Universe.Unchanged"/>
3009  /// and returns it instead, else enumerates result as <see cref="IEnumerable{String}"/>
3010  /// </summary>
3011  /// <remarks>This method is a work around for the fact that currently we can not create a delegate which returns
3012  /// an <see cref="IEnumerable{String}"/> from a python method returning an array, plus the fact that
3013  /// <see cref="Universe.Unchanged"/> can not be cast to an array</remarks>
3014  public static Func<T, IEnumerable<string>> ConvertToUniverseSelectionStringDelegate<T>(this Func<T, object> selector)
3015  {
3016  return data =>
3017  {
3018  var result = selector(data);
3019  return ReferenceEquals(result, Universe.Unchanged)
3020  ? Universe.Unchanged : ((object[])result).Select(x => (string)x);
3021  };
3022  }
3023 
3024  /// <summary>
3025  /// Convert a <see cref="PyObject"/> into a managed object
3026  /// </summary>
3027  /// <typeparam name="T">Target type of the resulting managed object</typeparam>
3028  /// <param name="pyObject">PyObject to be converted</param>
3029  /// <returns>Instance of type T</returns>
3030  public static T ConvertToDelegate<T>(this PyObject pyObject)
3031  {
3032  T result;
3033  if (pyObject.TryConvertToDelegate(out result))
3034  {
3035  return result;
3036  }
3037  else
3038  {
3039  throw new ArgumentException(Messages.Extensions.ConvertToDelegateCannotConverPyObjectToType("ConvertToDelegate", typeof(T)));
3040  }
3041  }
3042 
3043  /// <summary>
3044  /// Convert a <see cref="PyObject"/> into a managed dictionary
3045  /// </summary>
3046  /// <typeparam name="TKey">Target type of the resulting dictionary key</typeparam>
3047  /// <typeparam name="TValue">Target type of the resulting dictionary value</typeparam>
3048  /// <param name="pyObject">PyObject to be converted</param>
3049  /// <returns>Dictionary of TValue keyed by TKey</returns>
3050  public static Dictionary<TKey, TValue> ConvertToDictionary<TKey, TValue>(this PyObject pyObject)
3051  {
3052  var result = new List<KeyValuePair<TKey, TValue>>();
3053  using (Py.GIL())
3054  {
3055  var inputType = pyObject.GetPythonType().ToString();
3056  var targetType = nameof(PyDict);
3057 
3058  try
3059  {
3060  using (var pyDict = new PyDict(pyObject))
3061  {
3062  targetType = $"{typeof(TKey).Name}: {typeof(TValue).Name}";
3063 
3064  foreach (PyObject item in pyDict.Items())
3065  {
3066  inputType = $"{item[0].GetPythonType()}: {item[1].GetPythonType()}";
3067 
3068  var key = item[0].As<TKey>();
3069  var value = item[1].As<TValue>();
3070 
3071  result.Add(new KeyValuePair<TKey, TValue>(key, value));
3072  }
3073  }
3074  }
3075  catch (Exception e)
3076  {
3077  throw new ArgumentException(Messages.Extensions.ConvertToDictionaryFailed(inputType, targetType, e.Message), e);
3078  }
3079  }
3080 
3081  return result.ToDictionary();
3082  }
3083 
3084  /// <summary>
3085  /// Gets Enumerable of <see cref="Symbol"/> from a PyObject
3086  /// </summary>
3087  /// <param name="pyObject">PyObject containing Symbol or Array of Symbol</param>
3088  /// <returns>Enumerable of Symbol</returns>
3089  public static IEnumerable<Symbol> ConvertToSymbolEnumerable(this PyObject pyObject)
3090  {
3091  using (Py.GIL())
3092  {
3093  Exception exception = null;
3094  if (!PyList.IsListType(pyObject))
3095  {
3096  // it's not a pylist try to conver directly
3097  Symbol result = null;
3098  try
3099  {
3100  // we shouldn't dispose of an object we haven't created
3101  result = ConvertToSymbol(pyObject, dispose: false);
3102  }
3103  catch (Exception ex)
3104  {
3105  exception = ex;
3106  }
3107 
3108  if (result != null)
3109  {
3110  // happy case
3111  yield return result;
3112  }
3113  }
3114  else
3115  {
3116  using var iterator = pyObject.GetIterator();
3117  foreach (PyObject item in iterator)
3118  {
3119  Symbol result;
3120  try
3121  {
3122  result = ConvertToSymbol(item, dispose: true);
3123  }
3124  catch (Exception ex)
3125  {
3126  exception = ex;
3127  break;
3128  }
3129  yield return result;
3130  }
3131  }
3132 
3133  // let's give it once last try, relying on pythonnet internal conversions, else throw
3134  if (exception != null)
3135  {
3136  if (pyObject.TryConvert(out IEnumerable<Symbol> symbols))
3137  {
3138  foreach (var symbol in symbols)
3139  {
3140  yield return symbol;
3141  }
3142  }
3143  else
3144  {
3145  throw exception;
3146  }
3147  }
3148  }
3149  }
3150 
3151  /// <summary>
3152  /// Converts an IEnumerable to a PyList
3153  /// </summary>
3154  /// <param name="enumerable">IEnumerable object to convert</param>
3155  /// <returns>PyList</returns>
3156  public static PyList ToPyList(this IEnumerable enumerable)
3157  {
3158  using (Py.GIL())
3159  {
3160  return enumerable.ToPyListUnSafe();
3161  }
3162  }
3163 
3164  /// <summary>
3165  /// Converts an IEnumerable to a PyList
3166  /// </summary>
3167  /// <param name="enumerable">IEnumerable object to convert</param>
3168  /// <remarks>Requires the caller to own the GIL</remarks>
3169  /// <returns>PyList</returns>
3170  public static PyList ToPyListUnSafe(this IEnumerable enumerable)
3171  {
3172  var pyList = new PyList();
3173  foreach (var item in enumerable)
3174  {
3175  using (var pyObject = item.ToPython())
3176  {
3177  pyList.Append(pyObject);
3178  }
3179  }
3180 
3181  return pyList;
3182  }
3183 
3184  /// <summary>
3185  /// Converts the numeric value of one or more enumerated constants to an equivalent enumerated string.
3186  /// </summary>
3187  /// <param name="value">Numeric value</param>
3188  /// <param name="pyObject">Python object that encapsulated a Enum Type</param>
3189  /// <returns>String that represents the enumerated object</returns>
3190  public static string GetEnumString(this int value, PyObject pyObject)
3191  {
3192  Type type;
3193  if (pyObject.TryConvert(out type))
3194  {
3195  return value.ToStringInvariant().ConvertTo(type).ToString();
3196  }
3197  else
3198  {
3199  using (Py.GIL())
3200  {
3201  throw new ArgumentException($"GetEnumString(): {Messages.Extensions.ObjectFromPythonIsNotACSharpType(pyObject.Repr())}");
3202  }
3203  }
3204  }
3205 
3206  /// <summary>
3207  /// Try to create a type with a given name, if PyObject is not a CLR type. Otherwise, convert it.
3208  /// </summary>
3209  /// <param name="pyObject">Python object representing a type.</param>
3210  /// <param name="type">Type object</param>
3211  /// <returns>True if was able to create the type</returns>
3212  public static bool TryCreateType(this PyObject pyObject, out Type type)
3213  {
3214  if (pyObject.TryConvert(out type))
3215  {
3216  // handles pure C# types
3217  return true;
3218  }
3219 
3220  if (!PythonActivators.TryGetValue(pyObject.Handle, out var pythonType))
3221  {
3222  // Some examples:
3223  // pytype: "<class 'DropboxBaseDataUniverseSelectionAlgorithm.StockDataSource'>"
3224  // method: "<bound method CoarseFineFundamentalComboAlgorithm.CoarseSelectionFunction of <CoarseFineFunda..."
3225  // array: "[<QuantConnect.Symbol object at 0x000001EEF21ED480>]"
3226  if (pyObject.ToString().StartsWith("<class '", StringComparison.InvariantCulture))
3227  {
3228  type = CreateType(pyObject);
3229  return true;
3230  }
3231  return false;
3232  }
3233  type = pythonType.Type;
3234  return true;
3235  }
3236 
3237 
3238  /// <summary>
3239  /// Creates a type with a given name, if PyObject is not a CLR type. Otherwise, convert it.
3240  /// </summary>
3241  /// <param name="pyObject">Python object representing a type.</param>
3242  /// <returns>Type object</returns>
3243  public static Type CreateType(this PyObject pyObject)
3244  {
3245  Type type;
3246  if (pyObject.TryConvert(out type))
3247  {
3248  return type;
3249  }
3250 
3251  PythonActivator pythonType;
3252  if (!PythonActivators.TryGetValue(pyObject.Handle, out pythonType))
3253  {
3254  var assemblyName = pyObject.GetAssemblyName();
3255  var typeBuilder = AssemblyBuilder
3256  .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run)
3257  .DefineDynamicModule("MainModule")
3258  // creating the type as public is required to allow 'dynamic' to be able to bind at runtime
3259  .DefineType(assemblyName.Name, TypeAttributes.Class | TypeAttributes.Public, type);
3260 
3261  pythonType = new PythonActivator(typeBuilder.CreateType(), pyObject);
3262 
3263  ObjectActivator.AddActivator(pythonType.Type, pythonType.Factory);
3264 
3265  // Save to prevent future additions
3266  PythonActivators.Add(pyObject.Handle, pythonType);
3267  }
3268  return pythonType.Type;
3269  }
3270 
3271  /// <summary>
3272  /// Helper method to get the assembly name from a python type
3273  /// </summary>
3274  /// <param name="pyObject">Python object pointing to the python type. <see cref="PyObject.GetPythonType"/></param>
3275  /// <returns>The python type assembly name</returns>
3276  public static AssemblyName GetAssemblyName(this PyObject pyObject)
3277  {
3278  using (Py.GIL())
3279  {
3280  return new AssemblyName(pyObject.Repr().Split('\'')[1]);
3281  }
3282  }
3283 
3284  /// <summary>
3285  /// Performs on-line batching of the specified enumerator, emitting chunks of the requested batch size
3286  /// </summary>
3287  /// <typeparam name="T">The enumerable item type</typeparam>
3288  /// <param name="enumerable">The enumerable to be batched</param>
3289  /// <param name="batchSize">The number of items per batch</param>
3290  /// <returns>An enumerable of lists</returns>
3291  public static IEnumerable<List<T>> BatchBy<T>(this IEnumerable<T> enumerable, int batchSize)
3292  {
3293  using (var enumerator = enumerable.GetEnumerator())
3294  {
3295  List<T> list = null;
3296  while (enumerator.MoveNext())
3297  {
3298  if (list == null)
3299  {
3300  list = new List<T> {enumerator.Current};
3301  }
3302  else if (list.Count < batchSize)
3303  {
3304  list.Add(enumerator.Current);
3305  }
3306  else
3307  {
3308  yield return list;
3309  list = new List<T> {enumerator.Current};
3310  }
3311  }
3312 
3313  if (list?.Count > 0)
3314  {
3315  yield return list;
3316  }
3317  }
3318  }
3319 
3320  /// <summary>
3321  /// Safely blocks until the specified task has completed executing
3322  /// </summary>
3323  /// <typeparam name="TResult">The task's result type</typeparam>
3324  /// <param name="task">The task to be awaited</param>
3325  /// <returns>The result of the task</returns>
3326  public static TResult SynchronouslyAwaitTaskResult<TResult>(this Task<TResult> task)
3327  {
3328  return task.ConfigureAwait(false).GetAwaiter().GetResult();
3329  }
3330 
3331  /// <summary>
3332  /// Safely blocks until the specified task has completed executing
3333  /// </summary>
3334  /// <param name="task">The task to be awaited</param>
3335  /// <returns>The result of the task</returns>
3336  public static void SynchronouslyAwaitTask(this Task task)
3337  {
3338  task.ConfigureAwait(false).GetAwaiter().GetResult();
3339  }
3340 
3341  /// <summary>
3342  /// Safely blocks until the specified task has completed executing
3343  /// </summary>
3344  /// <param name="task">The task to be awaited</param>
3345  /// <returns>The result of the task</returns>
3346  public static T SynchronouslyAwaitTask<T>(this Task<T> task)
3347  {
3348  return SynchronouslyAwaitTaskResult(task);
3349  }
3350 
3351  /// <summary>
3352  /// Safely blocks until the specified task has completed executing
3353  /// </summary>
3354  /// <param name="task">The task to be awaited</param>
3355  /// <returns>The result of the task</returns>
3356  public static void SynchronouslyAwaitTask(this ValueTask task)
3357  {
3358  if (task.IsCompleted)
3359  {
3360  return;
3361  }
3362  task.ConfigureAwait(false).GetAwaiter().GetResult();
3363  }
3364 
3365  /// <summary>
3366  /// Safely blocks until the specified task has completed executing
3367  /// </summary>
3368  /// <param name="task">The task to be awaited</param>
3369  /// <returns>The result of the task</returns>
3370  public static T SynchronouslyAwaitTask<T>(this ValueTask<T> task)
3371  {
3372  if (task.IsCompleted)
3373  {
3374  return task.Result;
3375  }
3376  return task.ConfigureAwait(false).GetAwaiter().GetResult();
3377  }
3378 
3379  /// <summary>
3380  /// Convert dictionary to query string
3381  /// </summary>
3382  /// <param name="pairs"></param>
3383  /// <returns></returns>
3384  public static string ToQueryString(this IDictionary<string, object> pairs)
3385  {
3386  return string.Join("&", pairs.Select(pair => $"{pair.Key}={pair.Value}"));
3387  }
3388 
3389  /// <summary>
3390  /// Returns a new string in which specified ending in the current instance is removed.
3391  /// </summary>
3392  /// <param name="s">original string value</param>
3393  /// <param name="ending">the string to be removed</param>
3394  /// <returns></returns>
3395  public static string RemoveFromEnd(this string s, string ending)
3396  {
3397  if (s.EndsWith(ending, StringComparison.InvariantCulture))
3398  {
3399  return s.Substring(0, s.Length - ending.Length);
3400  }
3401  else
3402  {
3403  return s;
3404  }
3405  }
3406 
3407  /// <summary>
3408  /// Returns a new string in which specified start in the current instance is removed.
3409  /// </summary>
3410  /// <param name="s">original string value</param>
3411  /// <param name="start">the string to be removed</param>
3412  /// <returns>Substring with start removed</returns>
3413  public static string RemoveFromStart(this string s, string start)
3414  {
3415  if (!string.IsNullOrEmpty(s) && !string.IsNullOrEmpty(start) && s.StartsWith(start, StringComparison.InvariantCulture))
3416  {
3417  return s.Substring(start.Length);
3418  }
3419  else
3420  {
3421  return s;
3422  }
3423  }
3424 
3425  /// <summary>
3426  /// Helper method to determine symbol for a live subscription
3427  /// </summary>
3428  /// <remarks>Useful for continuous futures where we subscribe to the underlying</remarks>
3429  public static bool TryGetLiveSubscriptionSymbol(this Symbol symbol, out Symbol mapped)
3430  {
3431  mapped = null;
3432  if (symbol.SecurityType == SecurityType.Future && symbol.IsCanonical() && symbol.HasUnderlying)
3433  {
3434  mapped = symbol.Underlying;
3435  return true;
3436  }
3437  return false;
3438  }
3439 
3440  /// <summary>
3441  /// Gets the delisting date for the provided Symbol
3442  /// </summary>
3443  /// <param name="symbol">The symbol to lookup the last trading date</param>
3444  /// <param name="mapFile">Map file to use for delisting date. Defaults to SID.DefaultDate if no value is passed and is equity.</param>
3445  /// <returns></returns>
3446  public static DateTime GetDelistingDate(this Symbol symbol, MapFile mapFile = null)
3447  {
3448  if (symbol.IsCanonical())
3449  {
3450  return Time.EndOfTime;
3451  }
3452  switch (symbol.ID.SecurityType)
3453  {
3454  case SecurityType.Option:
3455  return OptionSymbol.GetLastDayOfTrading(symbol);
3456  case SecurityType.FutureOption:
3457  return FutureOptionSymbol.GetLastDayOfTrading(symbol);
3458  case SecurityType.Future:
3459  case SecurityType.IndexOption:
3460  return symbol.ID.Date;
3461  default:
3462  return mapFile?.DelistingDate ?? Time.EndOfTime;
3463  }
3464  }
3465 
3466  /// <summary>
3467  /// Helper method to determine if a given symbol is of custom data
3468  /// </summary>
3469  public static bool IsCustomDataType<T>(this Symbol symbol)
3470  {
3471  return symbol.SecurityType == SecurityType.Base
3472  && SecurityIdentifier.TryGetCustomDataType(symbol.ID.Symbol, out var type)
3473  && type.Equals(typeof(T).Name, StringComparison.InvariantCultureIgnoreCase);
3474  }
3475 
3476  /// <summary>
3477  /// Helper method that will return a back month, with future expiration, future contract based on the given offset
3478  /// </summary>
3479  /// <param name="symbol">The none canonical future symbol</param>
3480  /// <param name="offset">The quantity of contracts to move into the future expiration chain</param>
3481  /// <returns>A new future expiration symbol instance</returns>
3482  public static Symbol AdjustSymbolByOffset(this Symbol symbol, uint offset)
3483  {
3484  if (symbol.SecurityType != SecurityType.Future || symbol.IsCanonical())
3485  {
3486  throw new InvalidOperationException(Messages.Extensions.ErrorAdjustingSymbolByOffset);
3487  }
3488 
3489  var expiration = symbol.ID.Date;
3490  for (var i = 0; i < offset; i++)
3491  {
3492  var expiryFunction = FuturesExpiryFunctions.FuturesExpiryFunction(symbol);
3493  DateTime newExpiration;
3494  // for the current expiration we add a month to get the next one
3495  var monthOffset = 0;
3496  do
3497  {
3498  monthOffset++;
3499  newExpiration = expiryFunction(expiration.AddMonths(monthOffset)).Date;
3500  } while (newExpiration <= expiration);
3501 
3502  expiration = newExpiration;
3503  symbol = Symbol.CreateFuture(symbol.ID.Symbol, symbol.ID.Market, newExpiration);
3504  }
3505 
3506  return symbol;
3507  }
3508 
3509  /// <summary>
3510  /// Helper method to unsubscribe a given configuration, handling any required mapping
3511  /// </summary>
3512  public static void UnsubscribeWithMapping(this IDataQueueHandler dataQueueHandler, SubscriptionDataConfig dataConfig)
3513  {
3514  if (dataConfig.Symbol.TryGetLiveSubscriptionSymbol(out var mappedSymbol))
3515  {
3516  dataConfig = new SubscriptionDataConfig(dataConfig, symbol: mappedSymbol, mappedConfig: true);
3517  }
3518  dataQueueHandler.Unsubscribe(dataConfig);
3519  }
3520 
3521  /// <summary>
3522  /// Helper method to subscribe a given configuration, handling any required mapping
3523  /// </summary>
3524  public static IEnumerator<BaseData> SubscribeWithMapping(this IDataQueueHandler dataQueueHandler,
3525  SubscriptionDataConfig dataConfig,
3526  EventHandler newDataAvailableHandler,
3527  Func<SubscriptionDataConfig, bool> isExpired,
3528  out SubscriptionDataConfig subscribedConfig)
3529  {
3530  subscribedConfig = dataConfig;
3531  if (dataConfig.Symbol.TryGetLiveSubscriptionSymbol(out var mappedSymbol))
3532  {
3533  subscribedConfig = new SubscriptionDataConfig(dataConfig, symbol: mappedSymbol, mappedConfig: true);
3534  }
3535 
3536  // during warmup we might get requested to add some asset which has already expired in which case the live enumerator will be empty
3537  IEnumerator<BaseData> result = null;
3538  if (!isExpired(subscribedConfig))
3539  {
3540  result = dataQueueHandler.Subscribe(subscribedConfig, newDataAvailableHandler);
3541  }
3542  else
3543  {
3544  Log.Trace($"SubscribeWithMapping(): skip live subscription for expired asset {subscribedConfig}");
3545  }
3546  return result ?? Enumerable.Empty<BaseData>().GetEnumerator();
3547  }
3548 
3549  /// <summary>
3550  /// Helper method to stream read lines from a file
3551  /// </summary>
3552  /// <param name="dataProvider">The data provider to use</param>
3553  /// <param name="file">The file path to read from</param>
3554  /// <returns>Enumeration of lines in file</returns>
3555  public static IEnumerable<string> ReadLines(this IDataProvider dataProvider, string file)
3556  {
3557  if(dataProvider == null)
3558  {
3559  throw new ArgumentException(Messages.Extensions.NullDataProvider);
3560  }
3561  var stream = dataProvider.Fetch(file);
3562  if (stream == null)
3563  {
3564  yield break;
3565  }
3566 
3567  using (var streamReader = new StreamReader(stream))
3568  {
3569  string line;
3570  do
3571  {
3572  line = streamReader.ReadLine();
3573  if (line != null)
3574  {
3575  yield return line;
3576  }
3577  }
3578  while (line != null);
3579  }
3580  }
3581 
3582  /// <summary>
3583  /// Scale data based on factor function
3584  /// </summary>
3585  /// <param name="data">Data to Adjust</param>
3586  /// <param name="factorFunc">Function to factor prices by</param>
3587  /// <param name="volumeFactor">Factor to multiply volume/askSize/bidSize/quantity by</param>
3588  /// <param name="factor">Price scale</param>
3589  /// <param name="sumOfDividends">The current dividend sum</param>
3590  /// <remarks>Volume values are rounded to the nearest integer, lot size purposefully not considered
3591  /// as scaling only applies to equities</remarks>
3592  public static BaseData Scale(this BaseData data, Func<decimal, decimal, decimal, decimal> factorFunc, decimal volumeFactor, decimal factor, decimal sumOfDividends)
3593  {
3594  switch (data.DataType)
3595  {
3596  case MarketDataType.TradeBar:
3597  var tradeBar = data as TradeBar;
3598  if (tradeBar != null)
3599  {
3600  tradeBar.Open = factorFunc(tradeBar.Open, factor, sumOfDividends);
3601  tradeBar.High = factorFunc(tradeBar.High, factor, sumOfDividends);
3602  tradeBar.Low = factorFunc(tradeBar.Low, factor, sumOfDividends);
3603  tradeBar.Close = factorFunc(tradeBar.Close, factor, sumOfDividends);
3604  tradeBar.Volume = Math.Round(tradeBar.Volume * volumeFactor);
3605  }
3606  break;
3607  case MarketDataType.Tick:
3608  var securityType = data.Symbol.SecurityType;
3609  if (securityType != SecurityType.Equity &&
3610  securityType != SecurityType.Future &&
3611  !securityType.IsOption())
3612  {
3613  break;
3614  }
3615 
3616  var tick = data as Tick;
3617  if (tick == null || tick.TickType == TickType.OpenInterest)
3618  {
3619  break;
3620  }
3621 
3622  if (tick.TickType == TickType.Trade)
3623  {
3624  tick.Value = factorFunc(tick.Value, factor, sumOfDividends);
3625  tick.Quantity = Math.Round(tick.Quantity * volumeFactor);
3626  break;
3627  }
3628 
3629  tick.BidPrice = tick.BidPrice != 0 ? factorFunc(tick.BidPrice, factor, sumOfDividends) : 0;
3630  tick.BidSize = Math.Round(tick.BidSize * volumeFactor);
3631  tick.AskPrice = tick.AskPrice != 0 ? factorFunc(tick.AskPrice, factor, sumOfDividends) : 0;
3632  tick.AskSize = Math.Round(tick.AskSize * volumeFactor);
3633 
3634  if (tick.BidPrice == 0)
3635  {
3636  tick.Value = tick.AskPrice;
3637  break;
3638  }
3639  if (tick.AskPrice == 0)
3640  {
3641  tick.Value = tick.BidPrice;
3642  break;
3643  }
3644 
3645  tick.Value = (tick.BidPrice + tick.AskPrice) / 2m;
3646  break;
3647  case MarketDataType.QuoteBar:
3648  var quoteBar = data as QuoteBar;
3649  if (quoteBar != null)
3650  {
3651  if (quoteBar.Ask != null)
3652  {
3653  quoteBar.Ask.Open = factorFunc(quoteBar.Ask.Open, factor, sumOfDividends);
3654  quoteBar.Ask.High = factorFunc(quoteBar.Ask.High, factor, sumOfDividends);
3655  quoteBar.Ask.Low = factorFunc(quoteBar.Ask.Low, factor, sumOfDividends);
3656  quoteBar.Ask.Close = factorFunc(quoteBar.Ask.Close, factor, sumOfDividends);
3657  }
3658  if (quoteBar.Bid != null)
3659  {
3660  quoteBar.Bid.Open = factorFunc(quoteBar.Bid.Open, factor, sumOfDividends);
3661  quoteBar.Bid.High = factorFunc(quoteBar.Bid.High, factor, sumOfDividends);
3662  quoteBar.Bid.Low = factorFunc(quoteBar.Bid.Low, factor, sumOfDividends);
3663  quoteBar.Bid.Close = factorFunc(quoteBar.Bid.Close, factor, sumOfDividends);
3664  }
3665  quoteBar.Value = quoteBar.Close;
3666  quoteBar.LastAskSize = Math.Round(quoteBar.LastAskSize * volumeFactor);
3667  quoteBar.LastBidSize = Math.Round(quoteBar.LastBidSize * volumeFactor);
3668  }
3669  break;
3670  case MarketDataType.Auxiliary:
3671  case MarketDataType.Base:
3672  case MarketDataType.OptionChain:
3673  case MarketDataType.FuturesChain:
3674  break;
3675  default:
3676  throw new ArgumentOutOfRangeException();
3677  }
3678  return data;
3679  }
3680 
3681  /// <summary>
3682  /// Normalize prices based on configuration
3683  /// </summary>
3684  /// <param name="data">Data to be normalized</param>
3685  /// <param name="factor">Price scale</param>
3686  /// <param name="normalizationMode">The price scaling normalization mode</param>
3687  /// <param name="sumOfDividends">The current dividend sum</param>
3688  /// <returns>The provided data point adjusted</returns>
3689  public static BaseData Normalize(this BaseData data, decimal factor, DataNormalizationMode normalizationMode, decimal sumOfDividends)
3690  {
3691  switch (normalizationMode)
3692  {
3693  case DataNormalizationMode.Adjusted:
3694  case DataNormalizationMode.SplitAdjusted:
3695  case DataNormalizationMode.ScaledRaw:
3696  return data?.Scale(TimesFactor, 1 / factor, factor, decimal.Zero);
3697  case DataNormalizationMode.TotalReturn:
3698  return data.Scale(TimesFactor, 1 / factor, factor, sumOfDividends);
3699 
3700  case DataNormalizationMode.BackwardsRatio:
3701  return data.Scale(TimesFactor, 1, factor, decimal.Zero);
3702  case DataNormalizationMode.BackwardsPanamaCanal:
3703  return data.Scale(AdditionFactor, 1, factor, decimal.Zero);
3704  case DataNormalizationMode.ForwardPanamaCanal:
3705  return data.Scale(AdditionFactor, 1, factor, decimal.Zero);
3706 
3707  case DataNormalizationMode.Raw:
3708  default:
3709  return data;
3710  }
3711  }
3712 
3713  /// <summary>
3714  /// Applies a times factor. We define this so we don't need to create it constantly
3715  /// </summary>
3716  [MethodImpl(MethodImplOptions.AggressiveInlining)]
3717  private static decimal TimesFactor(decimal target, decimal factor, decimal sumOfDividends)
3718  {
3719  return target * factor + sumOfDividends;
3720  }
3721 
3722  /// <summary>
3723  /// Applies an addition factor. We define this so we don't need to create it constantly
3724  /// </summary>
3725  [MethodImpl(MethodImplOptions.AggressiveInlining)]
3726  private static decimal AdditionFactor(decimal target, decimal factor, decimal _)
3727  {
3728  return target + factor;
3729  }
3730 
3731  /// <summary>
3732  /// Helper method to determine if price scales need an update based on the given data point
3733  /// </summary>
3734  public static DateTime GetUpdatePriceScaleFrontier(this BaseData data)
3735  {
3736  if (data != null)
3737  {
3738  var priceScaleFrontier = data.Time;
3739  if (data.Time.Date != data.EndTime.Date && data.EndTime.TimeOfDay > TimeSpan.Zero)
3740  {
3741  // if the data point goes from one day to another after midnight we use EndTime, this is due to differences between 'data' and 'exchage' time zone,
3742  // for example: NYMEX future CL 'data' TZ is UTC while 'exchange' TZ is NY, so daily bars go from 8PM 'X day' to 8PM 'X+1 day'. Note that the data
3743  // in the daily bar itself is filtered by exchange open, so it has data from 09:30 'X+1 day' to 17:00 'X+1 day' as expected.
3744  // A potential solution to avoid the need of this check is to adjust the daily data time zone to match the exchange time zone, following this example above
3745  // the daily bar would go from midnight X+1 day to midnight X+2
3746  // TODO: see related issue https://github.com/QuantConnect/Lean/issues/6964 which would avoid the need for this
3747  priceScaleFrontier = data.EndTime;
3748  }
3749  return priceScaleFrontier;
3750  }
3751  return DateTime.MinValue;
3752  }
3753 
3754  /// <summary>
3755  /// Thread safe concurrent dictionary order by implementation by using <see cref="SafeEnumeration{TSource,TKey}"/>
3756  /// </summary>
3757  /// <remarks>See https://stackoverflow.com/questions/47630824/is-c-sharp-linq-orderby-threadsafe-when-used-with-concurrentdictionarytkey-tva</remarks>
3758  public static IOrderedEnumerable<KeyValuePair<TSource, TKey>> OrderBySafe<TSource, TKey>(
3759  this ConcurrentDictionary<TSource, TKey> source, Func<KeyValuePair<TSource, TKey>, TSource> keySelector
3760  )
3761  {
3762  return source.SafeEnumeration().OrderBy(keySelector);
3763  }
3764 
3765  /// <summary>
3766  /// Thread safe concurrent dictionary order by implementation by using <see cref="SafeEnumeration{TSource,TKey}"/>
3767  /// </summary>
3768  /// <remarks>See https://stackoverflow.com/questions/47630824/is-c-sharp-linq-orderby-threadsafe-when-used-with-concurrentdictionarytkey-tva</remarks>
3769  public static IOrderedEnumerable<KeyValuePair<TSource, TKey>> OrderBySafe<TSource, TKey>(
3770  this ConcurrentDictionary<TSource, TKey> source, Func<KeyValuePair<TSource, TKey>, TKey> keySelector
3771  )
3772  {
3773  return source.SafeEnumeration().OrderBy(keySelector);
3774  }
3775 
3776  /// <summary>
3777  /// Force concurrent dictionary enumeration using a thread safe implementation
3778  /// </summary>
3779  /// <remarks>See https://stackoverflow.com/questions/47630824/is-c-sharp-linq-orderby-threadsafe-when-used-with-concurrentdictionarytkey-tva</remarks>
3780  public static IEnumerable<KeyValuePair<TSource, TKey>> SafeEnumeration<TSource, TKey>(
3781  this ConcurrentDictionary<TSource, TKey> source)
3782  {
3783  foreach (var kvp in source)
3784  {
3785  yield return kvp;
3786  }
3787  }
3788 
3789  /// <summary>
3790  /// Helper method to determine the right data mapping mode to use by default
3791  /// </summary>
3792  public static DataMappingMode GetUniverseNormalizationModeOrDefault(this UniverseSettings universeSettings, SecurityType securityType, string market)
3793  {
3794  switch (securityType)
3795  {
3796  case SecurityType.Future:
3797  if ((universeSettings.DataMappingMode == DataMappingMode.OpenInterest
3798  || universeSettings.DataMappingMode == DataMappingMode.OpenInterestAnnual)
3799  && (market == Market.HKFE || market == Market.EUREX || market == Market.ICE))
3800  {
3801  // circle around default OI for currently no OI available data
3802  return DataMappingMode.LastTradingDay;
3803  }
3804  return universeSettings.DataMappingMode;
3805  default:
3806  return universeSettings.DataMappingMode;
3807  }
3808  }
3809 
3810  /// <summary>
3811  /// Helper method to determine the right data normalization mode to use by default
3812  /// </summary>
3814  {
3815  switch (securityType)
3816  {
3817  case SecurityType.Future:
3818  if (universeSettings.DataNormalizationMode is DataNormalizationMode.BackwardsRatio
3819  or DataNormalizationMode.BackwardsPanamaCanal or DataNormalizationMode.ForwardPanamaCanal
3820  or DataNormalizationMode.Raw)
3821  {
3822  return universeSettings.DataNormalizationMode;
3823  }
3824  return DataNormalizationMode.BackwardsRatio;
3825  default:
3826  return universeSettings.DataNormalizationMode;
3827  }
3828  }
3829 
3830  /// <summary>
3831  /// Returns a hex string of the byte array.
3832  /// </summary>
3833  /// <param name="source">the byte array to be represented as string</param>
3834  /// <returns>A new string containing the items in the enumerable</returns>
3835  public static string ToHexString(this byte[] source)
3836  {
3837  if (source == null || source.Length == 0)
3838  {
3839  throw new ArgumentException(Messages.Extensions.NullOrEmptySourceToConvertToHexString);
3840  }
3841 
3842  var hex = new StringBuilder(source.Length * 2);
3843  foreach (var b in source)
3844  {
3845  hex.AppendFormat(CultureInfo.InvariantCulture, "{0:x2}", b);
3846  }
3847 
3848  return hex.ToString();
3849  }
3850 
3851  /// <summary>
3852  /// Gets the option exercise order direction resulting from the specified <paramref name="right"/> and
3853  /// whether or not we wrote the option (<paramref name="isShort"/> is <code>true</code>) or bought to
3854  /// option (<paramref name="isShort"/> is <code>false</code>)
3855  /// </summary>
3856  /// <param name="right">The option right</param>
3857  /// <param name="isShort">True if we wrote the option, false if we purchased the option</param>
3858  /// <returns>The order direction resulting from an exercised option</returns>
3859  public static OrderDirection GetExerciseDirection(this OptionRight right, bool isShort)
3860  {
3861  switch (right)
3862  {
3863  case OptionRight.Call:
3864  return isShort ? OrderDirection.Sell : OrderDirection.Buy;
3865  default:
3866  return isShort ? OrderDirection.Buy : OrderDirection.Sell;
3867  }
3868  }
3869 
3870  /// <summary>
3871  /// Gets the <see cref="OrderDirection"/> for the specified <paramref name="quantity"/>
3872  /// </summary>
3873  public static OrderDirection GetOrderDirection(decimal quantity)
3874  {
3875  var sign = Math.Sign(quantity);
3876  switch (sign)
3877  {
3878  case 1: return OrderDirection.Buy;
3879  case 0: return OrderDirection.Hold;
3880  case -1: return OrderDirection.Sell;
3881  default:
3882  throw new ApplicationException(
3883  $"The skies are falling and the oceans are rising! Math.Sign({quantity}) returned {sign} :/"
3884  );
3885  }
3886  }
3887 
3888  /// <summary>
3889  /// Helper method to process an algorithms security changes, will add and remove securities according to them
3890  /// </summary>
3891  public static void ProcessSecurityChanges(this IAlgorithm algorithm, SecurityChanges securityChanges)
3892  {
3893  foreach (var security in securityChanges.AddedSecurities)
3894  {
3895  // uses TryAdd, so don't need to worry about duplicates here
3896  algorithm.Securities.Add(security);
3897  }
3898 
3899  var activeSecurities = algorithm.UniverseManager.ActiveSecurities;
3900  foreach (var security in securityChanges.RemovedSecurities)
3901  {
3902  if (!activeSecurities.ContainsKey(security.Symbol))
3903  {
3904  security.Reset();
3905  }
3906  }
3907  }
3908 
3909  /// <summary>
3910  /// Helper method to set the <see cref="Security.IsTradable"/> property to <code>true</code>
3911  /// for the given security when possible
3912  /// </summary>
3913  public static void MakeTradable(this Security security)
3914  {
3915  if (security.Type != SecurityType.Index || (security as Securities.Index.Index).ManualSetIsTradable)
3916  {
3917  security.IsTradable = true;
3918  }
3919  }
3920 
3921  /// <summary>
3922  /// Helper method to set an algorithm runtime exception in a normalized fashion
3923  /// </summary>
3924  public static void SetRuntimeError(this IAlgorithm algorithm, Exception exception, string context)
3925  {
3926  Log.Error(exception, $"Extensions.SetRuntimeError(): {Messages.Extensions.RuntimeError(algorithm, context)}");
3927  exception = StackExceptionInterpreter.Instance.Value.Interpret(exception);
3928  algorithm.RunTimeError = exception;
3929  algorithm.SetStatus(AlgorithmStatus.RuntimeError);
3930  }
3931 
3932  /// <summary>
3933  /// Creates a <see cref="OptionChainUniverse"/> for a given symbol
3934  /// </summary>
3935  /// <param name="algorithm">The algorithm instance to create universes for</param>
3936  /// <param name="symbol">Symbol of the option</param>
3937  /// <param name="filter">The option filter to use</param>
3938  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3939  /// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
3940  public static OptionChainUniverse CreateOptionChain(this IAlgorithm algorithm, Symbol symbol, PyObject filter, UniverseSettings universeSettings = null)
3941  {
3942  var result = CreateOptionChain(algorithm, symbol, out var option, universeSettings);
3943  option.SetFilter(filter);
3944  return result;
3945  }
3946 
3947  /// <summary>
3948  /// Creates a <see cref="OptionChainUniverse"/> for a given symbol
3949  /// </summary>
3950  /// <param name="algorithm">The algorithm instance to create universes for</param>
3951  /// <param name="symbol">Symbol of the option</param>
3952  /// <param name="filter">The option filter to use</param>
3953  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3954  /// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
3955  public static OptionChainUniverse CreateOptionChain(this IAlgorithm algorithm, Symbol symbol, Func<OptionFilterUniverse, OptionFilterUniverse> filter, UniverseSettings universeSettings = null)
3956  {
3957  var result = CreateOptionChain(algorithm, symbol, out var option, universeSettings);
3958  option.SetFilter(filter);
3959  return result;
3960  }
3961 
3962  /// <summary>
3963  /// Creates a <see cref="OptionChainUniverse"/> for a given symbol
3964  /// </summary>
3965  /// <param name="algorithm">The algorithm instance to create universes for</param>
3966  /// <param name="symbol">Symbol of the option</param>
3967  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3968  /// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
3969  private static OptionChainUniverse CreateOptionChain(this IAlgorithm algorithm, Symbol symbol, out Option option, UniverseSettings universeSettings = null)
3970  {
3971  if (!symbol.SecurityType.IsOption())
3972  {
3973  throw new ArgumentException(Messages.Extensions.CreateOptionChainRequiresOptionSymbol);
3974  }
3975 
3976  // resolve defaults if not specified
3977  var settings = universeSettings ?? algorithm.UniverseSettings;
3978 
3979  option = (Option)algorithm.AddSecurity(symbol.Canonical, settings.Resolution, settings.FillForward, settings.Leverage, settings.ExtendedMarketHours);
3980 
3981  return (OptionChainUniverse)algorithm.UniverseManager.Values.Single(universe => universe.Configuration.Symbol == symbol.Canonical);
3982  }
3983 
3984  /// <summary>
3985  /// Creates a <see cref="FuturesChainUniverse"/> for a given symbol
3986  /// </summary>
3987  /// <param name="algorithm">The algorithm instance to create universes for</param>
3988  /// <param name="symbol">Symbol of the future</param>
3989  /// <param name="filter">The future filter to use</param>
3990  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3991  public static IEnumerable<Universe> CreateFutureChain(this IAlgorithm algorithm, Symbol symbol, PyObject filter, UniverseSettings universeSettings = null)
3992  {
3993  var result = CreateFutureChain(algorithm, symbol, out var future, universeSettings);
3994  future.SetFilter(filter);
3995  return result;
3996  }
3997 
3998  /// <summary>
3999  /// Creates a <see cref="FuturesChainUniverse"/> for a given symbol
4000  /// </summary>
4001  /// <param name="algorithm">The algorithm instance to create universes for</param>
4002  /// <param name="symbol">Symbol of the future</param>
4003  /// <param name="filter">The future filter to use</param>
4004  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
4005  public static IEnumerable<Universe> CreateFutureChain(this IAlgorithm algorithm, Symbol symbol, Func<FutureFilterUniverse, FutureFilterUniverse> filter, UniverseSettings universeSettings = null)
4006  {
4007  var result = CreateFutureChain(algorithm, symbol, out var future, universeSettings);
4008  future.SetFilter(filter);
4009  return result;
4010  }
4011 
4012  /// <summary>
4013  /// Creates a <see cref="FuturesChainUniverse"/> for a given symbol
4014  /// </summary>
4015  private static IEnumerable<Universe> CreateFutureChain(this IAlgorithm algorithm, Symbol symbol, out Future future, UniverseSettings universeSettings = null)
4016  {
4017  if (symbol.SecurityType != SecurityType.Future)
4018  {
4019  throw new ArgumentException(Messages.Extensions.CreateFutureChainRequiresFutureSymbol);
4020  }
4021 
4022  // resolve defaults if not specified
4023  var settings = universeSettings ?? algorithm.UniverseSettings;
4024 
4025  var dataNormalizationMode = settings.GetUniverseNormalizationModeOrDefault(symbol.SecurityType);
4026 
4027  future = (Future)algorithm.AddSecurity(symbol.Canonical, settings.Resolution, settings.FillForward, settings.Leverage, settings.ExtendedMarketHours,
4028  settings.DataMappingMode, dataNormalizationMode, settings.ContractDepthOffset);
4029 
4030  // let's yield back both the future chain and the continuous future universe
4031  return algorithm.UniverseManager.Values.Where(universe => universe.Configuration.Symbol == symbol.Canonical || ContinuousContractUniverse.CreateSymbol(symbol.Canonical) == universe.Configuration.Symbol);
4032  }
4033 
4034  private static bool _notifiedUniverseSettingsUsed;
4035  private static readonly HashSet<SecurityType> _supportedSecurityTypes = new()
4036  {
4037  SecurityType.Equity,
4038  SecurityType.Forex,
4039  SecurityType.Cfd,
4040  SecurityType.Option,
4041  SecurityType.Future,
4042  SecurityType.FutureOption,
4043  SecurityType.IndexOption,
4044  SecurityType.Crypto,
4045  SecurityType.CryptoFuture
4046  };
4047 
4048  /// <summary>
4049  /// Gets the security for the specified symbol from the algorithm's securities collection.
4050  /// In case the security is not found, it will be created using the <see cref="IAlgorithm.UniverseSettings"/>
4051  /// and a best effort configuration setup.
4052  /// </summary>
4053  /// <param name="algorithm">The algorithm instance</param>
4054  /// <param name="symbol">The symbol which security is being looked up</param>
4055  /// <param name="security">The found or added security instance</param>
4056  /// <param name="onError">Callback to invoke in case of unsupported security type</param>
4057  /// <returns>True if the security was found or added</returns>
4058  public static bool GetOrAddUnrequestedSecurity(this IAlgorithm algorithm, Symbol symbol, out Security security,
4059  Action<IReadOnlyCollection<SecurityType>> onError = null)
4060  {
4061  if (!algorithm.Securities.TryGetValue(symbol, out security))
4062  {
4063  if (!_supportedSecurityTypes.Contains(symbol.SecurityType))
4064  {
4065  Log.Error("GetOrAddUnrequestedSecurity(): Unsupported security type: " + symbol.SecurityType + "-" + symbol.Value);
4066  onError?.Invoke(_supportedSecurityTypes);
4067  return false;
4068  }
4069 
4070  var resolution = algorithm.UniverseSettings.Resolution;
4071  var fillForward = algorithm.UniverseSettings.FillForward;
4072  var leverage = algorithm.UniverseSettings.Leverage;
4073  var extendedHours = algorithm.UniverseSettings.ExtendedMarketHours;
4074 
4075  if (!_notifiedUniverseSettingsUsed)
4076  {
4077  // let's just send the message once
4078  _notifiedUniverseSettingsUsed = true;
4079 
4080  var leverageMsg = $" Leverage = {leverage};";
4081  if (leverage == Security.NullLeverage)
4082  {
4083  leverageMsg = $" Leverage = default;";
4084  }
4085  algorithm.Debug($"Will use UniverseSettings for automatically added securities for open orders and holdings. UniverseSettings:" +
4086  $" Resolution = {resolution};{leverageMsg} FillForward = {fillForward}; ExtendedHours = {extendedHours}");
4087  }
4088 
4089  Log.Trace("GetOrAddUnrequestedSecurity(): Adding unrequested security: " + symbol.Value);
4090 
4091  if (symbol.SecurityType.IsOption())
4092  {
4093  // add current option contract to the system
4094  security = algorithm.AddOptionContract(symbol, resolution, fillForward, leverage, extendedHours);
4095  }
4096  else if (symbol.SecurityType == SecurityType.Future)
4097  {
4098  // add current future contract to the system
4099  security = algorithm.AddFutureContract(symbol, resolution, fillForward, leverage, extendedHours);
4100  }
4101  else
4102  {
4103  // for items not directly requested set leverage to 1 and at the min resolution
4104  security = algorithm.AddSecurity(symbol.SecurityType, symbol.Value, resolution, symbol.ID.Market, fillForward, leverage, extendedHours);
4105  }
4106  }
4107  return true;
4108  }
4109 
4110  /// <summary>
4111  /// Inverts the specified <paramref name="right"/>
4112  /// </summary>
4113  public static OptionRight Invert(this OptionRight right)
4114  {
4115  switch (right)
4116  {
4117  case OptionRight.Call: return OptionRight.Put;
4118  case OptionRight.Put: return OptionRight.Call;
4119  default:
4120  throw new ArgumentOutOfRangeException(nameof(right), right, null);
4121  }
4122  }
4123 
4124  /// <summary>
4125  /// Compares two values using given operator
4126  /// </summary>
4127  /// <typeparam name="T"></typeparam>
4128  /// <param name="op">Comparison operator</param>
4129  /// <param name="arg1">The first value</param>
4130  /// <param name="arg2">The second value</param>
4131  /// <returns>Returns true if its left-hand operand meets the operator value to its right-hand operand, false otherwise</returns>
4132  public static bool Compare<T>(this ComparisonOperatorTypes op, T arg1, T arg2) where T : IComparable
4133  {
4134  return ComparisonOperator.Compare(op, arg1, arg2);
4135  }
4136 
4137  /// <summary>
4138  /// Converts a <see cref="Data.HistoryRequest" /> instance to a <see cref="SubscriptionDataConfig"/> instance
4139  /// </summary>
4140  /// <param name="request">History request</param>
4141  /// <param name="isInternalFeed">
4142  /// Set to true if this subscription is added for the sole purpose of providing currency conversion rates,
4143  /// setting this flag to true will prevent the data from being sent into the algorithm's OnData methods
4144  /// </param>
4145  /// <param name="isFilteredSubscription">True if this subscription should have filters applied to it (market hours/user filters from security), false otherwise</param>
4146  /// <returns>Subscription data configuration</returns>
4147  public static SubscriptionDataConfig ToSubscriptionDataConfig(this Data.HistoryRequest request, bool isInternalFeed = false, bool isFilteredSubscription = true)
4148  {
4149  return new SubscriptionDataConfig(request.DataType,
4150  request.Symbol,
4151  request.Resolution,
4152  request.DataTimeZone,
4153  request.ExchangeHours.TimeZone,
4154  request.FillForwardResolution.HasValue,
4155  request.IncludeExtendedMarketHours,
4156  isInternalFeed,
4157  request.IsCustomData,
4158  request.TickType,
4159  isFilteredSubscription,
4160  request.DataNormalizationMode,
4161  request.DataMappingMode,
4162  request.ContractDepthOffset
4163  );
4164  }
4165 
4166  /// <summary>
4167  /// Centralized logic used at the top of the subscription enumerator stacks to determine if we should emit base data points
4168  /// based on the configuration for this subscription and the type of data we are handling.
4169  ///
4170  /// Currently we only want to emit split/dividends/delisting events for non internal <see cref="TradeBar"/> configurations
4171  /// this last part is because equities also have <see cref="QuoteBar"/> subscriptions which will also subscribe to the
4172  /// same aux events and we don't want duplicate emits of these events in the TimeSliceFactory
4173  /// </summary>
4174  /// <remarks>The "TimeSliceFactory" does not allow for multiple dividends/splits per symbol in the same time slice
4175  /// but we don't want to rely only on that to filter out duplicated aux data so we use this at the top of
4176  /// our data enumerator stacks to define what subscription should emit this data.</remarks>
4177  /// <remarks>We use this function to filter aux data at the top of the subscription enumerator stack instead of
4178  /// stopping the subscription stack from subscribing to aux data at the bottom because of a
4179  /// dependency with the FF enumerators requiring that they receive aux data to properly handle delistings.
4180  /// Otherwise we would have issues with delisted symbols continuing to fill forward after expiry/delisting.
4181  /// Reference PR #5485 and related issues for more.</remarks>
4182  public static bool ShouldEmitData(this SubscriptionDataConfig config, BaseData data, bool isUniverse = false)
4183  {
4184  // For now we are only filtering Auxiliary data; so if its another type just return true or if it's a margin interest rate which we want to emit always
4185  if (data.DataType != MarketDataType.Auxiliary)
4186  {
4187  return true;
4188  }
4189 
4190  // This filter does not apply to auxiliary data outside of delisting/splits/dividends so lets those emit
4191  var type = data.GetType();
4192  var expectedType = type.IsAssignableTo(config.Type);
4193 
4194  // Check our config type first to be lazy about using data.GetType() unless required
4195  var configTypeFilter = (config.Type == typeof(TradeBar) || config.Type.IsAssignableTo(typeof(BaseChainUniverseData)) ||
4196  config.Type == typeof(Tick) && config.TickType == TickType.Trade || config.IsCustomData);
4197 
4198  if (!configTypeFilter)
4199  {
4200  return expectedType;
4201  }
4202 
4203  // We don't want to pump in any data to `Universe.SelectSymbols(...)` if the
4204  // type is not configured to be consumed by the universe. This change fixes
4205  // a case where a `SymbolChangedEvent` was being passed to an ETF constituent universe
4206  // for filtering/selection, and would result in either a runtime error
4207  // if casting into the expected type explicitly, or call the filter function with
4208  // no data being provided, resulting in all universe Symbols being de-selected.
4209  if (isUniverse && !expectedType)
4210  {
4211  return (data as Delisting)?.Type == DelistingType.Delisted;
4212  }
4213 
4214  // We let delistings through. We need to emit delistings for all subscriptions, even internals like
4215  // continuous futures mapped contracts. For instance, an algorithm might hold a position for a mapped
4216  // contract and then the continuous future is mapped to a different contract. If the previously mapped
4217  // contract is delisted, we need to let the delisting through so that positions are closed out and the
4218  // security is removed from the algorithm and marked as delisted and non-tradable.
4219  if (!(type == typeof(Split) || type == typeof(Dividend)))
4220  {
4221  return true;
4222  }
4223 
4224  // If we made it here then only filter it if its an InternalFeed
4225  return !config.IsInternalFeed;
4226  }
4227 
4228  /// <summary>
4229  /// Gets the <see cref="OrderDirection"/> that corresponds to the specified <paramref name="side"/>
4230  /// </summary>
4231  /// <param name="side">The position side to be converted</param>
4232  /// <returns>The order direction that maps from the provided position side</returns>
4234  {
4235  switch (side)
4236  {
4237  case PositionSide.Short: return OrderDirection.Sell;
4238  case PositionSide.None: return OrderDirection.Hold;
4239  case PositionSide.Long: return OrderDirection.Buy;
4240  default:
4241  throw new ArgumentOutOfRangeException(nameof(side), side, null);
4242  }
4243  }
4244 
4245  /// <summary>
4246  /// Determines if an order with the specified <paramref name="direction"/> would close a position with the
4247  /// specified <paramref name="side"/>
4248  /// </summary>
4249  /// <param name="direction">The direction of the order, buy/sell</param>
4250  /// <param name="side">The side of the position, long/short</param>
4251  /// <returns>True if the order direction would close the position, otherwise false</returns>
4252  public static bool Closes(this OrderDirection direction, PositionSide side)
4253  {
4254  switch (side)
4255  {
4256  case PositionSide.Short:
4257  switch (direction)
4258  {
4259  case OrderDirection.Buy: return true;
4260  case OrderDirection.Sell: return false;
4261  case OrderDirection.Hold: return false;
4262  default:
4263  throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
4264  }
4265 
4266  case PositionSide.Long:
4267  switch (direction)
4268  {
4269  case OrderDirection.Buy: return false;
4270  case OrderDirection.Sell: return true;
4271  case OrderDirection.Hold: return false;
4272  default:
4273  throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
4274  }
4275 
4276  case PositionSide.None:
4277  return false;
4278 
4279  default:
4280  throw new ArgumentOutOfRangeException(nameof(side), side, null);
4281  }
4282  }
4283 
4284  /// <summary>
4285  /// Determines if the two lists are equal, including all items at the same indices.
4286  /// </summary>
4287  /// <typeparam name="T">The element type</typeparam>
4288  /// <param name="left">The left list</param>
4289  /// <param name="right">The right list</param>
4290  /// <returns>True if the two lists have the same counts and items at each index evaluate as equal</returns>
4291  public static bool ListEquals<T>(this IReadOnlyList<T> left, IReadOnlyList<T> right)
4292  {
4293  var count = left.Count;
4294  if (count != right.Count)
4295  {
4296  return false;
4297  }
4298 
4299  for (int i = 0; i < count; i++)
4300  {
4301  if (!left[i].Equals(right[i]))
4302  {
4303  return false;
4304  }
4305  }
4306 
4307  return true;
4308  }
4309 
4310  /// <summary>
4311  /// Computes a deterministic hash code based on the items in the list. This hash code is dependent on the
4312  /// ordering of items.
4313  /// </summary>
4314  /// <typeparam name="T">The element type</typeparam>
4315  /// <param name="list">The list</param>
4316  /// <returns>A hash code dependent on the ordering of elements in the list</returns>
4317  public static int GetListHashCode<T>(this IReadOnlyList<T> list)
4318  {
4319  unchecked
4320  {
4321  var hashCode = 17;
4322  for (int i = 0; i < list.Count; i++)
4323  {
4324  hashCode += (hashCode * 397) ^ list[i].GetHashCode();
4325  }
4326 
4327  return hashCode;
4328  }
4329  }
4330 
4331  /// <summary>
4332  /// Determine if this SecurityType requires mapping
4333  /// </summary>
4334  /// <param name="symbol">Type to check</param>
4335  /// <returns>True if it needs to be mapped</returns>
4336  public static bool RequiresMapping(this Symbol symbol)
4337  {
4338  switch (symbol.SecurityType)
4339  {
4340  case SecurityType.Base:
4341  return symbol.HasUnderlying && symbol.Underlying.RequiresMapping();
4342  case SecurityType.Future:
4343  return symbol.IsCanonical();
4344  case SecurityType.Equity:
4345  case SecurityType.Option:
4346  return true;
4347  default:
4348  return false;
4349  }
4350  }
4351 
4352  /// <summary>
4353  /// Checks whether the fill event for closing a trade is a winning trade
4354  /// </summary>
4355  /// <param name="fill">The fill event</param>
4356  /// <param name="security">The security being traded</param>
4357  /// <param name="profitLoss">The profit-loss for the closed trade</param>
4358  /// <returns>
4359  /// Whether the trade is a win.
4360  /// For options assignments this depends on whether the option is ITM or OTM and the position side.
4361  /// See <see cref="Trade.IsWin"/> for more information.
4362  /// </returns>
4363  public static bool IsWin(this OrderEvent fill, Security security, decimal profitLoss)
4364  {
4365  // For non-options or non-exercise orders, the trade is a win if the profit-loss is positive
4366  if (!fill.Symbol.SecurityType.IsOption() || fill.Ticket.OrderType != OrderType.OptionExercise)
4367  {
4368  return profitLoss > 0;
4369  }
4370 
4371  var option = (Option)security;
4372 
4373  // If the fill is a sell, the original transaction was a buy
4374  if (fill.Direction == OrderDirection.Sell)
4375  {
4376  // If the option is ITM, the trade is a win only if the profit is greater than the ITM amount
4377  return fill.IsInTheMoney && Math.Abs(profitLoss) < option.InTheMoneyAmount(fill.FillQuantity);
4378  }
4379 
4380  // It is a win if the buyer paid more than what they saved (the ITM amount)
4381  return !fill.IsInTheMoney || Math.Abs(profitLoss) > option.InTheMoneyAmount(fill.FillQuantity);
4382  }
4383 
4384  /// <summary>
4385  /// Gets the option's ITM amount for the given quantity.
4386  /// </summary>
4387  /// <param name="option">The option security</param>
4388  /// <param name="quantity">The quantity</param>
4389  /// <returns>The ITM amount for the absolute quantity</returns>
4390  /// <remarks>The returned value can be negative, which would mean the option is actually OTM.</remarks>
4391  public static ConvertibleCashAmount InTheMoneyAmount(this Option option, decimal quantity)
4392  {
4393  return option.Holdings.GetQuantityValue(Math.Abs(quantity), option.GetPayOff(option.Underlying.Price));
4394  }
4395 
4396  /// <summary>
4397  /// Gets the greatest common divisor of a list of numbers
4398  /// </summary>
4399  /// <param name="values">List of numbers which greatest common divisor is requested</param>
4400  /// <returns>The greatest common divisor for the given list of numbers</returns>
4401  public static int GreatestCommonDivisor(this IEnumerable<int> values)
4402  {
4403  int? result = null;
4404  foreach (var value in values)
4405  {
4406  if (result.HasValue)
4407  {
4408  result = GreatestCommonDivisor(result.Value, value);
4409  }
4410  else
4411  {
4412  result = value;
4413  }
4414  }
4415 
4416  if (!result.HasValue)
4417  {
4418  throw new ArgumentException(Messages.Extensions.GreatestCommonDivisorEmptyList);
4419  }
4420 
4421  return result.Value;
4422  }
4423 
4424  /// <summary>
4425  /// Gets the greatest common divisor of two numbers
4426  /// </summary>
4427  private static int GreatestCommonDivisor(int a, int b)
4428  {
4429  int remainder;
4430  while (b != 0)
4431  {
4432  remainder = a % b;
4433  a = b;
4434  b = remainder;
4435  }
4436  return Math.Abs(a);
4437  }
4438 
4439  /// <summary>
4440  /// Safe method to perform divisions avoiding DivideByZeroException and Overflow/Underflow exceptions
4441  /// </summary>
4442  /// <param name="failValue">Value to be returned if the denominator is zero</param>
4443  /// <returns>The numerator divided by the denominator if the denominator is not
4444  /// zero. Otherwise, the default failValue or the provided one</returns>
4445  public static decimal SafeDivision(this decimal numerator, decimal denominator, decimal failValue = 0)
4446  {
4447  try
4448  {
4449  return (denominator == 0) ? failValue : (numerator / denominator);
4450  }
4451  catch
4452  {
4453  return failValue;
4454  }
4455  }
4456 
4457  /// <summary>
4458  /// Retrieve a common custom data types from the given symbols if any
4459  /// </summary>
4460  /// <param name="symbols">The target symbols to search</param>
4461  /// <returns>The custom data type or null</returns>
4462  public static Type GetCustomDataTypeFromSymbols(Symbol[] symbols)
4463  {
4464  if (symbols.Length != 0)
4465  {
4466  if (!SecurityIdentifier.TryGetCustomDataTypeInstance(symbols[0].ID.Symbol, out var dataType)
4467  || symbols.Any(x => !SecurityIdentifier.TryGetCustomDataTypeInstance(x.ID.Symbol, out var customDataType) || customDataType != dataType))
4468  {
4469  return null;
4470  }
4471  return dataType;
4472  }
4473 
4474  return null;
4475  }
4476 
4477  /// <summary>
4478  /// Determines if certain data type is custom
4479  /// </summary>
4480  /// <param name="symbol">Symbol associated with the data type</param>
4481  /// <param name="type">Data type to determine if it's custom</param>
4482  public static bool IsCustomDataType(Symbol symbol, Type type)
4483  {
4484  return type.Namespace != typeof(Bar).Namespace || Extensions.GetCustomDataTypeFromSymbols(new Symbol[] { symbol }) != null;
4485  }
4486 
4487  /// <summary>
4488  /// Returns the amount of fee's charged by executing a market order with the given arguments
4489  /// </summary>
4490  /// <param name="security">Security for which we would like to make a market order</param>
4491  /// <param name="quantity">Quantity of the security we are seeking to trade</param>
4492  /// <param name="time">Time the order was placed</param>
4493  /// <param name="marketOrder">This out parameter will contain the market order constructed</param>
4494  public static CashAmount GetMarketOrderFees(Security security, decimal quantity, DateTime time, out MarketOrder marketOrder)
4495  {
4496  marketOrder = new MarketOrder(security.Symbol, quantity, time);
4497  return security.FeeModel.GetOrderFee(new OrderFeeParameters(security, marketOrder)).Value;
4498  }
4499 
4500  private static Symbol ConvertToSymbol(PyObject item, bool dispose)
4501  {
4502  if (PyString.IsStringType(item))
4503  {
4504  return SymbolCache.GetSymbol(dispose ? item.GetAndDispose<string>() : item.As<string>());
4505  }
4506  else
4507  {
4508  Symbol symbol;
4509  try
4510  {
4511  symbol = dispose ? item.GetAndDispose<Symbol>() : item.As<Symbol>();
4512  }
4513  catch (Exception e)
4514  {
4515  throw new ArgumentException(Messages.Extensions.ConvertToSymbolEnumerableFailed(item), e);
4516  }
4517  return symbol;
4518  }
4519  }
4520  }
4521 }