Added support fot Bitfinex (withdrawal is not supported by bitfinex)
This commit is contained in:
parent
35e54b31cb
commit
c1a41a1427
|
@ -0,0 +1,5 @@
|
||||||
|
.git
|
||||||
|
.DS_Store
|
||||||
|
dist/
|
||||||
|
bin/
|
||||||
|
build/
|
Binary file not shown.
Binary file not shown.
|
@ -24,10 +24,19 @@
|
||||||
<param name="price" />
|
<param name="price" />
|
||||||
<cryptocurrency>BTC</cryptocurrency>
|
<cryptocurrency>BTC</cryptocurrency>
|
||||||
</ratesource>
|
</ratesource>
|
||||||
|
<ratesource prefix="bitfinex" name ="Bitfinex.com Exchange" >
|
||||||
|
<cryptocurrency>BTC</cryptocurrency>
|
||||||
|
</ratesource>
|
||||||
<paymentprocessor prefix="bitcoinpay" name="BitcoinPay.com" >
|
<paymentprocessor prefix="bitcoinpay" name="BitcoinPay.com" >
|
||||||
<param name="apikey" />
|
<param name="apikey" />
|
||||||
<cryptocurrency>BTC</cryptocurrency>
|
<cryptocurrency>BTC</cryptocurrency>
|
||||||
</paymentprocessor>
|
</paymentprocessor>
|
||||||
|
<!-- Bitfinex extension by Orillia BVBA -->
|
||||||
|
<exchange prefix="bitfinex" name="Bitfinex.com Exchange">
|
||||||
|
<param name="apikey" />
|
||||||
|
<param name="apisecret" />
|
||||||
|
<cryptocurrency>BTC</cryptocurrency>
|
||||||
|
</exchange>
|
||||||
<cryptologo cryptocurrency="BTC" file="btc.png"/>
|
<cryptologo cryptocurrency="BTC" file="btc.png"/>
|
||||||
</extension>
|
</extension>
|
||||||
<extension class="com.generalbytes.batm.server.extensions.extra.dogecoin.DogecoinExtension">
|
<extension class="com.generalbytes.batm.server.extensions.extra.dogecoin.DogecoinExtension">
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package com.generalbytes.batm.server.extensions.extra.bitcoin;
|
package com.generalbytes.batm.server.extensions.extra.bitcoin;
|
||||||
|
|
||||||
import com.generalbytes.batm.server.extensions.*;
|
import com.generalbytes.batm.server.extensions.*;
|
||||||
|
import com.generalbytes.batm.server.extensions.extra.bitcoin.exchanges.bitfinex.BitfinexExchange;
|
||||||
import com.generalbytes.batm.server.extensions.extra.bitcoin.paymentprocessors.bitcoinpay.BitcoinPayPP;
|
import com.generalbytes.batm.server.extensions.extra.bitcoin.paymentprocessors.bitcoinpay.BitcoinPayPP;
|
||||||
import com.generalbytes.batm.server.extensions.extra.bitcoin.sources.BitcoinAverageRateSource;
|
import com.generalbytes.batm.server.extensions.extra.bitcoin.sources.BitcoinAverageRateSource;
|
||||||
import com.generalbytes.batm.server.extensions.extra.bitcoin.sources.FixPriceRateSource;
|
import com.generalbytes.batm.server.extensions.extra.bitcoin.sources.FixPriceRateSource;
|
||||||
|
@ -35,8 +36,19 @@ public class BitcoinExtension implements IExtension{
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IExchange createExchange(String exchangeLogin) {
|
public IExchange createExchange(String paramString) //(Bitstamp is in built-in extension)
|
||||||
return null; //no BTC exchange available in open source version so far (Bitstamp is in built-in extension)
|
{
|
||||||
|
if ((paramString != null) && (!paramString.trim().isEmpty()))
|
||||||
|
{
|
||||||
|
StringTokenizer paramTokenizer = new StringTokenizer(paramString, ":");
|
||||||
|
String prefix = paramTokenizer.nextToken();
|
||||||
|
if ("bitfinex".equalsIgnoreCase(prefix)) {
|
||||||
|
String apiKey = paramTokenizer.nextToken();
|
||||||
|
String apiSecret = paramTokenizer.nextToken();
|
||||||
|
return new BitfinexExchange(apiKey, apiSecret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -118,6 +130,15 @@ public class BitcoinExtension implements IExtension{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new FixPriceRateSource(rate);
|
return new FixPriceRateSource(rate);
|
||||||
|
}else if ("bitfinex".equalsIgnoreCase(exchangeType)) {
|
||||||
|
BigDecimal rate = BigDecimal.ZERO;
|
||||||
|
if (st.hasMoreTokens()) {
|
||||||
|
try {
|
||||||
|
rate = new BigDecimal(st.nextToken());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new BitfinexExchange("**","**");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -0,0 +1,244 @@
|
||||||
|
/*************************************************************************************
|
||||||
|
* Copyright (C) 2014 GENERAL BYTES s.r.o. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software may be distributed and modified under the terms of the GNU
|
||||||
|
* General Public License version 2 (GPL2) as published by the Free Software
|
||||||
|
* Foundation and appearing in the file GPL2.TXT included in the packaging of
|
||||||
|
* this file. Please note that GPL2 Section 2[b] requires that all works based
|
||||||
|
* on this software must also be made publicly available under the terms of
|
||||||
|
* the GPL2 ("Copyleft").
|
||||||
|
*
|
||||||
|
* Contact information
|
||||||
|
* -------------------
|
||||||
|
*
|
||||||
|
* GENERAL BYTES s.r.o.
|
||||||
|
* Web : http://www.generalbytes.com
|
||||||
|
*
|
||||||
|
* Other information:
|
||||||
|
*
|
||||||
|
* This implementation was created in cooperation with Orillia BVBA
|
||||||
|
************************************************************************************/
|
||||||
|
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 com.generalbytes.batm.server.extensions.IRateSource;
|
||||||
|
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;
|
||||||
|
import com.xeiam.xchange.currency.CurrencyPair;
|
||||||
|
import com.xeiam.xchange.dto.Order.OrderType;
|
||||||
|
import com.xeiam.xchange.dto.marketdata.Ticker;
|
||||||
|
import com.xeiam.xchange.dto.trade.MarketOrder;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger("batm.master.BitfinexEchange");
|
||||||
|
private Exchange exchange = null;
|
||||||
|
private String apiKey;
|
||||||
|
private String apiSecret;
|
||||||
|
|
||||||
|
private static HashMap<String,BigDecimal> rateAmounts = new HashMap<String, BigDecimal>();
|
||||||
|
private static HashMap<String,Long> rateTimes = new HashMap<String, Long>();
|
||||||
|
private static final long MAXIMUM_ALLOWED_TIME_OFFSET = 30 * 1000;
|
||||||
|
|
||||||
|
public BitfinexExchange(String apiKey, String apiSecret) {
|
||||||
|
this.apiKey = apiKey;
|
||||||
|
this.apiSecret = apiSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized Exchange getExchange() {
|
||||||
|
if (this.exchange == null) {
|
||||||
|
ExchangeSpecification bfxSpec = new com.xeiam.xchange.bitfinex.v1.BitfinexExchange().getDefaultExchangeSpecification();
|
||||||
|
bfxSpec.setApiKey(this.apiKey);
|
||||||
|
bfxSpec.setSecretKey(this.apiSecret);
|
||||||
|
this.exchange = ExchangeFactory.INSTANCE.createExchange(bfxSpec);
|
||||||
|
}
|
||||||
|
return this.exchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getCryptoCurrencies() {
|
||||||
|
Set<String> cryptoCurrencies = new HashSet<String>();
|
||||||
|
cryptoCurrencies.add(ICurrencies.BTC);
|
||||||
|
return cryptoCurrencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getFiatCurrencies() {
|
||||||
|
Set<String> fiatCurrencies = new HashSet<String>();
|
||||||
|
fiatCurrencies.add(ICurrencies.USD);
|
||||||
|
return fiatCurrencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPreferredFiatCurrency() {
|
||||||
|
return ICurrencies.USD;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized BigDecimal getExchangeRateLast(String cryptoCurrency, String fiatCurrency) {
|
||||||
|
String key = cryptoCurrency +"_" + fiatCurrency;
|
||||||
|
synchronized (rateAmounts) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
BigDecimal amount = rateAmounts.get(key);
|
||||||
|
if (amount == null) {
|
||||||
|
BigDecimal result = getExchangeRateLastSync(cryptoCurrency, fiatCurrency);
|
||||||
|
log.debug("Called bitfinex exchange for rate: " + key + " = " + result);
|
||||||
|
rateAmounts.put(key,result);
|
||||||
|
rateTimes.put(key,now+MAXIMUM_ALLOWED_TIME_OFFSET);
|
||||||
|
return result;
|
||||||
|
}else {
|
||||||
|
Long expirationTime = rateTimes.get(key);
|
||||||
|
if (expirationTime > now) {
|
||||||
|
return rateAmounts.get(key);
|
||||||
|
}else{
|
||||||
|
//do the job;
|
||||||
|
BigDecimal result = getExchangeRateLastSync(cryptoCurrency, fiatCurrency);
|
||||||
|
log.debug("Called bitfinex exchange for rate: " + key + " = " + result);
|
||||||
|
rateAmounts.put(key,result);
|
||||||
|
rateTimes.put(key,now+MAXIMUM_ALLOWED_TIME_OFFSET);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal getExchangeRateLastSync(String cryptoCurrency, String cashCurrency) {
|
||||||
|
PollingMarketDataService marketDataService = getExchange().getPollingMarketDataService();
|
||||||
|
try {
|
||||||
|
Ticker ticker = marketDataService.getTicker(new CurrencyPair(cryptoCurrency,cashCurrency));
|
||||||
|
return ticker.getLast();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getCryptoBalance(String cryptoCurrency) {
|
||||||
|
// [TODO] Can be extended to support LTC and DRK (and other currencies supported by BFX)
|
||||||
|
if (!ICurrencies.BTC.equalsIgnoreCase(cryptoCurrency)) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
log.debug("Calling Bitfinex exchange (getBalance)");
|
||||||
|
|
||||||
|
try {
|
||||||
|
return getExchange().getPollingAccountService().getAccountInfo().getBalance(cryptoCurrency);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
log.error("Bitfinex exchange (getBalance) failed with message: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getFiatBalance(String fiatCurrency) {
|
||||||
|
if (!ICurrencies.USD.equalsIgnoreCase(fiatCurrency)) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
log.debug("Calling Bitfinex exchange (getBalance)");
|
||||||
|
|
||||||
|
try {
|
||||||
|
return getExchange().getPollingAccountService().getAccountInfo().getBalance(fiatCurrency);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
log.error("Bitfinex exchange (getBalance) failed with message: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String sendCoins(String destinationAddress, BigDecimal amount, String cryptoCurrency, String description) {
|
||||||
|
if (!ICurrencies.BTC.equalsIgnoreCase(cryptoCurrency)) {
|
||||||
|
log.error("Bitfinex supports only " + ICurrencies.BTC);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Calling bitfinex exchange (withdrawal destination: " + destinationAddress + " amount: " + amount + " " + cryptoCurrency + ")");
|
||||||
|
|
||||||
|
PollingAccountService accountService = getExchange().getPollingAccountService();
|
||||||
|
try {
|
||||||
|
accountService.withdrawFunds(destinationAddress, amount, destinationAddress);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
log.error("Bitfinex exchange (withdrawFunds) failed with message: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String purchaseCoins(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
String 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// get open orders
|
||||||
|
log.debug("Open orders:");
|
||||||
|
boolean orderProcessed = false;
|
||||||
|
int numberOfChecks = 0;
|
||||||
|
while (!orderProcessed && numberOfChecks < 10) {
|
||||||
|
boolean orderFound = false;
|
||||||
|
OpenOrders openOrders = tradeService.getOpenOrders();
|
||||||
|
for (LimitOrder openOrder : openOrders.getOpenOrders()) {
|
||||||
|
log.debug("openOrder = " + openOrder);
|
||||||
|
if (orderId.equalsIgnoreCase(openOrder.getId())) {
|
||||||
|
orderFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (orderFound) {
|
||||||
|
log.debug("Waiting for order to be processed.");
|
||||||
|
try {
|
||||||
|
Thread.sleep(3000); //don't get your ip address banned
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
orderProcessed = true;
|
||||||
|
}
|
||||||
|
numberOfChecks++;
|
||||||
|
}
|
||||||
|
if (orderProcessed) {
|
||||||
|
return orderId;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
log.error("Bitfinex exchange (purchaseCoins) failed with message: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue