Files
quantconnect--lean/Common/Extensions.cs
T
2015-06-13 16:04:27 -04:00

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&lt;`1,`2&gt; to Dictionary&lt;string,int&gt;
/// </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);
}
}
}