435 lines
17 KiB
C#
435 lines
17 KiB
C#
/*
|
|
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
|
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Collections.Generic;
|
|
using System.Security.Cryptography;
|
|
using System.Collections.Concurrent;
|
|
using System.Timers;
|
|
|
|
namespace QuantConnect
|
|
{
|
|
/// <summary>
|
|
/// Extensions function collections - group all static extensions functions here.
|
|
/// </summary>
|
|
public static class Extensions
|
|
{
|
|
/// <summary>
|
|
/// Extension to move one element from list from A to position B.
|
|
/// </summary>
|
|
/// <typeparam name="T">Type of list</typeparam>
|
|
/// <param name="list">List we're operating on.</param>
|
|
/// <param name="oldIndex">Index of variable we want to move.</param>
|
|
/// <param name="newIndex">New location for the variable</param>
|
|
public static void Move<T>(this List<T> list, int oldIndex, int newIndex)
|
|
{
|
|
var oItem = list[oldIndex];
|
|
list.RemoveAt(oldIndex);
|
|
if (newIndex > oldIndex) newIndex--;
|
|
list.Insert(newIndex, oItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method to convert a string into a byte array
|
|
/// </summary>
|
|
/// <param name="str">String to convert to bytes.</param>
|
|
/// <returns>Byte array</returns>
|
|
public static byte[] GetBytes(this string str)
|
|
{
|
|
var bytes = new byte[str.Length * sizeof(char)];
|
|
Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
|
|
return bytes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extentsion method to clear all items from a thread safe queue
|
|
/// </summary>
|
|
/// <remarks>Small risk of race condition if a producer is adding to the list.</remarks>
|
|
/// <typeparam name="T">Queue type</typeparam>
|
|
/// <param name="queue">queue object</param>
|
|
public static void Clear<T>(this ConcurrentQueue<T> queue)
|
|
{
|
|
T item;
|
|
while (queue.TryDequeue(out item)) {
|
|
// NOP
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method to convert a byte array into a string.
|
|
/// </summary>
|
|
/// <param name="bytes">Byte array to convert.</param>
|
|
/// <returns>String from bytes.</returns>
|
|
public static string GetString(this byte[] bytes)
|
|
{
|
|
var chars = new char[bytes.Length / sizeof(char)];
|
|
Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
|
|
return new string(chars);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method to convert a string to a MD5 hash.
|
|
/// </summary>
|
|
/// <param name="str">String we want to MD5 encode.</param>
|
|
/// <returns>MD5 hash of a string</returns>
|
|
public static string ToMD5(this string str)
|
|
{
|
|
var builder = new StringBuilder();
|
|
using (var md5Hash = MD5.Create())
|
|
{
|
|
var data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(str));
|
|
foreach (var t in data) builder.Append(t.ToString("x2"));
|
|
}
|
|
return builder.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method to automatically set the update value to same as "add" value for TryAddUpdate.
|
|
/// This makes the API similar for traditional and concurrent dictionaries.
|
|
/// </summary>
|
|
/// <typeparam name="K">Key type for dictionary</typeparam>
|
|
/// <typeparam name="V">Value type for dictonary</typeparam>
|
|
/// <param name="dictionary">Dictionary object we're operating on</param>
|
|
/// <param name="key">Key we want to add or update.</param>
|
|
/// <param name="value">Value we want to set.</param>
|
|
public static void AddOrUpdate<K, V>(this ConcurrentDictionary<K, V> dictionary, K key, V value)
|
|
{
|
|
dictionary.AddOrUpdate(key, value, (oldkey, oldvalue) => value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method to round a double value to a fixed number of significant figures instead of a fixed decimal places.
|
|
/// </summary>
|
|
/// <param name="d">Double we're rounding</param>
|
|
/// <param name="digits">Number of significant figures</param>
|
|
/// <returns>New double rounded to digits-significant figures</returns>
|
|
public static double RoundToSignificantDigits(this double d, int digits)
|
|
{
|
|
if (d == 0) return 0;
|
|
var scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(d))) + 1);
|
|
return scale * Math.Round(d / scale, digits);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method to round a double value to a fixed number of significant figures instead of a fixed decimal places.
|
|
/// </summary>
|
|
/// <param name="d">Double we're rounding</param>
|
|
/// <param name="digits">Number of significant figures</param>
|
|
/// <returns>New double rounded to digits-significant figures</returns>
|
|
public static decimal RoundToSignificantDigits(this decimal d, int digits)
|
|
{
|
|
if (d == 0) return 0;
|
|
var scale = (decimal)Math.Pow(10, Math.Floor(Math.Log10((double) Math.Abs(d))) + 1);
|
|
return scale * Math.Round(d / scale, digits);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method for faster string to decimal conversion.
|
|
/// </summary>
|
|
/// <param name="str">String to be converted to decimal value</param>
|
|
/// <remarks>Method makes some assuptions - always numbers, no "signs" +,- etc.</remarks>
|
|
/// <returns>Decimal value of the string</returns>
|
|
public static decimal ToDecimal(this string str)
|
|
{
|
|
long value = 0;
|
|
var exp = 0;
|
|
var decimalPlaces = int.MinValue;
|
|
const long maxValueDivideTen = (long.MaxValue/10);
|
|
|
|
for (var i = 0; i < str.Length; i++)
|
|
{
|
|
var ch = str[i];
|
|
if (ch >= '0' && ch <= '9')
|
|
{
|
|
while (value >= maxValueDivideTen)
|
|
{
|
|
value >>= 1;
|
|
exp++;
|
|
}
|
|
value = value * 10 + (ch - '0');
|
|
decimalPlaces++;
|
|
}
|
|
else if (ch == '.')
|
|
{
|
|
decimalPlaces = 0;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (decimalPlaces > 0)
|
|
{
|
|
return (decimal)value / (int)Math.Pow(10, decimalPlaces);
|
|
}
|
|
|
|
return (decimal)value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the smallest positive number that can be added to a decimal instance and return
|
|
/// a new value that does not == the old value
|
|
/// </summary>
|
|
public static decimal GetDecimalEpsilon()
|
|
{
|
|
return new decimal(1, 0, 0, false, 27); //1e-27m;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="str">String we're looking for the extension for.</param>
|
|
/// <returns>Last 4 character string of string.</returns>
|
|
public static string GetExtension(this string str) {
|
|
var ext = str.Substring(Math.Max(0, str.Length - 4));
|
|
var allowedExt = new List<string>() { ".zip", ".csv", ".json" };
|
|
if (!allowedExt.Contains(ext))
|
|
{
|
|
ext = ".custom";
|
|
}
|
|
return ext;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method to convert strings to stream to be read.
|
|
/// </summary>
|
|
/// <param name="str">String to convert to stream</param>
|
|
/// <returns>Stream instance</returns>
|
|
public static Stream ToStream(this string str)
|
|
{
|
|
var stream = new MemoryStream();
|
|
var writer = new StreamWriter(stream);
|
|
writer.Write(str);
|
|
writer.Flush();
|
|
stream.Position = 0;
|
|
return stream;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method to round a timeSpan to nearest timespan period.
|
|
/// </summary>
|
|
/// <param name="time">TimeSpan To Round</param>
|
|
/// <param name="roundingInterval">Rounding Unit</param>
|
|
/// <param name="roundingType">Rounding method</param>
|
|
/// <returns>Rounded timespan</returns>
|
|
public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval, MidpointRounding roundingType)
|
|
{
|
|
if (roundingInterval == TimeSpan.Zero)
|
|
{
|
|
// divide by zero exception
|
|
return time;
|
|
}
|
|
|
|
return new TimeSpan(
|
|
Convert.ToInt64(Math.Round(
|
|
time.Ticks / (decimal)roundingInterval.Ticks,
|
|
roundingType
|
|
)) * roundingInterval.Ticks
|
|
);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Extension method to round timespan to nearest timespan period.
|
|
/// </summary>
|
|
/// <param name="time">Base timespan we're looking to round.</param>
|
|
/// <param name="roundingInterval">Timespan period we're rounding.</param>
|
|
/// <returns>Rounded timespan period</returns>
|
|
public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval)
|
|
{
|
|
return Round(time, roundingInterval, MidpointRounding.ToEven);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method to round a datetime down by a timespan interval.
|
|
/// </summary>
|
|
/// <param name="dateTime">Base DateTime object we're rounding down.</param>
|
|
/// <param name="interval">Timespan interval to round to.</param>
|
|
/// <returns>Rounded datetime</returns>
|
|
public static DateTime RoundDown(this DateTime dateTime, TimeSpan interval)
|
|
{
|
|
if (interval == TimeSpan.Zero)
|
|
{
|
|
// divide by zero exception
|
|
return dateTime;
|
|
}
|
|
return dateTime.AddTicks(-(dateTime.Ticks % interval.Ticks));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method to round a datetime to the nearest unit timespan.
|
|
/// </summary>
|
|
/// <param name="datetime">Datetime object we're rounding.</param>
|
|
/// <param name="roundingInterval">Timespan rounding period.s</param>
|
|
/// <returns>Rounded datetime</returns>
|
|
public static DateTime Round(this DateTime datetime, TimeSpan roundingInterval)
|
|
{
|
|
return new DateTime((datetime - DateTime.MinValue).Round(roundingInterval).Ticks);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension method to explicitly round up to the nearest timespan interval.
|
|
/// </summary>
|
|
/// <param name="time">Base datetime object to round up.</param>
|
|
/// <param name="d">Timespan interval for rounding</param>
|
|
/// <returns>Rounded datetime</returns>
|
|
public static DateTime RoundUp(this DateTime time, TimeSpan d)
|
|
{
|
|
if (d == TimeSpan.Zero)
|
|
{
|
|
// divide by zero exception
|
|
return time;
|
|
}
|
|
return new DateTime(((time.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add the reset method to the System.Timer class.
|
|
/// </summary>
|
|
/// <param name="timer">System.timer object</param>
|
|
public static void Reset(this Timer timer)
|
|
{
|
|
timer.Stop();
|
|
timer.Start();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Function used to match a type against a string type name. This function compares on the AssemblyQualfiedName,
|
|
/// the FullName, and then just the Name of the type.
|
|
/// </summary>
|
|
/// <param name="type">The type to test for a match</param>
|
|
/// <param name="typeName">The name of the type to match</param>
|
|
/// <returns>True if the specified type matches the type name, false otherwise</returns>
|
|
public static bool MatchesTypeName(this Type type, string typeName)
|
|
{
|
|
if (type.AssemblyQualifiedName == typeName)
|
|
{
|
|
return true;
|
|
}
|
|
if (type.FullName == typeName)
|
|
{
|
|
return true;
|
|
}
|
|
if (type.Name == typeName)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks the specified type to see if it is a subclass of the <paramref name="possibleSuperType"/>. This method will
|
|
/// crawl up the inheritance heirarchy to check for equality using generic type definitions (if exists)
|
|
/// </summary>
|
|
/// <param name="type">The type to be checked as a subclass of <paramref name="possibleSuperType"/></param>
|
|
/// <param name="possibleSuperType">The possible superclass of <paramref name="type"/></param>
|
|
/// <returns>True if <paramref name="type"/> is a subclass of the generic type definition <paramref name="possibleSuperType"/></returns>
|
|
public static bool IsSubclassOfGeneric(this Type type, Type possibleSuperType)
|
|
{
|
|
while (type != null && type != typeof(object))
|
|
{
|
|
Type cur;
|
|
if (type.IsGenericType && possibleSuperType.IsGenericTypeDefinition)
|
|
{
|
|
cur = type.GetGenericTypeDefinition();
|
|
}
|
|
else
|
|
{
|
|
cur = type;
|
|
}
|
|
if (possibleSuperType == cur)
|
|
{
|
|
return true;
|
|
}
|
|
type = type.BaseType;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a type's name with the generic parameters filled in the way they would look when
|
|
/// defined in code, such as converting Dictionary<`1,`2> to Dictionary<string,int>
|
|
/// </summary>
|
|
/// <param name="type">The type who's name we seek</param>
|
|
/// <returns>A better type name</returns>
|
|
public static string GetBetterTypeName(this Type type)
|
|
{
|
|
string name = type.Name;
|
|
if (type.IsGenericType)
|
|
{
|
|
var genericArguments = type.GetGenericArguments();
|
|
for (int i = 0; i < genericArguments.Length; i++)
|
|
{
|
|
string toBeReplaced = "`" + (i + 1);
|
|
name = name.Replace(toBeReplaced, genericArguments[i].GetBetterTypeName());
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts the Resolution instance into a TimeSpan instance
|
|
/// </summary>
|
|
/// <param name="resolution">The resolution to be converted</param>
|
|
/// <returns>A TimeSpan instance that represents the resolution specified</returns>
|
|
public static TimeSpan ToTimeSpan(this Resolution resolution)
|
|
{
|
|
switch (resolution)
|
|
{
|
|
case Resolution.Tick:
|
|
// ticks can be instantaneous
|
|
return TimeSpan.FromTicks(0);
|
|
case Resolution.Second:
|
|
return TimeSpan.FromSeconds(1);
|
|
case Resolution.Minute:
|
|
return TimeSpan.FromMinutes(1);
|
|
case Resolution.Hour:
|
|
return TimeSpan.FromHours(1);
|
|
case Resolution.Daily:
|
|
return TimeSpan.FromDays(1);
|
|
default:
|
|
throw new ArgumentOutOfRangeException("resolution");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts the specified string value into the specified type
|
|
/// </summary>
|
|
/// <typeparam name="T">The output type</typeparam>
|
|
/// <param name="value">The string value to be converted</param>
|
|
/// <returns>The converted value</returns>
|
|
public static T ConvertTo<T>(this string value)
|
|
{
|
|
var conversionType = typeof (T);
|
|
if (conversionType.IsEnum)
|
|
{
|
|
return (T) Enum.Parse(conversionType, value);
|
|
}
|
|
if (typeof (IConvertible).IsAssignableFrom(conversionType))
|
|
{
|
|
return (T) Convert.ChangeType(value, conversionType);
|
|
}
|
|
if (typeof (TimeSpan) == conversionType)
|
|
{
|
|
return (T) (object) TimeSpan.Parse(value);
|
|
}
|
|
|
|
throw new ArgumentException("Extensions.ConvertTo is unable to convert to type: " + typeof (T).Name);
|
|
}
|
|
}
|
|
} |