From 28476860696d8e667f37de17548d17f4de00c44b Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Tue, 23 Jan 2018 20:42:30 +0100 Subject: [PATCH] Sync with exchange 'provider' module @ v0.6.4 (#2) --- .../java/io/bisq/provider/ProviderMain.java | 120 +++++++++++++----- .../io/bisq/provider/ProviderVersion.java | 22 ++++ .../bisq/provider/fee/FeeRequestService.java | 40 +++--- .../fee/providers/BtcFeesProvider.java | 83 ++++++------ .../io/bisq/provider/price/PriceData.java | 43 +------ .../provider/price/PriceRequestService.java | 52 ++++---- .../price/providers/BtcAverageProvider.java | 47 +++---- .../providers/CoinmarketcapProvider.java | 45 ++----- .../price/providers/PoloniexProvider.java | 42 ++---- src/main/resources/logback.xml | 34 +++-- 10 files changed, 268 insertions(+), 260 deletions(-) create mode 100644 src/main/java/io/bisq/provider/ProviderVersion.java diff --git a/src/main/java/io/bisq/provider/ProviderMain.java b/src/main/java/io/bisq/provider/ProviderMain.java index e8d76d4..80df3a4 100644 --- a/src/main/java/io/bisq/provider/ProviderMain.java +++ b/src/main/java/io/bisq/provider/ProviderMain.java @@ -17,62 +17,114 @@ package io.bisq.provider; +import ch.qos.logback.classic.Level; +import io.bisq.common.app.Log; +import io.bisq.common.app.Version; +import io.bisq.common.util.Utilities; +import io.bisq.network.http.HttpException; import io.bisq.provider.fee.FeeRequestService; +import io.bisq.provider.fee.providers.BtcFeesProvider; import io.bisq.provider.price.PriceRequestService; - -import io.bisq.common.app.Log; - -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ch.qos.logback.classic.Level; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.spec.InvalidKeySpecException; +import java.util.Locale; +import java.util.concurrent.TimeUnit; import static spark.Spark.get; import static spark.Spark.port; public class ProviderMain { - private static final Logger log = LoggerFactory.getLogger(ProviderMain.class); - public static void main(String[] args) throws Exception { - Log.setup(System.getProperty("user.home") + File.separator + "provider"); + static { + // Need to set default locale initially otherwise we get problems at non-english OS + Locale.setDefault(new Locale("en", Locale.getDefault().getCountry())); + + Utilities.removeCryptographyRestrictions(); + } + + public static void main(String[] args) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, InvalidKeyException, HttpException { + final String logPath = System.getProperty("user.home") + File.separator + "provider"; + Log.setup(logPath); Log.setLevel(Level.INFO); + log.info("Log files under: " + logPath); + log.info("ProviderVersion.VERSION: " + ProviderVersion.VERSION); + log.info("Bisq exchange Version{" + + "VERSION=" + Version.VERSION + + ", P2P_NETWORK_VERSION=" + Version.P2P_NETWORK_VERSION + + ", LOCAL_DB_VERSION=" + Version.LOCAL_DB_VERSION + + ", TRADE_PROTOCOL_VERSION=" + Version.TRADE_PROTOCOL_VERSION + + ", BASE_CURRENCY_NETWORK=NOT SET" + + ", getP2PNetworkId()=NOT SET" + + '}'); + Utilities.printSysInfo(); + + String bitcoinAveragePrivKey = null; + String bitcoinAveragePubKey = null; + int capacity = BtcFeesProvider.CAPACITY; + int maxBlocks = BtcFeesProvider.MAX_BLOCKS; + long requestIntervalInMs = TimeUnit.MINUTES.toMillis(FeeRequestService.REQUEST_INTERVAL_MIN); + + // extract command line arguments + if (args.length < 2) { + log.error("You need to provide the BitcoinAverage API keys. Private key as first argument, public key as second argument."); + System.exit(1); + } + if (args.length >= 2) { + bitcoinAveragePrivKey = args[0]; + bitcoinAveragePubKey = args[1]; + } + if (args.length >= 4) { + capacity = Integer.valueOf(args[2]); + maxBlocks = Integer.valueOf(args[3]); + } + if (args.length >= 5) { + requestIntervalInMs = TimeUnit.MINUTES.toMillis(Long.valueOf(args[4])); + } - port(System.getenv("PORT") != null ? Integer.valueOf(System.getenv("PORT")) : 8080); + port(8080); - handleGetAllMarketPrices(); - handleGetFees(); + handleGetAllMarketPrices(bitcoinAveragePrivKey, bitcoinAveragePubKey); + handleGetFees(capacity, maxBlocks, requestIntervalInMs); + handleGetVersion(); + handleGetParams(capacity, maxBlocks, requestIntervalInMs); } - private static void handleGetAllMarketPrices() throws IOException, NoSuchAlgorithmException, InvalidKeyException { - if (System.getenv("BITCOIN_AVG_PRIVKEY") != null && System.getenv("BITCOIN_AVG_PUBKEY") != null) { - String bitcoinAveragePrivKey = System.getenv("BITCOIN_AVG_PRIVKEY"); - String bitcoinAveragePubKey = System.getenv("BITCOIN_AVG_PUBKEY"); - - PriceRequestService priceRequestService = - new PriceRequestService(bitcoinAveragePrivKey, bitcoinAveragePubKey); - - get("/getAllMarketPrices", (req, res) -> { - log.info("Incoming getAllMarketPrices request from: " + req.userAgent()); - return priceRequestService.getJson(); - }); - } else { - throw new IllegalArgumentException("You need to provide the BitcoinAverage API keys. " + - "Private key as BITCOIN_AVG_PRIVKEY environment variable, " + - "public key as BITCOIN_AVG_PUBKEY environment variable"); - } + private static void handleGetAllMarketPrices(String bitcoinAveragePrivKey, String bitcoinAveragePubKey) + throws IOException, NoSuchAlgorithmException, InvalidKeyException { + PriceRequestService priceRequestService = new PriceRequestService(bitcoinAveragePrivKey, bitcoinAveragePubKey); + get("/getAllMarketPrices", (req, res) -> { + log.info("Incoming getAllMarketPrices request from: " + req.userAgent()); + return priceRequestService.getJson(); + }); } - private static void handleGetFees() throws IOException { - FeeRequestService feeRequestService = new FeeRequestService(); + private static void handleGetFees(int capacity, int maxBlocks, long requestIntervalInMs) throws IOException { + FeeRequestService feeRequestService = new FeeRequestService(capacity, maxBlocks, requestIntervalInMs); get("/getFees", (req, res) -> { log.info("Incoming getFees request from: " + req.userAgent()); return feeRequestService.getJson(); }); } + + private static void handleGetVersion() throws IOException { + get("/getVersion", (req, res) -> { + log.info("Incoming getVersion request from: " + req.userAgent()); + return ProviderVersion.VERSION; + }); + } + + private static void handleGetParams(int capacity, int maxBlocks, long requestIntervalInMs) throws IOException { + get("/getParams", (req, res) -> { + log.info("Incoming getParams request from: " + req.userAgent()); + return capacity + ";" + maxBlocks + ";" + requestIntervalInMs; + }); + } } diff --git a/src/main/java/io/bisq/provider/ProviderVersion.java b/src/main/java/io/bisq/provider/ProviderVersion.java new file mode 100644 index 0000000..fa132b9 --- /dev/null +++ b/src/main/java/io/bisq/provider/ProviderVersion.java @@ -0,0 +1,22 @@ +/* + * This file is part of bisq. + * + * bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with bisq. If not, see . + */ + +package io.bisq.provider; + +public class ProviderVersion { + public static final String VERSION = "0.6.4"; +} diff --git a/src/main/java/io/bisq/provider/fee/FeeRequestService.java b/src/main/java/io/bisq/provider/fee/FeeRequestService.java index a351df9..05c198a 100644 --- a/src/main/java/io/bisq/provider/fee/FeeRequestService.java +++ b/src/main/java/io/bisq/provider/fee/FeeRequestService.java @@ -17,53 +17,47 @@ package io.bisq.provider.fee; -import io.bisq.provider.fee.providers.BtcFeesProvider; - import io.bisq.common.util.Utilities; - -import java.time.Instant; +import io.bisq.core.provider.fee.FeeService; +import io.bisq.provider.fee.providers.BtcFeesProvider; +import lombok.extern.slf4j.Slf4j; import java.io.IOException; - +import java.time.Instant; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +@Slf4j public class FeeRequestService { + public static int REQUEST_INTERVAL_MIN = 5; - private static final Logger log = LoggerFactory.getLogger(FeeRequestService.class); - - private static final long INTERVAL_BTC_FEES_MS = 600_000; // 10 min - - public static final long BTC_MIN_TX_FEE = 40; // satoshi/byte - public static final long BTC_MAX_TX_FEE = 2000; + public static final long BTC_MIN_TX_FEE = 10; // satoshi/byte + public static final long BTC_MAX_TX_FEE = 1000; private final Timer timerBitcoinFeesLocal = new Timer(); + private final BtcFeesProvider btcFeesProvider; private final Map dataMap = new ConcurrentHashMap<>(); - private long bitcoinFeesTs; private String json; - public FeeRequestService() throws IOException { - btcFeesProvider = new BtcFeesProvider(); + public FeeRequestService(int capacity, int maxBlocks, long requestIntervalInMs) throws IOException { + btcFeesProvider = new BtcFeesProvider(capacity, maxBlocks); // For now we don't need a fee estimation for LTC so we set it fixed, but we keep it in the provider to // be flexible if fee pressure grows on LTC - dataMap.put("ltcTxFee", 500L /*FeeService.LTC_DEFAULT_TX_FEE*/); - dataMap.put("dogeTxFee", 5_000_000L /*FeeService.DOGE_DEFAULT_TX_FEE*/); - dataMap.put("dashTxFee", 50L /*FeeService.DASH_DEFAULT_TX_FEE*/); + dataMap.put("ltcTxFee", FeeService.LTC_DEFAULT_TX_FEE); + dataMap.put("dogeTxFee", FeeService.DOGE_DEFAULT_TX_FEE); + dataMap.put("dashTxFee", FeeService.DASH_DEFAULT_TX_FEE); writeToJson(); - startRequests(); + startRequests(requestIntervalInMs); } - private void startRequests() throws IOException { + private void startRequests(long requestIntervalInMs) throws IOException { timerBitcoinFeesLocal.scheduleAtFixedRate(new TimerTask() { @Override public void run() { @@ -74,7 +68,7 @@ public void run() { e.printStackTrace(); } } - }, INTERVAL_BTC_FEES_MS, INTERVAL_BTC_FEES_MS); + }, requestIntervalInMs, requestIntervalInMs); requestBitcoinFees(); diff --git a/src/main/java/io/bisq/provider/fee/providers/BtcFeesProvider.java b/src/main/java/io/bisq/provider/fee/providers/BtcFeesProvider.java index af1fc3f..5c620f3 100644 --- a/src/main/java/io/bisq/provider/fee/providers/BtcFeesProvider.java +++ b/src/main/java/io/bisq/provider/fee/providers/BtcFeesProvider.java @@ -1,62 +1,46 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - package io.bisq.provider.fee.providers; -import io.bisq.provider.fee.FeeRequestService; - -import io.bisq.network.http.HttpClient; - -import io.bisq.common.util.MathUtils; - import com.google.gson.Gson; import com.google.gson.internal.LinkedTreeMap; +import io.bisq.common.util.MathUtils; +import io.bisq.network.http.HttpClient; +import io.bisq.provider.fee.FeeRequestService; +import lombok.extern.slf4j.Slf4j; import java.io.IOException; - import java.util.ArrayList; +import java.util.LinkedList; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +//TODO consider alternative https://www.bitgo.com/api/v1/tx/fee?numBlocks=3 +@Slf4j public class BtcFeesProvider { - - private static final Logger log = LoggerFactory.getLogger(BtcFeesProvider.class); + public static int CAPACITY = 4; // if we request each 5 min. we take average of last 20 min. + public static int MAX_BLOCKS = 10; private final HttpClient httpClient; - - public BtcFeesProvider() { - // we previously used https://bitcoinfees.21.co/api/v1/fees/recommended but fees were way too high - // see also: https://estimatefee.com/n/2 - this.httpClient = new HttpClient("https://bitcoinfees.21.co/api/v1/fees/"); + LinkedList fees = new LinkedList<>(); + private final int capacity; + private final int maxBlocks; + + // other: https://estimatefee.com/n/2 + public BtcFeesProvider(int capacity, int maxBlocks) { + this.capacity = capacity; + this.maxBlocks = maxBlocks; + this.httpClient = new HttpClient("https://bitcoinfees.earn.com/api/v1/fees/"); } public Long getFee() throws IOException { + // prev. used: https://bitcoinfees.earn.com/api/v1/fees/recommended + // but was way too high + + // https://bitcoinfees.earn.com/api/v1/fees/list String response = httpClient.requestWithGET("list", "User-Agent", ""); log.info("Get recommended fee response: " + response); - @SuppressWarnings("unchecked") - LinkedTreeMap>> treeMap = - new Gson().fromJson(response, LinkedTreeMap.class); - + LinkedTreeMap>> treeMap = new Gson().fromJson(response, LinkedTreeMap.class); final long[] fee = new long[1]; - - // we want a fee that gets us in within 10 blocks max - int maxBlocks = 10; + // we want a fee which is at least in 20 blocks in (21.co estimation seem to be way too high, so we get + // prob much faster in treeMap.entrySet().stream() .flatMap(e -> e.getValue().stream()) .forEach(e -> { @@ -65,7 +49,20 @@ public Long getFee() throws IOException { fee[0] = MathUtils.roundDoubleToLong(e.get("maxFee")); }); fee[0] = Math.min(Math.max(fee[0], FeeRequestService.BTC_MIN_TX_FEE), FeeRequestService.BTC_MAX_TX_FEE); - log.info("fee " + fee[0]); - return fee[0]; + + return getAverage(fee[0]); + } + + // We take the average of the last 12 calls (every 5 minute) so we smooth extreme values. + // We observed very radical jumps in the fee estimations, so that should help to avoid that. + long getAverage(long newFee) { + log.info("new fee " + newFee); + fees.add(newFee); + long average = ((Double) fees.stream().mapToDouble(e -> e).average().getAsDouble()).longValue(); + log.info("average of last {} calls: {}", fees.size(), average); + if (fees.size() == capacity) + fees.removeFirst(); + + return average; } } diff --git a/src/main/java/io/bisq/provider/price/PriceData.java b/src/main/java/io/bisq/provider/price/PriceData.java index b2efcf0..4bf848d 100644 --- a/src/main/java/io/bisq/provider/price/PriceData.java +++ b/src/main/java/io/bisq/provider/price/PriceData.java @@ -17,49 +17,12 @@ package io.bisq.provider.price; -import java.util.Objects; +import lombok.Value; +@Value public class PriceData { - private final String currencyCode; private final double price; private final long timestampSec; - - public PriceData(String currencyCode, double price, long timestampSec) { - this.currencyCode = currencyCode; - this.price = price; - this.timestampSec = timestampSec; - } - - double getPrice() { - return this.price; - } - - long getTimestampSec() { - return this.timestampSec; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - PriceData priceData = (PriceData) o; - return Double.compare(priceData.price, price) == 0 && - timestampSec == priceData.timestampSec && - Objects.equals(currencyCode, priceData.currencyCode); - } - - @Override - public int hashCode() { - return Objects.hash(currencyCode, price, timestampSec); - } - - @Override - public String toString() { - return "PriceData{" + - "currencyCode='" + currencyCode + '\'' + - ", price=" + price + - ", timestampSec=" + timestampSec + - '}'; - } + private final String provider; } diff --git a/src/main/java/io/bisq/provider/price/PriceRequestService.java b/src/main/java/io/bisq/provider/price/PriceRequestService.java index afb5342..02ef8ff 100644 --- a/src/main/java/io/bisq/provider/price/PriceRequestService.java +++ b/src/main/java/io/bisq/provider/price/PriceRequestService.java @@ -17,37 +17,37 @@ package io.bisq.provider.price; +import io.bisq.common.util.Utilities; import io.bisq.provider.price.providers.BtcAverageProvider; import io.bisq.provider.price.providers.CoinmarketcapProvider; import io.bisq.provider.price.providers.PoloniexProvider; +import lombok.extern.slf4j.Slf4j; -import io.bisq.common.util.Utilities; - -import java.time.Instant; - +import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; - -import java.io.IOException; - -import java.util.HashMap; +import java.time.Instant; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +@Slf4j public class PriceRequestService { + public static final String POLO_PROVIDER = "POLO"; + public static final String COINMKTC_PROVIDER = "CMC"; + public static final String BTCAVERAGE_LOCAL_PROVIDER = "BTCA_L"; + public static final String BTCAVERAGE_GLOBAL_PROVIDER = "BTCA_G"; - private static final Logger log = LoggerFactory.getLogger(PriceRequestService.class); + // We adjust request time to fit into BitcoinAverage developer plan (45k request per month). + // We get 42514 (29760+12754) request with below numbers. + private static final long INTERVAL_BTC_AV_LOCAL_MS = 90_000; // 90 sec; 29760 requests for 31 days + private static final long INTERVAL_BTC_AV_GLOBAL_MS = 210_000; // 3.5 min; 12754 requests for 31 days - private static final long INTERVAL_BTC_AV_LOCAL_MS = 60_000; // 60 sec - private static final long INTERVAL_BTC_AV_GLOBAL_MS = 150_000; // 2.5 min private static final long INTERVAL_POLONIEX_MS = 60_000; // 1 min - private static final long INTERVAL_COIN_MARKET_CAP_MS = 300_000; // 5 min + private static final long INTERVAL_COIN_MARKET_CAP_MS = 300_000; // 5 min that data structure is quite heavy so we don't request too often. private static final long MARKET_PRICE_TTL_SEC = 1800; // 30 min private final Timer timerBtcAverageLocal = new Timer(); @@ -66,11 +66,14 @@ public class PriceRequestService { private long btcAverageTs; private long poloniexTs; private long coinmarketcapTs; + private long btcAverageLCount; + private long btcAverageGCount; + private long poloniexCount; + private long coinmarketcapCount; private String json; - public PriceRequestService(String bitcoinAveragePrivKey, String bitcoinAveragePubKey) - throws IOException, NoSuchAlgorithmException, InvalidKeyException { + public PriceRequestService(String bitcoinAveragePrivKey, String bitcoinAveragePubKey) throws IOException, NoSuchAlgorithmException, InvalidKeyException { btcAverageProvider = new BtcAverageProvider(bitcoinAveragePrivKey, bitcoinAveragePubKey); poloniexProvider = new PoloniexProvider(); coinmarketcapProvider = new CoinmarketcapProvider(); @@ -88,10 +91,7 @@ private void startRequests() throws InvalidKeyException, NoSuchAlgorithmExceptio public void run() { try { requestBtcAverageLocalPrices(); - } catch (NoSuchAlgorithmException | InvalidKeyException e) { - log.error(e.toString()); - e.printStackTrace(); - } catch (IOException e) { + } catch (Throwable e) { log.warn(e.toString()); e.printStackTrace(); } @@ -156,6 +156,7 @@ private void requestCoinmarketcapPrices() throws IOException { .filter(e -> poloniexMap == null || !poloniexMap.containsKey(e.getKey())) .forEach(e -> allPricesMap.put(e.getKey(), e.getValue())); coinmarketcapTs = Instant.now().getEpochSecond(); + coinmarketcapCount = map.size(); if (map.get("LTC") != null) log.info("Coinmarketcap LTC (last): " + map.get("LTC").getPrice()); @@ -171,6 +172,7 @@ private void requestPoloniexPrices() throws IOException { removeOutdatedPrices(allPricesMap); allPricesMap.putAll(poloniexMap); poloniexTs = Instant.now().getEpochSecond(); + poloniexCount = poloniexMap.size(); if (poloniexMap.get("LTC") != null) log.info("Poloniex LTC (last): " + poloniexMap.get("LTC").getPrice()); @@ -189,6 +191,7 @@ private void requestBtcAverageLocalPrices() throws NoSuchAlgorithmException, Inv removeOutdatedPrices(allPricesMap); allPricesMap.putAll(btcAverageLocalMap); btcAverageTs = Instant.now().getEpochSecond(); + btcAverageLCount = btcAverageLocalMap.size(); writeToJson(); } @@ -208,14 +211,19 @@ private void requestBtcAverageGlobalPrices() throws NoSuchAlgorithmException, In .filter(e -> btcAverageLocalMap == null || !btcAverageLocalMap.containsKey(e.getKey())) .forEach(e -> allPricesMap.put(e.getKey(), e.getValue())); btcAverageTs = Instant.now().getEpochSecond(); + btcAverageGCount = map.size(); writeToJson(); } private void writeToJson() { - Map map = new HashMap<>(); + Map map = new LinkedHashMap<>(); map.put("btcAverageTs", btcAverageTs); map.put("poloniexTs", poloniexTs); map.put("coinmarketcapTs", coinmarketcapTs); + map.put("btcAverageLCount", btcAverageLCount); + map.put("btcAverageGCount", btcAverageGCount); + map.put("poloniexCount", poloniexCount); + map.put("coinmarketcapCount", coinmarketcapCount); map.put("data", allPricesMap.values().toArray()); json = Utilities.objectToJson(map); } diff --git a/src/main/java/io/bisq/provider/price/providers/BtcAverageProvider.java b/src/main/java/io/bisq/provider/price/providers/BtcAverageProvider.java index ab09599..8d649b5 100644 --- a/src/main/java/io/bisq/provider/price/providers/BtcAverageProvider.java +++ b/src/main/java/io/bisq/provider/price/providers/BtcAverageProvider.java @@ -17,34 +17,26 @@ package io.bisq.provider.price.providers; -import io.bisq.provider.price.PriceData; - -import io.bisq.network.http.HttpClient; - import com.google.gson.Gson; import com.google.gson.internal.LinkedTreeMap; - +import io.bisq.network.http.HttpClient; +import io.bisq.provider.price.PriceData; +import io.bisq.provider.price.PriceRequestService; import org.bouncycastle.util.encoders.Hex; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; - -import java.time.Instant; - +import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; - -import java.io.IOException; - +import java.time.Instant; import java.util.HashMap; import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class BtcAverageProvider { - private static final Logger log = LoggerFactory.getLogger(BtcAverageProvider.class); private final HttpClient httpClient; @@ -65,14 +57,14 @@ private String getHeader() throws NoSuchAlgorithmException, InvalidKeyException } public Map getLocal() throws NoSuchAlgorithmException, InvalidKeyException, IOException { - return getMap(httpClient.requestWithGETNoProxy("indices/local/ticker/all?crypto=BTC", "X-signature", getHeader())); + return getMap(httpClient.requestWithGETNoProxy("indices/local/ticker/all?crypto=BTC", "X-signature", getHeader()), PriceRequestService.BTCAVERAGE_LOCAL_PROVIDER); } public Map getGlobal() throws NoSuchAlgorithmException, InvalidKeyException, IOException { - return getMap(httpClient.requestWithGETNoProxy("indices/global/ticker/all?crypto=BTC", "X-signature", getHeader())); + return getMap(httpClient.requestWithGETNoProxy("indices/global/ticker/all?crypto=BTC", "X-signature", getHeader()), PriceRequestService.BTCAVERAGE_GLOBAL_PROVIDER); } - private Map getMap(String json) { + private Map getMap(String json, String provider) { Map marketPriceMap = new HashMap<>(); LinkedTreeMap treeMap = new Gson().>fromJson(json, LinkedTreeMap.class); long ts = Instant.now().getEpochSecond(); @@ -86,10 +78,21 @@ private Map getMap(String json) { // We ignore venezuelan currency as the official exchange rate is wishful thinking only.... // We should use that api with a custom provider: http://api.bitcoinvenezuela.com/1 if (!("VEF".equals(currencyCode))) { - marketPriceMap.put(currencyCode, - new PriceData(currencyCode, - (double) data.get("last"), - ts)); + try { + final Object lastAsObject = data.get("last"); + double last = 0; + if (lastAsObject instanceof String) + last = Double.valueOf((String) lastAsObject); + else if (lastAsObject instanceof Double) + last = (double) lastAsObject; + else + log.warn("Unexpected data type: lastAsObject=" + lastAsObject); + + marketPriceMap.put(currencyCode, + new PriceData(currencyCode, last, ts, provider)); + } catch (Throwable exception) { + log.error("Error converting btcaverage data: " + currencyCode, exception); + } } } }); diff --git a/src/main/java/io/bisq/provider/price/providers/CoinmarketcapProvider.java b/src/main/java/io/bisq/provider/price/providers/CoinmarketcapProvider.java index e297115..de7acf3 100644 --- a/src/main/java/io/bisq/provider/price/providers/CoinmarketcapProvider.java +++ b/src/main/java/io/bisq/provider/price/providers/CoinmarketcapProvider.java @@ -1,48 +1,23 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - package io.bisq.provider.price.providers; -import io.bisq.provider.price.PriceData; - -import io.bisq.network.http.HttpClient; - -import io.bisq.common.locale.CurrencyUtil; -import io.bisq.common.locale.TradeCurrency; - import com.google.gson.Gson; import com.google.gson.internal.LinkedTreeMap; - -import java.time.Instant; +import io.bisq.common.locale.CurrencyUtil; +import io.bisq.common.locale.TradeCurrency; +import io.bisq.network.http.HttpClient; +import io.bisq.provider.price.PriceData; +import io.bisq.provider.price.PriceRequestService; import java.io.IOException; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.time.Instant; +import java.util.*; import java.util.stream.Collectors; import static java.lang.Double.parseDouble; public class CoinmarketcapProvider { - private final Set supportedAltcoins; + private final HttpClient httpClient; public CoinmarketcapProvider() { @@ -62,9 +37,7 @@ public Map request() throws IOException { String code = (String) treeMap.get("symbol"); if (supportedAltcoins.contains(code)) { double price_btc = parseDouble((String) treeMap.get("price_btc")); - marketPriceMap.put(code, new PriceData(code, - price_btc, - ts)); + marketPriceMap.put(code, new PriceData(code, price_btc, ts, PriceRequestService.COINMKTC_PROVIDER)); } }); return marketPriceMap; diff --git a/src/main/java/io/bisq/provider/price/providers/PoloniexProvider.java b/src/main/java/io/bisq/provider/price/providers/PoloniexProvider.java index 8a301f4..48cb9e4 100644 --- a/src/main/java/io/bisq/provider/price/providers/PoloniexProvider.java +++ b/src/main/java/io/bisq/provider/price/providers/PoloniexProvider.java @@ -1,48 +1,25 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - package io.bisq.provider.price.providers; -import io.bisq.provider.price.PriceData; - -import io.bisq.network.http.HttpClient; - -import io.bisq.common.locale.CurrencyUtil; -import io.bisq.common.locale.TradeCurrency; - import com.google.gson.Gson; import com.google.gson.internal.LinkedTreeMap; - -import java.time.Instant; +import io.bisq.common.locale.CurrencyUtil; +import io.bisq.common.locale.TradeCurrency; +import io.bisq.network.http.HttpClient; +import io.bisq.provider.price.PriceData; +import io.bisq.provider.price.PriceRequestService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; - +import java.time.Instant; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import static java.lang.Double.parseDouble; public class PoloniexProvider { - private static final Logger log = LoggerFactory.getLogger(PoloniexProvider.class); private final Set supportedAltcoins; @@ -77,7 +54,8 @@ public Map request() throws IOException { marketPriceMap.put(altcoinCurrency, new PriceData(altcoinCurrency, parseDouble((String) data.get("last")), - ts) + ts, + PriceRequestService.POLO_PROVIDER) ); } } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 62d32a3..0dac7c4 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,12 +1,30 @@ + + - - - %highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg %xEx%n) - - + + + %highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg %xEx%n) + + + + + + - - -