public virtual void AddBuyOrder(IOrderDetails order) { lock (SyncRoot) { if (order.Side == OrderSide.Buy && (order.Result == OrderResult.Filled || order.Result == OrderResult.FilledPartially)) { string feesPair = order.FeesCurrency + tradingService.Config.Market; decimal feesPairCurrency = (feesPair == order.Pair) ? order.Fees : 0; decimal feesMarketCurrency = tradingService.CalculateOrderFees(order); decimal balanceOffset = -feesMarketCurrency; if (!order.IsNormalized || order.Pair.EndsWith(Constants.Markets.USDT)) { balanceOffset -= order.Cost; AddBalance(balanceOffset); } else { string normalizedMarket = tradingService.Exchange.GetPairMarket(order.OriginalPair) == Constants.Markets.USDT ? tradingService.Config.Market + tradingService.Exchange.GetPairMarket(order.OriginalPair) : tradingService.Exchange.GetPairMarket(order.OriginalPair) + tradingService.Config.Market; if (tradingPairs.TryGetValue(normalizedMarket, out TradingPair normalizedMarketPair)) { if (normalizedMarketPair.Cost > order.Cost) { decimal amount = order.Cost / tradingService.GetPrice(normalizedMarket, TradePriceType.Bid); normalizedMarketPair.Amount -= amount; if (normalizedMarketPair.Amount <= 0) { tradingPairs.TryRemove(normalizedMarket, out normalizedMarketPair); if (normalizedMarketPair.Amount < 0) { loggingService.Error($"Normalized pair {normalizedMarket} has negative amount: {normalizedMarketPair.Amount}"); } } } else { tradingPairs.TryRemove(normalizedMarket, out normalizedMarketPair); } } else { loggingService.Error($"Unable to get normalized pair {normalizedMarketPair}"); } } AddOrUpdatePair(order, order.Pair, feesMarketCurrency, feesPairCurrency); } } }
public IOrderDetails PlaceBuyOrder(BuyOptions options) { OrderDetails orderDetails = new OrderDetails(); tradingService.StopTrailingBuy(options.Pair); tradingService.StopTrailingSell(options.Pair); try { ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair, includeDust: true); options.Price = tradingService.GetPrice(options.Pair, TradePriceType.Ask, normalize: false); options.Amount = options.Amount ?? (options.MaxCost.Value / (options.Pair.EndsWith(Constants.Markets.USDT) ? 1 : options.Price)); options.Price = tradingService.Exchange.ClampOrderPrice(options.Pair, options.Price.Value); options.Amount = tradingService.Exchange.ClampOrderAmount(options.Pair, options.Amount.Value); if (tradingService.CanBuy(options, out string message)) { IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair); BuyOrder buyOrder = new BuyOrder { Type = pairConfig.BuyType, Date = DateTimeOffset.Now, Pair = options.Pair, Price = options.Price.Value, Amount = options.Amount.Value }; lock (tradingService.Account.SyncRoot) { loggingService.Info($"Place buy order for {tradingPair?.FormattedName ?? options.Pair}. " + $"Price: {buyOrder.Price:0.00000000}, Amount: {buyOrder.Amount:0.########}, Signal Rule: " + (options.Metadata.SignalRule ?? "N/A")); if (!tradingService.Config.VirtualTrading) { orderDetails = tradingService.Exchange.PlaceOrder(buyOrder) as OrderDetails; } else { string pairMarket = tradingService.Exchange.GetPairMarket(options.Pair); orderDetails = new OrderDetails { OrderId = DateTime.Now.ToFileTimeUtc().ToString(), Side = OrderSide.Buy, Result = OrderResult.Filled, Date = buyOrder.Date, Pair = buyOrder.Pair, Amount = buyOrder.Amount, AmountFilled = buyOrder.Amount, Price = buyOrder.Price, AveragePrice = buyOrder.Price, Fees = buyOrder.Amount * buyOrder.Price * tradingService.Config.VirtualTradingFees, FeesCurrency = pairMarket }; } NormalizeOrder(orderDetails, TradePriceType.Ask); options.Metadata.TradingRules = pairConfig.Rules.ToList(); options.Metadata.LastBuyMargin = options.Metadata.LastBuyMargin ?? tradingPair?.CurrentMargin ?? null; orderDetails.Metadata = options.Metadata; tradingService.Account.AddBuyOrder(orderDetails); tradingService.Account.Save(); tradingService.LogOrder(orderDetails); decimal fees = tradingService.CalculateOrderFees(orderDetails); tradingPair = tradingService.Account.GetTradingPair(orderDetails.Pair, includeDust: true); loggingService.Info("{@Trade}", orderDetails); loggingService.Info($"Buy order result for {orderDetails.OriginalPair ?? tradingPair.FormattedName}: {orderDetails.Result} ({orderDetails.Message}). " + $"Price: {orderDetails.AveragePrice:0.00000000}, Amount: {orderDetails.Amount:0.########}, " + $"Filled: {orderDetails.AmountFilled:0.########}, Cost: {orderDetails.Cost:0.00000000}, Fees: {fees:0.00000000}"); notificationService.Notify($"Bought {tradingPair.FormattedName}. Amount: {orderDetails.AmountFilled:0.########}, " + $"Price: {orderDetails.AveragePrice:0.00000000}, Cost: {(orderDetails.Cost + fees):0.00000000}"); } tradingService.ReapplyTradingRules(); } else { loggingService.Info(message); } } catch (Exception ex) { loggingService.Error($"Unable to place buy order for {options.Pair}", ex); notificationService.Notify($"Unable to buy {options.Pair}: {ex.Message}"); } return(orderDetails); }