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); }
public void ProcessTradingPairs() { int traidingPairsCount = 0; foreach (var tradingPair in tradingService.Account.GetTradingPairs()) { IPairConfig pairConfig = tradingService.GetPairConfig(tradingPair.Pair); tradingPair.SetCurrentValues(tradingService.GetPrice(tradingPair.Pair), tradingService.Exchange.GetPriceSpread(tradingPair.Pair)); tradingPair.Metadata.TradingRules = pairConfig.Rules.ToList(); tradingPair.Metadata.CurrentRating = tradingPair.Metadata.Signals != null?signalsService.GetRating(tradingPair.Pair, tradingPair.Metadata.Signals) : null; tradingPair.Metadata.CurrentGlobalRating = signalsService.GetGlobalRating(); if (trailingSells.TryGetValue(tradingPair.Pair, out SellTrailingInfo sellTrailingInfo)) { if (pairConfig.SellEnabled) { if (Math.Round(tradingPair.CurrentMargin, 1) != Math.Round(sellTrailingInfo.LastTrailingMargin, 1)) { if (LoggingEnabled) { loggingService.Info($"Continue trailing sell {tradingPair.FormattedName}. " + $"Price: {tradingPair.CurrentPrice:0.00000000}, Margin: {tradingPair.CurrentMargin:0.00}"); } } if (tradingPair.CurrentMargin <= sellTrailingInfo.TrailingStopMargin || tradingPair.CurrentMargin < (sellTrailingInfo.BestTrailingMargin - sellTrailingInfo.Trailing)) { StopTrailingSell(tradingPair.Pair); if (tradingPair.CurrentMargin > 0 || sellTrailingInfo.SellMargin < 0) { if (sellTrailingInfo.TrailingStopAction == SellTrailingStopAction.Sell || tradingPair.CurrentMargin > sellTrailingInfo.TrailingStopMargin) { orderingService.PlaceSellOrder(sellTrailingInfo.SellOptions); } else { if (LoggingEnabled) { loggingService.Info($"Stop trailing sell {tradingPair.FormattedName}. Reason: stop margin reached"); } } } else { if (LoggingEnabled) { loggingService.Info($"Stop trailing sell {tradingPair.FormattedName}. Reason: negative margin"); } } } else { sellTrailingInfo.LastTrailingMargin = tradingPair.CurrentMargin; if (tradingPair.CurrentMargin > sellTrailingInfo.BestTrailingMargin) { sellTrailingInfo.BestTrailingMargin = tradingPair.CurrentMargin; } } } else { StopTrailingSell(tradingPair.Pair); } } else { if (pairConfig.SellEnabled && tradingPair.CurrentMargin >= pairConfig.SellMargin) { InitiateSell(new SellOptions(tradingPair.Pair)); } else if (pairConfig.SellEnabled && pairConfig.SellStopLossEnabled && tradingPair.CurrentMargin <= pairConfig.SellStopLossMargin && tradingPair.CurrentAge >= pairConfig.SellStopLossMinAge && (pairConfig.NextDCAMargin == null || !pairConfig.SellStopLossAfterDCA)) { if (LoggingEnabled) { loggingService.Info($"Stop loss triggered for {tradingPair.FormattedName}. Margin: {tradingPair.CurrentMargin:0.00}"); } orderingService.PlaceSellOrder(new SellOptions(tradingPair.Pair)); } else if (pairConfig.NextDCAMargin != null && pairConfig.BuyEnabled && pairConfig.NextDCAMargin != null && !trailingBuys.ContainsKey(tradingPair.Pair) && !trailingSells.ContainsKey(tradingPair.Pair)) { if (tradingPair.CurrentMargin <= pairConfig.NextDCAMargin) { var buyOptions = new BuyOptions(tradingPair.Pair) { MaxCost = tradingPair.Cost * pairConfig.BuyMultiplier, IgnoreExisting = true }; if (tradingService.CanBuy(buyOptions, message: out string message)) { if (LoggingEnabled) { loggingService.Info($"DCA triggered for {tradingPair.FormattedName}. Margin: {tradingPair.CurrentMargin:0.00}, " + $"Level: {pairConfig.NextDCAMargin:0.00}, Multiplier: {pairConfig.BuyMultiplier}"); } InitiateBuy(buyOptions); } } } } traidingPairsCount++; } foreach (var kvp in trailingBuys) { string pair = kvp.Key; BuyTrailingInfo buyTrailingInfo = kvp.Value; ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair); IPairConfig pairConfig = tradingService.GetPairConfig(pair); decimal currentPrice = tradingService.GetPrice(pair); decimal currentMargin = Utils.CalculatePercentage(buyTrailingInfo.InitialPrice, currentPrice); if (pairConfig.BuyEnabled) { if (Math.Round(currentMargin, 1) != Math.Round(buyTrailingInfo.LastTrailingMargin, 1)) { if (LoggingEnabled) { loggingService.Info($"Continue trailing buy {tradingPair?.FormattedName ?? pair}. Price: {currentPrice:0.00000000}, Margin: {currentMargin:0.00}"); } } if (currentMargin >= buyTrailingInfo.TrailingStopMargin || currentMargin > (buyTrailingInfo.BestTrailingMargin - buyTrailingInfo.Trailing)) { StopTrailingBuy(pair); if (buyTrailingInfo.TrailingStopAction == BuyTrailingStopAction.Buy || currentMargin < buyTrailingInfo.TrailingStopMargin) { orderingService.PlaceBuyOrder(buyTrailingInfo.BuyOptions); } else { if (LoggingEnabled) { loggingService.Info($"Stop trailing buy {tradingPair?.FormattedName ?? pair}. Reason: stop margin reached"); } } } else { buyTrailingInfo.LastTrailingMargin = currentMargin; if (currentMargin < buyTrailingInfo.BestTrailingMargin) { buyTrailingInfo.BestTrailingMargin = currentMargin; } } } else { StopTrailingBuy(pair); } } healthCheckService.UpdateHealthCheck(Constants.HealthChecks.TradingPairsProcessed, $"Pairs: {traidingPairsCount}, Trailing buys: {trailingBuys.Count}, Trailing sells: {trailingSells.Count}"); }