Add market price feed for altcoins

This commit is contained in:
Manfred Karrer 2016-02-10 13:59:22 +01:00
parent d2e8c6afd7
commit eefafab977
14 changed files with 177 additions and 63 deletions

View file

@ -13,10 +13,21 @@ public class MarketPrice {
private final double last;
public MarketPrice(String currencyCode, String ask, String bid, String last) {
this(currencyCode, ask, bid, last, false);
}
public MarketPrice(String currencyCode, String ask, String bid, String last, boolean invert) {
this.currencyCode = currencyCode;
this.ask = parseDouble(ask);
this.bid = parseDouble(bid);
this.last = parseDouble(last);
if (invert) {
this.ask = 1d / parseDouble(ask);
this.bid = 1d / parseDouble(bid);
this.last = 1d / parseDouble(last);
} else {
this.ask = parseDouble(ask);
this.bid = parseDouble(bid);
this.last = parseDouble(last);
}
}
public double getPrice(MarketPriceFeed.Type type) {

View file

@ -6,6 +6,7 @@ import com.google.common.util.concurrent.SettableFuture;
import com.google.inject.Inject;
import io.bitsquare.app.Log;
import io.bitsquare.btc.pricefeed.providers.BitcoinAveragePriceProvider;
import io.bitsquare.btc.pricefeed.providers.PoloniexPriceProvider;
import io.bitsquare.btc.pricefeed.providers.PriceProvider;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.FaultHandler;
@ -18,6 +19,7 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@ -43,15 +45,15 @@ public class MarketPriceFeed {
}
}
// TODO
// https://poloniex.com/public?command=returnTicker 33 kb
private static long PERIOD = 30;
private static long PERIOD_FIAT = 1; // We load only the selected currency on interval. Only the first request we load all
private static long PERIOD_CRYPTO = 10; // We load the full list with 33kb so we don't want to load too often
private final ScheduledThreadPoolExecutor executorService = Utilities.getScheduledThreadPoolExecutor("MarketPriceFeed", 5, 10, 120L);
private final ScheduledThreadPoolExecutor executorService = Utilities.getScheduledThreadPoolExecutor("MarketPriceFeed", 5, 10, 700L);
private final Map<String, MarketPrice> cache = new HashMap<>();
private final PriceProvider fiatPriceProvider = new BitcoinAveragePriceProvider();
private final PriceProvider cryptoCurrenciesPriceProvider = new PoloniexPriceProvider();
private Consumer<Double> priceConsumer;
private FaultHandler faultHandler;
private PriceProvider fiatPriceProvider = new BitcoinAveragePriceProvider();
private Type type;
private String currencyCode;
transient private final StringProperty currencyCodeProperty = new SimpleStringProperty();
@ -66,6 +68,7 @@ public class MarketPriceFeed {
public MarketPriceFeed() {
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
@ -76,8 +79,18 @@ public class MarketPriceFeed {
requestAllPrices(fiatPriceProvider, () -> {
applyPrice();
executorService.scheduleAtFixedRate(() -> requestPrice(fiatPriceProvider), PERIOD, PERIOD, TimeUnit.SECONDS);
executorService.scheduleAtFixedRate(
() -> requestPrice(fiatPriceProvider),
PERIOD_FIAT, PERIOD_FIAT, TimeUnit.MINUTES);
});
requestAllPrices(cryptoCurrenciesPriceProvider, () -> {
applyPrice();
executorService.scheduleAtFixedRate(
() -> requestAllPrices(cryptoCurrenciesPriceProvider,
this::applyPrice),
PERIOD_CRYPTO, PERIOD_CRYPTO, TimeUnit.MINUTES);
});
requestAllPrices(cryptoCurrenciesPriceProvider, this::applyPrice);
}
@ -156,7 +169,7 @@ public class MarketPriceFeed {
});
}
private void requestAllPrices(PriceProvider provider, Runnable resultHandler) {
private void requestAllPrices(PriceProvider provider, @Nullable Runnable resultHandler) {
Log.traceCall();
GetPriceRequest getPriceRequest = new GetPriceRequest();
SettableFuture<Map<String, MarketPrice>> future = getPriceRequest.requestAllPrices(provider);
@ -164,7 +177,8 @@ public class MarketPriceFeed {
public void onSuccess(Map<String, MarketPrice> marketPriceMap) {
UserThread.execute(() -> {
cache.putAll(marketPriceMap);
resultHandler.run();
if (resultHandler != null)
resultHandler.run();
});
}

View file

@ -0,0 +1,81 @@
package io.bitsquare.btc.pricefeed.providers;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.internal.LinkedTreeMap;
import io.bitsquare.app.Log;
import io.bitsquare.btc.pricefeed.MarketPrice;
import io.bitsquare.http.HttpClient;
import io.bitsquare.http.HttpException;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.locale.TradeCurrency;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class PoloniexPriceProvider implements PriceProvider {
private static final Logger log = LoggerFactory.getLogger(PoloniexPriceProvider.class);
//https://poloniex.com/public?command=returnTicker
private final HttpClient httpClient = new HttpClient("https://poloniex.com/public");
public PoloniexPriceProvider() {
}
@Override
public Map<String, MarketPrice> getAllPrices() throws IOException, HttpException {
Map<String, MarketPrice> marketPriceMap = new HashMap<>();
String response = httpClient.requestWithGET("?command=returnTicker");
LinkedTreeMap<String, Object> treeMap = new Gson().fromJson(response, LinkedTreeMap.class);
Map<String, String> temp = new HashMap<>();
Set<String> supported = CurrencyUtil.getSortedCryptoCurrencies().stream()
.map(TradeCurrency::getCode)
.collect(Collectors.toSet());
treeMap.entrySet().stream().forEach(e -> {
Object value = e.getValue();
String currencyPair = e.getKey();
String otherCurrency = null;
if (currencyPair.startsWith("BTC")) {
String[] tokens = currencyPair.split("_");
if (tokens.length > 1) {
otherCurrency = tokens[1];
if (supported.contains(otherCurrency)) {
if (value instanceof LinkedTreeMap) {
LinkedTreeMap<String, Object> treeMap2 = (LinkedTreeMap) value;
temp.clear();
treeMap2.entrySet().stream().forEach(e2 -> temp.put(e2.getKey(), e2.getValue().toString()));
marketPriceMap.put(otherCurrency,
new MarketPrice(otherCurrency, temp.get("lowestAsk"), temp.get("highestBid"), temp.get("last"), true));
}
}
}
}
});
return marketPriceMap;
}
@Override
public MarketPrice getPrice(String currencyCode) throws IOException, HttpException {
Log.traceCall("currencyCode=" + currencyCode);
JsonObject jsonObject = new JsonParser()
.parse(httpClient.requestWithGET(currencyCode))
.getAsJsonObject();
return new MarketPrice(currencyCode,
jsonObject.get("ask").getAsString(),
jsonObject.get("bid").getAsString(),
jsonObject.get("last").getAsString());
}
@Override
public String toString() {
return "BitcoinAveragePriceProvider{" +
'}';
}
}

View file

@ -178,20 +178,20 @@ public class CurrencyUtil {
// result.add(new CryptoCurrency("XMR", "Monero"));
// result.add(new CryptoCurrency("BCN", "Bytecoin"));
result.add(new CryptoCurrency("DASH", "Dash"));
result.add(new CryptoCurrency("ANC", "Anoncoin"));
result.add(new CryptoCurrency("NBT", "NuBits"));
result.add(new CryptoCurrency("NSR", "NuShares"));
result.add(new CryptoCurrency("FAIR", "FairCoin"));
result.add(new CryptoCurrency("PPC", "Peercoin"));
result.add(new CryptoCurrency("XPM", "Primecoin"));
result.add(new CryptoCurrency("SC", "Siacoin"));
result.add(new CryptoCurrency("SJCX", "StorjcoinX"));
result.add(new CryptoCurrency("GEMZ", "Gemz"));
result.add(new CryptoCurrency("DOGE", "Dogecoin"));
result.add(new CryptoCurrency("BLK", "Blackcoin"));
result.add(new CryptoCurrency("FCT", "Factom"));
result.add(new CryptoCurrency("NXT", "Nxt"));
result.add(new CryptoCurrency("BTS", "BitShares"));
result.add(new CryptoCurrency("XCP", "Counterparty"));
result.add(new CryptoCurrency("XRP", "Ripple"));
// Stellar (XLM Lumen) uses an additional memo field. We dont support that for now
//result.add(new CryptoCurrency("STR", "Stellar"));
return result;
}