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 void InitiateBuy(BuyOptions options) { IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair); if (!options.ManualOrder && pairConfig.BuyTrailing != 0) { if (!trailingBuys.ContainsKey(options.Pair)) { StopTrailingSell(options.Pair); decimal currentPrice = tradingService.GetPrice(options.Pair); decimal currentMargin = 0; var trailingInfo = new BuyTrailingInfo { BuyOptions = options, Trailing = pairConfig.BuyTrailing, TrailingStopMargin = pairConfig.BuyTrailingStopMargin, TrailingStopAction = pairConfig.BuyTrailingStopAction, InitialPrice = currentPrice, LastTrailingMargin = currentMargin, BestTrailingMargin = currentMargin }; if (trailingBuys.TryAdd(options.Pair, trailingInfo)) { if (LoggingEnabled) { ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair); loggingService.Info($"Start trailing buy {tradingPair?.FormattedName ?? options.Pair}. " + $"Price: {currentPrice:0.00000000}, Margin: {currentMargin:0.00}"); } } } } else { orderingService.PlaceBuyOrder(options); } }
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 bool CheckConditions(IEnumerable <IRuleCondition> conditions, Dictionary <string, ISignal> signals, double?globalRating, string pair, ITradingPair tradingPair) { if (conditions != null) { foreach (var condition in conditions) { ISignal signal = null; if (condition.Signal != null && signals.TryGetValue(condition.Signal, out ISignal s)) { signal = s; } if (condition.MinPrice != null && (tradingService.GetPrice(pair) < condition.MinPrice) || condition.MaxPrice != null && (tradingService.GetPrice(pair) > condition.MaxPrice) || condition.MinSpread != null && (tradingService.Exchange.GetPriceSpread(pair) < condition.MinSpread) || condition.MaxSpread != null && (tradingService.Exchange.GetPriceSpread(pair) > condition.MaxSpread) || condition.MinArbitrage != null && tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, condition.ArbitrageMarket != null ? new List <ArbitrageMarket> { condition.ArbitrageMarket.Value } : null, condition.ArbitrageType).Percentage < condition.MinArbitrage || condition.MaxArbitrage != null && tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, condition.ArbitrageMarket != null ? new List <ArbitrageMarket> { condition.ArbitrageMarket.Value } : null, condition.ArbitrageType).Percentage > condition.MaxArbitrage || condition.MinVolume != null && (signal == null || signal.Volume == null || signal.Volume < condition.MinVolume) || condition.MaxVolume != null && (signal == null || signal.Volume == null || signal.Volume > condition.MaxVolume) || condition.MinVolumeChange != null && (signal == null || signal.VolumeChange == null || signal.VolumeChange < condition.MinVolumeChange) || condition.MaxVolumeChange != null && (signal == null || signal.VolumeChange == null || signal.VolumeChange > condition.MaxVolumeChange) || condition.MinPriceChange != null && (signal == null || signal.PriceChange == null || signal.PriceChange < condition.MinPriceChange) || condition.MaxPriceChange != null && (signal == null || signal.PriceChange == null || signal.PriceChange > condition.MaxPriceChange) || condition.MinRating != null && (signal == null || signal.Rating == null || signal.Rating < condition.MinRating) || condition.MaxRating != null && (signal == null || signal.Rating == null || signal.Rating > condition.MaxRating) || condition.MinRatingChange != null && (signal == null || signal.RatingChange == null || signal.RatingChange < condition.MinRatingChange) || condition.MaxRatingChange != null && (signal == null || signal.RatingChange == null || signal.RatingChange > condition.MaxRatingChange) || condition.MinVolatility != null && (signal == null || signal.Volatility == null || signal.Volatility < condition.MinVolatility) || condition.MaxVolatility != null && (signal == null || signal.Volatility == null || signal.Volatility > condition.MaxVolatility) || condition.MinGlobalRating != null && (globalRating == null || globalRating < condition.MinGlobalRating) || condition.MaxGlobalRating != null && (globalRating == null || globalRating > condition.MaxGlobalRating) || condition.Pairs != null && (pair == null || !condition.Pairs.Contains(pair)) || condition.NotPairs != null && (pair == null || condition.NotPairs.Contains(pair)) || condition.MinAge != null && (tradingPair == null || tradingPair.CurrentAge < condition.MinAge / Application.Speed) || condition.MaxAge != null && (tradingPair == null || tradingPair.CurrentAge > condition.MaxAge / Application.Speed) || condition.MinLastBuyAge != null && (tradingPair == null || tradingPair.LastBuyAge < condition.MinLastBuyAge / Application.Speed) || condition.MaxLastBuyAge != null && (tradingPair == null || tradingPair.LastBuyAge > condition.MaxLastBuyAge / Application.Speed) || condition.MinMargin != null && (tradingPair == null || tradingPair.CurrentMargin < condition.MinMargin) || condition.MaxMargin != null && (tradingPair == null || tradingPair.CurrentMargin > condition.MaxMargin) || condition.MinMarginChange != null && (tradingPair == null || tradingPair.Metadata.LastBuyMargin == null || (tradingPair.CurrentMargin - tradingPair.Metadata.LastBuyMargin) < condition.MinMarginChange) || condition.MaxMarginChange != null && (tradingPair == null || tradingPair.Metadata.LastBuyMargin == null || (tradingPair.CurrentMargin - tradingPair.Metadata.LastBuyMargin) > condition.MaxMarginChange) || condition.MinAmount != null && (tradingPair == null || tradingPair.Amount < condition.MinAmount) || condition.MaxAmount != null && (tradingPair == null || tradingPair.Amount > condition.MaxAmount) || condition.MinCost != null && (tradingPair == null || tradingPair.CurrentCost < condition.MinCost) || condition.MaxCost != null && (tradingPair == null || tradingPair.CurrentCost > condition.MaxCost) || condition.MinDCALevel != null && (tradingPair == null || tradingPair.DCALevel < condition.MinDCALevel) || condition.MaxDCALevel != null && (tradingPair == null || tradingPair.DCALevel > condition.MaxDCALevel) || condition.SignalRules != null && (tradingPair == null || tradingPair.Metadata.SignalRule == null || !condition.SignalRules.Contains(tradingPair.Metadata.SignalRule)) || condition.NotSignalRules != null && (tradingPair == null || tradingPair.Metadata.SignalRule == null || condition.NotSignalRules.Contains(tradingPair.Metadata.SignalRule))) { return(false); } } } return(true); }
public MarketPairsResponse MarketPairs(MarketPairsRequest request) { if (request?.SignalsFilter == null) { throw new ArgumentNullException(nameof(request)); } ICoreService coreService = Application.Resolve <ICoreService>(); ITradingService tradingService = Application.Resolve <ITradingService>(); ISignalsService signalsService = Application.Resolve <ISignalsService>(); IEnumerable <ISignal> allSignals = signalsService.GetAllSignals(); if (allSignals != null) { if (request.SignalsFilter.Count > 0) { allSignals = allSignals.Where(s => request.SignalsFilter.Contains(s.Name)); } var groupedSignals = allSignals.GroupBy(s => s.Pair).ToDictionary(g => g.Key, g => g.AsEnumerable()); IEnumerable <MarketPairApiModel> marketPairs = from signalGroup in groupedSignals let pair = signalGroup.Key let pairConfig = tradingService.GetPairConfig(pair) select new MarketPairApiModel { Name = pair, TradingViewName = $"{tradingService.Config.Exchange.ToUpperInvariant()}:{pair}", VolumeList = signalGroup.Value.Select(s => new NameValue <long?>(s.Name, s.Volume)), VolumeChangeList = signalGroup.Value.Select(s => new NameValue <double?>(s.Name, s.VolumeChange)), Price = tradingService.GetPrice(pair).ToString("0.00000000"), PriceChangeList = signalGroup.Value.Select(s => new NameValue <decimal?>(s.Name, s.PriceChange)), RatingList = signalGroup.Value.Select(s => new NameValue <double?>(s.Name, s.Rating)), RatingChangeList = signalGroup.Value.Select(s => new NameValue <double?>(s.Name, s.RatingChange)), VolatilityList = signalGroup.Value.Select(s => new NameValue <double?>(s.Name, s.Volatility)), Spread = tradingService.Exchange.GetPriceSpread(pair).ToString("0.00"), ArbitrageList = from market in Enum.GetNames(typeof(ArbitrageMarket)).Where(m => m != tradingService.Config.Market) let arbitrage = tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, new List <ArbitrageMarket> { Enum.Parse <ArbitrageMarket>(market) }) select new ArbitrageInfo { Name = $"{arbitrage.Market}-{arbitrage.Type.ToString()[0]}", Arbitrage = arbitrage.IsAssigned ? arbitrage.Percentage.ToString("0.00") : "N/A" }, SignalRules = signalsService.GetTrailingInfo(pair)?.Select(ti => ti.Rule.Name) ?? Array.Empty <string>(), HasTradingPair = tradingService.Account.HasTradingPair(pair), Config = pairConfig }; return(new MarketPairsResponse { MarketPairs = marketPairs.ToList() }); } else { return(null); } }