333 lines
13 KiB
C#
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;
|
|
}
|
|
}
|
|
} |