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 type, the exchange and data time zones are set to the same value (exchange tz).
197  // This is not actual options data, just option 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 == typeof(OptionUniverse)))
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  using (var md5Hash = MD5.Create())
930  {
931  var data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(str));
932  for (var i = 0; i < 16; i++)
933  {
934  builder.Append(data[i].ToStringInvariant("x2"));
935  }
936  }
937  return builder.ToString();
938  }
939 
940  /// <summary>
941  /// Encrypt the token:time data to make our API hash.
942  /// </summary>
943  /// <param name="data">Data to be hashed by SHA256</param>
944  /// <returns>Hashed string.</returns>
945  public static string ToSHA256(this string data)
946  {
947  var hash = new StringBuilder(64);
948  using (var crypt = SHA256.Create())
949  {
950  var crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(data));
951  for (var i = 0; i < 32; i++)
952  {
953  hash.Append(crypto[i].ToStringInvariant("x2"));
954  }
955  }
956  return hash.ToString();
957  }
958 
959  /// <summary>
960  /// Converts a long to an uppercase alpha numeric string
961  /// </summary>
962  public static string EncodeBase36(this ulong data)
963  {
964  var stack = new Stack<char>(15);
965  while (data != 0)
966  {
967  var value = data % 36;
968  var c = value < 10
969  ? (char)(value + '0')
970  : (char)(value - 10 + 'A');
971 
972  stack.Push(c);
973  data /= 36;
974  }
975  return new string(stack.ToArray());
976  }
977 
978  /// <summary>
979  /// Converts an upper case alpha numeric string into a long
980  /// </summary>
981  public static ulong DecodeBase36(this string symbol)
982  {
983  var result = 0ul;
984  var baseValue = 1ul;
985  for (var i = symbol.Length - 1; i > -1; i--)
986  {
987  var c = symbol[i];
988 
989  // assumes alpha numeric upper case only strings
990  var value = (uint)(c <= 57
991  ? c - '0'
992  : c - 'A' + 10);
993 
994  result += baseValue * value;
995  baseValue *= 36;
996  }
997 
998  return result;
999  }
1000 
1001  /// <summary>
1002  /// Convert a string to Base64 Encoding
1003  /// </summary>
1004  /// <param name="text">Text to encode</param>
1005  /// <returns>Encoded result</returns>
1006  public static string EncodeBase64(this string text)
1007  {
1008  if (string.IsNullOrEmpty(text))
1009  {
1010  return text;
1011  }
1012 
1013  byte[] textBytes = Encoding.UTF8.GetBytes(text);
1014  return Convert.ToBase64String(textBytes);
1015  }
1016 
1017  /// <summary>
1018  /// Decode a Base64 Encoded string
1019  /// </summary>
1020  /// <param name="base64EncodedText">Text to decode</param>
1021  /// <returns>Decoded result</returns>
1022  public static string DecodeBase64(this string base64EncodedText)
1023  {
1024  if (string.IsNullOrEmpty(base64EncodedText))
1025  {
1026  return base64EncodedText;
1027  }
1028 
1029  byte[] base64EncodedBytes = Convert.FromBase64String(base64EncodedText);
1030  return Encoding.UTF8.GetString(base64EncodedBytes);
1031  }
1032 
1033  /// <summary>
1034  /// Lazy string to upper implementation.
1035  /// Will first verify the string is not already upper and avoid
1036  /// the call to <see cref="string.ToUpperInvariant()"/> if possible.
1037  /// </summary>
1038  /// <param name="data">The string to upper</param>
1039  /// <returns>The upper string</returns>
1040  public static string LazyToUpper(this string data)
1041  {
1042  // for performance only call to upper if required
1043  var alreadyUpper = true;
1044  for (int i = 0; i < data.Length && alreadyUpper; i++)
1045  {
1046  alreadyUpper = char.IsUpper(data[i]);
1047  }
1048  return alreadyUpper ? data : data.ToUpperInvariant();
1049  }
1050 
1051  /// <summary>
1052  /// Lazy string to lower implementation.
1053  /// Will first verify the string is not already lower and avoid
1054  /// the call to <see cref="string.ToLowerInvariant()"/> if possible.
1055  /// </summary>
1056  /// <param name="data">The string to lower</param>
1057  /// <returns>The lower string</returns>
1058  public static string LazyToLower(this string data)
1059  {
1060  // for performance only call to lower if required
1061  var alreadyLower = true;
1062  for (int i = 0; i < data.Length && alreadyLower; i++)
1063  {
1064  alreadyLower = char.IsLower(data[i]);
1065  }
1066  return alreadyLower ? data : data.ToLowerInvariant();
1067  }
1068 
1069  /// <summary>
1070  /// Extension method to automatically set the update value to same as "add" value for TryAddUpdate.
1071  /// This makes the API similar for traditional and concurrent dictionaries.
1072  /// </summary>
1073  /// <typeparam name="K">Key type for dictionary</typeparam>
1074  /// <typeparam name="V">Value type for dictonary</typeparam>
1075  /// <param name="dictionary">Dictionary object we're operating on</param>
1076  /// <param name="key">Key we want to add or update.</param>
1077  /// <param name="value">Value we want to set.</param>
1078  public static void AddOrUpdate<K, V>(this ConcurrentDictionary<K, V> dictionary, K key, V value)
1079  {
1080  dictionary.AddOrUpdate(key, value, (oldkey, oldvalue) => value);
1081  }
1082 
1083  /// <summary>
1084  /// Extension method to automatically add/update lazy values in concurrent dictionary.
1085  /// </summary>
1086  /// <typeparam name="TKey">Key type for dictionary</typeparam>
1087  /// <typeparam name="TValue">Value type for dictonary</typeparam>
1088  /// <param name="dictionary">Dictionary object we're operating on</param>
1089  /// <param name="key">Key we want to add or update.</param>
1090  /// <param name="addValueFactory">The function used to generate a value for an absent key</param>
1091  /// <param name="updateValueFactory">The function used to generate a new value for an existing key based on the key's existing value</param>
1092  public static TValue AddOrUpdate<TKey, TValue>(this ConcurrentDictionary<TKey, Lazy<TValue>> dictionary, TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
1093  {
1094  var result = dictionary.AddOrUpdate(key, new Lazy<TValue>(() => addValueFactory(key)), (key2, old) => new Lazy<TValue>(() => updateValueFactory(key2, old.Value)));
1095  return result.Value;
1096  }
1097 
1098  /// <summary>
1099  /// Adds the specified element to the collection with the specified key. If an entry does not exist for the
1100  /// specified key then one will be created.
1101  /// </summary>
1102  /// <typeparam name="TKey">The key type</typeparam>
1103  /// <typeparam name="TElement">The collection element type</typeparam>
1104  /// <typeparam name="TCollection">The collection type</typeparam>
1105  /// <param name="dictionary">The source dictionary to be added to</param>
1106  /// <param name="key">The key</param>
1107  /// <param name="element">The element to be added</param>
1108  public static void Add<TKey, TElement, TCollection>(this IDictionary<TKey, TCollection> dictionary, TKey key, TElement element)
1109  where TCollection : ICollection<TElement>, new()
1110  {
1111  TCollection list;
1112  if (!dictionary.TryGetValue(key, out list))
1113  {
1114  list = new TCollection();
1115  dictionary.Add(key, list);
1116  }
1117  list.Add(element);
1118  }
1119 
1120  /// <summary>
1121  /// Adds the specified element to the collection with the specified key. If an entry does not exist for the
1122  /// specified key then one will be created.
1123  /// </summary>
1124  /// <typeparam name="TKey">The key type</typeparam>
1125  /// <typeparam name="TElement">The collection element type</typeparam>
1126  /// <param name="dictionary">The source dictionary to be added to</param>
1127  /// <param name="key">The key</param>
1128  /// <param name="element">The element to be added</param>
1129  public static ImmutableDictionary<TKey, ImmutableHashSet<TElement>> Add<TKey, TElement>(
1130  this ImmutableDictionary<TKey, ImmutableHashSet<TElement>> dictionary,
1131  TKey key,
1132  TElement element
1133  )
1134  {
1135  ImmutableHashSet<TElement> set;
1136  if (!dictionary.TryGetValue(key, out set))
1137  {
1138  set = ImmutableHashSet<TElement>.Empty.Add(element);
1139  return dictionary.Add(key, set);
1140  }
1141 
1142  return dictionary.SetItem(key, set.Add(element));
1143  }
1144 
1145  /// <summary>
1146  /// Adds the specified element to the collection with the specified key. If an entry does not exist for the
1147  /// specified key then one will be created.
1148  /// </summary>
1149  /// <typeparam name="TKey">The key type</typeparam>
1150  /// <typeparam name="TElement">The collection element type</typeparam>
1151  /// <param name="dictionary">The source dictionary to be added to</param>
1152  /// <param name="key">The key</param>
1153  /// <param name="element">The element to be added</param>
1154  public static ImmutableSortedDictionary<TKey, ImmutableHashSet<TElement>> Add<TKey, TElement>(
1155  this ImmutableSortedDictionary<TKey, ImmutableHashSet<TElement>> dictionary,
1156  TKey key,
1157  TElement element
1158  )
1159  {
1160  ImmutableHashSet<TElement> set;
1161  if (!dictionary.TryGetValue(key, out set))
1162  {
1163  set = ImmutableHashSet<TElement>.Empty.Add(element);
1164  return dictionary.Add(key, set);
1165  }
1166 
1167  return dictionary.SetItem(key, set.Add(element));
1168  }
1169 
1170  /// <summary>
1171  /// Adds the specified Tick to the Ticks collection. If an entry does not exist for the specified key then one will be created.
1172  /// </summary>
1173  /// <param name="dictionary">The ticks dictionary</param>
1174  /// <param name="key">The symbol</param>
1175  /// <param name="tick">The tick to add</param>
1176  /// <remarks>For performance we implement this method based on <see cref="Add{TKey,TElement,TCollection}"/></remarks>
1177  public static void Add(this Ticks dictionary, Symbol key, Tick tick)
1178  {
1179  List<Tick> list;
1180  if (!dictionary.TryGetValue(key, out list))
1181  {
1182  dictionary[key] = list = new List<Tick>(1);
1183  }
1184  list.Add(tick);
1185  }
1186 
1187  /// <summary>
1188  /// Extension method to round a double value to a fixed number of significant figures instead of a fixed decimal places.
1189  /// </summary>
1190  /// <param name="d">Double we're rounding</param>
1191  /// <param name="digits">Number of significant figures</param>
1192  /// <returns>New double rounded to digits-significant figures</returns>
1193  public static decimal RoundToSignificantDigits(this decimal d, int digits)
1194  {
1195  if (d == 0) return 0;
1196  var scale = (decimal)Math.Pow(10, Math.Floor(Math.Log10((double) Math.Abs(d))) + 1);
1197  return scale * Math.Round(d / scale, digits);
1198  }
1199 
1200  /// <summary>
1201  /// Converts a decimal into a rounded number ending with K (thousands), M (millions), B (billions), etc.
1202  /// </summary>
1203  /// <param name="number">Number to convert</param>
1204  /// <returns>Formatted number with figures written in shorthand form</returns>
1205  public static string ToFinancialFigures(this decimal number)
1206  {
1207  if (number < 1000)
1208  {
1209  return number.ToStringInvariant();
1210  }
1211 
1212  // Subtract by multiples of 5 to round down to nearest round number
1213  if (number < 10000)
1214  {
1215  return (number - 5m).ToString("#,.##", CultureInfo.InvariantCulture) + "K";
1216  }
1217 
1218  if (number < 100000)
1219  {
1220  return (number - 50m).ToString("#,.#", CultureInfo.InvariantCulture) + "K";
1221  }
1222 
1223  if (number < 1000000)
1224  {
1225  return (number - 500m).ToString("#,.", CultureInfo.InvariantCulture) + "K";
1226  }
1227 
1228  if (number < 10000000)
1229  {
1230  return (number - 5000m).ToString("#,,.##", CultureInfo.InvariantCulture) + "M";
1231  }
1232 
1233  if (number < 100000000)
1234  {
1235  return (number - 50000m).ToString("#,,.#", CultureInfo.InvariantCulture) + "M";
1236  }
1237 
1238  if (number < 1000000000)
1239  {
1240  return (number - 500000m).ToString("#,,.", CultureInfo.InvariantCulture) + "M";
1241  }
1242 
1243  return (number - 5000000m).ToString("#,,,.##", CultureInfo.InvariantCulture) + "B";
1244  }
1245 
1246  /// <summary>
1247  /// Discretizes the <paramref name="value"/> to a maximum precision specified by <paramref name="quanta"/>. Quanta
1248  /// can be an arbitrary positive number and represents the step size. Consider a quanta equal to 0.15 and rounding
1249  /// 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.
1250  /// </summary>
1251  /// <param name="value">The value to be rounded by discretization</param>
1252  /// <param name="quanta">The maximum precision allowed by the value</param>
1253  /// <param name="mode">Specifies how to handle the rounding of half value, defaulting to away from zero.</param>
1254  /// <returns></returns>
1255  public static decimal DiscretelyRoundBy(this decimal value, decimal quanta, MidpointRounding mode = MidpointRounding.AwayFromZero)
1256  {
1257  if (quanta == 0m)
1258  {
1259  return value;
1260  }
1261 
1262  // away from zero is the 'common sense' rounding.
1263  // +0.5 rounded by 1 yields +1
1264  // -0.5 rounded by 1 yields -1
1265  var multiplicand = Math.Round(value / quanta, mode);
1266  return quanta * multiplicand;
1267  }
1268 
1269  /// <summary>
1270  /// Will truncate the provided decimal, without rounding, to 3 decimal places
1271  /// </summary>
1272  /// <param name="value">The value to truncate</param>
1273  /// <returns>New instance with just 3 decimal places</returns>
1274  public static decimal TruncateTo3DecimalPlaces(this decimal value)
1275  {
1276  // we will multiply by 1k bellow, if its bigger it will stack overflow
1277  if (value >= decimal.MaxValue / 1000
1278  || value <= decimal.MinValue / 1000
1279  || value == 0)
1280  {
1281  return value;
1282  }
1283 
1284  return Math.Truncate(1000 * value) / 1000;
1285  }
1286 
1287  /// <summary>
1288  /// Provides global smart rounding, numbers larger than 1000 will round to 4 decimal places,
1289  /// while numbers smaller will round to 7 significant digits
1290  /// </summary>
1291  public static decimal? SmartRounding(this decimal? input)
1292  {
1293  if (!input.HasValue)
1294  {
1295  return null;
1296  }
1297  return input.Value.SmartRounding();
1298  }
1299 
1300  /// <summary>
1301  /// Provides global smart rounding, numbers larger than 1000 will round to 4 decimal places,
1302  /// while numbers smaller will round to 7 significant digits
1303  /// </summary>
1304  public static decimal SmartRounding(this decimal input)
1305  {
1306  input = Normalize(input);
1307 
1308  // any larger numbers we still want some decimal places
1309  if (input > 1000)
1310  {
1311  return Math.Round(input, 4);
1312  }
1313 
1314  // this is good for forex and other small numbers
1315  return input.RoundToSignificantDigits(7).Normalize();
1316  }
1317 
1318  /// <summary>
1319  /// Casts the specified input value to a decimal while acknowledging the overflow conditions
1320  /// </summary>
1321  /// <param name="input">The value to be cast</param>
1322  /// <returns>The input value as a decimal, if the value is too large or to small to be represented
1323  /// as a decimal, then the closest decimal value will be returned</returns>
1324  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1325  public static decimal SafeDecimalCast(this double input)
1326  {
1327  if (input.IsNaNOrInfinity())
1328  {
1329  throw new ArgumentException(
1331  nameof(input),
1332  new NotFiniteNumberException(input)
1333  );
1334  }
1335 
1336  if (input <= (double) decimal.MinValue) return decimal.MinValue;
1337  if (input >= (double) decimal.MaxValue) return decimal.MaxValue;
1338  return (decimal) input;
1339  }
1340 
1341  /// <summary>
1342  /// Will remove any trailing zeros for the provided decimal input
1343  /// </summary>
1344  /// <param name="input">The <see cref="decimal"/> to remove trailing zeros from</param>
1345  /// <returns>Provided input with no trailing zeros</returns>
1346  /// <remarks>Will not have the expected behavior when called from Python,
1347  /// since the returned <see cref="decimal"/> will be converted to python float,
1348  /// <see cref="NormalizeToStr"/></remarks>
1349  public static decimal Normalize(this decimal input)
1350  {
1351  // http://stackoverflow.com/a/7983330/1582922
1352  return input / 1.000000000000000000000000000000000m;
1353  }
1354 
1355  /// <summary>
1356  /// Will remove any trailing zeros for the provided decimal and convert to string.
1357  /// Uses <see cref="Normalize(decimal)"/>.
1358  /// </summary>
1359  /// <param name="input">The <see cref="decimal"/> to convert to <see cref="string"/></param>
1360  /// <returns>Input converted to <see cref="string"/> with no trailing zeros</returns>
1361  public static string NormalizeToStr(this decimal input)
1362  {
1363  return Normalize(input).ToString(CultureInfo.InvariantCulture);
1364  }
1365 
1366  /// <summary>
1367  /// Helper method to determine the amount of decimal places associated with the given decimal
1368  /// </summary>
1369  /// <param name="input">The value to get the decimal count from</param>
1370  /// <returns>The quantity of decimal places</returns>
1371  public static int GetDecimalPlaces(this decimal input)
1372  {
1373  return BitConverter.GetBytes(decimal.GetBits(input)[3])[2];
1374  }
1375 
1376  /// <summary>
1377  /// Extension method for faster string to decimal conversion.
1378  /// </summary>
1379  /// <param name="str">String to be converted to positive decimal value</param>
1380  /// <remarks>
1381  /// Leading and trailing whitespace chars are ignored
1382  /// </remarks>
1383  /// <returns>Decimal value of the string</returns>
1384  public static decimal ToDecimal(this string str)
1385  {
1386  long value = 0;
1387  var decimalPlaces = 0;
1388  var hasDecimals = false;
1389  var index = 0;
1390  var length = str.Length;
1391 
1392  while (index < length && char.IsWhiteSpace(str[index]))
1393  {
1394  index++;
1395  }
1396 
1397  var isNegative = index < length && str[index] == '-';
1398  if (isNegative)
1399  {
1400  index++;
1401  }
1402 
1403  while (index < length)
1404  {
1405  var ch = str[index++];
1406  if (ch == '.')
1407  {
1408  hasDecimals = true;
1409  decimalPlaces = 0;
1410  }
1411  else if (char.IsWhiteSpace(ch))
1412  {
1413  break;
1414  }
1415  else
1416  {
1417  value = value * 10 + (ch - '0');
1418  decimalPlaces++;
1419  }
1420  }
1421 
1422  var lo = (int)value;
1423  var mid = (int)(value >> 32);
1424  return new decimal(lo, mid, 0, isNegative, (byte)(hasDecimals ? decimalPlaces : 0));
1425  }
1426 
1427  /// <summary>
1428  /// Extension method for faster string to normalized decimal conversion, i.e. 20.0% should be parsed into 0.2
1429  /// </summary>
1430  /// <param name="str">String to be converted to positive decimal value</param>
1431  /// <remarks>
1432  /// Leading and trailing whitespace chars are ignored
1433  /// </remarks>
1434  /// <returns>Decimal value of the string</returns>
1435  public static decimal ToNormalizedDecimal(this string str)
1436  {
1437  var trimmed = str.Trim();
1438  var value = str.TrimEnd('%').ToDecimal();
1439  if (trimmed.EndsWith("%"))
1440  {
1441  value /= 100;
1442  }
1443 
1444  return value;
1445  }
1446 
1447  /// <summary>
1448  /// Extension method for string to decimal conversion where string can represent a number with exponent xe-y
1449  /// </summary>
1450  /// <param name="str">String to be converted to decimal value</param>
1451  /// <returns>Decimal value of the string</returns>
1452  public static decimal ToDecimalAllowExponent(this string str)
1453  {
1454  return decimal.Parse(str, NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture);
1455  }
1456 
1457  /// <summary>
1458  /// Extension method for faster string to Int32 conversion.
1459  /// </summary>
1460  /// <param name="str">String to be converted to positive Int32 value</param>
1461  /// <remarks>Method makes some assuptions - always numbers, no "signs" +,- etc.</remarks>
1462  /// <returns>Int32 value of the string</returns>
1463  public static int ToInt32(this string str)
1464  {
1465  int value = 0;
1466  for (var i = 0; i < str.Length; i++)
1467  {
1468  if (str[i] == '.')
1469  break;
1470 
1471  value = value * 10 + (str[i] - '0');
1472  }
1473  return value;
1474  }
1475 
1476  /// <summary>
1477  /// Extension method for faster string to Int64 conversion.
1478  /// </summary>
1479  /// <param name="str">String to be converted to positive Int64 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 long ToInt64(this string str)
1483  {
1484  long 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  /// Helper method to determine if a data type implements the Stream reader method
1497  /// </summary>
1498  public static bool ImplementsStreamReader(this Type baseDataType)
1499  {
1500  // we know these type implement the streamReader interface lets avoid dynamic reflection call to figure it out
1501  if (baseDataType == typeof(TradeBar) || baseDataType == typeof(QuoteBar) || baseDataType == typeof(Tick))
1502  {
1503  return true;
1504  }
1505 
1506  var method = baseDataType.GetMethod("Reader",
1507  new[] { typeof(SubscriptionDataConfig), typeof(StreamReader), typeof(DateTime), typeof(bool) });
1508  if (method != null && method.DeclaringType == baseDataType)
1509  {
1510  return true;
1511  }
1512  return false;
1513  }
1514 
1515  /// <summary>
1516  /// Breaks the specified string into csv components, all commas are considered separators
1517  /// </summary>
1518  /// <param name="str">The string to be broken into csv</param>
1519  /// <param name="size">The expected size of the output list</param>
1520  /// <returns>A list of the csv pieces</returns>
1521  public static List<string> ToCsv(this string str, int size = 4)
1522  {
1523  int last = 0;
1524  var csv = new List<string>(size);
1525  for (int i = 0; i < str.Length; i++)
1526  {
1527  if (str[i] == ',')
1528  {
1529  if (last != 0) last = last + 1;
1530  csv.Add(str.Substring(last, i - last));
1531  last = i;
1532  }
1533  }
1534  if (last != 0) last = last + 1;
1535  csv.Add(str.Substring(last));
1536  return csv;
1537  }
1538 
1539  /// <summary>
1540  /// Breaks the specified string into csv components, works correctly with commas in data fields
1541  /// </summary>
1542  /// <param name="str">The string to be broken into csv</param>
1543  /// <param name="size">The expected size of the output list</param>
1544  /// <param name="delimiter">The delimiter used to separate entries in the line</param>
1545  /// <returns>A list of the csv pieces</returns>
1546  public static List<string> ToCsvData(this string str, int size = 4, char delimiter = ',')
1547  {
1548  var csv = new List<string>(size);
1549 
1550  var last = -1;
1551  var count = 0;
1552  var textDataField = false;
1553 
1554  for (var i = 0; i < str.Length; i++)
1555  {
1556  var current = str[i];
1557  if (current == '"')
1558  {
1559  textDataField = !textDataField;
1560  }
1561  else if (!textDataField && current == delimiter)
1562  {
1563  csv.Add(str.Substring(last + 1, (i - last)).Trim(' ', ','));
1564  last = i;
1565  count++;
1566  }
1567  }
1568 
1569  if (last != 0)
1570  {
1571  csv.Add(str.Substring(last + 1).Trim());
1572  }
1573 
1574  return csv;
1575  }
1576 
1577  /// <summary>
1578  /// Gets the value at the specified index from a CSV line.
1579  /// </summary>
1580  /// <param name="csvLine">The CSV line</param>
1581  /// <param name="index">The index of the value to be extracted from the CSV line</param>
1582  /// <param name="result">The value at the given index</param>
1583  /// <returns>Whether there was a value at the given index and could be extracted</returns>
1584  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1585  public static bool TryGetFromCsv(this string csvLine, int index, out ReadOnlySpan<char> result)
1586  {
1587  result = ReadOnlySpan<char>.Empty;
1588  if (string.IsNullOrEmpty(csvLine) || index < 0)
1589  {
1590  return false;
1591  }
1592 
1593  var span = csvLine.AsSpan();
1594  for (int i = 0; i < index; i++)
1595  {
1596  var commaIndex = span.IndexOf(',');
1597  if (commaIndex == -1)
1598  {
1599  return false;
1600  }
1601  span = span.Slice(commaIndex + 1);
1602  }
1603 
1604  var nextCommaIndex = span.IndexOf(',');
1605  if (nextCommaIndex == -1)
1606  {
1607  nextCommaIndex = span.Length;
1608  }
1609 
1610  result = span.Slice(0, nextCommaIndex);
1611  return true;
1612  }
1613 
1614  /// <summary>
1615  /// Gets the value at the specified index from a CSV line, converted into a decimal.
1616  /// </summary>
1617  /// <param name="csvLine">The CSV line</param>
1618  /// <param name="index">The index of the value to be extracted from the CSV line</param>
1619  /// <param name="value">The decimal value at the given index</param>
1620  /// <returns>Whether there was a value at the given index and could be extracted and converted into a decimal</returns>
1621  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1622  public static bool TryGetDecimalFromCsv(this string csvLine, int index, out decimal value)
1623  {
1624  value = decimal.Zero;
1625  if (!csvLine.TryGetFromCsv(index, out var csvValue))
1626  {
1627  return false;
1628  }
1629 
1630  return decimal.TryParse(csvValue, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
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  /// <returns>The decimal value at the given index. If the index is invalid or conversion fails, it will return zero</returns>
1639  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1640  public static decimal GetDecimalFromCsv(this string csvLine, int index)
1641  {
1642  csvLine.TryGetDecimalFromCsv(index, out var value);
1643  return value;
1644  }
1645 
1646  /// <summary>
1647  /// Check if a number is NaN or infinity
1648  /// </summary>
1649  /// <param name="value">The double value to check</param>
1650  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1651  public static bool IsNaNOrInfinity(this double value)
1652  {
1653  return double.IsNaN(value) || double.IsInfinity(value);
1654  }
1655 
1656  /// <summary>
1657  /// Check if a number is NaN or equal to zero
1658  /// </summary>
1659  /// <param name="value">The double value to check</param>
1660  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1661  public static bool IsNaNOrZero(this double value)
1662  {
1663  return double.IsNaN(value) || Math.Abs(value) < double.Epsilon;
1664  }
1665 
1666  /// <summary>
1667  /// Gets the smallest positive number that can be added to a decimal instance and return
1668  /// a new value that does not == the old value
1669  /// </summary>
1670  public static decimal GetDecimalEpsilon()
1671  {
1672  return new decimal(1, 0, 0, false, 27); //1e-27m;
1673  }
1674 
1675  /// <summary>
1676  /// 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.
1677  /// </summary>
1678  /// <param name="str">String we're looking for the extension for.</param>
1679  /// <returns>Last 4 character string of string.</returns>
1680  public static string GetExtension(this string str) {
1681  var ext = str.Substring(Math.Max(0, str.Length - 4));
1682  var allowedExt = new List<string> { ".zip", ".csv", ".json", ".tsv" };
1683  if (!allowedExt.Contains(ext))
1684  {
1685  ext = ".custom";
1686  }
1687  return ext;
1688  }
1689 
1690  /// <summary>
1691  /// Extension method to convert strings to stream to be read.
1692  /// </summary>
1693  /// <param name="str">String to convert to stream</param>
1694  /// <returns>Stream instance</returns>
1695  public static Stream ToStream(this string str)
1696  {
1697  var stream = new MemoryStream();
1698  var writer = new StreamWriter(stream);
1699  writer.Write(str);
1700  writer.Flush();
1701  stream.Position = 0;
1702  return stream;
1703  }
1704 
1705  /// <summary>
1706  /// Extension method to round a timeSpan to nearest timespan period.
1707  /// </summary>
1708  /// <param name="time">TimeSpan To Round</param>
1709  /// <param name="roundingInterval">Rounding Unit</param>
1710  /// <param name="roundingType">Rounding method</param>
1711  /// <returns>Rounded timespan</returns>
1712  public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval, MidpointRounding roundingType)
1713  {
1714  if (roundingInterval == TimeSpan.Zero)
1715  {
1716  // divide by zero exception
1717  return time;
1718  }
1719 
1720  return new TimeSpan(
1721  Convert.ToInt64(Math.Round(
1722  time.Ticks / (decimal)roundingInterval.Ticks,
1723  roundingType
1724  )) * roundingInterval.Ticks
1725  );
1726  }
1727 
1728 
1729  /// <summary>
1730  /// Extension method to round timespan to nearest timespan period.
1731  /// </summary>
1732  /// <param name="time">Base timespan we're looking to round.</param>
1733  /// <param name="roundingInterval">Timespan period we're rounding.</param>
1734  /// <returns>Rounded timespan period</returns>
1735  public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval)
1736  {
1737  return Round(time, roundingInterval, MidpointRounding.ToEven);
1738  }
1739 
1740  /// <summary>
1741  /// Extension method to round a datetime down by a timespan interval.
1742  /// </summary>
1743  /// <param name="dateTime">Base DateTime object we're rounding down.</param>
1744  /// <param name="interval">Timespan interval to round to</param>
1745  /// <returns>Rounded datetime</returns>
1746  /// <remarks>Using this with timespans greater than 1 day may have unintended
1747  /// consequences. Be aware that rounding occurs against ALL time, so when using
1748  /// timespan such as 30 days we will see 30 day increments but it will be based
1749  /// on 30 day increments from the beginning of time.</remarks>
1750  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1751  public static DateTime RoundDown(this DateTime dateTime, TimeSpan interval)
1752  {
1753  if (interval == TimeSpan.Zero)
1754  {
1755  // divide by zero exception
1756  return dateTime;
1757  }
1758 
1759  var amount = dateTime.Ticks % interval.Ticks;
1760  if (amount > 0)
1761  {
1762  return dateTime.AddTicks(-amount);
1763  }
1764  return dateTime;
1765  }
1766 
1767  /// <summary>
1768  /// Rounds the specified date time in the specified time zone. Careful with calling this method in a loop while modifying dateTime, check unit tests.
1769  /// </summary>
1770  /// <param name="dateTime">Date time to be rounded</param>
1771  /// <param name="roundingInterval">Timespan rounding period</param>
1772  /// <param name="sourceTimeZone">Time zone of the date time</param>
1773  /// <param name="roundingTimeZone">Time zone in which the rounding is performed</param>
1774  /// <returns>The rounded date time in the source time zone</returns>
1775  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1776  public static DateTime RoundDownInTimeZone(this DateTime dateTime, TimeSpan roundingInterval, DateTimeZone sourceTimeZone, DateTimeZone roundingTimeZone)
1777  {
1778  var dateTimeInRoundingTimeZone = dateTime.ConvertTo(sourceTimeZone, roundingTimeZone);
1779  var roundedDateTimeInRoundingTimeZone = dateTimeInRoundingTimeZone.RoundDown(roundingInterval);
1780  return roundedDateTimeInRoundingTimeZone.ConvertTo(roundingTimeZone, sourceTimeZone);
1781  }
1782 
1783  /// <summary>
1784  /// Extension method to round a datetime down by a timespan interval until it's
1785  /// within the specified exchange's open hours. This works by first rounding down
1786  /// the specified time using the interval, then producing a bar between that
1787  /// rounded time and the interval plus the rounded time and incrementally walking
1788  /// backwards until the exchange is open
1789  /// </summary>
1790  /// <param name="dateTime">Time to be rounded down</param>
1791  /// <param name="interval">Timespan interval to round to.</param>
1792  /// <param name="exchangeHours">The exchange hours to determine open times</param>
1793  /// <param name="extendedMarketHours">True for extended market hours, otherwise false</param>
1794  /// <returns>Rounded datetime</returns>
1795  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1796  public static DateTime ExchangeRoundDown(this DateTime dateTime, TimeSpan interval, SecurityExchangeHours exchangeHours, bool extendedMarketHours)
1797  {
1798  // can't round against a zero interval
1799  if (interval == TimeSpan.Zero) return dateTime;
1800 
1801  var rounded = dateTime.RoundDown(interval);
1802  while (!exchangeHours.IsOpen(rounded, rounded + interval, extendedMarketHours))
1803  {
1804  rounded -= interval;
1805  }
1806  return rounded;
1807  }
1808 
1809  /// <summary>
1810  /// Extension method to round a datetime down by a timespan interval until it's
1811  /// within the specified exchange's open hours. The rounding is performed in the
1812  /// specified time zone
1813  /// </summary>
1814  /// <param name="dateTime">Time to be rounded down</param>
1815  /// <param name="interval">Timespan interval to round to.</param>
1816  /// <param name="exchangeHours">The exchange hours to determine open times</param>
1817  /// <param name="roundingTimeZone">The time zone to perform the rounding in</param>
1818  /// <param name="extendedMarketHours">True for extended market hours, otherwise false</param>
1819  /// <returns>Rounded datetime</returns>
1820  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1821  public static DateTime ExchangeRoundDownInTimeZone(this DateTime dateTime, TimeSpan interval, SecurityExchangeHours exchangeHours, DateTimeZone roundingTimeZone, bool extendedMarketHours)
1822  {
1823  // can't round against a zero interval
1824  if (interval == TimeSpan.Zero) return dateTime;
1825 
1826  var dateTimeInRoundingTimeZone = dateTime.ConvertTo(exchangeHours.TimeZone, roundingTimeZone);
1827  var roundedDateTimeInRoundingTimeZone = dateTimeInRoundingTimeZone.RoundDown(interval);
1828  var rounded = roundedDateTimeInRoundingTimeZone.ConvertTo(roundingTimeZone, exchangeHours.TimeZone);
1829 
1830  while (!exchangeHours.IsOpen(rounded, rounded + interval, extendedMarketHours))
1831  {
1832  // Will subtract interval to 'dateTime' in the roundingTimeZone (using the same value type instance) to avoid issues with daylight saving time changes.
1833  // GH issue 2368: subtracting interval to 'dateTime' in exchangeHours.TimeZone and converting back to roundingTimeZone
1834  // caused the substraction to be neutralized by daylight saving time change, which caused an infinite loop situation in this loop.
1835  // The issue also happens if substracting in roundingTimeZone and converting back to exchangeHours.TimeZone.
1836 
1837  dateTimeInRoundingTimeZone -= interval;
1838  roundedDateTimeInRoundingTimeZone = dateTimeInRoundingTimeZone.RoundDown(interval);
1839  rounded = roundedDateTimeInRoundingTimeZone.ConvertTo(roundingTimeZone, exchangeHours.TimeZone);
1840  }
1841  return rounded;
1842  }
1843 
1844  /// <summary>
1845  /// Helper method to determine if a specific market is open
1846  /// </summary>
1847  /// <param name="security">The target security</param>
1848  /// <param name="extendedMarketHours">True if should consider extended market hours</param>
1849  /// <returns>True if the market is open</returns>
1850  public static bool IsMarketOpen(this Security security, bool extendedMarketHours)
1851  {
1852  return security.Exchange.Hours.IsOpen(security.LocalTime, extendedMarketHours);
1853  }
1854 
1855  /// <summary>
1856  /// Helper method to determine if a specific market is open
1857  /// </summary>
1858  /// <param name="symbol">The target symbol</param>
1859  /// <param name="utcTime">The current UTC time</param>
1860  /// <param name="extendedMarketHours">True if should consider extended market hours</param>
1861  /// <returns>True if the market is open</returns>
1862  public static bool IsMarketOpen(this Symbol symbol, DateTime utcTime, bool extendedMarketHours)
1863  {
1864  var exchangeHours = MarketHoursDatabase.FromDataFolder()
1865  .GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType);
1866 
1867  var time = utcTime.ConvertFromUtc(exchangeHours.TimeZone);
1868 
1869  return exchangeHours.IsOpen(time, extendedMarketHours);
1870  }
1871 
1872  /// <summary>
1873  /// Extension method to round a datetime to the nearest unit timespan.
1874  /// </summary>
1875  /// <param name="datetime">Datetime object we're rounding.</param>
1876  /// <param name="roundingInterval">Timespan rounding period.</param>
1877  /// <returns>Rounded datetime</returns>
1878  public static DateTime Round(this DateTime datetime, TimeSpan roundingInterval)
1879  {
1880  return new DateTime((datetime - DateTime.MinValue).Round(roundingInterval).Ticks);
1881  }
1882 
1883  /// <summary>
1884  /// Extension method to explicitly round up to the nearest timespan interval.
1885  /// </summary>
1886  /// <param name="time">Base datetime object to round up.</param>
1887  /// <param name="interval">Timespan interval to round to</param>
1888  /// <returns>Rounded datetime</returns>
1889  /// <remarks>Using this with timespans greater than 1 day may have unintended
1890  /// consequences. Be aware that rounding occurs against ALL time, so when using
1891  /// timespan such as 30 days we will see 30 day increments but it will be based
1892  /// on 30 day increments from the beginning of time.</remarks>
1893  public static DateTime RoundUp(this DateTime time, TimeSpan interval)
1894  {
1895  if (interval == TimeSpan.Zero)
1896  {
1897  // divide by zero exception
1898  return time;
1899  }
1900 
1901  return new DateTime(((time.Ticks + interval.Ticks - 1) / interval.Ticks) * interval.Ticks);
1902  }
1903 
1904  /// <summary>
1905  /// Converts the specified time from the <paramref name="from"/> time zone to the <paramref name="to"/> time zone
1906  /// </summary>
1907  /// <param name="time">The time to be converted in terms of the <paramref name="from"/> time zone</param>
1908  /// <param name="from">The time zone the specified <paramref name="time"/> is in</param>
1909  /// <param name="to">The time zone to be converted to</param>
1910  /// <param name="strict">True for strict conversion, this will throw during ambiguitities, false for lenient conversion</param>
1911  /// <returns>The time in terms of the to time zone</returns>
1912  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1913  public static DateTime ConvertTo(this DateTime time, DateTimeZone from, DateTimeZone to, bool strict = false)
1914  {
1915  if (strict)
1916  {
1917  return from.AtStrictly(LocalDateTime.FromDateTime(time)).WithZone(to).ToDateTimeUnspecified();
1918  }
1919 
1920  // `InZone` sets the LocalDateTime's timezone, `WithZone` is the tz the time will be converted into.
1921  return LocalDateTime.FromDateTime(time)
1922  .InZone(from, _mappingResolver)
1923  .WithZone(to)
1924  .ToDateTimeUnspecified();
1925  }
1926 
1927  /// <summary>
1928  /// Converts the specified time from UTC to the <paramref name="to"/> time zone
1929  /// </summary>
1930  /// <param name="time">The time to be converted expressed in UTC</param>
1931  /// <param name="to">The destinatio time zone</param>
1932  /// <param name="strict">True for strict conversion, this will throw during ambiguitities, false for lenient conversion</param>
1933  /// <returns>The time in terms of the <paramref name="to"/> time zone</returns>
1934  public static DateTime ConvertFromUtc(this DateTime time, DateTimeZone to, bool strict = false)
1935  {
1936  return time.ConvertTo(TimeZones.Utc, to, strict);
1937  }
1938 
1939  /// <summary>
1940  /// Converts the specified time from the <paramref name="from"/> time zone to <see cref="TimeZones.Utc"/>
1941  /// </summary>
1942  /// <param name="time">The time to be converted in terms of the <paramref name="from"/> time zone</param>
1943  /// <param name="from">The time zone the specified <paramref name="time"/> is in</param>
1944  /// <param name="strict">True for strict conversion, this will throw during ambiguitities, false for lenient conversion</param>
1945  /// <returns>The time in terms of the to time zone</returns>
1946  [MethodImpl(MethodImplOptions.AggressiveInlining)]
1947  public static DateTime ConvertToUtc(this DateTime time, DateTimeZone from, bool strict = false)
1948  {
1949  if (strict)
1950  {
1951  return from.AtStrictly(LocalDateTime.FromDateTime(time)).ToDateTimeUtc();
1952  }
1953 
1954  // Set the local timezone with `InZone` and convert to UTC
1955  return LocalDateTime.FromDateTime(time)
1956  .InZone(from, _mappingResolver)
1957  .ToDateTimeUtc();
1958  }
1959 
1960  /// <summary>
1961  /// Business day here is defined as any day of the week that is not saturday or sunday
1962  /// </summary>
1963  /// <param name="date">The date to be examined</param>
1964  /// <returns>A bool indicating wether the datetime is a weekday or not</returns>
1965  public static bool IsCommonBusinessDay(this DateTime date)
1966  {
1967  return (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday);
1968  }
1969 
1970  /// <summary>
1971  /// Add the reset method to the System.Timer class.
1972  /// </summary>
1973  /// <param name="timer">System.timer object</param>
1974  public static void Reset(this Timer timer)
1975  {
1976  timer.Stop();
1977  timer.Start();
1978  }
1979 
1980  /// <summary>
1981  /// Function used to match a type against a string type name. This function compares on the AssemblyQualfiedName,
1982  /// the FullName, and then just the Name of the type.
1983  /// </summary>
1984  /// <param name="type">The type to test for a match</param>
1985  /// <param name="typeName">The name of the type to match</param>
1986  /// <returns>True if the specified type matches the type name, false otherwise</returns>
1987  public static bool MatchesTypeName(this Type type, string typeName)
1988  {
1989  if (type.AssemblyQualifiedName == typeName)
1990  {
1991  return true;
1992  }
1993  if (type.FullName == typeName)
1994  {
1995  return true;
1996  }
1997  if (type.Name == typeName)
1998  {
1999  return true;
2000  }
2001  return false;
2002  }
2003 
2004  /// <summary>
2005  /// Checks the specified type to see if it is a subclass of the <paramref name="possibleSuperType"/>. This method will
2006  /// crawl up the inheritance heirarchy to check for equality using generic type definitions (if exists)
2007  /// </summary>
2008  /// <param name="type">The type to be checked as a subclass of <paramref name="possibleSuperType"/></param>
2009  /// <param name="possibleSuperType">The possible superclass of <paramref name="type"/></param>
2010  /// <returns>True if <paramref name="type"/> is a subclass of the generic type definition <paramref name="possibleSuperType"/></returns>
2011  public static bool IsSubclassOfGeneric(this Type type, Type possibleSuperType)
2012  {
2013  while (type != null && type != typeof(object))
2014  {
2015  Type cur;
2016  if (type.IsGenericType && possibleSuperType.IsGenericTypeDefinition)
2017  {
2018  cur = type.GetGenericTypeDefinition();
2019  }
2020  else
2021  {
2022  cur = type;
2023  }
2024  if (possibleSuperType == cur)
2025  {
2026  return true;
2027  }
2028  type = type.BaseType;
2029  }
2030  return false;
2031  }
2032 
2033  /// <summary>
2034  /// Gets a type's name with the generic parameters filled in the way they would look when
2035  /// defined in code, such as converting Dictionary&lt;`1,`2&gt; to Dictionary&lt;string,int&gt;
2036  /// </summary>
2037  /// <param name="type">The type who's name we seek</param>
2038  /// <returns>A better type name</returns>
2039  public static string GetBetterTypeName(this Type type)
2040  {
2041  string name = type.Name;
2042  if (type.IsGenericType)
2043  {
2044  var genericArguments = type.GetGenericArguments();
2045  var toBeReplaced = "`" + (genericArguments.Length);
2046  name = name.Replace(toBeReplaced, $"<{string.Join(", ", genericArguments.Select(x => x.GetBetterTypeName()))}>");
2047  }
2048  return name;
2049  }
2050 
2051  /// <summary>
2052  /// Converts the Resolution instance into a TimeSpan instance
2053  /// </summary>
2054  /// <param name="resolution">The resolution to be converted</param>
2055  /// <returns>A TimeSpan instance that represents the resolution specified</returns>
2056  [MethodImpl(MethodImplOptions.AggressiveInlining)]
2057  public static TimeSpan ToTimeSpan(this Resolution resolution)
2058  {
2059  switch (resolution)
2060  {
2061  case Resolution.Tick:
2062  // ticks can be instantaneous
2063  return TimeSpan.Zero;
2064  case Resolution.Second:
2065  return Time.OneSecond;
2066  case Resolution.Minute:
2067  return Time.OneMinute;
2068  case Resolution.Hour:
2069  return Time.OneHour;
2070  case Resolution.Daily:
2071  return Time.OneDay;
2072  default:
2073  throw new ArgumentOutOfRangeException(nameof(resolution));
2074  }
2075  }
2076 
2077  /// <summary>
2078  /// Converts the specified time span into a resolution enum value. If an exact match
2079  /// is not found and `requireExactMatch` is false, then the higher resoluion will be
2080  /// returned. For example, timeSpan=5min will return Minute resolution.
2081  /// </summary>
2082  /// <param name="timeSpan">The time span to convert to resolution</param>
2083  /// <param name="requireExactMatch">True to throw an exception if an exact match is not found</param>
2084  /// <returns>The resolution</returns>
2085  public static Resolution ToHigherResolutionEquivalent(this TimeSpan timeSpan, bool requireExactMatch)
2086  {
2087  if (requireExactMatch)
2088  {
2089  if (TimeSpan.Zero == timeSpan) return Resolution.Tick;
2090  if (Time.OneSecond == timeSpan) return Resolution.Second;
2091  if (Time.OneMinute == timeSpan) return Resolution.Minute;
2092  if (Time.OneHour == timeSpan) return Resolution.Hour;
2093  if (Time.OneDay == timeSpan) return Resolution.Daily;
2094  throw new InvalidOperationException(Messages.Extensions.UnableToConvertTimeSpanToResolution(timeSpan));
2095  }
2096 
2097  // for non-perfect matches
2098  if (Time.OneSecond > timeSpan) return Resolution.Tick;
2099  if (Time.OneMinute > timeSpan) return Resolution.Second;
2100  if (Time.OneHour > timeSpan) return Resolution.Minute;
2101  if (Time.OneDay > timeSpan) return Resolution.Hour;
2102 
2103  return Resolution.Daily;
2104  }
2105 
2106  /// <summary>
2107  /// Attempts to convert the string into a <see cref="SecurityType"/> enum value
2108  /// </summary>
2109  /// <param name="value">string value to convert to SecurityType</param>
2110  /// <param name="securityType">SecurityType output</param>
2111  /// <param name="ignoreCase">Ignore casing</param>
2112  /// <returns>true if parsed into a SecurityType successfully, false otherwise</returns>
2113  /// <remarks>
2114  /// Logs once if we've encountered an invalid SecurityType
2115  /// </remarks>
2116  public static bool TryParseSecurityType(this string value, out SecurityType securityType, bool ignoreCase = true)
2117  {
2118  if (Enum.TryParse(value, ignoreCase, out securityType))
2119  {
2120  return true;
2121  }
2122 
2123  if (InvalidSecurityTypes.Add(value))
2124  {
2125  Log.Error($"Extensions.TryParseSecurityType(): {Messages.Extensions.UnableToParseUnknownSecurityType(value)}");
2126  }
2127 
2128  return false;
2129 
2130  }
2131 
2132  /// <summary>
2133  /// Converts the specified string value into the specified type
2134  /// </summary>
2135  /// <typeparam name="T">The output type</typeparam>
2136  /// <param name="value">The string value to be converted</param>
2137  /// <returns>The converted value</returns>
2138  public static T ConvertTo<T>(this string value)
2139  {
2140  return (T) value.ConvertTo(typeof (T));
2141  }
2142 
2143  /// <summary>
2144  /// Converts the specified string value into the specified type
2145  /// </summary>
2146  /// <param name="value">The string value to be converted</param>
2147  /// <param name="type">The output type</param>
2148  /// <returns>The converted value</returns>
2149  public static object ConvertTo(this string value, Type type)
2150  {
2151  if (type.IsEnum)
2152  {
2153  return Enum.Parse(type, value, true);
2154  }
2155 
2156  if (typeof (IConvertible).IsAssignableFrom(type))
2157  {
2158  return Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
2159  }
2160 
2161  // try and find a static parse method
2162  var parse = type.GetMethod("Parse", new[] {typeof (string)});
2163  if (parse != null)
2164  {
2165  var result = parse.Invoke(null, new object[] {value});
2166  return result;
2167  }
2168 
2169  return JsonConvert.DeserializeObject(value, type);
2170  }
2171 
2172  /// <summary>
2173  /// 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"/>.
2174  /// </summary>
2175  /// <param name="waitHandle">The wait handle to wait on</param>
2176  /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
2177  /// <exception cref="T:System.InvalidOperationException">The maximum number of waiters has been exceeded.</exception>
2178  /// <exception cref="T:System.OperationCanceledExcepton"><paramref name="cancellationToken"/> was canceled.</exception>
2179  /// <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>
2180  public static bool WaitOne(this WaitHandle waitHandle, CancellationToken cancellationToken)
2181  {
2182  return waitHandle.WaitOne(Timeout.Infinite, cancellationToken);
2183  }
2184 
2185  /// <summary>
2186  /// 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"/>.
2187  /// </summary>
2188  ///
2189  /// <returns>
2190  /// true if the <see cref="T:System.Threading.WaitHandle"/> was set; otherwise, false.
2191  /// </returns>
2192  /// <param name="waitHandle">The wait handle to wait on</param>
2193  /// <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>
2194  /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
2195  /// <exception cref="T:System.Threading.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
2196  /// <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>
2197  /// <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>
2198  public static bool WaitOne(this WaitHandle waitHandle, TimeSpan timeout, CancellationToken cancellationToken)
2199  {
2200  return waitHandle.WaitOne((int) timeout.TotalMilliseconds, cancellationToken);
2201  }
2202 
2203  /// <summary>
2204  /// 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"/>.
2205  /// </summary>
2206  ///
2207  /// <returns>
2208  /// true if the <see cref="T:System.Threading.WaitHandle"/> was set; otherwise, false.
2209  /// </returns>
2210  /// <param name="waitHandle">The wait handle to wait on</param>
2211  /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="F:System.Threading.Timeout.Infinite"/>(-1) to wait indefinitely.</param>
2212  /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param>
2213  /// <exception cref="T:System.Threading.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
2214  /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an infinite time-out.</exception>
2215  /// <exception cref="T:System.InvalidOperationException">The maximum number of waiters has been exceeded.</exception>
2216  /// <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, int millisecondsTimeout, CancellationToken cancellationToken)
2218  {
2219  return WaitHandle.WaitAny(new[] { waitHandle, cancellationToken.WaitHandle }, millisecondsTimeout) == 0;
2220  }
2221 
2222  /// <summary>
2223  /// Gets the MD5 hash from a stream
2224  /// </summary>
2225  /// <param name="stream">The stream to compute a hash for</param>
2226  /// <returns>The MD5 hash</returns>
2227  public static byte[] GetMD5Hash(this Stream stream)
2228  {
2229  using (var md5 = MD5.Create())
2230  {
2231  return md5.ComputeHash(stream);
2232  }
2233  }
2234 
2235  /// <summary>
2236  /// Convert a string into the same string with a URL! :)
2237  /// </summary>
2238  /// <param name="source">The source string to be converted</param>
2239  /// <returns>The same source string but with anchor tags around substrings matching a link regex</returns>
2240  public static string WithEmbeddedHtmlAnchors(this string source)
2241  {
2242  var regx = new Regex("http(s)?://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*([a-zA-Z0-9\\?\\#\\=\\/]){1})?", RegexOptions.IgnoreCase);
2243  var matches = regx.Matches(source);
2244  foreach (Match match in matches)
2245  {
2246  source = source.Replace(match.Value, $"<a href=\'{match.Value}\' target=\'blank\'>{match.Value}</a>");
2247  }
2248  return source;
2249  }
2250 
2251  /// <summary>
2252  /// Get the first occurence of a string between two characters from another string
2253  /// </summary>
2254  /// <param name="value">The original string</param>
2255  /// <param name="left">Left bound of the substring</param>
2256  /// <param name="right">Right bound of the substring</param>
2257  /// <returns>Substring from original string bounded by the two characters</returns>
2258  public static string GetStringBetweenChars(this string value, char left, char right)
2259  {
2260  var startIndex = 1 + value.IndexOf(left);
2261  var length = value.IndexOf(right, startIndex) - startIndex;
2262  if (length > 0)
2263  {
2264  value = value.Substring(startIndex, length);
2265  startIndex = 1 + value.IndexOf(left);
2266  return value.Substring(startIndex).Trim();
2267  }
2268  return string.Empty;
2269  }
2270 
2271  /// <summary>
2272  /// Return the first in the series of names, or find the one that matches the configured algorithmTypeName
2273  /// </summary>
2274  /// <param name="names">The list of class names</param>
2275  /// <param name="algorithmTypeName">The configured algorithm type name from the config</param>
2276  /// <returns>The name of the class being run</returns>
2277  public static string SingleOrAlgorithmTypeName(this List<string> names, string algorithmTypeName)
2278  {
2279  // If there's only one name use that guy
2280  if (names.Count == 1) { return names.Single(); }
2281 
2282  // If we have multiple names we need to search the names based on the given algorithmTypeName
2283  // If the given name already contains dots (fully named) use it as it is
2284  // otherwise add a dot to the beginning to avoid matching any subsets of other names
2285  var searchName = algorithmTypeName.Contains('.', StringComparison.InvariantCulture) ? algorithmTypeName : "." + algorithmTypeName;
2286  return names.SingleOrDefault(x => x.EndsWith(searchName));
2287  }
2288 
2289  /// <summary>
2290  /// Converts the specified <paramref name="enum"/> value to its corresponding lower-case string representation
2291  /// </summary>
2292  /// <param name="enum">The enumeration value</param>
2293  /// <returns>A lower-case string representation of the specified enumeration value</returns>
2294  public static string ToLower(this Enum @enum)
2295  {
2296  return @enum.ToString().ToLowerInvariant();
2297  }
2298 
2299  /// <summary>
2300  /// Asserts the specified <paramref name="securityType"/> value is valid
2301  /// </summary>
2302  /// <remarks>This method provides faster performance than <see cref="Enum.IsDefined"/> which uses reflection</remarks>
2303  /// <param name="securityType">The SecurityType value</param>
2304  /// <returns>True if valid security type value</returns>
2305  public static bool IsValid(this SecurityType securityType)
2306  {
2307  switch (securityType)
2308  {
2309  case SecurityType.Base:
2310  case SecurityType.Equity:
2311  case SecurityType.Option:
2312  case SecurityType.FutureOption:
2313  case SecurityType.Commodity:
2314  case SecurityType.Forex:
2315  case SecurityType.Future:
2316  case SecurityType.Cfd:
2317  case SecurityType.Crypto:
2318  case SecurityType.CryptoFuture:
2319  case SecurityType.Index:
2320  case SecurityType.IndexOption:
2321  return true;
2322  default:
2323  return false;
2324  }
2325  }
2326 
2327  /// <summary>
2328  /// Determines if the provided SecurityType is a type of Option.
2329  /// Valid option types are: Equity Options, Futures Options, and Index Options.
2330  /// </summary>
2331  /// <param name="securityType">The SecurityType to check if it's an option asset</param>
2332  /// <returns>
2333  /// true if the asset has the makings of an option (exercisable, expires, and is a derivative of some underlying),
2334  /// false otherwise.
2335  /// </returns>
2336  public static bool IsOption(this SecurityType securityType)
2337  {
2338  switch (securityType)
2339  {
2340  case SecurityType.Option:
2341  case SecurityType.FutureOption:
2342  case SecurityType.IndexOption:
2343  return true;
2344 
2345  default:
2346  return false;
2347  }
2348  }
2349 
2350  /// <summary>
2351  /// Determines if the provided SecurityType has a matching option SecurityType, used to represent
2352  /// the current SecurityType as a derivative.
2353  /// </summary>
2354  /// <param name="securityType">The SecurityType to check if it has options available</param>
2355  /// <returns>true if there are options for the SecurityType, false otherwise</returns>
2356  public static bool HasOptions(this SecurityType securityType)
2357  {
2358  switch (securityType)
2359  {
2360  case SecurityType.Equity:
2361  case SecurityType.Future:
2362  case SecurityType.Index:
2363  return true;
2364 
2365  default:
2366  return false;
2367  }
2368  }
2369 
2370  /// <summary>
2371  /// Gets the default <see cref="OptionStyle"/> for the provided <see cref="SecurityType"/>
2372  /// </summary>
2373  /// <param name="securityType">SecurityType to get default OptionStyle for</param>
2374  /// <returns>Default OptionStyle for the SecurityType</returns>
2375  /// <exception cref="ArgumentException">The SecurityType has no options available for it or it is not an option</exception>
2376  public static OptionStyle DefaultOptionStyle(this SecurityType securityType)
2377  {
2378  if (!securityType.HasOptions() && !securityType.IsOption())
2379  {
2380  throw new ArgumentException(Messages.Extensions.NoDefaultOptionStyleForSecurityType(securityType));
2381  }
2382 
2383  switch (securityType)
2384  {
2385  case SecurityType.Index:
2386  case SecurityType.IndexOption:
2387  return OptionStyle.European;
2388 
2389  default:
2390  return OptionStyle.American;
2391  }
2392  }
2393 
2394  /// <summary>
2395  /// Converts the specified string to its corresponding OptionStyle
2396  /// </summary>
2397  /// <remarks>This method provides faster performance than enum parse</remarks>
2398  /// <param name="optionStyle">The OptionStyle string value</param>
2399  /// <returns>The OptionStyle value</returns>
2400  public static OptionStyle ParseOptionStyle(this string optionStyle)
2401  {
2402  switch (optionStyle.LazyToLower())
2403  {
2404  case "american":
2405  return OptionStyle.American;
2406  case "european":
2407  return OptionStyle.European;
2408  default:
2409  throw new ArgumentException(Messages.Extensions.UnknownOptionStyle(optionStyle));
2410  }
2411  }
2412 
2413  /// <summary>
2414  /// Converts the specified string to its corresponding OptionRight
2415  /// </summary>
2416  /// <remarks>This method provides faster performance than enum parse</remarks>
2417  /// <param name="optionRight">The optionRight string value</param>
2418  /// <returns>The OptionRight value</returns>
2419  public static OptionRight ParseOptionRight(this string optionRight)
2420  {
2421  switch (optionRight.LazyToLower())
2422  {
2423  case "call":
2424  return OptionRight.Call;
2425  case "put":
2426  return OptionRight.Put;
2427  default:
2428  throw new ArgumentException(Messages.Extensions.UnknownOptionRight(optionRight));
2429  }
2430  }
2431 
2432  /// <summary>
2433  /// Converts the specified <paramref name="optionRight"/> value to its corresponding string representation
2434  /// </summary>
2435  /// <remarks>This method provides faster performance than enum <see cref="Object.ToString"/></remarks>
2436  /// <param name="optionRight">The optionRight value</param>
2437  /// <returns>A string representation of the specified OptionRight value</returns>
2438  public static string ToStringPerformance(this OptionRight optionRight)
2439  {
2440  switch (optionRight)
2441  {
2442  case OptionRight.Call:
2443  return "Call";
2444  case OptionRight.Put:
2445  return "Put";
2446  default:
2447  // just in case
2448  return optionRight.ToString();
2449  }
2450  }
2451 
2452  /// <summary>
2453  /// Converts the specified <paramref name="optionRight"/> value to its corresponding lower-case string representation
2454  /// </summary>
2455  /// <remarks>This method provides faster performance than <see cref="ToLower"/></remarks>
2456  /// <param name="optionRight">The optionRight value</param>
2457  /// <returns>A lower case string representation of the specified OptionRight value</returns>
2458  public static string OptionRightToLower(this OptionRight optionRight)
2459  {
2460  switch (optionRight)
2461  {
2462  case OptionRight.Call:
2463  return "call";
2464  case OptionRight.Put:
2465  return "put";
2466  default:
2467  throw new ArgumentException(Messages.Extensions.UnknownOptionRight(optionRight));
2468  }
2469  }
2470 
2471  /// <summary>
2472  /// Converts the specified <paramref name="optionStyle"/> 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="optionStyle">The optionStyle value</param>
2476  /// <returns>A lower case string representation of the specified optionStyle value</returns>
2477  public static string OptionStyleToLower(this OptionStyle optionStyle)
2478  {
2479  switch (optionStyle)
2480  {
2481  case OptionStyle.American:
2482  return "american";
2483  case OptionStyle.European:
2484  return "european";
2485  default:
2486  throw new ArgumentException(Messages.Extensions.UnknownOptionStyle(optionStyle));
2487  }
2488  }
2489 
2490  /// <summary>
2491  /// Converts the specified string to its corresponding DataMappingMode
2492  /// </summary>
2493  /// <remarks>This method provides faster performance than enum parse</remarks>
2494  /// <param name="dataMappingMode">The dataMappingMode string value</param>
2495  /// <returns>The DataMappingMode value</returns>
2496  public static DataMappingMode? ParseDataMappingMode(this string dataMappingMode)
2497  {
2498  if (string.IsNullOrEmpty(dataMappingMode))
2499  {
2500  return null;
2501  }
2502  switch (dataMappingMode.LazyToLower())
2503  {
2504  case "0":
2505  case "lasttradingday":
2506  return DataMappingMode.LastTradingDay;
2507  case "1":
2508  case "firstdaymonth":
2509  return DataMappingMode.FirstDayMonth;
2510  case "2":
2511  case "openinterest":
2512  return DataMappingMode.OpenInterest;
2513  case "3":
2514  case "openinterestannual":
2515  return DataMappingMode.OpenInterestAnnual;
2516  default:
2517  throw new ArgumentException(Messages.Extensions.UnknownDataMappingMode(dataMappingMode));
2518  }
2519  }
2520 
2521  /// <summary>
2522  /// Converts the specified <paramref name="securityType"/> value to its corresponding lower-case string representation
2523  /// </summary>
2524  /// <remarks>This method provides faster performance than <see cref="ToLower"/></remarks>
2525  /// <param name="securityType">The SecurityType value</param>
2526  /// <returns>A lower-case string representation of the specified SecurityType value</returns>
2527  public static string SecurityTypeToLower(this SecurityType securityType)
2528  {
2529  switch (securityType)
2530  {
2531  case SecurityType.Base:
2532  return "base";
2533  case SecurityType.Equity:
2534  return "equity";
2535  case SecurityType.Option:
2536  return "option";
2537  case SecurityType.FutureOption:
2538  return "futureoption";
2539  case SecurityType.IndexOption:
2540  return "indexoption";
2541  case SecurityType.Commodity:
2542  return "commodity";
2543  case SecurityType.Forex:
2544  return "forex";
2545  case SecurityType.Future:
2546  return "future";
2547  case SecurityType.Index:
2548  return "index";
2549  case SecurityType.Cfd:
2550  return "cfd";
2551  case SecurityType.Crypto:
2552  return "crypto";
2553  case SecurityType.CryptoFuture:
2554  return "cryptofuture";
2555  default:
2556  // just in case
2557  return securityType.ToLower();
2558  }
2559  }
2560 
2561  /// <summary>
2562  /// Converts the specified <paramref name="tickType"/> value to its corresponding lower-case string representation
2563  /// </summary>
2564  /// <remarks>This method provides faster performance than <see cref="ToLower"/></remarks>
2565  /// <param name="tickType">The tickType value</param>
2566  /// <returns>A lower-case string representation of the specified tickType value</returns>
2567  public static string TickTypeToLower(this TickType tickType)
2568  {
2569  switch (tickType)
2570  {
2571  case TickType.Trade:
2572  return "trade";
2573  case TickType.Quote:
2574  return "quote";
2575  case TickType.OpenInterest:
2576  return "openinterest";
2577  default:
2578  // just in case
2579  return tickType.ToLower();
2580  }
2581  }
2582 
2583  /// <summary>
2584  /// Converts the specified <paramref name="resolution"/> value to its corresponding lower-case string representation
2585  /// </summary>
2586  /// <remarks>This method provides faster performance than <see cref="ToLower"/></remarks>
2587  /// <param name="resolution">The resolution value</param>
2588  /// <returns>A lower-case string representation of the specified resolution value</returns>
2589  public static string ResolutionToLower(this Resolution resolution)
2590  {
2591  switch (resolution)
2592  {
2593  case Resolution.Tick:
2594  return "tick";
2595  case Resolution.Second:
2596  return "second";
2597  case Resolution.Minute:
2598  return "minute";
2599  case Resolution.Hour:
2600  return "hour";
2601  case Resolution.Daily:
2602  return "daily";
2603  default:
2604  // just in case
2605  return resolution.ToLower();
2606  }
2607  }
2608 
2609  /// <summary>
2610  /// Turn order into an order ticket
2611  /// </summary>
2612  /// <param name="order">The <see cref="Order"/> being converted</param>
2613  /// <param name="transactionManager">The transaction manager, <see cref="SecurityTransactionManager"/></param>
2614  /// <returns></returns>
2615  public static OrderTicket ToOrderTicket(this Order order, SecurityTransactionManager transactionManager)
2616  {
2617  var limitPrice = 0m;
2618  var stopPrice = 0m;
2619  var triggerPrice = 0m;
2620  var trailingAmount = 0m;
2621  var trailingAsPercentage = false;
2622 
2623  switch (order.Type)
2624  {
2625  case OrderType.Limit:
2626  var limitOrder = order as LimitOrder;
2627  limitPrice = limitOrder.LimitPrice;
2628  break;
2629  case OrderType.StopMarket:
2630  var stopMarketOrder = order as StopMarketOrder;
2631  stopPrice = stopMarketOrder.StopPrice;
2632  break;
2633  case OrderType.StopLimit:
2634  var stopLimitOrder = order as StopLimitOrder;
2635  stopPrice = stopLimitOrder.StopPrice;
2636  limitPrice = stopLimitOrder.LimitPrice;
2637  break;
2638  case OrderType.TrailingStop:
2639  var trailingStopOrder = order as TrailingStopOrder;
2640  stopPrice = trailingStopOrder.StopPrice;
2641  trailingAmount = trailingStopOrder.TrailingAmount;
2642  trailingAsPercentage = trailingStopOrder.TrailingAsPercentage;
2643  break;
2644  case OrderType.LimitIfTouched:
2645  var limitIfTouched = order as LimitIfTouchedOrder;
2646  triggerPrice = limitIfTouched.TriggerPrice;
2647  limitPrice = limitIfTouched.LimitPrice;
2648  break;
2649  case OrderType.OptionExercise:
2650  case OrderType.Market:
2651  case OrderType.MarketOnOpen:
2652  case OrderType.MarketOnClose:
2653  case OrderType.ComboMarket:
2654  limitPrice = order.Price;
2655  stopPrice = order.Price;
2656  break;
2657  case OrderType.ComboLimit:
2658  limitPrice = order.GroupOrderManager.LimitPrice;
2659  break;
2660  case OrderType.ComboLegLimit:
2661  var legLimitOrder = order as ComboLegLimitOrder;
2662  limitPrice = legLimitOrder.LimitPrice;
2663  break;
2664  default:
2665  throw new ArgumentOutOfRangeException();
2666  }
2667 
2668  var submitOrderRequest = new SubmitOrderRequest(order.Type,
2669  order.SecurityType,
2670  order.Symbol,
2671  order.Quantity,
2672  stopPrice,
2673  limitPrice,
2674  triggerPrice,
2675  trailingAmount,
2676  trailingAsPercentage,
2677  order.Time,
2678  order.Tag,
2679  order.Properties,
2680  order.GroupOrderManager);
2681 
2682  submitOrderRequest.SetOrderId(order.Id);
2683  var orderTicket = new OrderTicket(transactionManager, submitOrderRequest);
2684  orderTicket.SetOrder(order);
2685  return orderTicket;
2686  }
2687 
2688  /// <summary>
2689  /// Process all items in collection through given handler
2690  /// </summary>
2691  /// <typeparam name="T"></typeparam>
2692  /// <param name="collection">Collection to process</param>
2693  /// <param name="handler">Handler to process those items with</param>
2694  public static void ProcessUntilEmpty<T>(this IProducerConsumerCollection<T> collection, Action<T> handler)
2695  {
2696  T item;
2697  while (collection.TryTake(out item))
2698  {
2699  handler(item);
2700  }
2701  }
2702 
2703  /// <summary>
2704  /// Returns a <see cref="string"/> that represents the current <see cref="PyObject"/>
2705  /// </summary>
2706  /// <param name="pyObject">The <see cref="PyObject"/> being converted</param>
2707  /// <returns>string that represents the current PyObject</returns>
2708  public static string ToSafeString(this PyObject pyObject)
2709  {
2710  using (Py.GIL())
2711  {
2712  var value = "";
2713  // PyObject objects that have the to_string method, like some pandas objects,
2714  // can use this method to convert them into string objects
2715  if (pyObject.HasAttr("to_string"))
2716  {
2717  var pyValue = pyObject.InvokeMethod("to_string");
2718  value = Environment.NewLine + pyValue;
2719  pyValue.Dispose();
2720  }
2721  else
2722  {
2723  value = pyObject.ToString();
2724  if (string.IsNullOrWhiteSpace(value))
2725  {
2726  var pythonType = pyObject.GetPythonType();
2727  if (pythonType.GetType() == typeof(PyObject))
2728  {
2729  value = pythonType.ToString();
2730  }
2731  else
2732  {
2733  var type = pythonType.As<Type>();
2734  value = pyObject.AsManagedObject(type).ToString();
2735  }
2736  pythonType.Dispose();
2737  }
2738  }
2739  return value;
2740  }
2741  }
2742 
2743  /// <summary>
2744  /// Tries to convert a <see cref="PyObject"/> into a managed object
2745  /// </summary>
2746  /// <remarks>This method is not working correctly for a wrapped <see cref="TimeSpan"/> instance,
2747  /// probably because it is a struct, using <see cref="PyObject.As{T}"/> is a valid work around.
2748  /// Not used here because it caused errors
2749  /// </remarks>
2750  /// <typeparam name="T">Target type of the resulting managed object</typeparam>
2751  /// <param name="pyObject">PyObject to be converted</param>
2752  /// <param name="result">Managed object </param>
2753  /// <param name="allowPythonDerivative">True will convert python subclasses of T</param>
2754  /// <returns>True if successful conversion</returns>
2755  public static bool TryConvert<T>(this PyObject pyObject, out T result, bool allowPythonDerivative = false)
2756  {
2757  result = default(T);
2758  var type = typeof(T);
2759 
2760  if (pyObject == null)
2761  {
2762  return true;
2763  }
2764 
2765  using (Py.GIL())
2766  {
2767  try
2768  {
2769  // We must first check if allowPythonDerivative is true to then only return true
2770  // when the PyObject is assignable from Type or IEnumerable and is a C# type
2771  // wrapped in PyObject
2772  if (allowPythonDerivative)
2773  {
2774  result = (T)pyObject.AsManagedObject(type);
2775  return true;
2776  }
2777 
2778  // Special case: Type
2779  if (typeof(Type).IsAssignableFrom(type))
2780  {
2781  result = (T)pyObject.AsManagedObject(type);
2782  // pyObject is a C# object wrapped in PyObject, in this case return true
2783  if(!pyObject.HasAttr("__name__"))
2784  {
2785  return true;
2786  }
2787  // Otherwise, pyObject is a python object that subclass a C# class, only return true if 'allowPythonDerivative'
2788  var castedResult = (Type)pyObject.AsManagedObject(type);
2789  var pythonName = pyObject.GetAttr("__name__").GetAndDispose<string>();
2790  return pythonName == castedResult.Name;
2791  }
2792 
2793  // Special case: IEnumerable
2794  if (typeof(IEnumerable).IsAssignableFrom(type))
2795  {
2796  result = (T)pyObject.AsManagedObject(type);
2797  return true;
2798  }
2799 
2800  using var pythonType = pyObject.GetPythonType();
2801  var csharpType = pythonType.As<Type>();
2802 
2803  if (!type.IsAssignableFrom(csharpType))
2804  {
2805  return false;
2806  }
2807 
2808  result = (T)pyObject.AsManagedObject(type);
2809 
2810  // The PyObject is a Python object of a Python class that is a subclass of a C# class.
2811  // In this case, we return false just because we want the actual Python object
2812  // so it gets wrapped in a python wrapper, not the C# object.
2813  if (result is IPythonDerivedType)
2814  {
2815  return false;
2816  }
2817 
2818  // If the python type object is just a representation of the C# type, the conversion is direct,
2819  // the python object is an instance of the C# class.
2820  // We can compare by reference because pythonnet caches the PyTypes and because the behavior of
2821  // PyObject.Equals is not exactly what we want:
2822  // e.g. type(class PyClass(CSharpClass)) == type(CSharpClass) is true in Python
2823  if (PythonReferenceComparer.Instance.Equals(PyType.Get(csharpType), pythonType))
2824  {
2825  return true;
2826  }
2827 
2828  // If the PyObject type and the managed object names are the same,
2829  // pyObject is a C# object wrapped in PyObject, in this case return true
2830  // Otherwise, pyObject is a python object that subclass a C# class, only return true if 'allowPythonDerivative'
2831  var name = (((dynamic)pythonType).__name__ as PyObject).GetAndDispose<string>();
2832  return name == result.GetType().Name;
2833  }
2834  catch
2835  {
2836  // Do not throw or log the exception.
2837  // Return false as an exception means that the conversion could not be made.
2838  }
2839  }
2840 
2841  return false;
2842  }
2843 
2844  /// <summary>
2845  /// Tries to convert a <see cref="PyObject"/> into a managed object
2846  /// </summary>
2847  /// <typeparam name="T">Target type of the resulting managed object</typeparam>
2848  /// <param name="pyObject">PyObject to be converted</param>
2849  /// <param name="result">Managed object </param>
2850  /// <returns>True if successful conversion</returns>
2851  public static bool TryConvertToDelegate<T>(this PyObject pyObject, out T result)
2852  {
2853  var type = typeof(T);
2854 
2855  // The PyObject is a C# object wrapped
2856  if (TryConvert<T>(pyObject, out result))
2857  {
2858  return true;
2859  }
2860 
2861  if (!typeof(MulticastDelegate).IsAssignableFrom(type))
2862  {
2863  throw new ArgumentException(Messages.Extensions.ConvertToDelegateCannotConverPyObjectToType("TryConvertToDelegate", type));
2864  }
2865 
2866  result = default(T);
2867 
2868  if (pyObject == null)
2869  {
2870  return true;
2871  }
2872 
2873  var code = string.Empty;
2874  var types = type.GetGenericArguments();
2875 
2876  using (Py.GIL())
2877  {
2878  var locals = new PyDict();
2879  try
2880  {
2881  for (var i = 0; i < types.Length; i++)
2882  {
2883  var iString = i.ToStringInvariant();
2884  code += $",t{iString}";
2885  locals.SetItem($"t{iString}", types[i].ToPython());
2886  }
2887 
2888  locals.SetItem("pyObject", pyObject);
2889 
2890  var name = type.FullName.Substring(0, type.FullName.IndexOf('`'));
2891  code = $"import System; delegate = {name}[{code.Substring(1)}](pyObject)";
2892 
2893  PythonEngine.Exec(code, null, locals);
2894  result = (T)locals.GetItem("delegate").AsManagedObject(typeof(T));
2895  locals.Dispose();
2896  return true;
2897  }
2898  catch
2899  {
2900  // Do not throw or log the exception.
2901  // Return false as an exception means that the conversion could not be made.
2902  }
2903  locals.Dispose();
2904  }
2905  return false;
2906  }
2907 
2908  /// <summary>
2909  /// Safely convert PyObject to ManagedObject using Py.GIL Lock
2910  /// If no type is given it will convert the PyObject's Python Type to a ManagedObject Type
2911  /// in a attempt to resolve the target type to convert to.
2912  /// </summary>
2913  /// <param name="pyObject">PyObject to convert to managed</param>
2914  /// <param name="typeToConvertTo">The target type to convert to</param>
2915  /// <returns>The resulting ManagedObject</returns>
2916  public static dynamic SafeAsManagedObject(this PyObject pyObject, Type typeToConvertTo = null)
2917  {
2918  using (Py.GIL())
2919  {
2920  if (typeToConvertTo == null)
2921  {
2922  typeToConvertTo = pyObject.GetPythonType().AsManagedObject(typeof(Type)) as Type;
2923  }
2924 
2925  return pyObject.AsManagedObject(typeToConvertTo);
2926  }
2927  }
2928 
2929  /// <summary>
2930  /// Converts a Python function to a managed function returning a Symbol
2931  /// </summary>
2932  /// <param name="universeFilterFunc">Universe filter function from Python</param>
2933  /// <returns>Function that provides <typeparamref name="T"/> and returns an enumerable of Symbols</returns>
2934  public static Func<IEnumerable<T>, IEnumerable<Symbol>> ConvertPythonUniverseFilterFunction<T>(this PyObject universeFilterFunc) where T : BaseData
2935  {
2936  Func<IEnumerable<T>, object> convertedFunc;
2937  Func<IEnumerable<T>, IEnumerable<Symbol>> filterFunc = null;
2938 
2939  if (universeFilterFunc != null && universeFilterFunc.TryConvertToDelegate(out convertedFunc))
2940  {
2941  filterFunc = convertedFunc.ConvertToUniverseSelectionSymbolDelegate();
2942  }
2943 
2944  return filterFunc;
2945  }
2946 
2947  /// <summary>
2948  /// Wraps the provided universe selection selector checking if it returned <see cref="Universe.Unchanged"/>
2949  /// and returns it instead, else enumerates result as <see cref="IEnumerable{Symbol}"/>
2950  /// </summary>
2951  /// <remarks>This method is a work around for the fact that currently we can not create a delegate which returns
2952  /// an <see cref="IEnumerable{Symbol}"/> from a python method returning an array, plus the fact that
2953  /// <see cref="Universe.Unchanged"/> can not be cast to an array</remarks>
2954  public static Func<IEnumerable<T>, IEnumerable<Symbol>> ConvertToUniverseSelectionSymbolDelegate<T>(this Func<IEnumerable<T>, object> selector) where T : BaseData
2955  {
2956  if (selector == null)
2957  {
2958  return (dataPoints) => dataPoints.Select(x => x.Symbol);
2959  }
2960  return selector.ConvertSelectionSymbolDelegate();
2961  }
2962 
2963  /// <summary>
2964  /// Wraps the provided universe selection selector checking if it returned <see cref="Universe.Unchanged"/>
2965  /// and returns it instead, else enumerates result as <see cref="IEnumerable{Symbol}"/>
2966  /// </summary>
2967  /// <remarks>This method is a work around for the fact that currently we can not create a delegate which returns
2968  /// an <see cref="IEnumerable{Symbol}"/> from a python method returning an array, plus the fact that
2969  /// <see cref="Universe.Unchanged"/> can not be cast to an array</remarks>
2970  public static Func<T, IEnumerable<Symbol>> ConvertSelectionSymbolDelegate<T>(this Func<T, object> selector)
2971  {
2972  return data =>
2973  {
2974  var result = selector(data);
2975  return ReferenceEquals(result, Universe.Unchanged)
2977  : ((object[])result).Select(x =>
2978  {
2979  if (x is Symbol castedSymbol)
2980  {
2981  return castedSymbol;
2982  }
2983  return SymbolCache.TryGetSymbol((string)x, out var symbol) ? symbol : null;
2984  });
2985  };
2986  }
2987 
2988  /// <summary>
2989  /// Wraps the provided universe selection selector checking if it returned <see cref="Universe.Unchanged"/>
2990  /// and returns it instead, else enumerates result as <see cref="IEnumerable{String}"/>
2991  /// </summary>
2992  /// <remarks>This method is a work around for the fact that currently we can not create a delegate which returns
2993  /// an <see cref="IEnumerable{String}"/> from a python method returning an array, plus the fact that
2994  /// <see cref="Universe.Unchanged"/> can not be cast to an array</remarks>
2995  public static Func<T, IEnumerable<string>> ConvertToUniverseSelectionStringDelegate<T>(this Func<T, object> selector)
2996  {
2997  return data =>
2998  {
2999  var result = selector(data);
3000  return ReferenceEquals(result, Universe.Unchanged)
3001  ? Universe.Unchanged : ((object[])result).Select(x => (string)x);
3002  };
3003  }
3004 
3005  /// <summary>
3006  /// Convert a <see cref="PyObject"/> into a managed object
3007  /// </summary>
3008  /// <typeparam name="T">Target type of the resulting managed object</typeparam>
3009  /// <param name="pyObject">PyObject to be converted</param>
3010  /// <returns>Instance of type T</returns>
3011  public static T ConvertToDelegate<T>(this PyObject pyObject)
3012  {
3013  T result;
3014  if (pyObject.TryConvertToDelegate(out result))
3015  {
3016  return result;
3017  }
3018  else
3019  {
3020  throw new ArgumentException(Messages.Extensions.ConvertToDelegateCannotConverPyObjectToType("ConvertToDelegate", typeof(T)));
3021  }
3022  }
3023 
3024  /// <summary>
3025  /// Convert a <see cref="PyObject"/> into a managed dictionary
3026  /// </summary>
3027  /// <typeparam name="TKey">Target type of the resulting dictionary key</typeparam>
3028  /// <typeparam name="TValue">Target type of the resulting dictionary value</typeparam>
3029  /// <param name="pyObject">PyObject to be converted</param>
3030  /// <returns>Dictionary of TValue keyed by TKey</returns>
3031  public static Dictionary<TKey, TValue> ConvertToDictionary<TKey, TValue>(this PyObject pyObject)
3032  {
3033  var result = new List<KeyValuePair<TKey, TValue>>();
3034  using (Py.GIL())
3035  {
3036  var inputType = pyObject.GetPythonType().ToString();
3037  var targetType = nameof(PyDict);
3038 
3039  try
3040  {
3041  using (var pyDict = new PyDict(pyObject))
3042  {
3043  targetType = $"{typeof(TKey).Name}: {typeof(TValue).Name}";
3044 
3045  foreach (PyObject item in pyDict.Items())
3046  {
3047  inputType = $"{item[0].GetPythonType()}: {item[1].GetPythonType()}";
3048 
3049  var key = item[0].As<TKey>();
3050  var value = item[1].As<TValue>();
3051 
3052  result.Add(new KeyValuePair<TKey, TValue>(key, value));
3053  }
3054  }
3055  }
3056  catch (Exception e)
3057  {
3058  throw new ArgumentException(Messages.Extensions.ConvertToDictionaryFailed(inputType, targetType, e.Message), e);
3059  }
3060  }
3061 
3062  return result.ToDictionary();
3063  }
3064 
3065  /// <summary>
3066  /// Gets Enumerable of <see cref="Symbol"/> from a PyObject
3067  /// </summary>
3068  /// <param name="pyObject">PyObject containing Symbol or Array of Symbol</param>
3069  /// <returns>Enumerable of Symbol</returns>
3070  public static IEnumerable<Symbol> ConvertToSymbolEnumerable(this PyObject pyObject)
3071  {
3072  using (Py.GIL())
3073  {
3074  Exception exception = null;
3075  if (!PyList.IsListType(pyObject))
3076  {
3077  // it's not a pylist try to conver directly
3078  Symbol result = null;
3079  try
3080  {
3081  // we shouldn't dispose of an object we haven't created
3082  result = ConvertToSymbol(pyObject, dispose: false);
3083  }
3084  catch (Exception ex)
3085  {
3086  exception = ex;
3087  }
3088 
3089  if (result != null)
3090  {
3091  // happy case
3092  yield return result;
3093  }
3094  }
3095  else
3096  {
3097  using var iterator = pyObject.GetIterator();
3098  foreach (PyObject item in iterator)
3099  {
3100  Symbol result;
3101  try
3102  {
3103  result = ConvertToSymbol(item, dispose: true);
3104  }
3105  catch (Exception ex)
3106  {
3107  exception = ex;
3108  break;
3109  }
3110  yield return result;
3111  }
3112  }
3113 
3114  // let's give it once last try, relying on pythonnet internal conversions, else throw
3115  if (exception != null)
3116  {
3117  if (pyObject.TryConvert(out IEnumerable<Symbol> symbols))
3118  {
3119  foreach (var symbol in symbols)
3120  {
3121  yield return symbol;
3122  }
3123  }
3124  else
3125  {
3126  throw exception;
3127  }
3128  }
3129  }
3130  }
3131 
3132  /// <summary>
3133  /// Converts an IEnumerable to a PyList
3134  /// </summary>
3135  /// <param name="enumerable">IEnumerable object to convert</param>
3136  /// <returns>PyList</returns>
3137  public static PyList ToPyList(this IEnumerable enumerable)
3138  {
3139  using (Py.GIL())
3140  {
3141  return enumerable.ToPyListUnSafe();
3142  }
3143  }
3144 
3145  /// <summary>
3146  /// Converts an IEnumerable to a PyList
3147  /// </summary>
3148  /// <param name="enumerable">IEnumerable object to convert</param>
3149  /// <remarks>Requires the caller to own the GIL</remarks>
3150  /// <returns>PyList</returns>
3151  public static PyList ToPyListUnSafe(this IEnumerable enumerable)
3152  {
3153  var pyList = new PyList();
3154  foreach (var item in enumerable)
3155  {
3156  using (var pyObject = item.ToPython())
3157  {
3158  pyList.Append(pyObject);
3159  }
3160  }
3161 
3162  return pyList;
3163  }
3164 
3165  /// <summary>
3166  /// Converts the numeric value of one or more enumerated constants to an equivalent enumerated string.
3167  /// </summary>
3168  /// <param name="value">Numeric value</param>
3169  /// <param name="pyObject">Python object that encapsulated a Enum Type</param>
3170  /// <returns>String that represents the enumerated object</returns>
3171  public static string GetEnumString(this int value, PyObject pyObject)
3172  {
3173  Type type;
3174  if (pyObject.TryConvert(out type))
3175  {
3176  return value.ToStringInvariant().ConvertTo(type).ToString();
3177  }
3178  else
3179  {
3180  using (Py.GIL())
3181  {
3182  throw new ArgumentException($"GetEnumString(): {Messages.Extensions.ObjectFromPythonIsNotACSharpType(pyObject.Repr())}");
3183  }
3184  }
3185  }
3186 
3187  /// <summary>
3188  /// Try to create a type with a given name, if PyObject is not a CLR type. Otherwise, convert it.
3189  /// </summary>
3190  /// <param name="pyObject">Python object representing a type.</param>
3191  /// <param name="type">Type object</param>
3192  /// <returns>True if was able to create the type</returns>
3193  public static bool TryCreateType(this PyObject pyObject, out Type type)
3194  {
3195  if (pyObject.TryConvert(out type))
3196  {
3197  // handles pure C# types
3198  return true;
3199  }
3200 
3201  if (!PythonActivators.TryGetValue(pyObject.Handle, out var pythonType))
3202  {
3203  // Some examples:
3204  // pytype: "<class 'DropboxBaseDataUniverseSelectionAlgorithm.StockDataSource'>"
3205  // method: "<bound method CoarseFineFundamentalComboAlgorithm.CoarseSelectionFunction of <CoarseFineFunda..."
3206  // array: "[<QuantConnect.Symbol object at 0x000001EEF21ED480>]"
3207  if (pyObject.ToString().StartsWith("<class '", StringComparison.InvariantCulture))
3208  {
3209  type = CreateType(pyObject);
3210  return true;
3211  }
3212  return false;
3213  }
3214  type = pythonType.Type;
3215  return true;
3216  }
3217 
3218 
3219  /// <summary>
3220  /// Creates a type with a given name, if PyObject is not a CLR type. Otherwise, convert it.
3221  /// </summary>
3222  /// <param name="pyObject">Python object representing a type.</param>
3223  /// <returns>Type object</returns>
3224  public static Type CreateType(this PyObject pyObject)
3225  {
3226  Type type;
3227  if (pyObject.TryConvert(out type))
3228  {
3229  return type;
3230  }
3231 
3232  PythonActivator pythonType;
3233  if (!PythonActivators.TryGetValue(pyObject.Handle, out pythonType))
3234  {
3235  var assemblyName = pyObject.GetAssemblyName();
3236  var typeBuilder = AssemblyBuilder
3237  .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run)
3238  .DefineDynamicModule("MainModule")
3239  // creating the type as public is required to allow 'dynamic' to be able to bind at runtime
3240  .DefineType(assemblyName.Name, TypeAttributes.Class | TypeAttributes.Public, type);
3241 
3242  pythonType = new PythonActivator(typeBuilder.CreateType(), pyObject);
3243 
3244  ObjectActivator.AddActivator(pythonType.Type, pythonType.Factory);
3245 
3246  // Save to prevent future additions
3247  PythonActivators.Add(pyObject.Handle, pythonType);
3248  }
3249  return pythonType.Type;
3250  }
3251 
3252  /// <summary>
3253  /// Helper method to get the assembly name from a python type
3254  /// </summary>
3255  /// <param name="pyObject">Python object pointing to the python type. <see cref="PyObject.GetPythonType"/></param>
3256  /// <returns>The python type assembly name</returns>
3257  public static AssemblyName GetAssemblyName(this PyObject pyObject)
3258  {
3259  using (Py.GIL())
3260  {
3261  return new AssemblyName(pyObject.Repr().Split('\'')[1]);
3262  }
3263  }
3264 
3265  /// <summary>
3266  /// Performs on-line batching of the specified enumerator, emitting chunks of the requested batch size
3267  /// </summary>
3268  /// <typeparam name="T">The enumerable item type</typeparam>
3269  /// <param name="enumerable">The enumerable to be batched</param>
3270  /// <param name="batchSize">The number of items per batch</param>
3271  /// <returns>An enumerable of lists</returns>
3272  public static IEnumerable<List<T>> BatchBy<T>(this IEnumerable<T> enumerable, int batchSize)
3273  {
3274  using (var enumerator = enumerable.GetEnumerator())
3275  {
3276  List<T> list = null;
3277  while (enumerator.MoveNext())
3278  {
3279  if (list == null)
3280  {
3281  list = new List<T> {enumerator.Current};
3282  }
3283  else if (list.Count < batchSize)
3284  {
3285  list.Add(enumerator.Current);
3286  }
3287  else
3288  {
3289  yield return list;
3290  list = new List<T> {enumerator.Current};
3291  }
3292  }
3293 
3294  if (list?.Count > 0)
3295  {
3296  yield return list;
3297  }
3298  }
3299  }
3300 
3301  /// <summary>
3302  /// Safely blocks until the specified task has completed executing
3303  /// </summary>
3304  /// <typeparam name="TResult">The task's result type</typeparam>
3305  /// <param name="task">The task to be awaited</param>
3306  /// <returns>The result of the task</returns>
3307  public static TResult SynchronouslyAwaitTaskResult<TResult>(this Task<TResult> task)
3308  {
3309  return task.ConfigureAwait(false).GetAwaiter().GetResult();
3310  }
3311 
3312  /// <summary>
3313  /// Safely blocks until the specified task has completed executing
3314  /// </summary>
3315  /// <param name="task">The task to be awaited</param>
3316  /// <returns>The result of the task</returns>
3317  public static void SynchronouslyAwaitTask(this Task task)
3318  {
3319  task.ConfigureAwait(false).GetAwaiter().GetResult();
3320  }
3321 
3322  /// <summary>
3323  /// Safely blocks until the specified task has completed executing
3324  /// </summary>
3325  /// <param name="task">The task to be awaited</param>
3326  /// <returns>The result of the task</returns>
3327  public static T SynchronouslyAwaitTask<T>(this Task<T> task)
3328  {
3329  return task.ConfigureAwait(false).GetAwaiter().GetResult();
3330  }
3331 
3332  /// <summary>
3333  /// Convert dictionary to query string
3334  /// </summary>
3335  /// <param name="pairs"></param>
3336  /// <returns></returns>
3337  public static string ToQueryString(this IDictionary<string, object> pairs)
3338  {
3339  return string.Join("&", pairs.Select(pair => $"{pair.Key}={pair.Value}"));
3340  }
3341 
3342  /// <summary>
3343  /// Returns a new string in which specified ending in the current instance is removed.
3344  /// </summary>
3345  /// <param name="s">original string value</param>
3346  /// <param name="ending">the string to be removed</param>
3347  /// <returns></returns>
3348  public static string RemoveFromEnd(this string s, string ending)
3349  {
3350  if (s.EndsWith(ending, StringComparison.InvariantCulture))
3351  {
3352  return s.Substring(0, s.Length - ending.Length);
3353  }
3354  else
3355  {
3356  return s;
3357  }
3358  }
3359 
3360  /// <summary>
3361  /// Returns a new string in which specified start in the current instance is removed.
3362  /// </summary>
3363  /// <param name="s">original string value</param>
3364  /// <param name="start">the string to be removed</param>
3365  /// <returns>Substring with start removed</returns>
3366  public static string RemoveFromStart(this string s, string start)
3367  {
3368  if (!string.IsNullOrEmpty(s) && !string.IsNullOrEmpty(start) && s.StartsWith(start, StringComparison.InvariantCulture))
3369  {
3370  return s.Substring(start.Length);
3371  }
3372  else
3373  {
3374  return s;
3375  }
3376  }
3377 
3378  /// <summary>
3379  /// Helper method to determine symbol for a live subscription
3380  /// </summary>
3381  /// <remarks>Useful for continuous futures where we subscribe to the underlying</remarks>
3382  public static bool TryGetLiveSubscriptionSymbol(this Symbol symbol, out Symbol mapped)
3383  {
3384  mapped = null;
3385  if (symbol.SecurityType == SecurityType.Future && symbol.IsCanonical() && symbol.HasUnderlying)
3386  {
3387  mapped = symbol.Underlying;
3388  return true;
3389  }
3390  return false;
3391  }
3392 
3393  /// <summary>
3394  /// Gets the delisting date for the provided Symbol
3395  /// </summary>
3396  /// <param name="symbol">The symbol to lookup the last trading date</param>
3397  /// <param name="mapFile">Map file to use for delisting date. Defaults to SID.DefaultDate if no value is passed and is equity.</param>
3398  /// <returns></returns>
3399  public static DateTime GetDelistingDate(this Symbol symbol, MapFile mapFile = null)
3400  {
3401  if (symbol.IsCanonical())
3402  {
3403  return Time.EndOfTime;
3404  }
3405  switch (symbol.ID.SecurityType)
3406  {
3407  case SecurityType.Option:
3408  return OptionSymbol.GetLastDayOfTrading(symbol);
3409  case SecurityType.FutureOption:
3410  return FutureOptionSymbol.GetLastDayOfTrading(symbol);
3411  case SecurityType.Future:
3412  case SecurityType.IndexOption:
3413  return symbol.ID.Date;
3414  default:
3415  return mapFile?.DelistingDate ?? Time.EndOfTime;
3416  }
3417  }
3418 
3419  /// <summary>
3420  /// Helper method to determine if a given symbol is of custom data
3421  /// </summary>
3422  public static bool IsCustomDataType<T>(this Symbol symbol)
3423  {
3424  return symbol.SecurityType == SecurityType.Base
3425  && SecurityIdentifier.TryGetCustomDataType(symbol.ID.Symbol, out var type)
3426  && type.Equals(typeof(T).Name, StringComparison.InvariantCultureIgnoreCase);
3427  }
3428 
3429  /// <summary>
3430  /// Helper method that will return a back month, with future expiration, future contract based on the given offset
3431  /// </summary>
3432  /// <param name="symbol">The none canonical future symbol</param>
3433  /// <param name="offset">The quantity of contracts to move into the future expiration chain</param>
3434  /// <returns>A new future expiration symbol instance</returns>
3435  public static Symbol AdjustSymbolByOffset(this Symbol symbol, uint offset)
3436  {
3437  if (symbol.SecurityType != SecurityType.Future || symbol.IsCanonical())
3438  {
3439  throw new InvalidOperationException(Messages.Extensions.ErrorAdjustingSymbolByOffset);
3440  }
3441 
3442  var expiration = symbol.ID.Date;
3443  for (var i = 0; i < offset; i++)
3444  {
3445  var expiryFunction = FuturesExpiryFunctions.FuturesExpiryFunction(symbol);
3446  DateTime newExpiration;
3447  // for the current expiration we add a month to get the next one
3448  var monthOffset = 0;
3449  do
3450  {
3451  monthOffset++;
3452  newExpiration = expiryFunction(expiration.AddMonths(monthOffset)).Date;
3453  } while (newExpiration <= expiration);
3454 
3455  expiration = newExpiration;
3456  symbol = Symbol.CreateFuture(symbol.ID.Symbol, symbol.ID.Market, newExpiration);
3457  }
3458 
3459  return symbol;
3460  }
3461 
3462  /// <summary>
3463  /// Helper method to unsubscribe a given configuration, handling any required mapping
3464  /// </summary>
3465  public static void UnsubscribeWithMapping(this IDataQueueHandler dataQueueHandler, SubscriptionDataConfig dataConfig)
3466  {
3467  if (dataConfig.Symbol.TryGetLiveSubscriptionSymbol(out var mappedSymbol))
3468  {
3469  dataConfig = new SubscriptionDataConfig(dataConfig, symbol: mappedSymbol, mappedConfig: true);
3470  }
3471  dataQueueHandler.Unsubscribe(dataConfig);
3472  }
3473 
3474  /// <summary>
3475  /// Helper method to subscribe a given configuration, handling any required mapping
3476  /// </summary>
3477  public static IEnumerator<BaseData> SubscribeWithMapping(this IDataQueueHandler dataQueueHandler,
3478  SubscriptionDataConfig dataConfig,
3479  EventHandler newDataAvailableHandler,
3480  Func<SubscriptionDataConfig, bool> isExpired,
3481  out SubscriptionDataConfig subscribedConfig)
3482  {
3483  subscribedConfig = dataConfig;
3484  if (dataConfig.Symbol.TryGetLiveSubscriptionSymbol(out var mappedSymbol))
3485  {
3486  subscribedConfig = new SubscriptionDataConfig(dataConfig, symbol: mappedSymbol, mappedConfig: true);
3487  }
3488 
3489  // during warmup we might get requested to add some asset which has already expired in which case the live enumerator will be empty
3490  IEnumerator<BaseData> result = null;
3491  if (!isExpired(subscribedConfig))
3492  {
3493  result = dataQueueHandler.Subscribe(subscribedConfig, newDataAvailableHandler);
3494  }
3495  else
3496  {
3497  Log.Trace($"SubscribeWithMapping(): skip live subscription for expired asset {subscribedConfig}");
3498  }
3499  return result ?? Enumerable.Empty<BaseData>().GetEnumerator();
3500  }
3501 
3502  /// <summary>
3503  /// Helper method to stream read lines from a file
3504  /// </summary>
3505  /// <param name="dataProvider">The data provider to use</param>
3506  /// <param name="file">The file path to read from</param>
3507  /// <returns>Enumeration of lines in file</returns>
3508  public static IEnumerable<string> ReadLines(this IDataProvider dataProvider, string file)
3509  {
3510  if(dataProvider == null)
3511  {
3512  throw new ArgumentException(Messages.Extensions.NullDataProvider);
3513  }
3514  var stream = dataProvider.Fetch(file);
3515  if (stream == null)
3516  {
3517  yield break;
3518  }
3519 
3520  using (var streamReader = new StreamReader(stream))
3521  {
3522  string line;
3523  do
3524  {
3525  line = streamReader.ReadLine();
3526  if (line != null)
3527  {
3528  yield return line;
3529  }
3530  }
3531  while (line != null);
3532  }
3533  }
3534 
3535  /// <summary>
3536  /// Scale data based on factor function
3537  /// </summary>
3538  /// <param name="data">Data to Adjust</param>
3539  /// <param name="factorFunc">Function to factor prices by</param>
3540  /// <param name="volumeFactor">Factor to multiply volume/askSize/bidSize/quantity by</param>
3541  /// <param name="factor">Price scale</param>
3542  /// <param name="sumOfDividends">The current dividend sum</param>
3543  /// <remarks>Volume values are rounded to the nearest integer, lot size purposefully not considered
3544  /// as scaling only applies to equities</remarks>
3545  public static BaseData Scale(this BaseData data, Func<decimal, decimal, decimal, decimal> factorFunc, decimal volumeFactor, decimal factor, decimal sumOfDividends)
3546  {
3547  switch (data.DataType)
3548  {
3549  case MarketDataType.TradeBar:
3550  var tradeBar = data as TradeBar;
3551  if (tradeBar != null)
3552  {
3553  tradeBar.Open = factorFunc(tradeBar.Open, factor, sumOfDividends);
3554  tradeBar.High = factorFunc(tradeBar.High, factor, sumOfDividends);
3555  tradeBar.Low = factorFunc(tradeBar.Low, factor, sumOfDividends);
3556  tradeBar.Close = factorFunc(tradeBar.Close, factor, sumOfDividends);
3557  tradeBar.Volume = Math.Round(tradeBar.Volume * volumeFactor);
3558  }
3559  break;
3560  case MarketDataType.Tick:
3561  var securityType = data.Symbol.SecurityType;
3562  if (securityType != SecurityType.Equity &&
3563  securityType != SecurityType.Future &&
3564  !securityType.IsOption())
3565  {
3566  break;
3567  }
3568 
3569  var tick = data as Tick;
3570  if (tick == null || tick.TickType == TickType.OpenInterest)
3571  {
3572  break;
3573  }
3574 
3575  if (tick.TickType == TickType.Trade)
3576  {
3577  tick.Value = factorFunc(tick.Value, factor, sumOfDividends);
3578  tick.Quantity = Math.Round(tick.Quantity * volumeFactor);
3579  break;
3580  }
3581 
3582  tick.BidPrice = tick.BidPrice != 0 ? factorFunc(tick.BidPrice, factor, sumOfDividends) : 0;
3583  tick.BidSize = Math.Round(tick.BidSize * volumeFactor);
3584  tick.AskPrice = tick.AskPrice != 0 ? factorFunc(tick.AskPrice, factor, sumOfDividends) : 0;
3585  tick.AskSize = Math.Round(tick.AskSize * volumeFactor);
3586 
3587  if (tick.BidPrice == 0)
3588  {
3589  tick.Value = tick.AskPrice;
3590  break;
3591  }
3592  if (tick.AskPrice == 0)
3593  {
3594  tick.Value = tick.BidPrice;
3595  break;
3596  }
3597 
3598  tick.Value = (tick.BidPrice + tick.AskPrice) / 2m;
3599  break;
3600  case MarketDataType.QuoteBar:
3601  var quoteBar = data as QuoteBar;
3602  if (quoteBar != null)
3603  {
3604  if (quoteBar.Ask != null)
3605  {
3606  quoteBar.Ask.Open = factorFunc(quoteBar.Ask.Open, factor, sumOfDividends);
3607  quoteBar.Ask.High = factorFunc(quoteBar.Ask.High, factor, sumOfDividends);
3608  quoteBar.Ask.Low = factorFunc(quoteBar.Ask.Low, factor, sumOfDividends);
3609  quoteBar.Ask.Close = factorFunc(quoteBar.Ask.Close, factor, sumOfDividends);
3610  }
3611  if (quoteBar.Bid != null)
3612  {
3613  quoteBar.Bid.Open = factorFunc(quoteBar.Bid.Open, factor, sumOfDividends);
3614  quoteBar.Bid.High = factorFunc(quoteBar.Bid.High, factor, sumOfDividends);
3615  quoteBar.Bid.Low = factorFunc(quoteBar.Bid.Low, factor, sumOfDividends);
3616  quoteBar.Bid.Close = factorFunc(quoteBar.Bid.Close, factor, sumOfDividends);
3617  }
3618  quoteBar.Value = quoteBar.Close;
3619  quoteBar.LastAskSize = Math.Round(quoteBar.LastAskSize * volumeFactor);
3620  quoteBar.LastBidSize = Math.Round(quoteBar.LastBidSize * volumeFactor);
3621  }
3622  break;
3623  case MarketDataType.Auxiliary:
3624  case MarketDataType.Base:
3625  case MarketDataType.OptionChain:
3626  case MarketDataType.FuturesChain:
3627  break;
3628  default:
3629  throw new ArgumentOutOfRangeException();
3630  }
3631  return data;
3632  }
3633 
3634  /// <summary>
3635  /// Normalize prices based on configuration
3636  /// </summary>
3637  /// <param name="data">Data to be normalized</param>
3638  /// <param name="factor">Price scale</param>
3639  /// <param name="normalizationMode">The price scaling normalization mode</param>
3640  /// <param name="sumOfDividends">The current dividend sum</param>
3641  /// <returns>The provided data point adjusted</returns>
3642  public static BaseData Normalize(this BaseData data, decimal factor, DataNormalizationMode normalizationMode, decimal sumOfDividends)
3643  {
3644  switch (normalizationMode)
3645  {
3646  case DataNormalizationMode.Adjusted:
3647  case DataNormalizationMode.SplitAdjusted:
3648  case DataNormalizationMode.ScaledRaw:
3649  return data?.Scale(TimesFactor, 1 / factor, factor, decimal.Zero);
3650  case DataNormalizationMode.TotalReturn:
3651  return data.Scale(TimesFactor, 1 / factor, factor, sumOfDividends);
3652 
3653  case DataNormalizationMode.BackwardsRatio:
3654  return data.Scale(TimesFactor, 1, factor, decimal.Zero);
3655  case DataNormalizationMode.BackwardsPanamaCanal:
3656  return data.Scale(AdditionFactor, 1, factor, decimal.Zero);
3657  case DataNormalizationMode.ForwardPanamaCanal:
3658  return data.Scale(AdditionFactor, 1, factor, decimal.Zero);
3659 
3660  case DataNormalizationMode.Raw:
3661  default:
3662  return data;
3663  }
3664  }
3665 
3666  /// <summary>
3667  /// Applies a times factor. We define this so we don't need to create it constantly
3668  /// </summary>
3669  [MethodImpl(MethodImplOptions.AggressiveInlining)]
3670  private static decimal TimesFactor(decimal target, decimal factor, decimal sumOfDividends)
3671  {
3672  return target * factor + sumOfDividends;
3673  }
3674 
3675  /// <summary>
3676  /// Applies an addition factor. We define this so we don't need to create it constantly
3677  /// </summary>
3678  [MethodImpl(MethodImplOptions.AggressiveInlining)]
3679  private static decimal AdditionFactor(decimal target, decimal factor, decimal _)
3680  {
3681  return target + factor;
3682  }
3683 
3684  /// <summary>
3685  /// Helper method to determine if price scales need an update based on the given data point
3686  /// </summary>
3687  public static DateTime GetUpdatePriceScaleFrontier(this BaseData data)
3688  {
3689  if (data != null)
3690  {
3691  var priceScaleFrontier = data.Time;
3692  if (data.Time.Date != data.EndTime.Date && data.EndTime.TimeOfDay > TimeSpan.Zero)
3693  {
3694  // 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,
3695  // 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
3696  // 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.
3697  // 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
3698  // the daily bar would go from midnight X+1 day to midnight X+2
3699  // TODO: see related issue https://github.com/QuantConnect/Lean/issues/6964 which would avoid the need for this
3700  priceScaleFrontier = data.EndTime;
3701  }
3702  return priceScaleFrontier;
3703  }
3704  return DateTime.MinValue;
3705  }
3706 
3707  /// <summary>
3708  /// Thread safe concurrent dictionary order by implementation by using <see cref="SafeEnumeration{TSource,TKey}"/>
3709  /// </summary>
3710  /// <remarks>See https://stackoverflow.com/questions/47630824/is-c-sharp-linq-orderby-threadsafe-when-used-with-concurrentdictionarytkey-tva</remarks>
3711  public static IOrderedEnumerable<KeyValuePair<TSource, TKey>> OrderBySafe<TSource, TKey>(
3712  this ConcurrentDictionary<TSource, TKey> source, Func<KeyValuePair<TSource, TKey>, TSource> keySelector
3713  )
3714  {
3715  return source.SafeEnumeration().OrderBy(keySelector);
3716  }
3717 
3718  /// <summary>
3719  /// Thread safe concurrent dictionary order by implementation by using <see cref="SafeEnumeration{TSource,TKey}"/>
3720  /// </summary>
3721  /// <remarks>See https://stackoverflow.com/questions/47630824/is-c-sharp-linq-orderby-threadsafe-when-used-with-concurrentdictionarytkey-tva</remarks>
3722  public static IOrderedEnumerable<KeyValuePair<TSource, TKey>> OrderBySafe<TSource, TKey>(
3723  this ConcurrentDictionary<TSource, TKey> source, Func<KeyValuePair<TSource, TKey>, TKey> keySelector
3724  )
3725  {
3726  return source.SafeEnumeration().OrderBy(keySelector);
3727  }
3728 
3729  /// <summary>
3730  /// Force concurrent dictionary enumeration using a thread safe implementation
3731  /// </summary>
3732  /// <remarks>See https://stackoverflow.com/questions/47630824/is-c-sharp-linq-orderby-threadsafe-when-used-with-concurrentdictionarytkey-tva</remarks>
3733  public static IEnumerable<KeyValuePair<TSource, TKey>> SafeEnumeration<TSource, TKey>(
3734  this ConcurrentDictionary<TSource, TKey> source)
3735  {
3736  foreach (var kvp in source)
3737  {
3738  yield return kvp;
3739  }
3740  }
3741 
3742  /// <summary>
3743  /// Helper method to determine the right data normalization mode to use by default
3744  /// </summary>
3746  {
3747  switch (securityType)
3748  {
3749  case SecurityType.Future:
3750  if (universeSettings.DataNormalizationMode is DataNormalizationMode.BackwardsRatio
3751  or DataNormalizationMode.BackwardsPanamaCanal or DataNormalizationMode.ForwardPanamaCanal
3752  or DataNormalizationMode.Raw)
3753  {
3754  return universeSettings.DataNormalizationMode;
3755  }
3756  return DataNormalizationMode.BackwardsRatio;
3757  default:
3758  return universeSettings.DataNormalizationMode;
3759  }
3760  }
3761 
3762  /// <summary>
3763  /// Returns a hex string of the byte array.
3764  /// </summary>
3765  /// <param name="source">the byte array to be represented as string</param>
3766  /// <returns>A new string containing the items in the enumerable</returns>
3767  public static string ToHexString(this byte[] source)
3768  {
3769  if (source == null || source.Length == 0)
3770  {
3771  throw new ArgumentException(Messages.Extensions.NullOrEmptySourceToConvertToHexString);
3772  }
3773 
3774  var hex = new StringBuilder(source.Length * 2);
3775  foreach (var b in source)
3776  {
3777  hex.AppendFormat(CultureInfo.InvariantCulture, "{0:x2}", b);
3778  }
3779 
3780  return hex.ToString();
3781  }
3782 
3783  /// <summary>
3784  /// Gets the option exercise order direction resulting from the specified <paramref name="right"/> and
3785  /// whether or not we wrote the option (<paramref name="isShort"/> is <code>true</code>) or bought to
3786  /// option (<paramref name="isShort"/> is <code>false</code>)
3787  /// </summary>
3788  /// <param name="right">The option right</param>
3789  /// <param name="isShort">True if we wrote the option, false if we purchased the option</param>
3790  /// <returns>The order direction resulting from an exercised option</returns>
3791  public static OrderDirection GetExerciseDirection(this OptionRight right, bool isShort)
3792  {
3793  switch (right)
3794  {
3795  case OptionRight.Call:
3796  return isShort ? OrderDirection.Sell : OrderDirection.Buy;
3797  default:
3798  return isShort ? OrderDirection.Buy : OrderDirection.Sell;
3799  }
3800  }
3801 
3802  /// <summary>
3803  /// Gets the <see cref="OrderDirection"/> for the specified <paramref name="quantity"/>
3804  /// </summary>
3805  public static OrderDirection GetOrderDirection(decimal quantity)
3806  {
3807  var sign = Math.Sign(quantity);
3808  switch (sign)
3809  {
3810  case 1: return OrderDirection.Buy;
3811  case 0: return OrderDirection.Hold;
3812  case -1: return OrderDirection.Sell;
3813  default:
3814  throw new ApplicationException(
3815  $"The skies are falling and the oceans are rising! Math.Sign({quantity}) returned {sign} :/"
3816  );
3817  }
3818  }
3819 
3820  /// <summary>
3821  /// Helper method to process an algorithms security changes, will add and remove securities according to them
3822  /// </summary>
3823  public static void ProcessSecurityChanges(this IAlgorithm algorithm, SecurityChanges securityChanges)
3824  {
3825  foreach (var security in securityChanges.AddedSecurities)
3826  {
3827  security.IsTradable = true;
3828 
3829  // uses TryAdd, so don't need to worry about duplicates here
3830  algorithm.Securities.Add(security);
3831  }
3832 
3833  var activeSecurities = algorithm.UniverseManager.ActiveSecurities;
3834  foreach (var security in securityChanges.RemovedSecurities)
3835  {
3836  if (!activeSecurities.ContainsKey(security.Symbol))
3837  {
3838  security.IsTradable = false;
3839  }
3840  }
3841  }
3842 
3843  /// <summary>
3844  /// Helper method to set an algorithm runtime exception in a normalized fashion
3845  /// </summary>
3846  public static void SetRuntimeError(this IAlgorithm algorithm, Exception exception, string context)
3847  {
3848  Log.Error(exception, $"Extensions.SetRuntimeError(): {Messages.Extensions.RuntimeError(algorithm, context)}");
3849  exception = StackExceptionInterpreter.Instance.Value.Interpret(exception);
3850  algorithm.RunTimeError = exception;
3851  algorithm.SetStatus(AlgorithmStatus.RuntimeError);
3852  }
3853 
3854  /// <summary>
3855  /// Creates a <see cref="OptionChainUniverse"/> for a given symbol
3856  /// </summary>
3857  /// <param name="algorithm">The algorithm instance to create universes for</param>
3858  /// <param name="symbol">Symbol of the option</param>
3859  /// <param name="filter">The option filter to use</param>
3860  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3861  /// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
3862  public static OptionChainUniverse CreateOptionChain(this IAlgorithm algorithm, Symbol symbol, PyObject filter, UniverseSettings universeSettings = null)
3863  {
3864  var result = CreateOptionChain(algorithm, symbol, out var option, universeSettings);
3865  option.SetFilter(filter);
3866  return result;
3867  }
3868 
3869  /// <summary>
3870  /// Creates a <see cref="OptionChainUniverse"/> for a given symbol
3871  /// </summary>
3872  /// <param name="algorithm">The algorithm instance to create universes for</param>
3873  /// <param name="symbol">Symbol of the option</param>
3874  /// <param name="filter">The option filter to use</param>
3875  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3876  /// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
3877  public static OptionChainUniverse CreateOptionChain(this IAlgorithm algorithm, Symbol symbol, Func<OptionFilterUniverse, OptionFilterUniverse> filter, UniverseSettings universeSettings = null)
3878  {
3879  var result = CreateOptionChain(algorithm, symbol, out var option, universeSettings);
3880  option.SetFilter(filter);
3881  return result;
3882  }
3883 
3884  /// <summary>
3885  /// Creates a <see cref="OptionChainUniverse"/> for a given symbol
3886  /// </summary>
3887  /// <param name="algorithm">The algorithm instance to create universes for</param>
3888  /// <param name="symbol">Symbol of the option</param>
3889  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3890  /// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
3891  private static OptionChainUniverse CreateOptionChain(this IAlgorithm algorithm, Symbol symbol, out Option option, UniverseSettings universeSettings = null)
3892  {
3893  if (!symbol.SecurityType.IsOption())
3894  {
3895  throw new ArgumentException(Messages.Extensions.CreateOptionChainRequiresOptionSymbol);
3896  }
3897 
3898  // resolve defaults if not specified
3899  var settings = universeSettings ?? algorithm.UniverseSettings;
3900 
3901  option = (Option)algorithm.AddSecurity(symbol.Canonical, settings.Resolution, settings.FillForward, settings.Leverage, settings.ExtendedMarketHours);
3902 
3903  return (OptionChainUniverse)algorithm.UniverseManager.Values.Single(universe => universe.Configuration.Symbol == symbol.Canonical);
3904  }
3905 
3906  /// <summary>
3907  /// Creates a <see cref="FuturesChainUniverse"/> for a given symbol
3908  /// </summary>
3909  /// <param name="algorithm">The algorithm instance to create universes for</param>
3910  /// <param name="symbol">Symbol of the future</param>
3911  /// <param name="filter">The future filter to use</param>
3912  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3913  public static IEnumerable<Universe> CreateFutureChain(this IAlgorithm algorithm, Symbol symbol, PyObject filter, UniverseSettings universeSettings = null)
3914  {
3915  var result = CreateFutureChain(algorithm, symbol, out var future, universeSettings);
3916  future.SetFilter(filter);
3917  return result;
3918  }
3919 
3920  /// <summary>
3921  /// Creates a <see cref="FuturesChainUniverse"/> for a given symbol
3922  /// </summary>
3923  /// <param name="algorithm">The algorithm instance to create universes for</param>
3924  /// <param name="symbol">Symbol of the future</param>
3925  /// <param name="filter">The future filter to use</param>
3926  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3927  public static IEnumerable<Universe> CreateFutureChain(this IAlgorithm algorithm, Symbol symbol, Func<FutureFilterUniverse, FutureFilterUniverse> filter, UniverseSettings universeSettings = null)
3928  {
3929  var result = CreateFutureChain(algorithm, symbol, out var future, universeSettings);
3930  future.SetFilter(filter);
3931  return result;
3932  }
3933 
3934  /// <summary>
3935  /// Creates a <see cref="FuturesChainUniverse"/> for a given symbol
3936  /// </summary>
3937  private static IEnumerable<Universe> CreateFutureChain(this IAlgorithm algorithm, Symbol symbol, out Future future, UniverseSettings universeSettings = null)
3938  {
3939  if (symbol.SecurityType != SecurityType.Future)
3940  {
3941  throw new ArgumentException(Messages.Extensions.CreateFutureChainRequiresFutureSymbol);
3942  }
3943 
3944  // resolve defaults if not specified
3945  var settings = universeSettings ?? algorithm.UniverseSettings;
3946 
3947  var dataNormalizationMode = settings.GetUniverseNormalizationModeOrDefault(symbol.SecurityType);
3948 
3949  future = (Future)algorithm.AddSecurity(symbol.Canonical, settings.Resolution, settings.FillForward, settings.Leverage, settings.ExtendedMarketHours,
3950  settings.DataMappingMode, dataNormalizationMode, settings.ContractDepthOffset);
3951 
3952  // let's yield back both the future chain and the continuous future universe
3953  return algorithm.UniverseManager.Values.Where(universe => universe.Configuration.Symbol == symbol.Canonical || ContinuousContractUniverse.CreateSymbol(symbol.Canonical) == universe.Configuration.Symbol);
3954  }
3955 
3956  private static bool _notifiedUniverseSettingsUsed;
3957  private static readonly HashSet<SecurityType> _supportedSecurityTypes = new()
3958  {
3959  SecurityType.Equity,
3960  SecurityType.Forex,
3961  SecurityType.Cfd,
3962  SecurityType.Option,
3963  SecurityType.Future,
3964  SecurityType.FutureOption,
3965  SecurityType.IndexOption,
3966  SecurityType.Crypto,
3967  SecurityType.CryptoFuture
3968  };
3969 
3970  /// <summary>
3971  /// Gets the security for the specified symbol from the algorithm's securities collection.
3972  /// In case the security is not found, it will be created using the <see cref="IAlgorithm.UniverseSettings"/>
3973  /// and a best effort configuration setup.
3974  /// </summary>
3975  /// <param name="algorithm">The algorithm instance</param>
3976  /// <param name="symbol">The symbol which security is being looked up</param>
3977  /// <param name="security">The found or added security instance</param>
3978  /// <param name="onError">Callback to invoke in case of unsupported security type</param>
3979  /// <returns>True if the security was found or added</returns>
3980  public static bool GetOrAddUnrequestedSecurity(this IAlgorithm algorithm, Symbol symbol, out Security security,
3981  Action<IReadOnlyCollection<SecurityType>> onError = null)
3982  {
3983  if (!algorithm.Securities.TryGetValue(symbol, out security))
3984  {
3985  if (!_supportedSecurityTypes.Contains(symbol.SecurityType))
3986  {
3987  Log.Error("GetOrAddUnrequestedSecurity(): Unsupported security type: " + symbol.SecurityType + "-" + symbol.Value);
3988  onError?.Invoke(_supportedSecurityTypes);
3989  return false;
3990  }
3991 
3992  var resolution = algorithm.UniverseSettings.Resolution;
3993  var fillForward = algorithm.UniverseSettings.FillForward;
3994  var leverage = algorithm.UniverseSettings.Leverage;
3995  var extendedHours = algorithm.UniverseSettings.ExtendedMarketHours;
3996 
3997  if (!_notifiedUniverseSettingsUsed)
3998  {
3999  // let's just send the message once
4000  _notifiedUniverseSettingsUsed = true;
4001 
4002  var leverageMsg = $" Leverage = {leverage};";
4003  if (leverage == Security.NullLeverage)
4004  {
4005  leverageMsg = $" Leverage = default;";
4006  }
4007  algorithm.Debug($"Will use UniverseSettings for automatically added securities for open orders and holdings. UniverseSettings:" +
4008  $" Resolution = {resolution};{leverageMsg} FillForward = {fillForward}; ExtendedHours = {extendedHours}");
4009  }
4010 
4011  Log.Trace("GetOrAddUnrequestedSecurity(): Adding unrequested security: " + symbol.Value);
4012 
4013  if (symbol.SecurityType.IsOption())
4014  {
4015  // add current option contract to the system
4016  security = algorithm.AddOptionContract(symbol, resolution, fillForward, leverage, extendedHours);
4017  }
4018  else if (symbol.SecurityType == SecurityType.Future)
4019  {
4020  // add current future contract to the system
4021  security = algorithm.AddFutureContract(symbol, resolution, fillForward, leverage, extendedHours);
4022  }
4023  else
4024  {
4025  // for items not directly requested set leverage to 1 and at the min resolution
4026  security = algorithm.AddSecurity(symbol.SecurityType, symbol.Value, resolution, symbol.ID.Market, fillForward, leverage, extendedHours);
4027  }
4028  }
4029  return true;
4030  }
4031 
4032  /// <summary>
4033  /// Inverts the specified <paramref name="right"/>
4034  /// </summary>
4035  public static OptionRight Invert(this OptionRight right)
4036  {
4037  switch (right)
4038  {
4039  case OptionRight.Call: return OptionRight.Put;
4040  case OptionRight.Put: return OptionRight.Call;
4041  default:
4042  throw new ArgumentOutOfRangeException(nameof(right), right, null);
4043  }
4044  }
4045 
4046  /// <summary>
4047  /// Compares two values using given operator
4048  /// </summary>
4049  /// <typeparam name="T"></typeparam>
4050  /// <param name="op">Comparison operator</param>
4051  /// <param name="arg1">The first value</param>
4052  /// <param name="arg2">The second value</param>
4053  /// <returns>Returns true if its left-hand operand meets the operator value to its right-hand operand, false otherwise</returns>
4054  public static bool Compare<T>(this ComparisonOperatorTypes op, T arg1, T arg2) where T : IComparable
4055  {
4056  return ComparisonOperator.Compare(op, arg1, arg2);
4057  }
4058 
4059  /// <summary>
4060  /// Converts a <see cref="Data.HistoryRequest" /> instance to a <see cref="SubscriptionDataConfig"/> instance
4061  /// </summary>
4062  /// <param name="request">History request</param>
4063  /// <param name="isInternalFeed">
4064  /// Set to true if this subscription is added for the sole purpose of providing currency conversion rates,
4065  /// setting this flag to true will prevent the data from being sent into the algorithm's OnData methods
4066  /// </param>
4067  /// <param name="isFilteredSubscription">True if this subscription should have filters applied to it (market hours/user filters from security), false otherwise</param>
4068  /// <returns>Subscription data configuration</returns>
4069  public static SubscriptionDataConfig ToSubscriptionDataConfig(this Data.HistoryRequest request, bool isInternalFeed = false, bool isFilteredSubscription = true)
4070  {
4071  return new SubscriptionDataConfig(request.DataType,
4072  request.Symbol,
4073  request.Resolution,
4074  request.DataTimeZone,
4075  request.ExchangeHours.TimeZone,
4076  request.FillForwardResolution.HasValue,
4077  request.IncludeExtendedMarketHours,
4078  isInternalFeed,
4079  request.IsCustomData,
4080  request.TickType,
4081  isFilteredSubscription,
4082  request.DataNormalizationMode,
4083  request.DataMappingMode,
4084  request.ContractDepthOffset
4085  );
4086  }
4087 
4088  /// <summary>
4089  /// Centralized logic used at the top of the subscription enumerator stacks to determine if we should emit base data points
4090  /// based on the configuration for this subscription and the type of data we are handling.
4091  ///
4092  /// Currently we only want to emit split/dividends/delisting events for non internal <see cref="TradeBar"/> configurations
4093  /// this last part is because equities also have <see cref="QuoteBar"/> subscriptions which will also subscribe to the
4094  /// same aux events and we don't want duplicate emits of these events in the TimeSliceFactory
4095  /// </summary>
4096  /// <remarks>The "TimeSliceFactory" does not allow for multiple dividends/splits per symbol in the same time slice
4097  /// but we don't want to rely only on that to filter out duplicated aux data so we use this at the top of
4098  /// our data enumerator stacks to define what subscription should emit this data.</remarks>
4099  /// <remarks>We use this function to filter aux data at the top of the subscription enumerator stack instead of
4100  /// stopping the subscription stack from subscribing to aux data at the bottom because of a
4101  /// dependency with the FF enumerators requiring that they receive aux data to properly handle delistings.
4102  /// Otherwise we would have issues with delisted symbols continuing to fill forward after expiry/delisting.
4103  /// Reference PR #5485 and related issues for more.</remarks>
4104  public static bool ShouldEmitData(this SubscriptionDataConfig config, BaseData data, bool isUniverse = false)
4105  {
4106  // 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
4107  if (data.DataType != MarketDataType.Auxiliary)
4108  {
4109  return true;
4110  }
4111 
4112  // This filter does not apply to auxiliary data outside of delisting/splits/dividends so lets those emit
4113  var type = data.GetType();
4114  var expectedType = type.IsAssignableTo(config.Type);
4115 
4116  // Check our config type first to be lazy about using data.GetType() unless required
4117  var configTypeFilter = (config.Type == typeof(TradeBar) || config.Type == typeof(ZipEntryName) ||
4118  config.Type == typeof(Tick) && config.TickType == TickType.Trade || config.IsCustomData);
4119 
4120  if (!configTypeFilter)
4121  {
4122  return expectedType;
4123  }
4124 
4125  // We don't want to pump in any data to `Universe.SelectSymbols(...)` if the
4126  // type is not configured to be consumed by the universe. This change fixes
4127  // a case where a `SymbolChangedEvent` was being passed to an ETF constituent universe
4128  // for filtering/selection, and would result in either a runtime error
4129  // if casting into the expected type explicitly, or call the filter function with
4130  // no data being provided, resulting in all universe Symbols being de-selected.
4131  if (isUniverse && !expectedType)
4132  {
4133  return (data as Delisting)?.Type == DelistingType.Delisted;
4134  }
4135 
4136  if (!(type == typeof(Delisting) || type == typeof(Split) || type == typeof(Dividend)))
4137  {
4138  return true;
4139  }
4140 
4141  // If we made it here then only filter it if its an InternalFeed
4142  return !config.IsInternalFeed;
4143  }
4144 
4145  /// <summary>
4146  /// Gets the <see cref="OrderDirection"/> that corresponds to the specified <paramref name="side"/>
4147  /// </summary>
4148  /// <param name="side">The position side to be converted</param>
4149  /// <returns>The order direction that maps from the provided position side</returns>
4151  {
4152  switch (side)
4153  {
4154  case PositionSide.Short: return OrderDirection.Sell;
4155  case PositionSide.None: return OrderDirection.Hold;
4156  case PositionSide.Long: return OrderDirection.Buy;
4157  default:
4158  throw new ArgumentOutOfRangeException(nameof(side), side, null);
4159  }
4160  }
4161 
4162  /// <summary>
4163  /// Determines if an order with the specified <paramref name="direction"/> would close a position with the
4164  /// specified <paramref name="side"/>
4165  /// </summary>
4166  /// <param name="direction">The direction of the order, buy/sell</param>
4167  /// <param name="side">The side of the position, long/short</param>
4168  /// <returns>True if the order direction would close the position, otherwise false</returns>
4169  public static bool Closes(this OrderDirection direction, PositionSide side)
4170  {
4171  switch (side)
4172  {
4173  case PositionSide.Short:
4174  switch (direction)
4175  {
4176  case OrderDirection.Buy: return true;
4177  case OrderDirection.Sell: return false;
4178  case OrderDirection.Hold: return false;
4179  default:
4180  throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
4181  }
4182 
4183  case PositionSide.Long:
4184  switch (direction)
4185  {
4186  case OrderDirection.Buy: return false;
4187  case OrderDirection.Sell: return true;
4188  case OrderDirection.Hold: return false;
4189  default:
4190  throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
4191  }
4192 
4193  case PositionSide.None:
4194  return false;
4195 
4196  default:
4197  throw new ArgumentOutOfRangeException(nameof(side), side, null);
4198  }
4199  }
4200 
4201  /// <summary>
4202  /// Determines if the two lists are equal, including all items at the same indices.
4203  /// </summary>
4204  /// <typeparam name="T">The element type</typeparam>
4205  /// <param name="left">The left list</param>
4206  /// <param name="right">The right list</param>
4207  /// <returns>True if the two lists have the same counts and items at each index evaluate as equal</returns>
4208  public static bool ListEquals<T>(this IReadOnlyList<T> left, IReadOnlyList<T> right)
4209  {
4210  var count = left.Count;
4211  if (count != right.Count)
4212  {
4213  return false;
4214  }
4215 
4216  for (int i = 0; i < count; i++)
4217  {
4218  if (!left[i].Equals(right[i]))
4219  {
4220  return false;
4221  }
4222  }
4223 
4224  return true;
4225  }
4226 
4227  /// <summary>
4228  /// Computes a deterministic hash code based on the items in the list. This hash code is dependent on the
4229  /// ordering of items.
4230  /// </summary>
4231  /// <typeparam name="T">The element type</typeparam>
4232  /// <param name="list">The list</param>
4233  /// <returns>A hash code dependent on the ordering of elements in the list</returns>
4234  public static int GetListHashCode<T>(this IReadOnlyList<T> list)
4235  {
4236  unchecked
4237  {
4238  var hashCode = 17;
4239  for (int i = 0; i < list.Count; i++)
4240  {
4241  hashCode += (hashCode * 397) ^ list[i].GetHashCode();
4242  }
4243 
4244  return hashCode;
4245  }
4246  }
4247 
4248  /// <summary>
4249  /// Determine if this SecurityType requires mapping
4250  /// </summary>
4251  /// <param name="symbol">Type to check</param>
4252  /// <returns>True if it needs to be mapped</returns>
4253  public static bool RequiresMapping(this Symbol symbol)
4254  {
4255  switch (symbol.SecurityType)
4256  {
4257  case SecurityType.Base:
4258  return symbol.HasUnderlying && symbol.Underlying.RequiresMapping();
4259  case SecurityType.Future:
4260  return symbol.IsCanonical();
4261  case SecurityType.Equity:
4262  case SecurityType.Option:
4263  return true;
4264  default:
4265  return false;
4266  }
4267  }
4268 
4269  /// <summary>
4270  /// Checks whether the fill event for closing a trade is a winning trade
4271  /// </summary>
4272  /// <param name="fill">The fill event</param>
4273  /// <param name="security">The security being traded</param>
4274  /// <param name="profitLoss">The profit-loss for the closed trade</param>
4275  /// <returns>
4276  /// Whether the trade is a win.
4277  /// For options assignments this depends on whether the option is ITM or OTM and the position side.
4278  /// See <see cref="Trade.IsWin"/> for more information.
4279  /// </returns>
4280  public static bool IsWin(this OrderEvent fill, Security security, decimal profitLoss)
4281  {
4282  // For non-options or non-exercise orders, the trade is a win if the profit-loss is positive
4283  if (!fill.Symbol.SecurityType.IsOption() || fill.Ticket.OrderType != OrderType.OptionExercise)
4284  {
4285  return profitLoss > 0;
4286  }
4287 
4288  var option = (Option)security;
4289 
4290  // If the fill is a sell, the original transaction was a buy
4291  if (fill.Direction == OrderDirection.Sell)
4292  {
4293  // If the option is ITM, the trade is a win only if the profit is greater than the ITM amount
4294  return fill.IsInTheMoney && Math.Abs(profitLoss) < option.InTheMoneyAmount(fill.FillQuantity);
4295  }
4296 
4297  // It is a win if the buyer paid more than what they saved (the ITM amount)
4298  return !fill.IsInTheMoney || Math.Abs(profitLoss) > option.InTheMoneyAmount(fill.FillQuantity);
4299  }
4300 
4301  /// <summary>
4302  /// Gets the option's ITM amount for the given quantity.
4303  /// </summary>
4304  /// <param name="option">The option security</param>
4305  /// <param name="quantity">The quantity</param>
4306  /// <returns>The ITM amount for the absolute quantity</returns>
4307  /// <remarks>The returned value can be negative, which would mean the option is actually OTM.</remarks>
4308  public static ConvertibleCashAmount InTheMoneyAmount(this Option option, decimal quantity)
4309  {
4310  return option.Holdings.GetQuantityValue(Math.Abs(quantity), option.GetPayOff(option.Underlying.Price));
4311  }
4312 
4313  /// <summary>
4314  /// Gets the greatest common divisor of a list of numbers
4315  /// </summary>
4316  /// <param name="values">List of numbers which greatest common divisor is requested</param>
4317  /// <returns>The greatest common divisor for the given list of numbers</returns>
4318  public static int GreatestCommonDivisor(this IEnumerable<int> values)
4319  {
4320  int? result = null;
4321  foreach (var value in values)
4322  {
4323  if (result.HasValue)
4324  {
4325  result = GreatestCommonDivisor(result.Value, value);
4326  }
4327  else
4328  {
4329  result = value;
4330  }
4331  }
4332 
4333  if (!result.HasValue)
4334  {
4335  throw new ArgumentException(Messages.Extensions.GreatestCommonDivisorEmptyList);
4336  }
4337 
4338  return result.Value;
4339  }
4340 
4341  /// <summary>
4342  /// Gets the greatest common divisor of two numbers
4343  /// </summary>
4344  private static int GreatestCommonDivisor(int a, int b)
4345  {
4346  int remainder;
4347  while (b != 0)
4348  {
4349  remainder = a % b;
4350  a = b;
4351  b = remainder;
4352  }
4353  return Math.Abs(a);
4354  }
4355 
4356  /// <summary>
4357  /// Safe method to perform divisions avoiding DivideByZeroException and Overflow/Underflow exceptions
4358  /// </summary>
4359  /// <param name="failValue">Value to be returned if the denominator is zero</param>
4360  /// <returns>The numerator divided by the denominator if the denominator is not
4361  /// zero. Otherwise, the default failValue or the provided one</returns>
4362  public static decimal SafeDivision(this decimal numerator, decimal denominator, decimal failValue = 0)
4363  {
4364  try
4365  {
4366  return (denominator == 0) ? failValue : (numerator / denominator);
4367  }
4368  catch
4369  {
4370  return failValue;
4371  }
4372  }
4373 
4374  public static Type GetCustomDataTypeFromSymbols(Symbol[] symbols)
4375  {
4376  if (symbols.Any())
4377  {
4378  if (!SecurityIdentifier.TryGetCustomDataTypeInstance(symbols[0].ID.Symbol, out var dataType)
4379  || symbols.Any(x => !SecurityIdentifier.TryGetCustomDataTypeInstance(x.ID.Symbol, out var customDataType) || customDataType != dataType))
4380  {
4381  return null;
4382  }
4383  return dataType;
4384  }
4385 
4386  return null;
4387  }
4388 
4389  /// <summary>
4390  /// Determines if certain data type is custom
4391  /// </summary>
4392  /// <param name="symbol">Symbol associated with the data type</param>
4393  /// <param name="type">Data type to determine if it's custom</param>
4394  public static bool IsCustomDataType(Symbol symbol, Type type)
4395  {
4396  return type.Namespace != typeof(Bar).Namespace || Extensions.GetCustomDataTypeFromSymbols(new Symbol[] { symbol }) != null;
4397  }
4398 
4399  /// <summary>
4400  /// Returns the amount of fee's charged by executing a market order with the given arguments
4401  /// </summary>
4402  /// <param name="security">Security for which we would like to make a market order</param>
4403  /// <param name="quantity">Quantity of the security we are seeking to trade</param>
4404  /// <param name="time">Time the order was placed</param>
4405  /// <param name="marketOrder">This out parameter will contain the market order constructed</param>
4406  public static CashAmount GetMarketOrderFees(Security security, decimal quantity, DateTime time, out MarketOrder marketOrder)
4407  {
4408  marketOrder = new MarketOrder(security.Symbol, quantity, time);
4409  return security.FeeModel.GetOrderFee(new OrderFeeParameters(security, marketOrder)).Value;
4410  }
4411 
4412  private static Symbol ConvertToSymbol(PyObject item, bool dispose)
4413  {
4414  if (PyString.IsStringType(item))
4415  {
4416  return SymbolCache.GetSymbol(dispose ? item.GetAndDispose<string>() : item.As<string>());
4417  }
4418  else
4419  {
4420  Symbol symbol;
4421  try
4422  {
4423  symbol = dispose ? item.GetAndDispose<Symbol>() : item.As<Symbol>();
4424  }
4425  catch (Exception e)
4426  {
4427  throw new ArgumentException(Messages.Extensions.ConvertToSymbolEnumerableFailed(item), e);
4428  }
4429  return symbol;
4430  }
4431  }
4432  }
4433 }