/* * This file is part of Bitsquare. * * Bitsquare 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. * * Bitsquare 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 Bitsquare. If not, see <http://www.gnu.org/licenses/>. */ package io.bitsquare.pricefeed; import io.bitsquare.common.util.Utilities; import io.bitsquare.http.HttpException; import io.bitsquare.pricefeed.providers.BtcAverageProvider; import io.bitsquare.pricefeed.providers.CoinmarketcapProvider; import io.bitsquare.pricefeed.providers.PoloniexProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; 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; class PriceRequestService { private static final Logger log = LoggerFactory.getLogger(PriceRequestService.class); 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 final Timer timerBtcAverageLocal = new Timer(); private final Timer timerBtcAverageGlobal = new Timer(); private final Timer timerPoloniex = new Timer(); private final Timer timerCoinmarketcap = new Timer(); private final BtcAverageProvider btcAverageProvider; private final PoloniexProvider poloniexProvider; private final CoinmarketcapProvider coinmarketcapProvider; private final Map<String, PriceData> allPricesMap = new ConcurrentHashMap<>(); private Map<String, PriceData> btcAverageLocalMap; private Map<String, PriceData> poloniexMap; private long btcAverageTs; private long poloniexTs; private long coinmarketcapTs; private String json; public PriceRequestService(String bitcoinAveragePrivKey, String bitcoinAveragePubKey) throws IOException, NoSuchAlgorithmException, InvalidKeyException { btcAverageProvider = new BtcAverageProvider(bitcoinAveragePrivKey, bitcoinAveragePubKey); poloniexProvider = new PoloniexProvider(); coinmarketcapProvider = new CoinmarketcapProvider(); startRequests(); } public String getJson() { return json; } private void startRequests() throws InvalidKeyException, NoSuchAlgorithmException, IOException { timerBtcAverageLocal.scheduleAtFixedRate(new TimerTask() { @Override public void run() { try { requestBtcAverageLocalPrices(); } catch (NoSuchAlgorithmException | InvalidKeyException e) { log.error(e.toString()); e.printStackTrace(); } catch (HttpException | IOException e) { log.warn(e.toString()); e.printStackTrace(); } } }, INTERVAL_BTC_AV_LOCAL_MS, INTERVAL_BTC_AV_LOCAL_MS); timerBtcAverageGlobal.scheduleAtFixedRate(new TimerTask() { @Override public void run() { try { requestBtcAverageGlobalPrices(); } catch (NoSuchAlgorithmException | InvalidKeyException e) { log.error(e.toString()); e.printStackTrace(); } catch (HttpException | IOException e) { log.warn(e.toString()); e.printStackTrace(); } } }, INTERVAL_BTC_AV_GLOBAL_MS, INTERVAL_BTC_AV_GLOBAL_MS); timerPoloniex.scheduleAtFixedRate(new TimerTask() { @Override public void run() { try { requestPoloniexPrices(); } catch (IOException | HttpException e) { log.warn(e.toString()); e.printStackTrace(); } } }, INTERVAL_POLONIEX_MS, INTERVAL_POLONIEX_MS); timerCoinmarketcap.scheduleAtFixedRate(new TimerTask() { @Override public void run() { try { requestCoinmarketcapPrices(); } catch (IOException | HttpException e) { log.warn(e.toString()); e.printStackTrace(); } } }, INTERVAL_COIN_MARKET_CAP_MS, INTERVAL_COIN_MARKET_CAP_MS); try { requestBtcAverageLocalPrices(); requestBtcAverageGlobalPrices(); requestPoloniexPrices(); requestCoinmarketcapPrices(); } catch (HttpException e) { log.warn(e.toString()); e.printStackTrace(); } } private void requestCoinmarketcapPrices() throws IOException, HttpException { Map<String, PriceData> map = coinmarketcapProvider.request(); // we don't replace prices which we got form the Poloniex request, just in case the Coinmarketcap data are // received earlier at startup we allow them but Poloniex will overwrite them. map.entrySet().stream() .filter(e -> poloniexMap == null || !poloniexMap.containsKey(e.getKey())) .forEach(e -> allPricesMap.put(e.getKey(), e.getValue())); coinmarketcapTs = Instant.now().getEpochSecond(); log.info("Coinmarketcap LTC (last): " + map.get("LTC").l); writeToJson(); } private void requestPoloniexPrices() throws IOException, HttpException { poloniexMap = poloniexProvider.request(); allPricesMap.putAll(poloniexMap); poloniexTs = Instant.now().getEpochSecond(); log.info("Poloniex LTC (last): " + poloniexMap.get("LTC").l); writeToJson(); } private void requestBtcAverageLocalPrices() throws NoSuchAlgorithmException, InvalidKeyException, IOException, HttpException { btcAverageLocalMap = btcAverageProvider.getLocal(); log.info("BTCAverage local USD (last):" + btcAverageLocalMap.get("USD").l); allPricesMap.putAll(btcAverageLocalMap); btcAverageTs = Instant.now().getEpochSecond(); writeToJson(); } private void requestBtcAverageGlobalPrices() throws NoSuchAlgorithmException, InvalidKeyException, IOException, HttpException { Map<String, PriceData> map = btcAverageProvider.getGlobal(); log.info("BTCAverage global USD (last):" + map.get("USD").l); // we don't replace prices which we got form the local request, just in case the global data are received // earlier at startup we allow them but the local request will overwrite them. map.entrySet().stream() .filter(e -> btcAverageLocalMap == null || !btcAverageLocalMap.containsKey(e.getKey())) .forEach(e -> allPricesMap.put(e.getKey(), e.getValue())); btcAverageTs = Instant.now().getEpochSecond(); writeToJson(); } private void writeToJson() { Map<String, Object> map = new HashMap<>(); map.put("btcAverageTs", btcAverageTs); map.put("poloniexTs", poloniexTs); map.put("coinmarketcapTs", coinmarketcapTs); map.put("data", allPricesMap.values().toArray()); json = Utilities.objectToJson(map); } }