public void StoreNormalResults(ArbitrageResult result) { //Generate a result key so we can match the same results later with an O(1) lookup string resultKey = string.Join(',', result.Exchanges) + '-' + string.Join(',', result.Pairs.Select(x => x.AltCurrency + "/" + x.BaseCurrency)); if (bestNormalProfit.Profit < result.Profit) { bestNormalProfit = result; } if (worstNormalProfit.Profit > result.Profit) { worstNormalProfit = result; } if (!normalResults.TryGetValue(resultKey, out ArbitrageResult currentResult)) { normalResults.Add(resultKey, result); } else { currentResult = result; } if (result.Profit > result.TransactionFee) { var @newTradeEvent = new ArbitrageFoundIntegrationEvent(result); _eventBus.Publish(@newTradeEvent); } }
private ArbitrageResult ComparePrices(ICurrencyCoin value1, ICurrencyCoin value2) { ArbitrageResult result = null; if (value1.Price > value2.Price && ((1 - (value2.Price / value1.Price)) * 100) > 5) { result = new ArbitrageResult { Market = value1.Market, Symbol = value1.TickerSymbol, Exchange1 = value1.Exchange, Exchange1Logo = value1.Logo, Exchange1Price = value1.Price, Exchange2 = value2.Exchange, Exchange2Logo = value2.Logo, Exchange2Price = value2.Price, }; } else if (((1 - (value1.Price / value2.Price)) * 100) > 5) { result = new ArbitrageResult { Market = value1.Market, Symbol = value1.TickerSymbol, Exchange1 = value2.Exchange, Exchange1Logo = value2.Logo, Exchange1Price = value2.Price, Exchange2 = value1.Exchange, Exchange2Logo = value1.Logo, Exchange2Price = value1.Price, }; } return(result); }
public ArbitrageFoundIntegrationEvent(ArbitrageResult result) { Result = result; }
public async Task ExecuteArbitrage(ArbitrageResult result) { try { //Temp bot grabbing so we can check settings (to be called from bot later?) Bot bot; ExchangeConfig account; if (result.Type == ArbitrageType.Triangle) { bot = _context.Bots .Include(x => x.TradeSettings) .Include(x => x.Exchanges) .First(x => x.Name == "Triangle Arbitrage"); } else { bot = _context.Bots .Include(x => x.TradeSettings) .Include(x => x.Exchanges) .First(x => x.Name == "Normal Arbitrage"); } if (bot.TradeSettings.TradingEnabled) { //Grab bot accounts for this trade var exchange = bot.Exchanges.FirstOrDefault(x => x.Name == result.Exchanges.First()); if (exchange == null || exchange.SelectedConfig == null) { //_logger.LogWarning("No account chosen for " + result.Exchanges.First()); return; } else { account = exchange.SelectedConfig; } var arbitrageTradeResults = new TradeResults(); if (result.Type == ArbitrageType.Triangle) { var ex = _exchanges.First(x => x.Name == result.Exchanges.First()); //Triangle arb only has one exchange if (!account.Simulated && !ex.IsAuthenticated) { throw new Exception("Can't create a trade, user is not authenticated."); } //TODO: Check that we have enough liquidity for trades (and potentially swap from available liquidity to fill them) //Step 1: Buy/Sell initial coins //Step 2: Swap coin into misc coin //Step 3: Swap misc coin back to original var previousCurrency = result.InitialCurrency; //Set start currency for choosing buy/sell var currentLiquidity = result.InitialLiquidity; //The amount of the current asset we have var simDelay = new Random().NextDouble() + 0.1; foreach (var pair in result.Pairs) { //If we start on the base, we want to buy the alt var side = pair.BaseCurrency == previousCurrency ? OrderSide.Buy : OrderSide.Sell; ExchangeOrderResult orderResult; if (account.Simulated) { orderResult = await ex.SimulateOrder(pair.MarketSymbol, side, OrderType.Market, 0, currentLiquidity, simDelay); //Use delay to simulate lag between placing and filling the order } else { orderResult = await ex.CreateOrder(pair.MarketSymbol, side, OrderType.Market, 0, currentLiquidity); } arbitrageTradeResults.BotId = bot.Name; arbitrageTradeResults.Trades.Add(new TradeResult() { Amount = orderResult.Amount, AmountFilled = orderResult.AmountFilled, AveragePrice = orderResult.AveragePrice, Fees = orderResult.Fees, FeesCurrency = orderResult.FeesCurrency, FillDate = orderResult.FillDate, OrderSide = side, MarketSymbol = pair.MarketSymbol, Message = orderResult.Message, OrderDate = orderResult.OrderDate, OrderId = orderResult.OrderId, Price = orderResult.Price, Result = orderResult.Result.ToOrderResult(), TradeId = orderResult.TradeId, }); if (orderResult.Result == ExchangeAPIOrderResult.Canceled || orderResult.Result == ExchangeAPIOrderResult.Error || orderResult.Result == ExchangeAPIOrderResult.Unknown) { throw new Exception("Something went wrong with order " + orderResult.OrderId + ",\r\nResult:" + orderResult.Result + "\r\n" + orderResult.Message); } if (side == OrderSide.Buy) { previousCurrency = pair.AltCurrency; } else { previousCurrency = pair.BaseCurrency; } currentLiquidity = orderResult.AmountFilled; } arbitrageTradeResults.InitialCurrency = result.InitialCurrency; arbitrageTradeResults.EstimatedProfit = result.Profit; arbitrageTradeResults.ActualProfit = (arbitrageTradeResults.Trades.Last().AmountFilled - result.InitialLiquidity) / result.InitialLiquidity * 100; //Trade 1 gets converted back to the initial currency and added to the dust collected var trade1 = arbitrageTradeResults.Trades.First(); if (trade1.OrderSide == OrderSide.Buy) { arbitrageTradeResults.Dust += trade1.AveragePrice * (trade1.Amount - trade1.AmountFilled); } else { arbitrageTradeResults.Dust += (trade1.Amount - trade1.AmountFilled) / trade1.AveragePrice; } //Trade 2 gets converted back to the second currency from the first trade and then to the initial currency var trade2 = arbitrageTradeResults.Trades[1]; if (trade2.OrderSide == OrderSide.Buy) { var baseAmount = trade2.AveragePrice * (trade2.Amount - trade2.AmountFilled); if (trade1.OrderSide == OrderSide.Buy) { arbitrageTradeResults.Dust += trade1.AveragePrice * baseAmount; } else { arbitrageTradeResults.Dust += baseAmount / trade1.AveragePrice; } } else { var altAmount = (trade2.Amount - trade2.AmountFilled) / trade2.AveragePrice; if (trade1.OrderSide == OrderSide.Buy) { arbitrageTradeResults.Dust += trade1.AveragePrice * altAmount; } else { arbitrageTradeResults.Dust += altAmount / trade1.AveragePrice; } } //Dust for the final trade is already in the right currency var trade3 = arbitrageTradeResults.Trades.Last(); arbitrageTradeResults.Dust += trade3.Amount - trade3.AmountFilled; arbitrageTradeResults.TimeFinished = DateTime.Now; _context.ArbitrageResults.Add(arbitrageTradeResults); _context.SaveChanges(); } else { //TODO: Implemented normal arb } } } catch (Exception e) { _logger.LogCritical("Something went wrong executing arbitrage", e); } }
public void CheckExchangeForNormalArbitrage(IExchange startExchange) { var orderbooks = startExchange.Orderbooks; decimal endAmountBought; decimal audInvested = 100; try { foreach (var startOrderbook in orderbooks.Values) { foreach (var exchange in _exchanges.Where(x => x.Name != startExchange.Name)) { //Base->Alt->Base var baseAmount = ConvertAudToCrypto(orderbooks, startOrderbook.BaseCurrency, audInvested); if (baseAmount == 0) { continue; //Asset prices not loaded yet } if (!exchange.Orderbooks.TryGetValue(startOrderbook.AltCurrency + "/" + startOrderbook.BaseCurrency, out Orderbook endOrderbook)) { exchange.Orderbooks.TryGetValue(startOrderbook.BaseCurrency + "/" + startOrderbook.AltCurrency, out endOrderbook); } if (endOrderbook == null) { continue; //Other exchange doesn't have the pair } var asks = startOrderbook.Asks; if (asks.Count() == 0) { continue; } var startAmountBought = baseAmount / PriceCalculator.GetPriceQuote(asks, PriceCalculator.ConvertBaseToAlt(asks.First().Price, baseAmount)); if (endOrderbook.BaseCurrency == startOrderbook.BaseCurrency) { var endBids = endOrderbook.Bids; if (endBids.Count() == 0) { continue; } endAmountBought = startAmountBought * PriceCalculator.GetPriceQuote(endBids, startAmountBought); } else { var endAsks = endOrderbook.Asks; if (endAsks.Count() == 0) { continue; } endAmountBought = startAmountBought / PriceCalculator.GetPriceQuote(endAsks, PriceCalculator.ConvertBaseToAlt(endAsks.First().Price, startAmountBought)); } decimal percentProfit = (endAmountBought - baseAmount) / baseAmount * 100; var result = new ArbitrageResult() { Exchanges = new List <string>() { startExchange.Name, exchange.Name }, Pairs = new List <Pair>() { new Pair(startOrderbook.Pair, startOrderbook.BaseCurrency, startOrderbook.AltCurrency) }, Profit = percentProfit, TransactionFee = startExchange.Fee + exchange.Fee, InitialCurrency = startOrderbook.BaseCurrency, InitialLiquidity = baseAmount, Type = ArbitrageType.Normal }; StoreNormalResults(result); //Alt->Base->Alt baseAmount = ConvertAudToCrypto(orderbooks, startOrderbook.AltCurrency, audInvested); if (baseAmount == 0) { continue; //Asset prices not loaded yet } var bids = startOrderbook.Bids; if (bids.Count() == 0) { continue; } startAmountBought = baseAmount * PriceCalculator.GetPriceQuote(bids, baseAmount); if (endOrderbook.BaseCurrency == startOrderbook.BaseCurrency) { var endAsks = endOrderbook.Asks; if (endAsks.Count() == 0) { continue; } endAmountBought = startAmountBought / PriceCalculator.GetPriceQuote(endAsks, PriceCalculator.ConvertBaseToAlt(endAsks.First().Price, startAmountBought)); } else { var endBids = endOrderbook.Bids; if (endBids.Count() == 0) { continue; } endAmountBought = startAmountBought * PriceCalculator.GetPriceQuote(endBids, startAmountBought); } percentProfit = (endAmountBought - baseAmount) / baseAmount * 100; result = new ArbitrageResult() { Exchanges = new List <string>() { startExchange.Name, exchange.Name }, Pairs = new List <Pair>() { new Pair(startOrderbook.Pair, startOrderbook.AltCurrency, startOrderbook.BaseCurrency) }, Profit = percentProfit, TransactionFee = startExchange.Fee + exchange.Fee, InitialCurrency = startOrderbook.AltCurrency, InitialLiquidity = baseAmount, Type = ArbitrageType.Normal }; StoreNormalResults(result); } } } catch (Exception e) { Console.WriteLine("Error in CheckExchangeForNormalArbitrage (" + e.Message + ")"); } }
public void CheckExchangeForTriangleArbitrage(IExchange exchange) { try { decimal altAmount, alt2Amount, finalAmount, baseAmount; //Assuming we are testing BTC/ETH->ETH/XRP->XRP/BTC, startCurrency == BTC, middleCurrency == ETH, endCurrency == XRP string startCurrency, middleCurrency, endCurrency; //The currency that's bought/sold from in the first transaction var audInvested = 100; var orderbooks = exchange.Orderbooks; foreach (var market in orderbooks.Values) { //Loop every market with a matching currency except itself (this could start on the base or alt currency) foreach (var market2 in orderbooks.Values.Where(x => x.Pair != market.Pair && (x.AltCurrency == market.AltCurrency || x.BaseCurrency == market.AltCurrency || x.AltCurrency == market.BaseCurrency || x.BaseCurrency == market.BaseCurrency))) { //If the base/alt currency for the next market is the base currency, we need to bid (i.e. Buy for first trade) if (market.BaseCurrency == market2.BaseCurrency || market.BaseCurrency == market2.AltCurrency) { baseAmount = ConvertAudToCrypto(orderbooks, market.AltCurrency, audInvested); if (baseAmount == 0) { continue; //Asset prices not loaded yet } try { var bids = orderbooks[market.AltCurrency + "/" + market.BaseCurrency].Bids; if (bids.Count() == 0) { continue; } altAmount = baseAmount * PriceCalculator.GetPriceQuote(bids, baseAmount); } catch (Exception e) { Console.WriteLine(e.Message); continue; } startCurrency = market.AltCurrency; middleCurrency = market.BaseCurrency; } else //Else we need to ask (i.e. Sell for first trade) { baseAmount = ConvertAudToCrypto(orderbooks, market.BaseCurrency, audInvested); if (baseAmount == 0) { continue; //Asset prices not loaded yet } try { var asks = orderbooks[market.AltCurrency + "/" + market.BaseCurrency].Asks; if (asks.Count() == 0) { continue; //Prices not loaded yet } altAmount = baseAmount / PriceCalculator.GetPriceQuote(asks, PriceCalculator.ConvertBaseToAlt(asks.First().Price, baseAmount)); //~3000 ETH from 100 BTC } catch (Exception e) { Console.WriteLine(e.Message); continue; } startCurrency = market.BaseCurrency; middleCurrency = market.AltCurrency; } //If the alt bought in step 1 is now a base, use ask price if (market2.BaseCurrency == middleCurrency) { endCurrency = market2.AltCurrency; try { var asks = orderbooks[market2.AltCurrency + "/" + market2.BaseCurrency].Asks; if (asks.Count() == 0) { continue; //Prices not loaded yet } alt2Amount = altAmount / PriceCalculator.GetPriceQuote(asks, PriceCalculator.ConvertBaseToAlt(asks.First().Price, altAmount)); } catch (Exception e) { Console.WriteLine(e.Message); continue; } } else //Otherwise it's the alt currency (i.e. we're selling to the new coin) { endCurrency = market2.BaseCurrency; try { var bids = orderbooks[market2.AltCurrency + "/" + market2.BaseCurrency].Bids; if (bids.Count() == 0) { continue; //Not loaded yet } alt2Amount = altAmount * PriceCalculator.GetPriceQuote(bids, altAmount); } catch (Exception e) { Console.WriteLine(e.Message); continue; } } //Find the final market (i.e. the market that has the middle and end currencies) if (!orderbooks.TryGetValue(startCurrency + "/" + endCurrency, out Orderbook finalMarket)) { orderbooks.TryGetValue(endCurrency + "/" + startCurrency, out finalMarket); } //If null, there's no pairs to finish the arb if (finalMarket == null) { continue; } //If the base currency is the first currency, we need to sell (i.e. use bid) if (finalMarket.BaseCurrency == startCurrency) { try { var bids = orderbooks[finalMarket.AltCurrency + "/" + finalMarket.BaseCurrency].Bids; if (bids.Count() == 0) { continue; //Not loaded yet } finalAmount = alt2Amount * PriceCalculator.GetPriceQuote(bids, alt2Amount); } catch (Exception e) { Console.WriteLine(e.Message); continue; } } else //Else we buy (i.e. use ask) { try { var asks = orderbooks[finalMarket.AltCurrency + "/" + finalMarket.BaseCurrency].Asks; if (asks.Count() == 0) { continue; //Prices not loaded yet } finalAmount = alt2Amount / PriceCalculator.GetPriceQuote(asks, PriceCalculator.ConvertBaseToAlt(asks.First().Price, alt2Amount)); } catch (Exception e) { Console.WriteLine(e.Message); continue; } } decimal percentProfit = (finalAmount - baseAmount) / baseAmount * 100; var result = new ArbitrageResult() { Exchanges = new List <string>() { exchange.Name }, Pairs = new List <Pair>() { new Pair(market.Pair, market.BaseCurrency, market.AltCurrency), new Pair(market2.Pair, market2.BaseCurrency, market2.AltCurrency), new Pair(finalMarket.Pair, finalMarket.BaseCurrency, finalMarket.AltCurrency) }, Profit = percentProfit, TransactionFee = exchange.Fee * 3, InitialCurrency = startCurrency, InitialLiquidity = baseAmount, Type = ArbitrageType.Triangle }; StoreTriangleResults(result); } } } catch (Exception e) { Console.WriteLine(e.Message); } }