Files
quantconnect--lean/Common/Extensions.cs
T
2015-01-12 12:03:33 -03:00

333 lines
13 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 NAMESPACES
**********************************************************/
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Collections.Concurrent;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
namespace QuantConnect
{
/********************************************************
* CLASS DEFINITIONS
*********************************************************/
/// <summary>
/// Extensions function collections - group all static extensions functions here.
/// </summary>
public static class Extensions {
/********************************************************
* CLASS VARIABLES
*********************************************************/
/********************************************************
* CLASS PROPERTIES
*********************************************************/
/********************************************************
* CLASS METHODS
*********************************************************/
/// <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 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)
{
var divider = 10;
for (var i = 1; i < decimalPlaces; i++) divider *= 10;
return (decimal)value / divider;
}
return (decimal)value;
}
/// <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)
{
return new TimeSpan(
Convert.ToInt64(System.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)
{
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)
{
return new DateTime(((time.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks);
}
/// <summary>
/// Extension method to searches the composition container for an export that has a matching type name. This function
/// will first try to match on Type.FullName, and if unsuccessful will try to match on Type.Name
///
/// This method will not throw if multiple types are found matching the name, it will just return the first one it finds.
/// </summary>
/// <typeparam name="T">The type of the export</typeparam>
/// <param name="container">The container to search</param>
/// <param name="typeName">The name of the type to find. This can be an assembly qualified name, a full name, or just the type's name</param>
/// <returns>The export instance</returns>
public static T GetExportedValueByTypeName<T>(this CompositionContainer container, string typeName)
where T : class
{
var values = container.GetExportedValues<T>().ToList();
// first check assembly qualified name
var value = values.FirstOrDefault(x => x.GetType().AssemblyQualifiedName == typeName);
if (value != null)
{
return value;
}
// check for full type name
value = values.FirstOrDefault(x => x.GetType().FullName == typeName);
if (value != null)
{
return value;
}
// lastly, just check for the type's name
value = values.FirstOrDefault(x => x.GetType().Name == typeName);
if (value == null)
{
throw new ArgumentException("Unable to locate any exports matching the requested typeName: " + typeName, "typeName");
}
return value;
}
}
}