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