public double CalculateLotSize(double price, string currency, string fullquotename, double risk = 0) { var MySymbolConf = this.Pairs.Where(y => y.Name == fullquotename).First(); var MinimalLot = MySymbolConf.Filters.OfType <BinanceSymbolLotSizeFilter>().FirstOrDefault(); var minNotionalFilter = MySymbolConf.Filters.OfType <BinanceSymbolMinNotionalFilter>().SingleOrDefault(); var PriceSize = MySymbolConf.Filters.OfType <BinanceSymbolPriceFilter>().FirstOrDefault(); var AdjustedPrice = BinanceHelpers.ClampPrice(PriceSize.MinPrice, PriceSize.MaxPrice, PriceSize.TickSize, price.ChangeType <decimal>()); var definedsize = (AvailableForOrder(currency) / price).ChangeType <decimal>(); var AdjustedLotSize = BinanceHelpers.ClampQuantity(MinimalLot.MinQuantity, MinimalLot.MaxQuantity, MinimalLot.StepSize, (AvailableForOrder(currency) / price).ChangeType <decimal>()).ChangeType <double>(); var Validate = AdjustedLotSize * price > minNotionalFilter.MinNotional.ChangeType <double>(); if (Validate) { return(AdjustedLotSize); } else { return(minNotionalFilter.MinNotional.ChangeType <double>()); } }
internal override async Task <BinanceTradeRuleResult> CheckTradeRules(string symbol, decimal?quantity, decimal?price, decimal?stopPrice, OrderType type, CancellationToken ct) { var outputQuantity = quantity; var outputPrice = price; var outputStopPrice = stopPrice; if (BaseClient.TradeRulesBehaviour == TradeRulesBehaviour.None) { return(BinanceTradeRuleResult.CreatePassed(outputQuantity, outputPrice, outputStopPrice)); } if (ExchangeInfo == null || LastExchangeInfoUpdate == null || (DateTime.UtcNow - LastExchangeInfoUpdate.Value).TotalMinutes > BaseClient.TradeRulesUpdateInterval.TotalMinutes) { await System.GetExchangeInfoAsync(ct).ConfigureAwait(false); } if (ExchangeInfo == null) { return(BinanceTradeRuleResult.CreateFailed("Unable to retrieve trading rules, validation failed")); } var symbolData = ExchangeInfo.Symbols.SingleOrDefault(s => string.Equals(s.Name, symbol, StringComparison.CurrentCultureIgnoreCase)); if (symbolData == null) { return(BinanceTradeRuleResult.CreateFailed($"Trade rules check failed: Symbol {symbol} not found")); } if (!symbolData.OrderTypes.Contains(type)) { return(BinanceTradeRuleResult.CreateFailed($"Trade rules check failed: {type} order type not allowed for {symbol}")); } if (symbolData.LotSizeFilter != null || (symbolData.MarketLotSizeFilter != null && type == OrderType.Market)) { var minQty = symbolData.LotSizeFilter?.MinQuantity; var maxQty = symbolData.LotSizeFilter?.MaxQuantity; var stepSize = symbolData.LotSizeFilter?.StepSize; if (type == OrderType.Market && symbolData.MarketLotSizeFilter != null) { minQty = symbolData.MarketLotSizeFilter.MinQuantity; if (symbolData.MarketLotSizeFilter.MaxQuantity != 0) { maxQty = symbolData.MarketLotSizeFilter.MaxQuantity; } if (symbolData.MarketLotSizeFilter.StepSize != 0) { stepSize = symbolData.MarketLotSizeFilter.StepSize; } } if (minQty.HasValue && quantity.HasValue) { outputQuantity = BinanceHelpers.ClampQuantity(minQty.Value, maxQty !.Value, stepSize !.Value, quantity.Value); if (outputQuantity != quantity.Value) { if (BaseClient.TradeRulesBehaviour == TradeRulesBehaviour.ThrowError) { return(BinanceTradeRuleResult.CreateFailed($"Trade rules check failed: LotSize filter failed. Original quantity: {quantity}, Closest allowed: {outputQuantity}")); } _log.Write(LogVerbosity.Info, $"Quantity clamped from {quantity} to {outputQuantity}"); } } } if (price == null) { return(BinanceTradeRuleResult.CreatePassed(outputQuantity, null, outputStopPrice)); } if (symbolData.PriceFilter != null) { if (symbolData.PriceFilter.MaxPrice != 0 && symbolData.PriceFilter.MinPrice != 0) { outputPrice = BinanceHelpers.ClampPrice(symbolData.PriceFilter.MinPrice, symbolData.PriceFilter.MaxPrice, price.Value); if (outputPrice != price) { if (BaseClient.TradeRulesBehaviour == TradeRulesBehaviour.ThrowError) { return(BinanceTradeRuleResult.CreateFailed($"Trade rules check failed: Price filter max/min failed. Original price: {price}, Closest allowed: {outputPrice}")); } _log.Write(LogVerbosity.Info, $"price clamped from {price} to {outputPrice}"); } if (stopPrice != null) { outputStopPrice = BinanceHelpers.ClampPrice(symbolData.PriceFilter.MinPrice, symbolData.PriceFilter.MaxPrice, stopPrice.Value); if (outputStopPrice != stopPrice) { if (BaseClient.TradeRulesBehaviour == TradeRulesBehaviour.ThrowError) { return(BinanceTradeRuleResult.CreateFailed( $"Trade rules check failed: Stop price filter max/min failed. Original stop price: {stopPrice}, Closest allowed: {outputStopPrice}")); } _log.Write(LogVerbosity.Info, $"Stop price clamped from {stopPrice} to {outputStopPrice} based on price filter"); } } } if (symbolData.PriceFilter.TickSize != 0) { var beforePrice = outputPrice; outputPrice = BinanceHelpers.FloorPrice(symbolData.PriceFilter.TickSize, price.Value); if (outputPrice != beforePrice) { if (BaseClient.TradeRulesBehaviour == TradeRulesBehaviour.ThrowError) { return(BinanceTradeRuleResult.CreateFailed($"Trade rules check failed: Price filter tick failed. Original price: {price}, Closest allowed: {outputPrice}")); } _log.Write(LogVerbosity.Info, $"price rounded from {beforePrice} to {outputPrice}"); } if (stopPrice != null) { var beforeStopPrice = outputStopPrice; outputStopPrice = BinanceHelpers.FloorPrice(symbolData.PriceFilter.TickSize, stopPrice.Value); if (outputStopPrice != beforeStopPrice) { if (BaseClient.TradeRulesBehaviour == TradeRulesBehaviour.ThrowError) { return(BinanceTradeRuleResult.CreateFailed( $"Trade rules check failed: Stop price filter tick failed. Original stop price: {stopPrice}, Closest allowed: {outputStopPrice}")); } _log.Write(LogVerbosity.Info, $"Stop price floored from {beforeStopPrice} to {outputStopPrice} based on price filter"); } } } } return(BinanceTradeRuleResult.CreatePassed(outputQuantity, outputPrice, outputStopPrice)); }
public void Sell(IPair SourceCandle, string TargetPair, OrderType TypeOforder, Guid Operation, decimal _quantity = 0) { Console.WriteLine("Order SVC starting for sell : {0}", TargetPair); try { var pivotalbuy = SourceCandle.Candle.Close.ChangeType <decimal>(); var MySymbolConf = this.Pairs.Where(y => y.Name == TargetPair).First(); var MinimalLot = MySymbolConf.Filters.OfType <BinanceSymbolLotSizeFilter>().FirstOrDefault(); var minNotionalFilter = MySymbolConf.Filters.OfType <BinanceSymbolMinNotionalFilter>().SingleOrDefault(); var PriceSize = MySymbolConf.Filters.OfType <BinanceSymbolPriceFilter>().FirstOrDefault(); var AdjustedPrice = BinanceHelpers.ClampPrice(PriceSize.MinPrice, PriceSize.MaxPrice, PriceSize.TickSize, pivotalbuy); var AdjustedLotSize = (MinimalLot.MinQuantity / BinanceHelpers.ClampQuantity(MinimalLot.MinQuantity, MinimalLot.MaxQuantity, MinimalLot.StepSize, _quantity)); while ((_quantity - MinimalLot.MinQuantity) % MinimalLot.StepSize != 0) { _quantity += MinimalLot.StepSize; } var test = (_quantity - MinimalLot.MinQuantity) % MinimalLot.StepSize == 0; while ((_quantity * AdjustedPrice) < minNotionalFilter.MinNotional) { _quantity += MinimalLot.StepSize; } var cost = AdjustedLotSize * AdjustedPrice; if (TypeOforder == OrderType.Limit) { var result = this.Service.Client.PlaceOrder(TargetPair, OrderSide.Sell, OrderType.Limit, timeInForce: TimeInForce.GoodTillCancel, quantity: _quantity , price: AdjustedPrice); if (!result.Success) { } else { bool checker = false; int pass = 0; while (!checker) { Console.WriteLine("Checking if order filled for pair - {0}", Pairs); pass++; if (pass > 60) { this.Service.Client.CancelOrder(TargetPair, result.Data.OrderId); //this.Service.Client.PlaceOrder(TargetPair, OrderSide.Sell, OrderType.Market, timeInForce: TimeInForce.GoodTillCancel, quantity: _quantity); checker = true; } var orderrslt = this.Service.Client.QueryOrder(TargetPair, result.Data.OrderId); if (orderrslt.Data.Status == OrderStatus.Filled) { Console.WriteLine("Pair {0} - Sell Order passed test api : {1} at quantity {2} price {3} ", TargetPair, result.Success, _quantity, AdjustedPrice); var SourceOrder = this.ExecutedOrders.Find(y => y.OperationID == Operation); var IndexOfOrder = this.ExecutedOrders.IndexOf(SourceOrder); SourceOrder.Soldprice = AdjustedPrice; SourceOrder.State = OrderExecutionStatus.Submited; SourceOrder.SellLinkedOrder = result.Data; this.ExecutedOrders[IndexOfOrder] = SourceOrder; Console.WriteLine("Order filled"); checker = true; } System.Threading.Thread.Sleep(5000); } } } } catch (Exception ex) { } }
public void Buy(IPair SourceCandle, string TargetPair, OrderType TypeOforder, Guid Operation, decimal _quantity = 0) { Console.WriteLine("Order Management starting for : {0}", TargetPair); try { var pivotalbuy = SourceCandle.Candle.Close.ChangeType <decimal>(); var MySymbolConf = this.Pairs.Where(y => y.Name == TargetPair).First(); var MinimalLot = MySymbolConf.Filters.OfType <BinanceSymbolLotSizeFilter>().FirstOrDefault(); var minNotionalFilter = MySymbolConf.Filters.OfType <BinanceSymbolMinNotionalFilter>().SingleOrDefault(); var PriceSize = MySymbolConf.Filters.OfType <BinanceSymbolPriceFilter>().FirstOrDefault(); var AdjustedPrice = BinanceHelpers.ClampPrice(PriceSize.MinPrice, PriceSize.MaxPrice, PriceSize.TickSize, pivotalbuy); var AdjustedLotSize = (MinimalLot.MinQuantity / BinanceHelpers.ClampQuantity(MinimalLot.MinQuantity, MinimalLot.MaxQuantity, MinimalLot.StepSize, _quantity)); while ((_quantity - MinimalLot.MinQuantity) % MinimalLot.StepSize != 0) { _quantity += MinimalLot.StepSize; } var test = (_quantity - MinimalLot.MinQuantity) % MinimalLot.StepSize == 0; while ((_quantity * AdjustedPrice) < minNotionalFilter.MinNotional) { _quantity += MinimalLot.StepSize; } var Trade = new TradeItem { BoughtPrice = AdjustedPrice, Soldprice = 0m, Symbol = TargetPair, OperationID = Operation, State = OrderExecutionStatus.Ready, }; var DenyOperation = ExecutedOrders.Count() > 1 && ExecutedOrders.Where(y => y.OperationID == Operation).ToList().Count() > 1; if (TypeOforder == OrderType.Limit && !DenyOperation) { var result = this.Service.Client.PlaceOrder(TargetPair, OrderSide.Buy, OrderType.Limit, timeInForce: TimeInForce.GoodTillCancel, quantity: _quantity , price: AdjustedPrice); ExecutedOrders.Add(Trade); if (!result.Success) { Console.WriteLine("Order not filled due to : {0}", result.Error.Message); ExecutedOrders.Where(y => y.OperationID == Operation).First().State = OrderExecutionStatus.Error; } else { bool checker = false; int pass = 0; //Update order price ExecutedOrders.Where(y => y.OperationID == Operation).First().BuyLinkedOrder = result.Data; ExecutedOrders.Where(y => y.OperationID == Operation).First().BoughtPrice = AdjustedPrice; ExecutedOrders.Where(y => y.OperationID == Operation).First().OrderID = result.Data.OrderId; ExecutedOrders.Where(y => y.OperationID == Operation).First().State = OrderExecutionStatus.Submited; while (!checker) { ExecutedOrders.Where(y => y.OperationID == Operation).First().State = OrderExecutionStatus.Waiting; pass++; if (pass > 60) { this.Service.Client.CancelOrder(TargetPair, result.Data.OrderId); ExecutedOrders.Where(y => y.OperationID == Operation).First().State = OrderExecutionStatus.Error; checker = true; } Console.WriteLine("Checking if order filled for pair - {0}", Pairs); var orderrslt = this.Service.Client.QueryOrder(TargetPair, result.Data.OrderId); if (orderrslt.Data.Status == OrderStatus.Filled) { ExecutedOrders.Where(y => y.OperationID == Operation).First().State = OrderExecutionStatus.Filled; Console.WriteLine("Pair {0} - Buy Order passed : {1} at quantity {2} price {3} ", TargetPair, result.Success, _quantity, AdjustedPrice); Console.WriteLine("Order filled"); checker = true; } System.Threading.Thread.Sleep(5000); } } } } catch (Exception ex) { } }
public IRuleResult RuleExecuted(Solbot solbot) { var result = false; var message = string.Empty; if (solbot.Communication.StopLoss.IsReady) { WebCallResult <BinancePlacedOrder> stopLossOrderResult = null; var quantity = BinanceHelpers.ClampQuantity(solbot.Communication.Symbol.MinQuantity, solbot.Communication.Symbol.MaxQuantity, solbot.Communication.Symbol.StepSize, solbot.Communication.AvailableAsset.Base); if (solbot.Strategy.AvailableStrategy.StopLossType == StopLossType.MARKETSELL) { var minNotional = quantity * solbot.Communication.Price.Current; if (minNotional > solbot.Communication.Symbol.MinNotional) { stopLossOrderResult = _binanceClient.PlaceOrder( solbot.Strategy.AvailableStrategy.Symbol, OrderSide.Sell, OrderType.Market, quantity: quantity); } else { message = "not enough"; } } else { var stopLossPrice = BinanceHelpers.ClampPrice(solbot.Communication.Symbol.MinPrice, solbot.Communication.Symbol.MaxPrice, solbot.Communication.Price.Current); var minNotional = quantity * stopLossPrice; if (minNotional > solbot.Communication.Symbol.MinNotional) { stopLossOrderResult = _binanceClient.PlaceOrder( solbot.Strategy.AvailableStrategy.Symbol, OrderSide.Sell, OrderType.StopLossLimit, quantity: quantity, stopPrice: BinanceHelpers.FloorPrice(solbot.Communication.Symbol.TickSize, stopLossPrice), price: BinanceHelpers.FloorPrice(solbot.Communication.Symbol.TickSize, stopLossPrice), timeInForce: TimeInForce.GoodTillCancel); } else { message = "not enough"; } } if (!(stopLossOrderResult is null)) { result = stopLossOrderResult.Success; if (stopLossOrderResult.Success) { solbot.Actions.BoughtPrice = 0; solbot.Actions.StopLossReached = true; Logger.Info(LogGenerator.TradeResultStart(stopLossOrderResult.Data.OrderId)); var prices = new List <decimal>(); var quantityAll = new List <decimal>(); var commission = new List <decimal>(); if (stopLossOrderResult.Data.Fills.AnyAndNotNull()) { foreach (var item in stopLossOrderResult.Data.Fills) { Logger.Info(LogGenerator.TradeResult(MarketOrder, item)); prices.Add(item.Price); quantityAll.Add(item.Quantity); commission.Add(item.Commission); } } Logger.Info(LogGenerator.TradeResultEnd(stopLossOrderResult.Data.OrderId, prices.Average(), quantityAll.Sum(), commission.Sum())); _pushOverNotificationService.Send( LogGenerator.NotificationTitle(WorkingType.PRODUCTION, MarketOrder, solbot.Strategy.AvailableStrategy.Symbol), LogGenerator.NotificationMessage( solbot.Communication.Average.Current, solbot.Communication.Price.Current, solbot.Communication.StopLoss.Change)); } else { Logger.Warn(stopLossOrderResult.Error.Message); } } } return(new MarketRuleResult() { Success = result, Message = result ? LogGenerator.OrderMarketSuccess(MarketOrder) : LogGenerator.OrderMarketError(MarketOrder, message) }); }