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  using var pythonType = pyObject.GetPythonType();
2670  var csharpType = pythonType.As<Type>();
2671 
2672  if (!type.IsAssignableFrom(csharpType))
2673  {
2674  return false;
2675  }
2676 
2677  result = (T)pyObject.AsManagedObject(type);
2678 
2679  // The PyObject is a Python object of a Python class that is a subclass of a C# class.
2680  // In this case, we return false just because we want the actual Python object
2681  // so it gets wrapped in a python wrapper, not the C# object.
2682  if (result is IPythonDerivedType)
2683  {
2684  return false;
2685  }
2686 
2687  // If the python type object is just a representation of the C# type, the conversion is direct,
2688  // the python object is an instance of the C# class.
2689  // We can compare by reference because pythonnet caches the PyTypes and because the behavior of
2690  // PyObject.Equals is not exactly what we want:
2691  // e.g. type(class PyClass(CSharpClass)) == type(CSharpClass) is true in Python
2692  if (PythonReferenceComparer.Instance.Equals(PyType.Get(csharpType), pythonType))
2693  {
2694  return true;
2695  }
2696 
2697  // If the PyObject type and the managed object names are the same,
2698  // pyObject is a C# object wrapped in PyObject, in this case return true
2699  // Otherwise, pyObject is a python object that subclass a C# class, only return true if 'allowPythonDerivative'
2700  var name = (((dynamic)pythonType).__name__ as PyObject).GetAndDispose<string>();
2701  return name == result.GetType().Name;
2702  }
2703  catch
2704  {
2705  // Do not throw or log the exception.
2706  // Return false as an exception means that the conversion could not be made.
2707  }
2708  }
2709 
2710  return false;
2711  }
2712 
2713  /// <summary>
2714  /// Tries to convert a <see cref="PyObject"/> into a managed object
2715  /// </summary>
2716  /// <typeparam name="T">Target type of the resulting managed object</typeparam>
2717  /// <param name="pyObject">PyObject to be converted</param>
2718  /// <param name="result">Managed object </param>
2719  /// <returns>True if successful conversion</returns>
2720  public static bool TryConvertToDelegate<T>(this PyObject pyObject, out T result)
2721  {
2722  var type = typeof(T);
2723 
2724  if (!typeof(MulticastDelegate).IsAssignableFrom(type))
2725  {
2726  throw new ArgumentException(Messages.Extensions.ConvertToDelegateCannotConverPyObjectToType("TryConvertToDelegate", type));
2727  }
2728 
2729  result = default(T);
2730 
2731  if (pyObject == null)
2732  {
2733  return true;
2734  }
2735 
2736  var code = string.Empty;
2737  var types = type.GetGenericArguments();
2738 
2739  using (Py.GIL())
2740  {
2741  var locals = new PyDict();
2742  try
2743  {
2744  for (var i = 0; i < types.Length; i++)
2745  {
2746  var iString = i.ToStringInvariant();
2747  code += $",t{iString}";
2748  locals.SetItem($"t{iString}", types[i].ToPython());
2749  }
2750 
2751  locals.SetItem("pyObject", pyObject);
2752 
2753  var name = type.FullName.Substring(0, type.FullName.IndexOf('`'));
2754  code = $"import System; delegate = {name}[{code.Substring(1)}](pyObject)";
2755 
2756  PythonEngine.Exec(code, null, locals);
2757  result = (T)locals.GetItem("delegate").AsManagedObject(typeof(T));
2758  locals.Dispose();
2759  return true;
2760  }
2761  catch
2762  {
2763  // Do not throw or log the exception.
2764  // Return false as an exception means that the conversion could not be made.
2765  }
2766  locals.Dispose();
2767  }
2768  return false;
2769  }
2770 
2771  /// <summary>
2772  /// Safely convert PyObject to ManagedObject using Py.GIL Lock
2773  /// If no type is given it will convert the PyObject's Python Type to a ManagedObject Type
2774  /// in a attempt to resolve the target type to convert to.
2775  /// </summary>
2776  /// <param name="pyObject">PyObject to convert to managed</param>
2777  /// <param name="typeToConvertTo">The target type to convert to</param>
2778  /// <returns>The resulting ManagedObject</returns>
2779  public static dynamic SafeAsManagedObject(this PyObject pyObject, Type typeToConvertTo = null)
2780  {
2781  using (Py.GIL())
2782  {
2783  if (typeToConvertTo == null)
2784  {
2785  typeToConvertTo = pyObject.GetPythonType().AsManagedObject(typeof(Type)) as Type;
2786  }
2787 
2788  return pyObject.AsManagedObject(typeToConvertTo);
2789  }
2790  }
2791 
2792  /// <summary>
2793  /// Converts a Python function to a managed function returning a Symbol
2794  /// </summary>
2795  /// <param name="universeFilterFunc">Universe filter function from Python</param>
2796  /// <returns>Function that provides <typeparamref name="T"/> and returns an enumerable of Symbols</returns>
2797  public static Func<IEnumerable<T>, IEnumerable<Symbol>> ConvertPythonUniverseFilterFunction<T>(this PyObject universeFilterFunc) where T : BaseData
2798  {
2799  Func<IEnumerable<T>, object> convertedFunc;
2800  Func<IEnumerable<T>, IEnumerable<Symbol>> filterFunc = null;
2801 
2802  if (universeFilterFunc != null && universeFilterFunc.TryConvertToDelegate(out convertedFunc))
2803  {
2804  filterFunc = convertedFunc.ConvertToUniverseSelectionSymbolDelegate();
2805  }
2806 
2807  return filterFunc;
2808  }
2809 
2810  /// <summary>
2811  /// Wraps the provided universe selection selector checking if it returned <see cref="Universe.Unchanged"/>
2812  /// and returns it instead, else enumerates result as <see cref="IEnumerable{Symbol}"/>
2813  /// </summary>
2814  /// <remarks>This method is a work around for the fact that currently we can not create a delegate which returns
2815  /// an <see cref="IEnumerable{Symbol}"/> from a python method returning an array, plus the fact that
2816  /// <see cref="Universe.Unchanged"/> can not be cast to an array</remarks>
2817  public static Func<IEnumerable<T>, IEnumerable<Symbol>> ConvertToUniverseSelectionSymbolDelegate<T>(this Func<IEnumerable<T>, object> selector) where T : BaseData
2818  {
2819  if (selector == null)
2820  {
2821  return (dataPoints) => dataPoints.Select(x => x.Symbol);
2822  }
2823  return selector.ConvertSelectionSymbolDelegate();
2824  }
2825 
2826  /// <summary>
2827  /// Wraps the provided universe selection selector checking if it returned <see cref="Universe.Unchanged"/>
2828  /// and returns it instead, else enumerates result as <see cref="IEnumerable{Symbol}"/>
2829  /// </summary>
2830  /// <remarks>This method is a work around for the fact that currently we can not create a delegate which returns
2831  /// an <see cref="IEnumerable{Symbol}"/> from a python method returning an array, plus the fact that
2832  /// <see cref="Universe.Unchanged"/> can not be cast to an array</remarks>
2833  public static Func<T, IEnumerable<Symbol>> ConvertSelectionSymbolDelegate<T>(this Func<T, object> selector)
2834  {
2835  return data =>
2836  {
2837  var result = selector(data);
2838  return ReferenceEquals(result, Universe.Unchanged)
2840  : ((object[])result).Select(x =>
2841  {
2842  if (x is Symbol castedSymbol)
2843  {
2844  return castedSymbol;
2845  }
2846  return SymbolCache.TryGetSymbol((string)x, out var symbol) ? symbol : null;
2847  });
2848  };
2849  }
2850 
2851  /// <summary>
2852  /// Wraps the provided universe selection selector checking if it returned <see cref="Universe.Unchanged"/>
2853  /// and returns it instead, else enumerates result as <see cref="IEnumerable{String}"/>
2854  /// </summary>
2855  /// <remarks>This method is a work around for the fact that currently we can not create a delegate which returns
2856  /// an <see cref="IEnumerable{String}"/> from a python method returning an array, plus the fact that
2857  /// <see cref="Universe.Unchanged"/> can not be cast to an array</remarks>
2858  public static Func<T, IEnumerable<string>> ConvertToUniverseSelectionStringDelegate<T>(this Func<T, object> selector)
2859  {
2860  return data =>
2861  {
2862  var result = selector(data);
2863  return ReferenceEquals(result, Universe.Unchanged)
2864  ? Universe.Unchanged : ((object[])result).Select(x => (string)x);
2865  };
2866  }
2867 
2868  /// <summary>
2869  /// Convert a <see cref="PyObject"/> into a managed object
2870  /// </summary>
2871  /// <typeparam name="T">Target type of the resulting managed object</typeparam>
2872  /// <param name="pyObject">PyObject to be converted</param>
2873  /// <returns>Instance of type T</returns>
2874  public static T ConvertToDelegate<T>(this PyObject pyObject)
2875  {
2876  T result;
2877  if (pyObject.TryConvertToDelegate(out result))
2878  {
2879  return result;
2880  }
2881  else
2882  {
2883  throw new ArgumentException(Messages.Extensions.ConvertToDelegateCannotConverPyObjectToType("ConvertToDelegate", typeof(T)));
2884  }
2885  }
2886 
2887  /// <summary>
2888  /// Convert a <see cref="PyObject"/> into a managed dictionary
2889  /// </summary>
2890  /// <typeparam name="TKey">Target type of the resulting dictionary key</typeparam>
2891  /// <typeparam name="TValue">Target type of the resulting dictionary value</typeparam>
2892  /// <param name="pyObject">PyObject to be converted</param>
2893  /// <returns>Dictionary of TValue keyed by TKey</returns>
2894  public static Dictionary<TKey, TValue> ConvertToDictionary<TKey, TValue>(this PyObject pyObject)
2895  {
2896  var result = new List<KeyValuePair<TKey, TValue>>();
2897  using (Py.GIL())
2898  {
2899  var inputType = pyObject.GetPythonType().ToString();
2900  var targetType = nameof(PyDict);
2901 
2902  try
2903  {
2904  using (var pyDict = new PyDict(pyObject))
2905  {
2906  targetType = $"{typeof(TKey).Name}: {typeof(TValue).Name}";
2907 
2908  foreach (PyObject item in pyDict.Items())
2909  {
2910  inputType = $"{item[0].GetPythonType()}: {item[1].GetPythonType()}";
2911 
2912  var key = item[0].As<TKey>();
2913  var value = item[1].As<TValue>();
2914 
2915  result.Add(new KeyValuePair<TKey, TValue>(key, value));
2916  }
2917  }
2918  }
2919  catch (Exception e)
2920  {
2921  throw new ArgumentException(Messages.Extensions.ConvertToDictionaryFailed(inputType, targetType, e.Message), e);
2922  }
2923  }
2924 
2925  return result.ToDictionary();
2926  }
2927 
2928  /// <summary>
2929  /// Gets Enumerable of <see cref="Symbol"/> from a PyObject
2930  /// </summary>
2931  /// <param name="pyObject">PyObject containing Symbol or Array of Symbol</param>
2932  /// <returns>Enumerable of Symbol</returns>
2933  public static IEnumerable<Symbol> ConvertToSymbolEnumerable(this PyObject pyObject)
2934  {
2935  using (Py.GIL())
2936  {
2937  if (!PyList.IsListType(pyObject))
2938  {
2939  pyObject = new PyList(new[] {pyObject});
2940  }
2941 
2942  using var iterator = pyObject.GetIterator();
2943  foreach (PyObject item in iterator)
2944  {
2945  if (PyString.IsStringType(item))
2946  {
2947  yield return SymbolCache.GetSymbol(item.GetAndDispose<string>());
2948  }
2949  else
2950  {
2951  Symbol symbol;
2952  try
2953  {
2954  symbol = item.GetAndDispose<Symbol>();
2955  }
2956  catch (Exception e)
2957  {
2958  throw new ArgumentException(Messages.Extensions.ConvertToSymbolEnumerableFailed(item), e);
2959  }
2960 
2961  yield return symbol;
2962  }
2963  }
2964  }
2965  }
2966 
2967  /// <summary>
2968  /// Converts an IEnumerable to a PyList
2969  /// </summary>
2970  /// <param name="enumerable">IEnumerable object to convert</param>
2971  /// <returns>PyList</returns>
2972  public static PyList ToPyList(this IEnumerable enumerable)
2973  {
2974  using (Py.GIL())
2975  {
2976  return enumerable.ToPyListUnSafe();
2977  }
2978  }
2979 
2980  /// <summary>
2981  /// Converts an IEnumerable to a PyList
2982  /// </summary>
2983  /// <param name="enumerable">IEnumerable object to convert</param>
2984  /// <remarks>Requires the caller to own the GIL</remarks>
2985  /// <returns>PyList</returns>
2986  public static PyList ToPyListUnSafe(this IEnumerable enumerable)
2987  {
2988  var pyList = new PyList();
2989  foreach (var item in enumerable)
2990  {
2991  using (var pyObject = item.ToPython())
2992  {
2993  pyList.Append(pyObject);
2994  }
2995  }
2996 
2997  return pyList;
2998  }
2999 
3000  /// <summary>
3001  /// Converts the numeric value of one or more enumerated constants to an equivalent enumerated string.
3002  /// </summary>
3003  /// <param name="value">Numeric value</param>
3004  /// <param name="pyObject">Python object that encapsulated a Enum Type</param>
3005  /// <returns>String that represents the enumerated object</returns>
3006  public static string GetEnumString(this int value, PyObject pyObject)
3007  {
3008  Type type;
3009  if (pyObject.TryConvert(out type))
3010  {
3011  return value.ToStringInvariant().ConvertTo(type).ToString();
3012  }
3013  else
3014  {
3015  using (Py.GIL())
3016  {
3017  throw new ArgumentException($"GetEnumString(): {Messages.Extensions.ObjectFromPythonIsNotACSharpType(pyObject.Repr())}");
3018  }
3019  }
3020  }
3021 
3022  /// <summary>
3023  /// Try to create a type with a given name, if PyObject is not a CLR type. Otherwise, convert it.
3024  /// </summary>
3025  /// <param name="pyObject">Python object representing a type.</param>
3026  /// <param name="type">Type object</param>
3027  /// <returns>True if was able to create the type</returns>
3028  public static bool TryCreateType(this PyObject pyObject, out Type type)
3029  {
3030  if (pyObject.TryConvert(out type))
3031  {
3032  // handles pure C# types
3033  return true;
3034  }
3035 
3036  if (!PythonActivators.TryGetValue(pyObject.Handle, out var pythonType))
3037  {
3038  // Some examples:
3039  // pytype: "<class 'DropboxBaseDataUniverseSelectionAlgorithm.StockDataSource'>"
3040  // method: "<bound method CoarseFineFundamentalComboAlgorithm.CoarseSelectionFunction of <CoarseFineFunda..."
3041  // array: "[<QuantConnect.Symbol object at 0x000001EEF21ED480>]"
3042  if (pyObject.ToString().StartsWith("<class '", StringComparison.InvariantCulture))
3043  {
3044  type = CreateType(pyObject);
3045  return true;
3046  }
3047  return false;
3048  }
3049  type = pythonType.Type;
3050  return true;
3051  }
3052 
3053 
3054  /// <summary>
3055  /// Creates a type with a given name, if PyObject is not a CLR type. Otherwise, convert it.
3056  /// </summary>
3057  /// <param name="pyObject">Python object representing a type.</param>
3058  /// <returns>Type object</returns>
3059  public static Type CreateType(this PyObject pyObject)
3060  {
3061  Type type;
3062  if (pyObject.TryConvert(out type))
3063  {
3064  return type;
3065  }
3066 
3067  PythonActivator pythonType;
3068  if (!PythonActivators.TryGetValue(pyObject.Handle, out pythonType))
3069  {
3070  var assemblyName = pyObject.GetAssemblyName();
3071  var typeBuilder = AssemblyBuilder
3072  .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run)
3073  .DefineDynamicModule("MainModule")
3074  // creating the type as public is required to allow 'dynamic' to be able to bind at runtime
3075  .DefineType(assemblyName.Name, TypeAttributes.Class | TypeAttributes.Public, type);
3076 
3077  pythonType = new PythonActivator(typeBuilder.CreateType(), pyObject);
3078 
3079  ObjectActivator.AddActivator(pythonType.Type, pythonType.Factory);
3080 
3081  // Save to prevent future additions
3082  PythonActivators.Add(pyObject.Handle, pythonType);
3083  }
3084  return pythonType.Type;
3085  }
3086 
3087  /// <summary>
3088  /// Helper method to get the assembly name from a python type
3089  /// </summary>
3090  /// <param name="pyObject">Python object pointing to the python type. <see cref="PyObject.GetPythonType"/></param>
3091  /// <returns>The python type assembly name</returns>
3092  public static AssemblyName GetAssemblyName(this PyObject pyObject)
3093  {
3094  using (Py.GIL())
3095  {
3096  return new AssemblyName(pyObject.Repr().Split('\'')[1]);
3097  }
3098  }
3099 
3100  /// <summary>
3101  /// Performs on-line batching of the specified enumerator, emitting chunks of the requested batch size
3102  /// </summary>
3103  /// <typeparam name="T">The enumerable item type</typeparam>
3104  /// <param name="enumerable">The enumerable to be batched</param>
3105  /// <param name="batchSize">The number of items per batch</param>
3106  /// <returns>An enumerable of lists</returns>
3107  public static IEnumerable<List<T>> BatchBy<T>(this IEnumerable<T> enumerable, int batchSize)
3108  {
3109  using (var enumerator = enumerable.GetEnumerator())
3110  {
3111  List<T> list = null;
3112  while (enumerator.MoveNext())
3113  {
3114  if (list == null)
3115  {
3116  list = new List<T> {enumerator.Current};
3117  }
3118  else if (list.Count < batchSize)
3119  {
3120  list.Add(enumerator.Current);
3121  }
3122  else
3123  {
3124  yield return list;
3125  list = new List<T> {enumerator.Current};
3126  }
3127  }
3128 
3129  if (list?.Count > 0)
3130  {
3131  yield return list;
3132  }
3133  }
3134  }
3135 
3136  /// <summary>
3137  /// Safely blocks until the specified task has completed executing
3138  /// </summary>
3139  /// <typeparam name="TResult">The task's result type</typeparam>
3140  /// <param name="task">The task to be awaited</param>
3141  /// <returns>The result of the task</returns>
3142  public static TResult SynchronouslyAwaitTaskResult<TResult>(this Task<TResult> task)
3143  {
3144  return task.ConfigureAwait(false).GetAwaiter().GetResult();
3145  }
3146 
3147  /// <summary>
3148  /// Safely blocks until the specified task has completed executing
3149  /// </summary>
3150  /// <param name="task">The task to be awaited</param>
3151  /// <returns>The result of the task</returns>
3152  public static void SynchronouslyAwaitTask(this Task task)
3153  {
3154  task.ConfigureAwait(false).GetAwaiter().GetResult();
3155  }
3156 
3157  /// <summary>
3158  /// Safely blocks until the specified task has completed executing
3159  /// </summary>
3160  /// <param name="task">The task to be awaited</param>
3161  /// <returns>The result of the task</returns>
3162  public static T SynchronouslyAwaitTask<T>(this Task<T> task)
3163  {
3164  return task.ConfigureAwait(false).GetAwaiter().GetResult();
3165  }
3166 
3167  /// <summary>
3168  /// Convert dictionary to query string
3169  /// </summary>
3170  /// <param name="pairs"></param>
3171  /// <returns></returns>
3172  public static string ToQueryString(this IDictionary<string, object> pairs)
3173  {
3174  return string.Join("&", pairs.Select(pair => $"{pair.Key}={pair.Value}"));
3175  }
3176 
3177  /// <summary>
3178  /// Returns a new string in which specified ending in the current instance is removed.
3179  /// </summary>
3180  /// <param name="s">original string value</param>
3181  /// <param name="ending">the string to be removed</param>
3182  /// <returns></returns>
3183  public static string RemoveFromEnd(this string s, string ending)
3184  {
3185  if (s.EndsWith(ending, StringComparison.InvariantCulture))
3186  {
3187  return s.Substring(0, s.Length - ending.Length);
3188  }
3189  else
3190  {
3191  return s;
3192  }
3193  }
3194 
3195  /// <summary>
3196  /// Returns a new string in which specified start in the current instance is removed.
3197  /// </summary>
3198  /// <param name="s">original string value</param>
3199  /// <param name="start">the string to be removed</param>
3200  /// <returns>Substring with start removed</returns>
3201  public static string RemoveFromStart(this string s, string start)
3202  {
3203  if (!string.IsNullOrEmpty(s) && !string.IsNullOrEmpty(start) && s.StartsWith(start, StringComparison.InvariantCulture))
3204  {
3205  return s.Substring(start.Length);
3206  }
3207  else
3208  {
3209  return s;
3210  }
3211  }
3212 
3213  /// <summary>
3214  /// Helper method to determine symbol for a live subscription
3215  /// </summary>
3216  /// <remarks>Useful for continuous futures where we subscribe to the underlying</remarks>
3217  public static bool TryGetLiveSubscriptionSymbol(this Symbol symbol, out Symbol mapped)
3218  {
3219  mapped = null;
3220  if (symbol.SecurityType == SecurityType.Future && symbol.IsCanonical() && symbol.HasUnderlying)
3221  {
3222  mapped = symbol.Underlying;
3223  return true;
3224  }
3225  return false;
3226  }
3227 
3228  /// <summary>
3229  /// Gets the delisting date for the provided Symbol
3230  /// </summary>
3231  /// <param name="symbol">The symbol to lookup the last trading date</param>
3232  /// <param name="mapFile">Map file to use for delisting date. Defaults to SID.DefaultDate if no value is passed and is equity.</param>
3233  /// <returns></returns>
3234  public static DateTime GetDelistingDate(this Symbol symbol, MapFile mapFile = null)
3235  {
3236  if (symbol.IsCanonical())
3237  {
3238  return Time.EndOfTime;
3239  }
3240  switch (symbol.ID.SecurityType)
3241  {
3242  case SecurityType.Option:
3243  return OptionSymbol.GetLastDayOfTrading(symbol);
3244  case SecurityType.FutureOption:
3245  return FutureOptionSymbol.GetLastDayOfTrading(symbol);
3246  case SecurityType.Future:
3247  case SecurityType.IndexOption:
3248  return symbol.ID.Date;
3249  default:
3250  return mapFile?.DelistingDate ?? Time.EndOfTime;
3251  }
3252  }
3253 
3254  /// <summary>
3255  /// Helper method to determine if a given symbol is of custom data
3256  /// </summary>
3257  public static bool IsCustomDataType<T>(this Symbol symbol)
3258  {
3259  return symbol.SecurityType == SecurityType.Base
3260  && SecurityIdentifier.TryGetCustomDataType(symbol.ID.Symbol, out var type)
3261  && type.Equals(typeof(T).Name, StringComparison.InvariantCultureIgnoreCase);
3262  }
3263 
3264  /// <summary>
3265  /// Helper method that will return a back month, with future expiration, future contract based on the given offset
3266  /// </summary>
3267  /// <param name="symbol">The none canonical future symbol</param>
3268  /// <param name="offset">The quantity of contracts to move into the future expiration chain</param>
3269  /// <returns>A new future expiration symbol instance</returns>
3270  public static Symbol AdjustSymbolByOffset(this Symbol symbol, uint offset)
3271  {
3272  if (symbol.SecurityType != SecurityType.Future || symbol.IsCanonical())
3273  {
3274  throw new InvalidOperationException(Messages.Extensions.ErrorAdjustingSymbolByOffset);
3275  }
3276 
3277  var expiration = symbol.ID.Date;
3278  for (var i = 0; i < offset; i++)
3279  {
3280  var expiryFunction = FuturesExpiryFunctions.FuturesExpiryFunction(symbol);
3281  DateTime newExpiration;
3282  // for the current expiration we add a month to get the next one
3283  var monthOffset = 0;
3284  do
3285  {
3286  monthOffset++;
3287  newExpiration = expiryFunction(expiration.AddMonths(monthOffset)).Date;
3288  } while (newExpiration <= expiration);
3289 
3290  expiration = newExpiration;
3291  symbol = Symbol.CreateFuture(symbol.ID.Symbol, symbol.ID.Market, newExpiration);
3292  }
3293 
3294  return symbol;
3295  }
3296 
3297  /// <summary>
3298  /// Helper method to unsubscribe a given configuration, handling any required mapping
3299  /// </summary>
3300  public static void UnsubscribeWithMapping(this IDataQueueHandler dataQueueHandler, SubscriptionDataConfig dataConfig)
3301  {
3302  if (dataConfig.Symbol.TryGetLiveSubscriptionSymbol(out var mappedSymbol))
3303  {
3304  dataConfig = new SubscriptionDataConfig(dataConfig, symbol: mappedSymbol, mappedConfig: true);
3305  }
3306  dataQueueHandler.Unsubscribe(dataConfig);
3307  }
3308 
3309  /// <summary>
3310  /// Helper method to subscribe a given configuration, handling any required mapping
3311  /// </summary>
3312  public static IEnumerator<BaseData> SubscribeWithMapping(this IDataQueueHandler dataQueueHandler,
3313  SubscriptionDataConfig dataConfig,
3314  EventHandler newDataAvailableHandler,
3315  Func<SubscriptionDataConfig, bool> isExpired,
3316  out SubscriptionDataConfig subscribedConfig)
3317  {
3318  subscribedConfig = dataConfig;
3319  if (dataConfig.Symbol.TryGetLiveSubscriptionSymbol(out var mappedSymbol))
3320  {
3321  subscribedConfig = new SubscriptionDataConfig(dataConfig, symbol: mappedSymbol, mappedConfig: true);
3322  }
3323 
3324  // during warmup we might get requested to add some asset which has already expired in which case the live enumerator will be empty
3325  IEnumerator<BaseData> result = null;
3326  if (!isExpired(subscribedConfig))
3327  {
3328  result = dataQueueHandler.Subscribe(subscribedConfig, newDataAvailableHandler);
3329  }
3330  else
3331  {
3332  Log.Trace($"SubscribeWithMapping(): skip live subscription for expired asset {subscribedConfig}");
3333  }
3334  return result ?? Enumerable.Empty<BaseData>().GetEnumerator();
3335  }
3336 
3337  /// <summary>
3338  /// Helper method to stream read lines from a file
3339  /// </summary>
3340  /// <param name="dataProvider">The data provider to use</param>
3341  /// <param name="file">The file path to read from</param>
3342  /// <returns>Enumeration of lines in file</returns>
3343  public static IEnumerable<string> ReadLines(this IDataProvider dataProvider, string file)
3344  {
3345  if(dataProvider == null)
3346  {
3347  throw new ArgumentException(Messages.Extensions.NullDataProvider);
3348  }
3349  var stream = dataProvider.Fetch(file);
3350  if (stream == null)
3351  {
3352  yield break;
3353  }
3354 
3355  using (var streamReader = new StreamReader(stream))
3356  {
3357  string line;
3358  do
3359  {
3360  line = streamReader.ReadLine();
3361  if (line != null)
3362  {
3363  yield return line;
3364  }
3365  }
3366  while (line != null);
3367  }
3368  }
3369 
3370  /// <summary>
3371  /// Scale data based on factor function
3372  /// </summary>
3373  /// <param name="data">Data to Adjust</param>
3374  /// <param name="factorFunc">Function to factor prices by</param>
3375  /// <param name="volumeFactor">Factor to multiply volume/askSize/bidSize/quantity by</param>
3376  /// <param name="factor">Price scale</param>
3377  /// <param name="sumOfDividends">The current dividend sum</param>
3378  /// <remarks>Volume values are rounded to the nearest integer, lot size purposefully not considered
3379  /// as scaling only applies to equities</remarks>
3380  public static BaseData Scale(this BaseData data, Func<decimal, decimal, decimal, decimal> factorFunc, decimal volumeFactor, decimal factor, decimal sumOfDividends)
3381  {
3382  switch (data.DataType)
3383  {
3384  case MarketDataType.TradeBar:
3385  var tradeBar = data as TradeBar;
3386  if (tradeBar != null)
3387  {
3388  tradeBar.Open = factorFunc(tradeBar.Open, factor, sumOfDividends);
3389  tradeBar.High = factorFunc(tradeBar.High, factor, sumOfDividends);
3390  tradeBar.Low = factorFunc(tradeBar.Low, factor, sumOfDividends);
3391  tradeBar.Close = factorFunc(tradeBar.Close, factor, sumOfDividends);
3392  tradeBar.Volume = Math.Round(tradeBar.Volume * volumeFactor);
3393  }
3394  break;
3395  case MarketDataType.Tick:
3396  var securityType = data.Symbol.SecurityType;
3397  if (securityType != SecurityType.Equity &&
3398  securityType != SecurityType.Future &&
3399  !securityType.IsOption())
3400  {
3401  break;
3402  }
3403 
3404  var tick = data as Tick;
3405  if (tick == null || tick.TickType == TickType.OpenInterest)
3406  {
3407  break;
3408  }
3409 
3410  if (tick.TickType == TickType.Trade)
3411  {
3412  tick.Value = factorFunc(tick.Value, factor, sumOfDividends);
3413  tick.Quantity = Math.Round(tick.Quantity * volumeFactor);
3414  break;
3415  }
3416 
3417  tick.BidPrice = tick.BidPrice != 0 ? factorFunc(tick.BidPrice, factor, sumOfDividends) : 0;
3418  tick.BidSize = Math.Round(tick.BidSize * volumeFactor);
3419  tick.AskPrice = tick.AskPrice != 0 ? factorFunc(tick.AskPrice, factor, sumOfDividends) : 0;
3420  tick.AskSize = Math.Round(tick.AskSize * volumeFactor);
3421 
3422  if (tick.BidPrice == 0)
3423  {
3424  tick.Value = tick.AskPrice;
3425  break;
3426  }
3427  if (tick.AskPrice == 0)
3428  {
3429  tick.Value = tick.BidPrice;
3430  break;
3431  }
3432 
3433  tick.Value = (tick.BidPrice + tick.AskPrice) / 2m;
3434  break;
3435  case MarketDataType.QuoteBar:
3436  var quoteBar = data as QuoteBar;
3437  if (quoteBar != null)
3438  {
3439  if (quoteBar.Ask != null)
3440  {
3441  quoteBar.Ask.Open = factorFunc(quoteBar.Ask.Open, factor, sumOfDividends);
3442  quoteBar.Ask.High = factorFunc(quoteBar.Ask.High, factor, sumOfDividends);
3443  quoteBar.Ask.Low = factorFunc(quoteBar.Ask.Low, factor, sumOfDividends);
3444  quoteBar.Ask.Close = factorFunc(quoteBar.Ask.Close, factor, sumOfDividends);
3445  }
3446  if (quoteBar.Bid != null)
3447  {
3448  quoteBar.Bid.Open = factorFunc(quoteBar.Bid.Open, factor, sumOfDividends);
3449  quoteBar.Bid.High = factorFunc(quoteBar.Bid.High, factor, sumOfDividends);
3450  quoteBar.Bid.Low = factorFunc(quoteBar.Bid.Low, factor, sumOfDividends);
3451  quoteBar.Bid.Close = factorFunc(quoteBar.Bid.Close, factor, sumOfDividends);
3452  }
3453  quoteBar.Value = quoteBar.Close;
3454  quoteBar.LastAskSize = Math.Round(quoteBar.LastAskSize * volumeFactor);
3455  quoteBar.LastBidSize = Math.Round(quoteBar.LastBidSize * volumeFactor);
3456  }
3457  break;
3458  case MarketDataType.Auxiliary:
3459  case MarketDataType.Base:
3460  case MarketDataType.OptionChain:
3461  case MarketDataType.FuturesChain:
3462  break;
3463  default:
3464  throw new ArgumentOutOfRangeException();
3465  }
3466  return data;
3467  }
3468 
3469  /// <summary>
3470  /// Normalize prices based on configuration
3471  /// </summary>
3472  /// <param name="data">Data to be normalized</param>
3473  /// <param name="factor">Price scale</param>
3474  /// <param name="normalizationMode">The price scaling normalization mode</param>
3475  /// <param name="sumOfDividends">The current dividend sum</param>
3476  /// <returns>The provided data point adjusted</returns>
3477  public static BaseData Normalize(this BaseData data, decimal factor, DataNormalizationMode normalizationMode, decimal sumOfDividends)
3478  {
3479  switch (normalizationMode)
3480  {
3481  case DataNormalizationMode.Adjusted:
3482  case DataNormalizationMode.SplitAdjusted:
3483  case DataNormalizationMode.ScaledRaw:
3484  return data?.Scale(TimesFactor, 1 / factor, factor, decimal.Zero);
3485  case DataNormalizationMode.TotalReturn:
3486  return data.Scale(TimesFactor, 1 / factor, factor, sumOfDividends);
3487 
3488  case DataNormalizationMode.BackwardsRatio:
3489  return data.Scale(TimesFactor, 1, factor, decimal.Zero);
3490  case DataNormalizationMode.BackwardsPanamaCanal:
3491  return data.Scale(AdditionFactor, 1, factor, decimal.Zero);
3492  case DataNormalizationMode.ForwardPanamaCanal:
3493  return data.Scale(AdditionFactor, 1, factor, decimal.Zero);
3494 
3495  case DataNormalizationMode.Raw:
3496  default:
3497  return data;
3498  }
3499  }
3500 
3501  /// <summary>
3502  /// Applies a times factor. We define this so we don't need to create it constantly
3503  /// </summary>
3504  [MethodImpl(MethodImplOptions.AggressiveInlining)]
3505  private static decimal TimesFactor(decimal target, decimal factor, decimal sumOfDividends)
3506  {
3507  return target * factor + sumOfDividends;
3508  }
3509 
3510  /// <summary>
3511  /// Applies an addition factor. We define this so we don't need to create it constantly
3512  /// </summary>
3513  [MethodImpl(MethodImplOptions.AggressiveInlining)]
3514  private static decimal AdditionFactor(decimal target, decimal factor, decimal _)
3515  {
3516  return target + factor;
3517  }
3518 
3519  /// <summary>
3520  /// Helper method to determine if price scales need an update based on the given data point
3521  /// </summary>
3522  public static DateTime GetUpdatePriceScaleFrontier(this BaseData data)
3523  {
3524  if (data != null)
3525  {
3526  var priceScaleFrontier = data.Time;
3527  if (data.Time.Date != data.EndTime.Date && data.EndTime.TimeOfDay > TimeSpan.Zero)
3528  {
3529  // 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,
3530  // 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
3531  // 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.
3532  // 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
3533  // the daily bar would go from midnight X+1 day to midnight X+2
3534  // TODO: see related issue https://github.com/QuantConnect/Lean/issues/6964 which would avoid the need for this
3535  priceScaleFrontier = data.EndTime;
3536  }
3537  return priceScaleFrontier;
3538  }
3539  return DateTime.MinValue;
3540  }
3541 
3542  /// <summary>
3543  /// Thread safe concurrent dictionary order by implementation by using <see cref="SafeEnumeration{TSource,TKey}"/>
3544  /// </summary>
3545  /// <remarks>See https://stackoverflow.com/questions/47630824/is-c-sharp-linq-orderby-threadsafe-when-used-with-concurrentdictionarytkey-tva</remarks>
3546  public static IOrderedEnumerable<KeyValuePair<TSource, TKey>> OrderBySafe<TSource, TKey>(
3547  this ConcurrentDictionary<TSource, TKey> source, Func<KeyValuePair<TSource, TKey>, TSource> keySelector
3548  )
3549  {
3550  return source.SafeEnumeration().OrderBy(keySelector);
3551  }
3552 
3553  /// <summary>
3554  /// Thread safe concurrent dictionary order by implementation by using <see cref="SafeEnumeration{TSource,TKey}"/>
3555  /// </summary>
3556  /// <remarks>See https://stackoverflow.com/questions/47630824/is-c-sharp-linq-orderby-threadsafe-when-used-with-concurrentdictionarytkey-tva</remarks>
3557  public static IOrderedEnumerable<KeyValuePair<TSource, TKey>> OrderBySafe<TSource, TKey>(
3558  this ConcurrentDictionary<TSource, TKey> source, Func<KeyValuePair<TSource, TKey>, TKey> keySelector
3559  )
3560  {
3561  return source.SafeEnumeration().OrderBy(keySelector);
3562  }
3563 
3564  /// <summary>
3565  /// Force concurrent dictionary enumeration using a thread safe implementation
3566  /// </summary>
3567  /// <remarks>See https://stackoverflow.com/questions/47630824/is-c-sharp-linq-orderby-threadsafe-when-used-with-concurrentdictionarytkey-tva</remarks>
3568  public static IEnumerable<KeyValuePair<TSource, TKey>> SafeEnumeration<TSource, TKey>(
3569  this ConcurrentDictionary<TSource, TKey> source)
3570  {
3571  foreach (var kvp in source)
3572  {
3573  yield return kvp;
3574  }
3575  }
3576 
3577  /// <summary>
3578  /// Helper method to determine the right data normalization mode to use by default
3579  /// </summary>
3581  {
3582  switch (securityType)
3583  {
3584  case SecurityType.Future:
3585  if (universeSettings.DataNormalizationMode is DataNormalizationMode.BackwardsRatio
3586  or DataNormalizationMode.BackwardsPanamaCanal or DataNormalizationMode.ForwardPanamaCanal
3587  or DataNormalizationMode.Raw)
3588  {
3589  return universeSettings.DataNormalizationMode;
3590  }
3591  return DataNormalizationMode.BackwardsRatio;
3592  default:
3593  return universeSettings.DataNormalizationMode;
3594  }
3595  }
3596 
3597  /// <summary>
3598  /// Returns a hex string of the byte array.
3599  /// </summary>
3600  /// <param name="source">the byte array to be represented as string</param>
3601  /// <returns>A new string containing the items in the enumerable</returns>
3602  public static string ToHexString(this byte[] source)
3603  {
3604  if (source == null || source.Length == 0)
3605  {
3606  throw new ArgumentException(Messages.Extensions.NullOrEmptySourceToConvertToHexString);
3607  }
3608 
3609  var hex = new StringBuilder(source.Length * 2);
3610  foreach (var b in source)
3611  {
3612  hex.AppendFormat(CultureInfo.InvariantCulture, "{0:x2}", b);
3613  }
3614 
3615  return hex.ToString();
3616  }
3617 
3618  /// <summary>
3619  /// Gets the option exercise order direction resulting from the specified <paramref name="right"/> and
3620  /// whether or not we wrote the option (<paramref name="isShort"/> is <code>true</code>) or bought to
3621  /// option (<paramref name="isShort"/> is <code>false</code>)
3622  /// </summary>
3623  /// <param name="right">The option right</param>
3624  /// <param name="isShort">True if we wrote the option, false if we purchased the option</param>
3625  /// <returns>The order direction resulting from an exercised option</returns>
3626  public static OrderDirection GetExerciseDirection(this OptionRight right, bool isShort)
3627  {
3628  switch (right)
3629  {
3630  case OptionRight.Call:
3631  return isShort ? OrderDirection.Sell : OrderDirection.Buy;
3632  default:
3633  return isShort ? OrderDirection.Buy : OrderDirection.Sell;
3634  }
3635  }
3636 
3637  /// <summary>
3638  /// Gets the <see cref="OrderDirection"/> for the specified <paramref name="quantity"/>
3639  /// </summary>
3640  public static OrderDirection GetOrderDirection(decimal quantity)
3641  {
3642  var sign = Math.Sign(quantity);
3643  switch (sign)
3644  {
3645  case 1: return OrderDirection.Buy;
3646  case 0: return OrderDirection.Hold;
3647  case -1: return OrderDirection.Sell;
3648  default:
3649  throw new ApplicationException(
3650  $"The skies are falling and the oceans are rising! Math.Sign({quantity}) returned {sign} :/"
3651  );
3652  }
3653  }
3654 
3655  /// <summary>
3656  /// Helper method to process an algorithms security changes, will add and remove securities according to them
3657  /// </summary>
3658  public static void ProcessSecurityChanges(this IAlgorithm algorithm, SecurityChanges securityChanges)
3659  {
3660  foreach (var security in securityChanges.AddedSecurities)
3661  {
3662  security.IsTradable = true;
3663 
3664  // uses TryAdd, so don't need to worry about duplicates here
3665  algorithm.Securities.Add(security);
3666  }
3667 
3668  var activeSecurities = algorithm.UniverseManager.ActiveSecurities;
3669  foreach (var security in securityChanges.RemovedSecurities)
3670  {
3671  if (!activeSecurities.ContainsKey(security.Symbol))
3672  {
3673  security.IsTradable = false;
3674  }
3675  }
3676  }
3677 
3678  /// <summary>
3679  /// Helper method to set an algorithm runtime exception in a normalized fashion
3680  /// </summary>
3681  public static void SetRuntimeError(this IAlgorithm algorithm, Exception exception, string context)
3682  {
3683  Log.Error(exception, $"Extensions.SetRuntimeError(): {Messages.Extensions.RuntimeError(algorithm, context)}");
3684  exception = StackExceptionInterpreter.Instance.Value.Interpret(exception);
3685  algorithm.RunTimeError = exception;
3686  algorithm.SetStatus(AlgorithmStatus.RuntimeError);
3687  }
3688 
3689  /// <summary>
3690  /// Creates a <see cref="OptionChainUniverse"/> for a given symbol
3691  /// </summary>
3692  /// <param name="algorithm">The algorithm instance to create universes for</param>
3693  /// <param name="symbol">Symbol of the option</param>
3694  /// <param name="filter">The option filter to use</param>
3695  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3696  /// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
3697  public static OptionChainUniverse CreateOptionChain(this IAlgorithm algorithm, Symbol symbol, PyObject filter, UniverseSettings universeSettings = null)
3698  {
3699  var result = CreateOptionChain(algorithm, symbol, out var option, universeSettings);
3700  option.SetFilter(filter);
3701  return result;
3702  }
3703 
3704  /// <summary>
3705  /// Creates a <see cref="OptionChainUniverse"/> for a given symbol
3706  /// </summary>
3707  /// <param name="algorithm">The algorithm instance to create universes for</param>
3708  /// <param name="symbol">Symbol of the option</param>
3709  /// <param name="filter">The option filter to use</param>
3710  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3711  /// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
3712  public static OptionChainUniverse CreateOptionChain(this IAlgorithm algorithm, Symbol symbol, Func<OptionFilterUniverse, OptionFilterUniverse> filter, UniverseSettings universeSettings = null)
3713  {
3714  var result = CreateOptionChain(algorithm, symbol, out var option, universeSettings);
3715  option.SetFilter(filter);
3716  return result;
3717  }
3718 
3719  /// <summary>
3720  /// Creates a <see cref="OptionChainUniverse"/> for a given symbol
3721  /// </summary>
3722  /// <param name="algorithm">The algorithm instance to create universes for</param>
3723  /// <param name="symbol">Symbol of the option</param>
3724  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3725  /// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
3726  private static OptionChainUniverse CreateOptionChain(this IAlgorithm algorithm, Symbol symbol, out Option option, UniverseSettings universeSettings = null)
3727  {
3728  if (!symbol.SecurityType.IsOption())
3729  {
3730  throw new ArgumentException(Messages.Extensions.CreateOptionChainRequiresOptionSymbol);
3731  }
3732 
3733  // resolve defaults if not specified
3734  var settings = universeSettings ?? algorithm.UniverseSettings;
3735 
3736  option = (Option)algorithm.AddSecurity(symbol.Canonical, settings.Resolution, settings.FillForward, settings.Leverage, settings.ExtendedMarketHours);
3737 
3738  return (OptionChainUniverse)algorithm.UniverseManager.Values.Single(universe => universe.Configuration.Symbol == symbol.Canonical);
3739  }
3740 
3741  /// <summary>
3742  /// Creates a <see cref="FuturesChainUniverse"/> for a given symbol
3743  /// </summary>
3744  /// <param name="algorithm">The algorithm instance to create universes for</param>
3745  /// <param name="symbol">Symbol of the future</param>
3746  /// <param name="filter">The future filter to use</param>
3747  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3748  public static IEnumerable<Universe> CreateFutureChain(this IAlgorithm algorithm, Symbol symbol, PyObject filter, UniverseSettings universeSettings = null)
3749  {
3750  var result = CreateFutureChain(algorithm, symbol, out var future, universeSettings);
3751  future.SetFilter(filter);
3752  return result;
3753  }
3754 
3755  /// <summary>
3756  /// Creates a <see cref="FuturesChainUniverse"/> for a given symbol
3757  /// </summary>
3758  /// <param name="algorithm">The algorithm instance to create universes for</param>
3759  /// <param name="symbol">Symbol of the future</param>
3760  /// <param name="filter">The future filter to use</param>
3761  /// <param name="universeSettings">The universe settings, will use algorithm settings if null</param>
3762  public static IEnumerable<Universe> CreateFutureChain(this IAlgorithm algorithm, Symbol symbol, Func<FutureFilterUniverse, FutureFilterUniverse> filter, UniverseSettings universeSettings = null)
3763  {
3764  var result = CreateFutureChain(algorithm, symbol, out var future, universeSettings);
3765  future.SetFilter(filter);
3766  return result;
3767  }
3768 
3769  /// <summary>
3770  /// Creates a <see cref="FuturesChainUniverse"/> for a given symbol
3771  /// </summary>
3772  private static IEnumerable<Universe> CreateFutureChain(this IAlgorithm algorithm, Symbol symbol, out Future future, UniverseSettings universeSettings = null)
3773  {
3774  if (symbol.SecurityType != SecurityType.Future)
3775  {
3776  throw new ArgumentException(Messages.Extensions.CreateFutureChainRequiresFutureSymbol);
3777  }
3778 
3779  // resolve defaults if not specified
3780  var settings = universeSettings ?? algorithm.UniverseSettings;
3781 
3782  var dataNormalizationMode = settings.GetUniverseNormalizationModeOrDefault(symbol.SecurityType);
3783 
3784  future = (Future)algorithm.AddSecurity(symbol.Canonical, settings.Resolution, settings.FillForward, settings.Leverage, settings.ExtendedMarketHours,
3785  settings.DataMappingMode, dataNormalizationMode, settings.ContractDepthOffset);
3786 
3787  // let's yield back both the future chain and the continuous future universe
3788  return algorithm.UniverseManager.Values.Where(universe => universe.Configuration.Symbol == symbol.Canonical || ContinuousContractUniverse.CreateSymbol(symbol.Canonical) == universe.Configuration.Symbol);
3789  }
3790 
3791  private static bool _notifiedUniverseSettingsUsed;
3792  private static readonly HashSet<SecurityType> _supportedSecurityTypes = new()
3793  {
3794  SecurityType.Equity,
3795  SecurityType.Forex,
3796  SecurityType.Cfd,
3797  SecurityType.Option,
3798  SecurityType.Future,
3799  SecurityType.FutureOption,
3800  SecurityType.IndexOption,
3801  SecurityType.Crypto,
3802  SecurityType.CryptoFuture
3803  };
3804 
3805  /// <summary>
3806  /// Gets the security for the specified symbol from the algorithm's securities collection.
3807  /// In case the security is not found, it will be created using the <see cref="IAlgorithm.UniverseSettings"/>
3808  /// and a best effort configuration setup.
3809  /// </summary>
3810  /// <param name="algorithm">The algorithm instance</param>
3811  /// <param name="symbol">The symbol which security is being looked up</param>
3812  /// <param name="security">The found or added security instance</param>
3813  /// <param name="onError">Callback to invoke in case of unsupported security type</param>
3814  /// <returns>True if the security was found or added</returns>
3815  public static bool GetOrAddUnrequestedSecurity(this IAlgorithm algorithm, Symbol symbol, out Security security,
3816  Action<IReadOnlyCollection<SecurityType>> onError = null)
3817  {
3818  if (!algorithm.Securities.TryGetValue(symbol, out security))
3819  {
3820  if (!_supportedSecurityTypes.Contains(symbol.SecurityType))
3821  {
3822  Log.Error("GetOrAddUnrequestedSecurity(): Unsupported security type: " + symbol.SecurityType + "-" + symbol.Value);
3823  onError?.Invoke(_supportedSecurityTypes);
3824  return false;
3825  }
3826 
3827  var resolution = algorithm.UniverseSettings.Resolution;
3828  var fillForward = algorithm.UniverseSettings.FillForward;
3829  var leverage = algorithm.UniverseSettings.Leverage;
3830  var extendedHours = algorithm.UniverseSettings.ExtendedMarketHours;
3831 
3832  if (!_notifiedUniverseSettingsUsed)
3833  {
3834  // let's just send the message once
3835  _notifiedUniverseSettingsUsed = true;
3836 
3837  var leverageMsg = $" Leverage = {leverage};";
3838  if (leverage == Security.NullLeverage)
3839  {
3840  leverageMsg = $" Leverage = default;";
3841  }
3842  algorithm.Debug($"Will use UniverseSettings for automatically added securities for open orders and holdings. UniverseSettings:" +
3843  $" Resolution = {resolution};{leverageMsg} FillForward = {fillForward}; ExtendedHours = {extendedHours}");
3844  }
3845 
3846  Log.Trace("GetOrAddUnrequestedSecurity(): Adding unrequested security: " + symbol.Value);
3847 
3848  if (symbol.SecurityType.IsOption())
3849  {
3850  // add current option contract to the system
3851  security = algorithm.AddOptionContract(symbol, resolution, fillForward, leverage, extendedHours);
3852  }
3853  else if (symbol.SecurityType == SecurityType.Future)
3854  {
3855  // add current future contract to the system
3856  security = algorithm.AddFutureContract(symbol, resolution, fillForward, leverage, extendedHours);
3857  }
3858  else
3859  {
3860  // for items not directly requested set leverage to 1 and at the min resolution
3861  security = algorithm.AddSecurity(symbol.SecurityType, symbol.Value, resolution, symbol.ID.Market, fillForward, leverage, extendedHours);
3862  }
3863  }
3864  return true;
3865  }
3866 
3867  /// <summary>
3868  /// Inverts the specified <paramref name="right"/>
3869  /// </summary>
3870  public static OptionRight Invert(this OptionRight right)
3871  {
3872  switch (right)
3873  {
3874  case OptionRight.Call: return OptionRight.Put;
3875  case OptionRight.Put: return OptionRight.Call;
3876  default:
3877  throw new ArgumentOutOfRangeException(nameof(right), right, null);
3878  }
3879  }
3880 
3881  /// <summary>
3882  /// Compares two values using given operator
3883  /// </summary>
3884  /// <typeparam name="T"></typeparam>
3885  /// <param name="op">Comparison operator</param>
3886  /// <param name="arg1">The first value</param>
3887  /// <param name="arg2">The second value</param>
3888  /// <returns>Returns true if its left-hand operand meets the operator value to its right-hand operand, false otherwise</returns>
3889  public static bool Compare<T>(this ComparisonOperatorTypes op, T arg1, T arg2) where T : IComparable
3890  {
3891  return ComparisonOperator.Compare(op, arg1, arg2);
3892  }
3893 
3894  /// <summary>
3895  /// Converts a <see cref="Data.HistoryRequest" /> instance to a <see cref="SubscriptionDataConfig"/> instance
3896  /// </summary>
3897  /// <param name="request">History request</param>
3898  /// <param name="isInternalFeed">
3899  /// Set to true if this subscription is added for the sole purpose of providing currency conversion rates,
3900  /// setting this flag to true will prevent the data from being sent into the algorithm's OnData methods
3901  /// </param>
3902  /// <param name="isFilteredSubscription">True if this subscription should have filters applied to it (market hours/user filters from security), false otherwise</param>
3903  /// <returns>Subscription data configuration</returns>
3904  public static SubscriptionDataConfig ToSubscriptionDataConfig(this Data.HistoryRequest request, bool isInternalFeed = false, bool isFilteredSubscription = true)
3905  {
3906  return new SubscriptionDataConfig(request.DataType,
3907  request.Symbol,
3908  request.Resolution,
3909  request.DataTimeZone,
3910  request.ExchangeHours.TimeZone,
3911  request.FillForwardResolution.HasValue,
3912  request.IncludeExtendedMarketHours,
3913  isInternalFeed,
3914  request.IsCustomData,
3915  request.TickType,
3916  isFilteredSubscription,
3917  request.DataNormalizationMode,
3918  request.DataMappingMode,
3919  request.ContractDepthOffset
3920  );
3921  }
3922 
3923  /// <summary>
3924  /// Centralized logic used at the top of the subscription enumerator stacks to determine if we should emit base data points
3925  /// based on the configuration for this subscription and the type of data we are handling.
3926  ///
3927  /// Currently we only want to emit split/dividends/delisting events for non internal <see cref="TradeBar"/> configurations
3928  /// this last part is because equities also have <see cref="QuoteBar"/> subscriptions which will also subscribe to the
3929  /// same aux events and we don't want duplicate emits of these events in the TimeSliceFactory
3930  /// </summary>
3931  /// <remarks>The "TimeSliceFactory" does not allow for multiple dividends/splits per symbol in the same time slice
3932  /// but we don't want to rely only on that to filter out duplicated aux data so we use this at the top of
3933  /// our data enumerator stacks to define what subscription should emit this data.</remarks>
3934  /// <remarks>We use this function to filter aux data at the top of the subscription enumerator stack instead of
3935  /// stopping the subscription stack from subscribing to aux data at the bottom because of a
3936  /// dependency with the FF enumerators requiring that they receive aux data to properly handle delistings.
3937  /// Otherwise we would have issues with delisted symbols continuing to fill forward after expiry/delisting.
3938  /// Reference PR #5485 and related issues for more.</remarks>
3939  public static bool ShouldEmitData(this SubscriptionDataConfig config, BaseData data, bool isUniverse = false)
3940  {
3941  // 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
3942  if (data.DataType != MarketDataType.Auxiliary)
3943  {
3944  return true;
3945  }
3946 
3947  // This filter does not apply to auxiliary data outside of delisting/splits/dividends so lets those emit
3948  var type = data.GetType();
3949  var expectedType = type.IsAssignableTo(config.Type);
3950 
3951  // Check our config type first to be lazy about using data.GetType() unless required
3952  var configTypeFilter = (config.Type == typeof(TradeBar) || config.Type == typeof(ZipEntryName) ||
3953  config.Type == typeof(Tick) && config.TickType == TickType.Trade || config.IsCustomData);
3954 
3955  if (!configTypeFilter)
3956  {
3957  return expectedType;
3958  }
3959 
3960  // We don't want to pump in any data to `Universe.SelectSymbols(...)` if the
3961  // type is not configured to be consumed by the universe. This change fixes
3962  // a case where a `SymbolChangedEvent` was being passed to an ETF constituent universe
3963  // for filtering/selection, and would result in either a runtime error
3964  // if casting into the expected type explicitly, or call the filter function with
3965  // no data being provided, resulting in all universe Symbols being de-selected.
3966  if (isUniverse && !expectedType)
3967  {
3968  return (data as Delisting)?.Type == DelistingType.Delisted;
3969  }
3970 
3971  if (!(type == typeof(Delisting) || type == typeof(Split) || type == typeof(Dividend)))
3972  {
3973  return true;
3974  }
3975 
3976  // If we made it here then only filter it if its an InternalFeed
3977  return !config.IsInternalFeed;
3978  }
3979 
3980  /// <summary>
3981  /// Gets the <see cref="OrderDirection"/> that corresponds to the specified <paramref name="side"/>
3982  /// </summary>
3983  /// <param name="side">The position side to be converted</param>
3984  /// <returns>The order direction that maps from the provided position side</returns>
3986  {
3987  switch (side)
3988  {
3989  case PositionSide.Short: return OrderDirection.Sell;
3990  case PositionSide.None: return OrderDirection.Hold;
3991  case PositionSide.Long: return OrderDirection.Buy;
3992  default:
3993  throw new ArgumentOutOfRangeException(nameof(side), side, null);
3994  }
3995  }
3996 
3997  /// <summary>
3998  /// Determines if an order with the specified <paramref name="direction"/> would close a position with the
3999  /// specified <paramref name="side"/>
4000  /// </summary>
4001  /// <param name="direction">The direction of the order, buy/sell</param>
4002  /// <param name="side">The side of the position, long/short</param>
4003  /// <returns>True if the order direction would close the position, otherwise false</returns>
4004  public static bool Closes(this OrderDirection direction, PositionSide side)
4005  {
4006  switch (side)
4007  {
4008  case PositionSide.Short:
4009  switch (direction)
4010  {
4011  case OrderDirection.Buy: return true;
4012  case OrderDirection.Sell: return false;
4013  case OrderDirection.Hold: return false;
4014  default:
4015  throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
4016  }
4017 
4018  case PositionSide.Long:
4019  switch (direction)
4020  {
4021  case OrderDirection.Buy: return false;
4022  case OrderDirection.Sell: return true;
4023  case OrderDirection.Hold: return false;
4024  default:
4025  throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
4026  }
4027 
4028  case PositionSide.None:
4029  return false;
4030 
4031  default:
4032  throw new ArgumentOutOfRangeException(nameof(side), side, null);
4033  }
4034  }
4035 
4036  /// <summary>
4037  /// Determines if the two lists are equal, including all items at the same indices.
4038  /// </summary>
4039  /// <typeparam name="T">The element type</typeparam>
4040  /// <param name="left">The left list</param>
4041  /// <param name="right">The right list</param>
4042  /// <returns>True if the two lists have the same counts and items at each index evaluate as equal</returns>
4043  public static bool ListEquals<T>(this IReadOnlyList<T> left, IReadOnlyList<T> right)
4044  {
4045  var count = left.Count;
4046  if (count != right.Count)
4047  {
4048  return false;
4049  }
4050 
4051  for (int i = 0; i < count; i++)
4052  {
4053  if (!left[i].Equals(right[i]))
4054  {
4055  return false;
4056  }
4057  }
4058 
4059  return true;
4060  }
4061 
4062  /// <summary>
4063  /// Computes a deterministic hash code based on the items in the list. This hash code is dependent on the
4064  /// ordering of items.
4065  /// </summary>
4066  /// <typeparam name="T">The element type</typeparam>
4067  /// <param name="list">The list</param>
4068  /// <returns>A hash code dependent on the ordering of elements in the list</returns>
4069  public static int GetListHashCode<T>(this IReadOnlyList<T> list)
4070  {
4071  unchecked
4072  {
4073  var hashCode = 17;
4074  for (int i = 0; i < list.Count; i++)
4075  {
4076  hashCode += (hashCode * 397) ^ list[i].GetHashCode();
4077  }
4078 
4079  return hashCode;
4080  }
4081  }
4082 
4083  /// <summary>
4084  /// Determine if this SecurityType requires mapping
4085  /// </summary>
4086  /// <param name="symbol">Type to check</param>
4087  /// <returns>True if it needs to be mapped</returns>
4088  public static bool RequiresMapping(this Symbol symbol)
4089  {
4090  switch (symbol.SecurityType)
4091  {
4092  case SecurityType.Base:
4093  return symbol.HasUnderlying && symbol.Underlying.RequiresMapping();
4094  case SecurityType.Future:
4095  return symbol.IsCanonical();
4096  case SecurityType.Equity:
4097  case SecurityType.Option:
4098  return true;
4099  default:
4100  return false;
4101  }
4102  }
4103 
4104  /// <summary>
4105  /// Checks whether the fill event for closing a trade is a winning trade
4106  /// </summary>
4107  /// <param name="fill">The fill event</param>
4108  /// <param name="security">The security being traded</param>
4109  /// <param name="profitLoss">The profit-loss for the closed trade</param>
4110  /// <returns>
4111  /// Whether the trade is a win.
4112  /// For options assignments this depends on whether the option is ITM or OTM and the position side.
4113  /// See <see cref="Trade.IsWin"/> for more information.
4114  /// </returns>
4115  public static bool IsWin(this OrderEvent fill, Security security, decimal profitLoss)
4116  {
4117  // For non-options or non-exercise orders, the trade is a win if the profit-loss is positive
4118  if (!fill.Symbol.SecurityType.IsOption() || fill.Ticket.OrderType != OrderType.OptionExercise)
4119  {
4120  return profitLoss > 0;
4121  }
4122 
4123  var option = (Option)security;
4124 
4125  // If the fill is a sell, the original transaction was a buy
4126  if (fill.Direction == OrderDirection.Sell)
4127  {
4128  // If the option is ITM, the trade is a win only if the profit is greater than the ITM amount
4129  return fill.IsInTheMoney && Math.Abs(profitLoss) < option.InTheMoneyAmount(fill.FillQuantity);
4130  }
4131 
4132  // It is a win if the buyer paid more than what they saved (the ITM amount)
4133  return !fill.IsInTheMoney || Math.Abs(profitLoss) > option.InTheMoneyAmount(fill.FillQuantity);
4134  }
4135 
4136  /// <summary>
4137  /// Gets the option's ITM amount for the given quantity.
4138  /// </summary>
4139  /// <param name="option">The option security</param>
4140  /// <param name="quantity">The quantity</param>
4141  /// <returns>The ITM amount for the absolute quantity</returns>
4142  /// <remarks>The returned value can be negative, which would mean the option is actually OTM.</remarks>
4143  public static ConvertibleCashAmount InTheMoneyAmount(this Option option, decimal quantity)
4144  {
4145  return option.Holdings.GetQuantityValue(Math.Abs(quantity), option.GetPayOff(option.Underlying.Price));
4146  }
4147 
4148  /// <summary>
4149  /// Gets the greatest common divisor of a list of numbers
4150  /// </summary>
4151  /// <param name="values">List of numbers which greatest common divisor is requested</param>
4152  /// <returns>The greatest common divisor for the given list of numbers</returns>
4153  public static int GreatestCommonDivisor(this IEnumerable<int> values)
4154  {
4155  int? result = null;
4156  foreach (var value in values)
4157  {
4158  if (result.HasValue)
4159  {
4160  result = GreatestCommonDivisor(result.Value, value);
4161  }
4162  else
4163  {
4164  result = value;
4165  }
4166  }
4167 
4168  if (!result.HasValue)
4169  {
4170  throw new ArgumentException(Messages.Extensions.GreatestCommonDivisorEmptyList);
4171  }
4172 
4173  return result.Value;
4174  }
4175 
4176  /// <summary>
4177  /// Gets the greatest common divisor of two numbers
4178  /// </summary>
4179  private static int GreatestCommonDivisor(int a, int b)
4180  {
4181  int remainder;
4182  while (b != 0)
4183  {
4184  remainder = a % b;
4185  a = b;
4186  b = remainder;
4187  }
4188  return Math.Abs(a);
4189  }
4190 
4191  /// <summary>
4192  /// Safe method to perform divisions avoiding DivideByZeroException and Overflow/Underflow exceptions
4193  /// </summary>
4194  /// <param name="failValue">Value to be returned if the denominator is zero</param>
4195  /// <returns>The numerator divided by the denominator if the denominator is not
4196  /// zero. Otherwise, the default failValue or the provided one</returns>
4197  public static decimal SafeDivision(this decimal numerator, decimal denominator, decimal failValue = 0)
4198  {
4199  try
4200  {
4201  return (denominator == 0) ? failValue : (numerator / denominator);
4202  }
4203  catch
4204  {
4205  return failValue;
4206  }
4207  }
4208  }
4209 }