Skip to content

Commit

Permalink
Update symbol price validation for new "PERCENT_PRICE" filter
Browse files Browse the repository at this point in the history
For #111. Extend InclusiveRange (w/ PriceRange class) to transparently get symbol average price to calculate minimum and maximum price limits.
  • Loading branch information
sonvister committed Dec 5, 2018
1 parent c793f57 commit 6938996
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 29 deletions.
29 changes: 22 additions & 7 deletions samples/BinanceConsoleApp/Controllers/IntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,32 @@ public async Task<bool> HandleAsync(string command, CancellationToken token = de


///////////////////////////////////////////////////////////////////
var valid = symbol.IsPriceQuantityValid(5000.01m, 0.1m);
var _valid = symbol.IsPriceQuantityValid(50000.01m, 0.1m);
var __valid = symbol.IsPriceQuantityValid(50.01m, 0.1m);

lock (Program.ConsoleSync)
{
Console.WriteLine();
Console.WriteLine($"Price/Quantity Valid: {valid}");
Console.WriteLine($"Price/Quantity Valid: {_valid}");
Console.WriteLine($"Price/Quantity Valid: {__valid}");
}
///////////////////////////////////////////////////////////////////


/*/////////////////////////////////////////////////////////////////
var price = await Program.Api.GetAvgPriceAsync(symbol, token);
lock (Program.ConsoleSync)
{
Console.WriteLine();
Program.Display(price);
}
///////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////*/


///////////////////////////////////////////////////////////////////
/*/////////////////////////////////////////////////////////////////
var aggTrades = (await Program.Api.GetAggregateTradesAsync(symbol, endTime.Subtract(TimeSpan.FromMinutes(1)), endTime, token))
.Reverse().ToArray();
Expand All @@ -52,10 +67,10 @@ public async Task<bool> HandleAsync(string command, CancellationToken token = de
}
}
}
///////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////*/


///////////////////////////////////////////////////////////////////
/*/////////////////////////////////////////////////////////////////
var trades = (await Program.Api.GetAccountTradesAsync(Program.User, symbol, endTime.Subtract(TimeSpan.FromHours(24)), endTime, token: token))
.Reverse().ToArray();
Expand All @@ -78,10 +93,10 @@ public async Task<bool> HandleAsync(string command, CancellationToken token = de
}
}
}
///////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////*/


///////////////////////////////////////////////////////////////////
/*/////////////////////////////////////////////////////////////////
var orders = await Program.Api
.GetOrdersAsync(Program.User, symbol, endTime.Subtract(TimeSpan.FromHours(24)), endTime, token: token);
Expand All @@ -104,7 +119,7 @@ public async Task<bool> HandleAsync(string command, CancellationToken token = de
}
}
}
///////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////*/


return true;
Expand Down
43 changes: 32 additions & 11 deletions src/Binance/Api/BinanceApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,17 +419,38 @@ public virtual async Task<IEnumerable<Symbol>> GetSymbolsAsync(CancellationToken

var filters = jToken["filters"];

var quoteMinPrice = filters[0]["minPrice"].Value<decimal>();
var quoteMaxPrice = filters[0]["maxPrice"].Value<decimal>();
var quoteIncrement = filters[0]["tickSize"].Value<decimal>();

var baseMinQty = filters[1]["minQty"].Value<decimal>();
var baseMaxQty = filters[1]["maxQty"].Value<decimal>();
var baseIncrement = filters[1]["stepSize"].Value<decimal>();

var minNotional = filters[2]["minNotional"].Value<decimal>();

var symbol = new Symbol(status, baseAsset, quoteAsset, (baseMinQty, baseMaxQty, baseIncrement), (quoteMinPrice, quoteMaxPrice, quoteIncrement), minNotional, icebergAllowed, orderTypes);
decimal quoteIncrement = 0;
var priceFilter = filters.FirstOrDefault(f => f["filterType"].Value<string>() == "PRICE_FILTER");
if (priceFilter != null)
{
quoteIncrement = priceFilter["tickSize"].Value<decimal>();
}

decimal multiplierUp = 0, multiplierDown = 0;
var percentFilter = filters.FirstOrDefault(f => f["filterType"].Value<string>() == "PERCENT_PRICE");
if (percentFilter != null)
{
multiplierUp = percentFilter["multiplierUp"].Value<decimal>();
multiplierDown = percentFilter["multiplierDown"].Value<decimal>();
}

decimal baseMinQty = 0, baseMaxQty = 0, baseIncrement = 0;
var quantityFilter = filters.FirstOrDefault(f => f["filterType"].Value<string>() == "LOT_SIZE");
if (quantityFilter != null)
{
baseMinQty = quantityFilter["minQty"].Value<decimal>();
baseMaxQty = quantityFilter["maxQty"].Value<decimal>();
baseIncrement = quantityFilter["stepSize"].Value<decimal>();
}

decimal minNotional = 0;
var minNotionalFilter = filters.FirstOrDefault(f => f["filterType"].Value<string>() == "MIN_NOTIONAL");
if (minNotionalFilter != null)
{
minNotional = minNotionalFilter["minNotional"].Value<decimal>();
}

var symbol = new Symbol(status, baseAsset, quoteAsset, (baseMinQty, baseMaxQty, baseIncrement), new PriceRange(this, jToken["symbol"].Value<string>(), multiplierUp, multiplierDown, quoteIncrement), minNotional, icebergAllowed, orderTypes);

if (symbol.ToString() == jToken["symbol"].Value<string>())
return symbol;
Expand Down
26 changes: 17 additions & 9 deletions src/Binance/InclusiveRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@

namespace Binance
{
public sealed class InclusiveRange
public class InclusiveRange
{
#region Public Properties

/// <summary>
/// Get the miniumum value.
/// </summary>
public decimal Minimum { get; }
public virtual decimal Minimum => _minimum;

/// <summary>
/// Get the maximum value.
/// </summary>
public decimal Maximum { get; }
public virtual decimal Maximum => _maximum;

/// <summary>
/// Get the increment value.
Expand All @@ -33,6 +33,13 @@ public static implicit operator InclusiveRange((decimal, decimal, decimal) range

#endregion Implicit Operators

#region Private Fields

private decimal _minimum;
private decimal _maximum;

#endregion Private Fields

#region Constructors

/// <summary>
Expand All @@ -43,15 +50,16 @@ public static implicit operator InclusiveRange((decimal, decimal, decimal) range
/// <param name="increment"></param>
public InclusiveRange(decimal minimum, decimal maximum, decimal increment)
{
if (minimum <= 0)
throw new ArgumentException($"{nameof(InclusiveRange)}: value must be greater than 0.", nameof(minimum));
if (maximum <= 0)
throw new ArgumentException($"{nameof(InclusiveRange)}: value must be greater than 0.", nameof(maximum));
if (minimum < 0)
throw new ArgumentException($"{nameof(InclusiveRange)}: value must not be less than 0.", nameof(minimum));
if (maximum < 0)
throw new ArgumentException($"{nameof(InclusiveRange)}: value must not be less than 0.", nameof(maximum));
if (increment <= 0)
throw new ArgumentException($"{nameof(InclusiveRange)}: value must be greater than 0.", nameof(increment));

Minimum = minimum;
Maximum = maximum;
_minimum = minimum;
_maximum = maximum;

Increment = increment;
}

Expand Down
63 changes: 63 additions & 0 deletions src/Binance/PriceRange.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;

namespace Binance
{
public sealed class PriceRange : InclusiveRange
{
#region Public Properties

public decimal MultiplierUp { get; }

public decimal MultiplierDown { get; }

public override decimal Minimum => Math.Floor(GetAveragePrice() * MultiplierDown / Increment) * Increment;

public override decimal Maximum => Math.Ceiling(GetAveragePrice() * MultiplierUp / Increment) * Increment;

#endregion Public Properties

#region Private Fields

private IBinanceApi _api;

private string _symbol;

private decimal _averagePrice;

private DateTime _lastUpdate = DateTime.MinValue;

#endregion Private Fields

#region Constructor

public PriceRange(IBinanceApi api, string symbol, decimal multiplierUp, decimal multiplierDown, decimal increment)
: base(0, 0, increment)
{
Throw.IfNull(api, nameof(api));
Throw.IfNullOrWhiteSpace(symbol, nameof(symbol));

_api = api;
_symbol = symbol;

MultiplierUp = multiplierUp;
MultiplierDown = multiplierDown;
}

#endregion Constructor

#region Private Methods

private decimal GetAveragePrice()
{
if (_averagePrice == 0 || DateTime.UtcNow - _lastUpdate > TimeSpan.FromSeconds(1))
{
_averagePrice = _api.GetAvgPriceAsync(_symbol).GetAwaiter().GetResult().Value;
_lastUpdate = DateTime.UtcNow;
}

return _averagePrice;
}

#endregion Private Methods
}
}
2 changes: 0 additions & 2 deletions test/Binance.Tests/InclusiveRangeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ public class InclusiveRangeTest
public void Throws()
{
Assert.Throws<ArgumentException>("minimum", () => new InclusiveRange(-1, 1, 1));
Assert.Throws<ArgumentException>("minimum", () => new InclusiveRange(0, 1, 1));
Assert.Throws<ArgumentException>("maximum", () => new InclusiveRange(1, -1, 1));
Assert.Throws<ArgumentException>("maximum", () => new InclusiveRange(1, 0, 1));
Assert.Throws<ArgumentException>("increment", () => new InclusiveRange(1, 1, -1));
Assert.Throws<ArgumentException>("increment", () => new InclusiveRange(1, 1, 0));
}
Expand Down

0 comments on commit 6938996

Please sign in to comment.