public ITradingPair AddOrUpdatePair(IOrderDetails order, string pair, decimal feesMarketCurrency, decimal feesPairCurrency, decimal?amountOverride = null, decimal?averagePriceOverride = null) { decimal amount = amountOverride ?? order.AmountFilled; decimal amountAfterFees = amount - feesPairCurrency; decimal averagePrice = averagePriceOverride ?? (order.AveragePrice + (feesMarketCurrency / amount)); if (tradingPairs.TryGetValue(pair, out TradingPair tradingPair)) { if (!tradingPair.OrderIds.Contains(order.OrderId)) { tradingPair.OrderIds.Add(order.OrderId); tradingPair.OrderDates.Add(order.Date); } tradingPair.AveragePrice = (tradingPair.Cost + amountAfterFees * averagePrice) / (tradingPair.Amount + amountAfterFees); tradingPair.Amount += amountAfterFees; tradingPair.Fees += feesMarketCurrency; tradingPair.SetMetadata(tradingPair.Metadata.MergeWith(order.Metadata)); } else { tradingPair = new TradingPair { Pair = pair, OrderIds = new List <string> { order.OrderId }, OrderDates = new List <DateTimeOffset> { order.Date }, AveragePrice = averagePrice, Amount = amountAfterFees, Fees = feesMarketCurrency, Metadata = order.Metadata }; tradingPairs.TryAdd(pair, tradingPair); tradingPair.SetCurrentValues(tradingService.GetPrice(tradingPair.Pair), tradingService.Exchange.GetPriceSpread(tradingPair.Pair)); tradingPair.Metadata.CurrentRating = tradingPair.Metadata.Signals != null?signalsService.GetRating(tradingPair.Pair, tradingPair.Metadata.Signals) : null; tradingPair.Metadata.CurrentGlobalRating = signalsService.GetGlobalRating(); if (tradingPair.Metadata.LastBuyMargin == null) { tradingPair.Metadata.LastBuyMargin = tradingPair.CurrentMargin; } } return(tradingPair); }
private void ArbitrageReverse(ArbitrageOptions options) { string marketPair = Exchange.GetArbitrageMarketPair(options.Arbitrage.Market); ITradingPair existingMarketPair = Account.GetTradingPair(marketPair); IPairConfig pairConfig = GetPairConfig(options.Pair); bool useExistingMarketPair = (existingMarketPair != null && existingMarketPair.CurrentCost > pairConfig.BuyMaxCost && existingMarketPair.AveragePrice <= existingMarketPair.CurrentPrice); var buyMarketPairOptions = new BuyOptions(marketPair) { Arbitrage = true, MaxCost = pairConfig.BuyMaxCost, ManualOrder = options.ManualOrder, IgnoreBalance = useExistingMarketPair, Metadata = options.Metadata }; if (CanBuy(buyMarketPairOptions, out string message)) { IOrderDetails buyMarketPairOrderDetails = null; if (useExistingMarketPair) { buyMarketPairOrderDetails = Account.AddBlankOrder(buyMarketPairOptions.Pair, buyMarketPairOptions.MaxCost.Value / GetPrice(buyMarketPairOptions.Pair, TradePriceType.Ask), includeFees: false); loggingService.Info($"Use existing market pair for arbitrage: {marketPair}. " + $"Average price: {existingMarketPair.AveragePrice}, Current price: {existingMarketPair.CurrentPrice}"); } else { buyMarketPairOrderDetails = orderingService.PlaceBuyOrder(buyMarketPairOptions); } if (buyMarketPairOrderDetails.Result == OrderResult.Filled) { decimal buyArbitragePairMultiplier = pairConfig.ArbitrageBuyMultiplier ?? DEFAULT_ARBITRAGE_BUY_MULTIPLIER; decimal buyMarketPairFees = CalculateOrderFees(buyMarketPairOrderDetails); string arbitragePair = Exchange.ChangeMarket(options.Pair, options.Arbitrage.Market.ToString()); decimal buyArbitragePairAmount = options.Arbitrage.Market == ArbitrageMarket.USDT ? buyMarketPairOrderDetails.AmountFilled * GetPrice(buyMarketPairOrderDetails.Pair, TradePriceType.Ask, normalize: false) / GetPrice(arbitragePair, TradePriceType.Ask) : buyMarketPairOrderDetails.AmountFilled / GetPrice(arbitragePair, TradePriceType.Ask); var buyArbitragePairOptions = new BuyOptions(arbitragePair) { Arbitrage = true, ManualOrder = options.ManualOrder, Amount = buyArbitragePairAmount * buyArbitragePairMultiplier, Metadata = options.Metadata }; IOrderDetails buyArbitragePairOrderDetails = orderingService.PlaceBuyOrder(buyArbitragePairOptions); if (buyArbitragePairOrderDetails.Result == OrderResult.Filled) { decimal buyArbitragePairFees = CalculateOrderFees(buyArbitragePairOrderDetails); options.Metadata.FeesNonDeductible = buyMarketPairFees * buyArbitragePairMultiplier; var sellArbitragePairOptions = new SellOptions(buyArbitragePairOrderDetails.Pair) { Arbitrage = true, Amount = buyArbitragePairOrderDetails.AmountFilled, ManualOrder = options.ManualOrder, Metadata = options.Metadata }; TradingPair existingArbitragePair = Account.GetTradingPair(buyArbitragePairOrderDetails.Pair) as TradingPair; existingArbitragePair.OverrideCost(buyArbitragePairOrderDetails.Cost + buyArbitragePairFees * 2); IOrderDetails sellArbitragePairOrderDetails = orderingService.PlaceSellOrder(sellArbitragePairOptions); existingArbitragePair.OverrideCost(null); if (sellArbitragePairOrderDetails.Result == OrderResult.Filled) { loggingService.Info($"{pairConfig.ArbitrageType} arbitrage successful: {marketPair} -> {arbitragePair} -> {existingArbitragePair.Pair}"); } else { loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to sell arbitrage pair {arbitragePair}"); notificationService.Notify($"Unable to arbitrage {options.Pair}: Failed to sell arbitrage pair {arbitragePair}"); } } else { loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to buy arbitrage pair {arbitragePair}"); notificationService.Notify($"Unable to arbitrage {options.Pair}: Failed to buy arbitrage pair {arbitragePair}"); } } else { loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to buy market pair {marketPair}"); } } else { loggingService.Info($"Unable to arbitrage {options.Pair}: {message}"); } }
public virtual void AddBuyOrder(IOrderDetails order) { lock (SyncRoot) { if (order.Side == OrderSide.Buy && (order.Result == OrderResult.Filled || order.Result == OrderResult.FilledPartially)) { decimal balanceDifference = -order.AverageCost; decimal feesPairCurrency = 0; decimal feesMarketCurrency = 0; decimal amountAfterFees = order.AmountFilled; if (order.Fees != 0 && order.FeesCurrency != null) { if (order.FeesCurrency == tradingService.Config.Market) { feesMarketCurrency = order.Fees; balanceDifference -= order.Fees; } else { string feesPair = order.FeesCurrency + tradingService.Config.Market; if (feesPair == order.Pair) { feesPairCurrency = order.Fees; amountAfterFees -= order.Fees; } else { feesMarketCurrency = tradingService.GetCurrentPrice(feesPair) * order.Fees; } } } balance += balanceDifference; if (tradingPairs.TryGetValue(order.Pair, out TradingPair tradingPair)) { if (!tradingPair.OrderIds.Contains(order.OrderId)) { tradingPair.OrderIds.Add(order.OrderId); tradingPair.OrderDates.Add(order.Date); } tradingPair.AveragePricePaid = (tradingPair.AverageCostPaid + order.AverageCost) / (tradingPair.TotalAmount + order.AmountFilled); tradingPair.FeesPairCurrency += feesPairCurrency; tradingPair.FeesMarketCurrency += feesMarketCurrency; tradingPair.TotalAmount += amountAfterFees; tradingPair.Metadata = tradingPair.Metadata.MergeWith(order.Metadata); } else { tradingPair = new TradingPair { Pair = order.Pair, OrderIds = new List <string> { order.OrderId }, OrderDates = new List <DateTimeOffset> { order.Date }, AveragePricePaid = order.AveragePrice, FeesPairCurrency = feesPairCurrency, FeesMarketCurrency = feesMarketCurrency, TotalAmount = amountAfterFees, Metadata = order.Metadata }; tradingPairs.TryAdd(order.Pair, tradingPair); tradingPair.SetCurrentValues(tradingService.GetCurrentPrice(tradingPair.Pair), tradingService.GetCurrentSpread(tradingPair.Pair)); tradingPair.Metadata.CurrentRating = tradingPair.Metadata.Signals != null?signalsService.GetRating(tradingPair.Pair, tradingPair.Metadata.Signals) : null; tradingPair.Metadata.CurrentGlobalRating = signalsService.GetGlobalRating(); } } } }