From 8c968e1e09ec556d5f3a7bdc2875cf2d983d95d0 Mon Sep 17 00:00:00 2001 From: b00lean Date: Mon, 19 Jan 2015 10:47:48 +0100 Subject: [PATCH] Bitfinex implementation upgraded to AdvancedExchange asynchronous implementation --- .../exchanges/bitfinex/BitfinexExchange.java | 153 ++++++++++++++++-- 1 file changed, 137 insertions(+), 16 deletions(-) diff --git a/server_extensions_extra/src/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitfinex/BitfinexExchange.java b/server_extensions_extra/src/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitfinex/BitfinexExchange.java index 443c6e9..029900b 100644 --- a/server_extensions_extra/src/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitfinex/BitfinexExchange.java +++ b/server_extensions_extra/src/com/generalbytes/batm/server/extensions/extra/bitcoin/exchanges/bitfinex/BitfinexExchange.java @@ -22,18 +22,15 @@ package com.generalbytes.batm.server.extensions.extra.bitcoin.exchanges.bitfinex import java.io.IOException; import java.math.BigDecimal; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; +import java.util.*; -import com.generalbytes.batm.server.extensions.IRateSource; +import com.generalbytes.batm.server.extensions.*; +import com.xeiam.xchange.ExchangeException; import com.xeiam.xchange.dto.trade.LimitOrder; import com.xeiam.xchange.dto.trade.OpenOrders; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.generalbytes.batm.server.extensions.ICurrencies; -import com.generalbytes.batm.server.extensions.IExchange; import com.xeiam.xchange.Exchange; import com.xeiam.xchange.ExchangeFactory; import com.xeiam.xchange.ExchangeSpecification; @@ -45,7 +42,7 @@ import com.xeiam.xchange.service.polling.PollingAccountService; import com.xeiam.xchange.service.polling.PollingMarketDataService; import com.xeiam.xchange.service.polling.PollingTradeService; -public class BitfinexExchange implements IExchange, IRateSource { +public class BitfinexExchange implements IExchangeAdvanced, IRateSource { private static final Logger log = LoggerFactory.getLogger("batm.master.BitfinexExchange"); private Exchange exchange = null; @@ -223,8 +220,9 @@ public class BitfinexExchange implements IExchange, IRateSource { OpenOrders openOrders = tradeService.getOpenOrders(); for (LimitOrder openOrder : openOrders.getOpenOrders()) { log.debug("openOrder = " + openOrder); - if (orderId.equalsIgnoreCase(openOrder.getId())) { + if (orderId.equals(openOrder.getId())) { orderFound = true; + break; } } if (orderFound) { @@ -249,6 +247,19 @@ public class BitfinexExchange implements IExchange, IRateSource { return null; } + @Override + public ITask createPurchaseCoinsTask(BigDecimal amount, String cryptoCurrency, String fiatCurrencyToUse, String description) { + if (!ICurrencies.BTC.equalsIgnoreCase(cryptoCurrency)) { + log.error("Bitfinex implementation supports only " + ICurrencies.BTC); + return null; + } + if (!ICurrencies.USD.equalsIgnoreCase(fiatCurrencyToUse)) { + log.error("Bitfinex supports only " + ICurrencies.USD ); + return null; + } + return new PurchaseCoinsTaks(amount,cryptoCurrency,fiatCurrencyToUse,description); + } + @Override public String getDepositAddress(String cryptoCurrency) { if (!ICurrencies.BTC.equalsIgnoreCase(cryptoCurrency)) { @@ -264,17 +275,127 @@ public class BitfinexExchange implements IExchange, IRateSource { return null; } - public static void main(String[] args) { - BitfinexExchange ex = new BitfinexExchange("UKe2l4ij2jKg8zwaCIJ9qpwIasJ4EhfxiiB0KToG9Ky","35XkZ6IJD9EOfRex80xHwXSgaun7uXdBgzVdiOg5XOi"); - BigDecimal btc = ex.getCryptoBalance(ICurrencies.BTC); - System.out.println("btc = " + btc); - BigDecimal fiatBalance = ex.getFiatBalance(ICurrencies.USD); - System.out.println("fiatBalance = " + fiatBalance); + class PurchaseCoinsTaks implements ITask { + private long MAXIMUM_TIME_TO_WAIT_FOR_ORDER_TO_FINISH = 5 * 60 * 60 * 1000; //5 hours - String test = ex.sendCoins("15nvjrMZbxyLAAr9RAnqkoMXcFriAmbeLE", new BigDecimal("0.02"), ICurrencies.BTC, "Test"); - System.out.println("test = " + test); + private BigDecimal amount; + private String cryptoCurrency; + private String fiatCurrencyToUse; + private String description; + private String orderId; + private String result; + private boolean finished; + PurchaseCoinsTaks(BigDecimal amount, String cryptoCurrency, String fiatCurrencyToUse, String description) { + this.amount = amount; + this.cryptoCurrency = cryptoCurrency; + this.fiatCurrencyToUse = fiatCurrencyToUse; + this.description = description; + } + + @Override + public boolean onCreate() { + log.info("Calling Bitfinex exchange (purchase " + amount + " " + cryptoCurrency + ")"); + PollingAccountService accountService = getExchange().getPollingAccountService(); + PollingTradeService tradeService = getExchange().getPollingTradeService(); + + try { + log.debug("AccountInfo as String: " + accountService.getAccountInfo().toString()); + + CurrencyPair currencyPair = new CurrencyPair(cryptoCurrency, fiatCurrencyToUse); + + MarketOrder order = new MarketOrder(OrderType.BID, amount, currencyPair); + log.debug("marketOrder = " + order); + + orderId = tradeService.placeMarketOrder(order); + log.debug("orderId = " + orderId + " " + order); + + try { + Thread.sleep(2000); //give exchange 2 seconds to reflect open order in order book + } catch (InterruptedException e) { + e.printStackTrace(); + } + } catch (IOException e) { + e.printStackTrace(); + log.error("Bitfinex exchange (purchaseCoins) failed with message: " + e.getMessage()); + } catch (Throwable e) { + e.printStackTrace(); + } + return (orderId != null); + } + + @Override + public boolean onDoStep() { + if (orderId == null) { + log.debug("Giving up on waiting for trade to complete. Because it did not happen"); + finished = true; + result = "Skipped"; + return false; + } + PollingTradeService tradeService = getExchange().getPollingTradeService(); + // get open orders + boolean orderProcessed = false; + long checkTillTime = System.currentTimeMillis() + MAXIMUM_TIME_TO_WAIT_FOR_ORDER_TO_FINISH; + if (System.currentTimeMillis() > checkTillTime) { + log.debug("Giving up on waiting for trade " + orderId + " to complete"); + finished = true; + return false; + } + + log.debug("Open orders:"); + boolean orderFound = false; + try { + OpenOrders openOrders = tradeService.getOpenOrders(); + for (LimitOrder openOrder : openOrders.getOpenOrders()) { + log.debug("openOrder = " + openOrder); + if (orderId.equals(openOrder.getId())) { + orderFound = true; + break; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + + if (orderFound) { + log.debug("Waiting for order to be processed."); + }else{ + orderProcessed = true; + } + + if (orderProcessed) { + result = orderId; + finished = true; + } + + return result != null; + } + + @Override + public boolean isFinished() { + return finished; + } + + @Override + public String getResult() { + return result; + } + + @Override + public boolean isFailed() { + return finished && result == null; + } + + @Override + public void onFinish() { + log.debug("Purchase task finished."); + } + + @Override + public long getShortestTimeForNexStepInvocation() { + return 5 * 1000; //it doesn't make sense to run step sooner than after 5 seconds + } } }