diff --git a/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPrice.java b/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPrice.java index b821a61052..7a5eee0f47 100644 --- a/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPrice.java +++ b/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPrice.java @@ -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) { diff --git a/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPriceFeed.java b/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPriceFeed.java index f75d53c521..31a49a21bc 100644 --- a/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPriceFeed.java +++ b/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPriceFeed.java @@ -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 cache = new HashMap<>(); + private final PriceProvider fiatPriceProvider = new BitcoinAveragePriceProvider(); + private final PriceProvider cryptoCurrenciesPriceProvider = new PoloniexPriceProvider(); private Consumer 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> future = getPriceRequest.requestAllPrices(provider); @@ -164,7 +177,8 @@ public class MarketPriceFeed { public void onSuccess(Map marketPriceMap) { UserThread.execute(() -> { cache.putAll(marketPriceMap); - resultHandler.run(); + if (resultHandler != null) + resultHandler.run(); }); } diff --git a/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PoloniexPriceProvider.java b/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PoloniexPriceProvider.java new file mode 100644 index 0000000000..4b8d0c8a63 --- /dev/null +++ b/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PoloniexPriceProvider.java @@ -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 getAllPrices() throws IOException, HttpException { + Map marketPriceMap = new HashMap<>(); + String response = httpClient.requestWithGET("?command=returnTicker"); + LinkedTreeMap treeMap = new Gson().fromJson(response, LinkedTreeMap.class); + Map temp = new HashMap<>(); + Set 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 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{" + + '}'; + } +} diff --git a/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java b/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java index 399944df17..244925670d 100644 --- a/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java +++ b/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java @@ -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; } diff --git a/gui/src/main/java/io/bitsquare/gui/main/MainView.java b/gui/src/main/java/io/bitsquare/gui/main/MainView.java index 8fd01865e3..5f35ad3deb 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/MainView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/MainView.java @@ -247,7 +247,7 @@ public class MainView extends InitializableView { private Tuple3 getMarketPriceBox(String text) { TextField textField = new TextField(); textField.setEditable(false); - textField.setPrefWidth(140); + textField.setPrefWidth(150); textField.setMouseTransparent(true); textField.setFocusTraversable(false); textField.setStyle("-fx-alignment: center; -fx-background-color: -bs-bg-grey;"); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/OfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/OfferView.java index faaec3bd1f..8bf08c3916 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/OfferView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/OfferView.java @@ -83,11 +83,20 @@ public abstract class OfferView extends ActivatableView { UserThread.execute(InputTextField::hideErrorMessageDisplay); if (newValue != null) { if (newValue.equals(createOfferTab) && createOfferView != null) { - createOfferView.onTabSelected(); + createOfferView.onTabSelected(true); } else if (newValue.equals(takeOfferTab) && takeOfferView != null) { - takeOfferView.onTabSelected(); + takeOfferView.onTabSelected(true); } else if (newValue.equals(offerBookTab) && offerBookView != null) { - offerBookView.onTabSelected(); + offerBookView.onTabSelected(true); + } + } + if (oldValue != null) { + if (oldValue.equals(createOfferTab) && createOfferView != null) { + createOfferView.onTabSelected(false); + } else if (oldValue.equals(takeOfferTab) && takeOfferView != null) { + takeOfferView.onTabSelected(false); + } else if (oldValue.equals(offerBookTab) && offerBookView != null) { + offerBookView.onTabSelected(false); } } }); @@ -130,7 +139,8 @@ public abstract class OfferView extends ActivatableView { offerBookTab.setContent(view.getRoot()); tabPane.getTabs().add(offerBookTab); offerBookView = (OfferBookView) view; - + offerBookView.onTabSelected(true); + OfferActionHandler offerActionHandler = new OfferActionHandler() { @Override public void onCreateOffer(TradeCurrency tradeCurrency) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java index d821b60812..e3ced51e78 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java @@ -103,6 +103,7 @@ class CreateOfferDataModel extends ActivatableDataModel { private PaymentAccount paymentAccount; private WalletEventListener walletEventListener; + private boolean isTabSelected; /////////////////////////////////////////////////////////////////////////////////////////// @@ -181,6 +182,9 @@ class CreateOfferDataModel extends ActivatableDataModel { if (direction == Offer.Direction.BUY) calculateTotalToPay(); + + if (isTabSelected) + marketPriceFeed.setCurrencyCode(tradeCurrencyCode.get()); } @Override @@ -227,8 +231,10 @@ class CreateOfferDataModel extends ActivatableDataModel { marketPriceFeed.setCurrencyCode(tradeCurrencyCode.get()); } - void onTabSelected() { - marketPriceFeed.setCurrencyCode(tradeCurrencyCode.get()); + void onTabSelected(boolean isSelected) { + this.isTabSelected = isSelected; + if (isTabSelected) + marketPriceFeed.setCurrencyCode(tradeCurrencyCode.get()); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java index 96d07e9a75..dc1bfbb0ae 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java @@ -201,8 +201,8 @@ public class CreateOfferView extends ActivatableViewAndModel offerBookListItems; private final ListChangeListener listChangeListener; + private boolean isTabSelected; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, lifecycle @@ -94,7 +95,6 @@ class OfferBookViewModel extends ActivatableViewModel { tradeCurrency = CurrencyUtil.getDefaultTradeCurrency(); tradeCurrencyCode.set(tradeCurrency.getCode()); - marketPriceFeed.setCurrencyCode(tradeCurrencyCode.get()); } @Override @@ -103,6 +103,8 @@ class OfferBookViewModel extends ActivatableViewModel { offerBookListItems.addListener(listChangeListener); offerBook.fillOfferBookListItems(); filterList(); + if (isTabSelected) + marketPriceFeed.setCurrencyCode(tradeCurrencyCode.get()); } @Override @@ -119,10 +121,12 @@ class OfferBookViewModel extends ActivatableViewModel { this.direction = direction; } - void onTabSelected() { - marketPriceFeed.setCurrencyCode(tradeCurrencyCode.get()); + void onTabSelected(boolean isSelected) { + this.isTabSelected = isSelected; + if (isTabSelected) + marketPriceFeed.setCurrencyCode(tradeCurrencyCode.get()); } - + /////////////////////////////////////////////////////////////////////////////////////////// // UI actions diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java index d2115269a8..a54ab70eef 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java @@ -82,6 +82,7 @@ class TakeOfferDataModel extends ActivatableDataModel { private BalanceListener balanceListener; private PaymentAccount paymentAccount; + private boolean isTabSelected; /////////////////////////////////////////////////////////////////////////////////////////// @@ -114,6 +115,9 @@ class TakeOfferDataModel extends ActivatableDataModel { addBindings(); addListeners(); updateBalance(walletService.getBalanceForAddress(addressEntry.getAddress())); + + if (isTabSelected) + marketPriceFeed.setCurrencyCode(offer.getCurrencyCode()); } @Override @@ -162,8 +166,10 @@ class TakeOfferDataModel extends ActivatableDataModel { tradeManager.checkOfferAvailability(offer, resultHandler); } - void onTabSelected() { - marketPriceFeed.setCurrencyCode(offer.getCurrencyCode()); + void onTabSelected(boolean isSelected) { + this.isTabSelected = isSelected; + if (isTabSelected) + marketPriceFeed.setCurrencyCode(offer.getCurrencyCode()); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java index 72f30c6612..d74ac297c4 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java @@ -310,8 +310,8 @@ public class TakeOfferView extends ActivatableViewAndModel section you can withdraw those funds.").show();*/ } - public void onTabSelected() { - model.dataModel.onTabSelected(); + public void onTabSelected(boolean isSelected) { + model.dataModel.onTabSelected(isSelected); } diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java index b1a091a688..4a1771c898 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java @@ -23,6 +23,7 @@ import io.bitsquare.gui.common.view.FxmlView; import io.bitsquare.gui.components.HyperlinkWithIcon; import io.bitsquare.gui.popups.OpenEmergencyTicketPopup; import io.bitsquare.gui.popups.TradeDetailsPopup; +import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.trade.Trade; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyObjectWrapper; @@ -50,6 +51,7 @@ import javax.inject.Inject; public class PendingTradesView extends ActivatableViewAndModel { private final TradeDetailsPopup tradeDetailsPopup; + private BSFormatter formatter; @FXML TableView table; @FXML @@ -74,9 +76,10 @@ public class PendingTradesView extends ActivatableViewAndModel tradeDetailsPopup.show(item.getTrade())); field.setTooltip(new Tooltip("Open popup for details")); setGraphic(field); @@ -267,7 +270,7 @@ public class PendingTradesView extends ActivatableViewAndModel() { @Override public String toString(Coin value) { - return model.formatTradeAmount(value); + return formatter.formatCoinWithCode(value); } @Override @@ -297,7 +300,7 @@ public class PendingTradesView extends ActivatableViewAndModel() { @Override public String toString(Fiat value) { - return model.formatPrice(value); + return formatter.formatPriceWithCode(value); } @Override @@ -313,7 +316,7 @@ public class PendingTradesView extends ActivatableViewAndModel() { @Override public String toString(Fiat value) { - return model.formatTradeVolume(value); + return formatter.formatFiatWithCode(value); } @Override diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java index dcc813dc39..163992acdf 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java @@ -34,8 +34,6 @@ import io.bitsquare.trade.offer.Offer; import javafx.beans.property.*; import javafx.collections.ObservableList; import org.bitcoinj.core.BlockChainListener; -import org.bitcoinj.core.Coin; -import org.bitcoinj.utils.Fiat; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; @@ -225,21 +223,6 @@ public class PendingTradesViewModel extends ActivatableWithDataModel= trade.getCheckPaymentTimeAsBlockHeight(); }