using CryptoExchange.Net.Objects;
using System;
using System.Security.Cryptography;
using System.Threading;
namespace CryptoExchange.Net
{
///
/// General helpers functions
///
public static class ExchangeHelpers
{
private const string _allowedRandomChars = "ABCDEFGHIJKLMONOPQRSTUVWXYZabcdefghijklmonopqrstuvwxyz0123456789";
///
/// The last used id, use NextId() to get the next id and up this
///
private static int _lastId;
///
/// Clamp a value between a min and max
///
///
///
///
///
public static decimal ClampValue(decimal min, decimal max, decimal value)
{
value = Math.Min(max, value);
value = Math.Max(min, value);
return value;
}
///
/// Adjust a value to be between the min and max parameters and rounded to the closest step.
///
/// The min value
/// The max value
/// The step size the value should be floored to. For example, value 2.548 with a step size of 0.01 will output 2.54
/// How to round
/// The input value
///
public static decimal AdjustValueStep(decimal min, decimal max, decimal? step, RoundingType roundingType, decimal value)
{
if(step == 0)
throw new ArgumentException($"0 not allowed for parameter {nameof(step)}, pass in null to ignore the step size", nameof(step));
value = Math.Min(max, value);
value = Math.Max(min, value);
if (step == null)
return value;
var offset = value % step.Value;
if(roundingType == RoundingType.Down)
{
value -= offset;
}
else
{
if (offset < step / 2)
value -= offset;
else value += (step.Value - offset);
}
value = RoundDown(value, 8);
return value.Normalize();
}
///
/// Adjust a value to be between the min and max parameters and rounded to the closest precision.
///
/// The min value
/// The max value
/// The precision the value should be rounded to. For example, value 2.554215 with a precision of 5 will output 2.5542
/// How to round
/// The input value
///
public static decimal AdjustValuePrecision(decimal min, decimal max, int? precision, RoundingType roundingType, decimal value)
{
value = Math.Min(max, value);
value = Math.Max(min, value);
if (precision == null)
return value;
return RoundToSignificantDigits(value, precision.Value, roundingType);
}
///
/// Round a value to have the provided total number of digits. For example, value 253.12332 with 5 digits would be 253.12
///
/// The value to round
/// The total amount of digits (NOT decimal places) to round to
/// How to round
///
public static decimal RoundToSignificantDigits(decimal value, int digits, RoundingType roundingType)
{
var val = (double)value;
if (value == 0)
return 0;
double scale = Math.Pow(10, Math.Floor(Math.Log10(Math.Abs(val))) + 1);
if(roundingType == RoundingType.Closest)
return (decimal)(scale * Math.Round(val / scale, digits));
else
return (decimal)(scale * (double)RoundDown((decimal)(val / scale), digits));
}
///
/// Rounds a value down to
///
///
///
///
public static decimal RoundDown(decimal i, double decimalPlaces)
{
var power = Convert.ToDecimal(Math.Pow(10, decimalPlaces));
return Math.Floor(i * power) / power;
}
///
/// Strips any trailing zero's of a decimal value, useful when converting the value to string.
///
///
///
public static decimal Normalize(this decimal value)
{
return value / 1.000000000000000000000000000000000m;
}
///
/// Generate a new unique id. The id is staticly stored so it is guarenteed to be unique
///
///
public static int NextId() => Interlocked.Increment(ref _lastId);
///
/// Return the last unique id that was generated
///
///
public static int LastId() => _lastId;
///
/// Generate a random string of specified length
///
/// Length of the random string
///
public static string RandomString(int length)
{
var randomChars = new char[length];
#if NETSTANDARD2_1_OR_GREATER
for (int i = 0; i < length; i++)
randomChars[i] = _allowedRandomChars[RandomNumberGenerator.GetInt32(0, _allowedRandomChars.Length)];
#else
var random = new Random();
for (int i = 0; i < length; i++)
randomChars[i] = _allowedRandomChars[random.Next(0, _allowedRandomChars.Length)];
#endif
return new string(randomChars);
}
///
/// Generate a random string of specified length
///
/// The initial string
/// Total length of the resulting string
///
public static string AppendRandomString(string source, int totalLength)
{
if (totalLength < source.Length)
throw new ArgumentException("Total length smaller than source string length", nameof(totalLength));
if (totalLength == source.Length)
return source;
return source + RandomString(totalLength - source.Length);
}
}
}