From 517ee371db0502b2418454da1fc5f4b496b611d4 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 14 Apr 2016 19:12:43 +0200 Subject: [PATCH 1/9] add percentage price (WIP) --- .../java/io/bitsquare/trade/offer/Offer.java | 25 +++++- .../main/java/io/bitsquare/gui/bitsquare.css | 20 +++++ .../createoffer/CreateOfferDataModel.java | 10 +++ .../offer/createoffer/CreateOfferView.java | 78 ++++++++++++++++--- .../offerbook/OfferBookViewModelTest.java | 2 + 5 files changed, 119 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/io/bitsquare/trade/offer/Offer.java b/core/src/main/java/io/bitsquare/trade/offer/Offer.java index 7c4ca84cf6..31a0eed0a6 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/Offer.java +++ b/core/src/main/java/io/bitsquare/trade/offer/Offer.java @@ -106,6 +106,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload private final long date; private final long protocolVersion; private final long fiatPrice; + private final double percentagePrice; + private final boolean usePercentageBasedPrice; private final long amount; private final long minAmount; private final NodeAddress offererNodeAddress; @@ -138,6 +140,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload PubKeyRing pubKeyRing, Direction direction, long fiatPrice, + double percentagePrice, + boolean usePercentageBasedPrice, long amount, long minAmount, String currencyCode, @@ -153,6 +157,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload this.pubKeyRing = pubKeyRing; this.direction = direction; this.fiatPrice = fiatPrice; + this.percentagePrice = percentagePrice; + this.usePercentageBasedPrice = usePercentageBasedPrice; this.amount = amount; this.minAmount = minAmount; this.currencyCode = currencyCode; @@ -315,6 +321,14 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return Fiat.valueOf(currencyCode, fiatPrice); } + public double getPercentagePrice() { + return percentagePrice; + } + + public boolean isUsePercentageBasedPrice() { + return usePercentageBasedPrice; + } + public Coin getAmount() { return Coin.valueOf(amount); } @@ -391,11 +405,11 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Offer)) return false; - Offer offer = (Offer) o; - if (date != offer.date) return false; if (fiatPrice != offer.fiatPrice) return false; + if (Double.compare(offer.percentagePrice, percentagePrice) != 0) return false; + if (usePercentageBasedPrice != offer.usePercentageBasedPrice) return false; if (amount != offer.amount) return false; if (minAmount != offer.minAmount) return false; if (id != null ? !id.equals(offer.id) : offer.id != null) return false; @@ -418,7 +432,6 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload if (arbitratorNodeAddresses != null ? !arbitratorNodeAddresses.equals(offer.arbitratorNodeAddresses) : offer.arbitratorNodeAddresses != null) return false; return !(offerFeePaymentTxID != null ? !offerFeePaymentTxID.equals(offer.offerFeePaymentTxID) : offer.offerFeePaymentTxID != null); - } @Override @@ -428,6 +441,9 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload result = 31 * result + (currencyCode != null ? currencyCode.hashCode() : 0); result = 31 * result + (int) (date ^ (date >>> 32)); result = 31 * result + (int) (fiatPrice ^ (fiatPrice >>> 32)); + long temp = Double.doubleToLongBits(percentagePrice); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + (usePercentageBasedPrice ? 1 : 0); result = 31 * result + (int) (amount ^ (amount >>> 32)); result = 31 * result + (int) (minAmount ^ (minAmount >>> 32)); result = 31 * result + (offererNodeAddress != null ? offererNodeAddress.hashCode() : 0); @@ -451,6 +467,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload "\n\tcurrencyCode='" + currencyCode + '\'' + "\n\tdate=" + date + "\n\tfiatPrice=" + fiatPrice + + "\n\tpercentagePrice=" + percentagePrice + + "\n\tusePercentageBasedPrice=" + usePercentageBasedPrice + "\n\tamount=" + amount + "\n\tminAmount=" + minAmount + "\n\toffererAddress=" + offererNodeAddress + @@ -471,5 +489,4 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload "\n\tTAC_TAKER=" + TAC_TAKER + '}'; } - } diff --git a/gui/src/main/java/io/bitsquare/gui/bitsquare.css b/gui/src/main/java/io/bitsquare/gui/bitsquare.css index 44c789f286..9aca50cd64 100644 --- a/gui/src/main/java/io/bitsquare/gui/bitsquare.css +++ b/gui/src/main/java/io/bitsquare/gui/bitsquare.css @@ -496,6 +496,26 @@ textfield */ -fx-border-insets: 0 0 0 -2; } +#toggle-price-left { + -fx-border-radius: 4 0 0 4; + -fx-padding: 4 4 4 4; + -fx-border-color: #aaa; + -fx-border-style: solid none solid solid; + -fx-border-insets: 0 -2 0 0; + -fx-background-insets: 0 -2 0 0; + -fx-background-radius: 4 0 0 4; +} + +#toggle-price-right { + -fx-border-radius: 0 4 4 0; + -fx-padding: 4 4 4 4; + -fx-border-color: #aaa; + -fx-border-style: solid solid solid none; + -fx-border-insets: 0 0 0 -2; + -fx-background-insets: 0 0 0 -2; + -fx-background-radius: 0 4 4 0; +} + #totals-separator { -fx-background: #AAAAAA; } 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 20da4b83ee..ca3ac4c378 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 @@ -89,6 +89,7 @@ class CreateOfferDataModel extends ActivatableDataModel { final StringProperty btcCode = new SimpleStringProperty(); final BooleanProperty isWalletFunded = new SimpleBooleanProperty(); + final BooleanProperty usePercentageBasedPrice = new SimpleBooleanProperty(); //final BooleanProperty isMainNet = new SimpleBooleanProperty(); //final BooleanProperty isFeeFromFundingTxSufficient = new SimpleBooleanProperty(); @@ -96,6 +97,7 @@ class CreateOfferDataModel extends ActivatableDataModel { final ObjectProperty amountAsCoin = new SimpleObjectProperty<>(); final ObjectProperty minAmountAsCoin = new SimpleObjectProperty<>(); final ObjectProperty priceAsFiat = new SimpleObjectProperty<>(); + final ObjectProperty priceAsPercentage = new SimpleObjectProperty<>(); final ObjectProperty volumeAsFiat = new SimpleObjectProperty<>(); final ObjectProperty totalToPayAsCoin = new SimpleObjectProperty<>(); final ObjectProperty missingCoin = new SimpleObjectProperty<>(Coin.ZERO); @@ -253,6 +255,8 @@ class CreateOfferDataModel extends ActivatableDataModel { Offer createAndGetOffer() { long fiatPrice = priceAsFiat.get() != null ? priceAsFiat.get().getValue() : 0L; + + double percentagePrice = 0; long amount = amountAsCoin.get() != null ? amountAsCoin.get().getValue() : 0L; long minAmount = minAmountAsCoin.get() != null ? minAmountAsCoin.get().getValue() : 0L; @@ -284,6 +288,8 @@ class CreateOfferDataModel extends ActivatableDataModel { keyRing.getPubKeyRing(), direction, fiatPrice, + percentagePrice, + usePercentageBasedPrice.get(), amount, minAmount, tradeCurrencyCode.get(), @@ -378,6 +384,10 @@ class CreateOfferDataModel extends ActivatableDataModel { return user.getAcceptedArbitrators().size() > 0; } + public void setUsePercentageBasedPrice(boolean usePercentageBasedPrice) { + this.usePercentageBasedPrice.set(usePercentageBasedPrice); + } + /*boolean isFeeFromFundingTxSufficient() { return !isMainNet.get() || feeFromFundingTxProperty.get().compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0; }*/ 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 a8b32b6a06..e49fc624a6 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 @@ -88,8 +88,8 @@ public class CreateOfferView extends ActivatableViewAndModel paymentAccountsComboBox; private ComboBox currencyComboBox; private PopOver totalToPayInfoPopover; + private Label priceAsPercentageLabel; + private ToggleButton fixedPriceButton; + private ToggleButton percentagePriceButton; private OfferView.CloseHandler closeHandler; @@ -175,6 +178,9 @@ public class CreateOfferView extends ActivatableViewAndModel model.tradeCurrencyCode.get() + "/" + model.btcCode.get(), model.btcCode, model.tradeCurrencyCode)); + priceTextField.disableProperty().bind(model.dataModel.usePercentageBasedPrice); + priceAsPercentageTextField.disableProperty().bind(model.dataModel.usePercentageBasedPrice.not()); + priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty()); volumeCurrencyLabel.textProperty().bind(model.tradeCurrencyCode); minAmountBtcLabel.textProperty().bind(model.btcCode); priceDescriptionLabel.textProperty().bind(createStringBinding(() -> BSResources.get("createOffer.amountPriceBox.priceDescription", model.tradeCurrencyCode.get()), model.tradeCurrencyCode)); @@ -452,6 +461,8 @@ public class CreateOfferView extends ActivatableViewAndModel tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, BSResources.get("createOffer.amountPriceBox.next"), BSResources.get("shared.cancel")); nextButton = tuple.first; @@ -897,7 +908,7 @@ public class CreateOfferView extends ActivatableViewAndModel priceValueCurrencyBoxTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.price.prompt")); HBox priceValueCurrencyBox = priceValueCurrencyBoxTuple.first; priceTextField = priceValueCurrencyBoxTuple.second; @@ -906,6 +917,26 @@ public class CreateOfferView extends ActivatableViewAndModel { + model.dataModel.setUsePercentageBasedPrice(!newValue); + }); + + percentagePriceButton = new ToggleButton("Percentage"); + percentagePriceButton.setId("toggle-price-right"); + percentagePriceButton.setToggleGroup(toggleGroup); + percentagePriceButton.selectedProperty().addListener((ov, oldValue, newValue) -> { + model.dataModel.setUsePercentageBasedPrice(newValue); + }); + + HBox toggleButtons = new HBox(); + toggleButtons.setPadding(new Insets(18, 0, 0, 0)); + toggleButtons.getChildren().addAll(fixedPriceButton, percentagePriceButton); + // = Label resultLabel = new Label("="); resultLabel.setFont(Font.font("Helvetica-Bold", 20)); @@ -923,7 +954,7 @@ public class CreateOfferView extends ActivatableViewAndModel priceAsPercentageTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.price.prompt")); + HBox priceAsPercentageValueCurrencyBox = priceAsPercentageTuple.first; + priceAsPercentageTextField = priceAsPercentageTuple.second; + priceAsPercentageLabel = priceAsPercentageTuple.third; + + Tuple2 priceAsPercentageInputBoxTuple = getTradeInputBox(priceAsPercentageValueCurrencyBox, "Distance in % from market price"); + priceAsPercentageInputBoxTuple.first.setPrefWidth(200); + VBox priceAsPercentageInputBox = priceAsPercentageInputBoxTuple.second; + + priceAsPercentageTextField.setPromptText("Enter % value"); + priceAsPercentageLabel.setText("% dist."); + priceAsPercentageLabel.setStyle("-fx-alignment: center;"); + Tuple3 amountValueCurrencyBoxTuple = getValueCurrencyBox(BSResources.get("createOffer.amount.prompt")); HBox amountValueCurrencyBox = amountValueCurrencyBoxTuple.first; minAmountTextField = amountValueCurrencyBoxTuple.second; @@ -939,11 +983,21 @@ public class CreateOfferView extends ActivatableViewAndModel amountInputBoxTuple = getTradeInputBox(amountValueCurrencyBox, BSResources.get("createOffer.amountPriceBox" + ".minAmountDescription")); - VBox box = amountInputBoxTuple.second; - GridPane.setRowIndex(box, ++gridRow); - GridPane.setColumnIndex(box, 1); - GridPane.setMargin(box, new Insets(5, 10, 5, 0)); - gridPane.getChildren().add(box); + + Label xLabel = new Label("x"); + xLabel.setFont(Font.font("Helvetica-Bold", 20)); + xLabel.setPadding(new Insets(14, 3, 0, 3)); + xLabel.setVisible(false); // we just use it to get the same layout as the upper row + + HBox hBox = new HBox(); + hBox.setSpacing(5); + hBox.setAlignment(Pos.CENTER_LEFT); + hBox.getChildren().addAll(amountInputBoxTuple.second, xLabel, priceAsPercentageInputBox); + GridPane.setRowIndex(hBox, ++gridRow); + GridPane.setColumnIndex(hBox, 1); + GridPane.setMargin(hBox, new Insets(5, 10, 5, 0)); + GridPane.setColumnSpan(hBox, 2); + gridPane.getChildren().add(hBox); } diff --git a/gui/src/test/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModelTest.java b/gui/src/test/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModelTest.java index c609f861f2..40c259c712 100644 --- a/gui/src/test/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModelTest.java +++ b/gui/src/test/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModelTest.java @@ -267,6 +267,8 @@ public class OfferBookViewModelTest { null, 0, 0, + false, + 0, 0, tradeCurrencyCode, null, From d5118b048b7d6d729ae2f9dc03678663c052d26f Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 14 Apr 2016 21:59:14 +0200 Subject: [PATCH 2/9] perc. price UI (WIP) --- .../java/io/bitsquare/trade/offer/Offer.java | 16 ++-- .../io/bitsquare/gui/main/MainViewModel.java | 2 +- .../createoffer/CreateOfferDataModel.java | 16 +++- .../offer/createoffer/CreateOfferView.java | 20 +++- .../createoffer/CreateOfferViewModel.java | 92 ++++++++++++++++--- .../settings/preferences/PreferencesView.java | 15 ++- .../io/bitsquare/gui/util/BSFormatter.java | 53 ++++++++++- 7 files changed, 176 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/io/bitsquare/trade/offer/Offer.java b/core/src/main/java/io/bitsquare/trade/offer/Offer.java index 31a0eed0a6..6fc326b289 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/Offer.java +++ b/core/src/main/java/io/bitsquare/trade/offer/Offer.java @@ -106,7 +106,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload private final long date; private final long protocolVersion; private final long fiatPrice; - private final double percentagePrice; + private final double percentageBasedPrice; private final boolean usePercentageBasedPrice; private final long amount; private final long minAmount; @@ -140,7 +140,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload PubKeyRing pubKeyRing, Direction direction, long fiatPrice, - double percentagePrice, + double percentageBasedPrice, boolean usePercentageBasedPrice, long amount, long minAmount, @@ -157,7 +157,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload this.pubKeyRing = pubKeyRing; this.direction = direction; this.fiatPrice = fiatPrice; - this.percentagePrice = percentagePrice; + this.percentageBasedPrice = percentageBasedPrice; this.usePercentageBasedPrice = usePercentageBasedPrice; this.amount = amount; this.minAmount = minAmount; @@ -321,8 +321,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return Fiat.valueOf(currencyCode, fiatPrice); } - public double getPercentagePrice() { - return percentagePrice; + public double getPercentageBasedPrice() { + return percentageBasedPrice; } public boolean isUsePercentageBasedPrice() { @@ -408,7 +408,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload Offer offer = (Offer) o; if (date != offer.date) return false; if (fiatPrice != offer.fiatPrice) return false; - if (Double.compare(offer.percentagePrice, percentagePrice) != 0) return false; + if (Double.compare(offer.percentageBasedPrice, percentageBasedPrice) != 0) return false; if (usePercentageBasedPrice != offer.usePercentageBasedPrice) return false; if (amount != offer.amount) return false; if (minAmount != offer.minAmount) return false; @@ -441,7 +441,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload result = 31 * result + (currencyCode != null ? currencyCode.hashCode() : 0); result = 31 * result + (int) (date ^ (date >>> 32)); result = 31 * result + (int) (fiatPrice ^ (fiatPrice >>> 32)); - long temp = Double.doubleToLongBits(percentagePrice); + long temp = Double.doubleToLongBits(percentageBasedPrice); result = 31 * result + (int) (temp ^ (temp >>> 32)); result = 31 * result + (usePercentageBasedPrice ? 1 : 0); result = 31 * result + (int) (amount ^ (amount >>> 32)); @@ -467,7 +467,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload "\n\tcurrencyCode='" + currencyCode + '\'' + "\n\tdate=" + date + "\n\tfiatPrice=" + fiatPrice + - "\n\tpercentagePrice=" + percentagePrice + + "\n\tpercentagePrice=" + percentageBasedPrice + "\n\tusePercentageBasedPrice=" + usePercentageBasedPrice + "\n\tamount=" + amount + "\n\tminAmount=" + minAmount + diff --git a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java index b8ef9a54d7..efb4140b77 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java @@ -404,7 +404,7 @@ public class MainViewModel implements ViewModel { result = numPeersString + " / synchronized with " + btcNetworkAsString; btcSplashSyncIconId.set("image-connection-synced"); } else if (percentage > 0.0) { - result = numPeersString + " / synchronizing with " + btcNetworkAsString + ": " + formatter.formatToPercent(percentage); + result = numPeersString + " / synchronizing with " + btcNetworkAsString + ": " + formatter.formatToPercentWithSymbol(percentage); } else { result = numPeersString + " / connecting to " + btcNetworkAsString; } 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 ca3ac4c378..b7b464fc14 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 @@ -97,7 +97,6 @@ class CreateOfferDataModel extends ActivatableDataModel { final ObjectProperty amountAsCoin = new SimpleObjectProperty<>(); final ObjectProperty minAmountAsCoin = new SimpleObjectProperty<>(); final ObjectProperty priceAsFiat = new SimpleObjectProperty<>(); - final ObjectProperty priceAsPercentage = new SimpleObjectProperty<>(); final ObjectProperty volumeAsFiat = new SimpleObjectProperty<>(); final ObjectProperty totalToPayAsCoin = new SimpleObjectProperty<>(); final ObjectProperty missingCoin = new SimpleObjectProperty<>(Coin.ZERO); @@ -110,6 +109,7 @@ class CreateOfferDataModel extends ActivatableDataModel { private Notification walletFundedNotification; boolean useSavingsWallet; Coin totalAvailableBalance; + private double percentageBasedPrice = 0; /////////////////////////////////////////////////////////////////////////////////////////// @@ -256,7 +256,6 @@ class CreateOfferDataModel extends ActivatableDataModel { Offer createAndGetOffer() { long fiatPrice = priceAsFiat.get() != null ? priceAsFiat.get().getValue() : 0L; - double percentagePrice = 0; long amount = amountAsCoin.get() != null ? amountAsCoin.get().getValue() : 0L; long minAmount = minAmountAsCoin.get() != null ? minAmountAsCoin.get().getValue() : 0L; @@ -283,12 +282,15 @@ class CreateOfferDataModel extends ActivatableDataModel { String countryCode = paymentAccount instanceof CountryBasedPaymentAccount ? ((CountryBasedPaymentAccount) paymentAccount).getCountry().code : null; checkNotNull(p2PService.getAddress(), "Address must not be null"); + log.error("fiatPrice " + fiatPrice); + log.error("percentageBasedPrice " + percentageBasedPrice); + log.error("usePercentageBasedPrice " + usePercentageBasedPrice.get()); return new Offer(offerId, p2PService.getAddress(), keyRing.getPubKeyRing(), direction, fiatPrice, - percentagePrice, + percentageBasedPrice, usePercentageBasedPrice.get(), amount, minAmount, @@ -494,4 +496,12 @@ class CreateOfferDataModel extends ActivatableDataModel { walletService.swapTradeEntryToAvailableEntry(offerId, AddressEntry.Context.OFFER_FUNDING); walletService.swapTradeEntryToAvailableEntry(offerId, AddressEntry.Context.RESERVED_FOR_TRADE); } + + double getPercentageBasedPrice() { + return percentageBasedPrice; + } + + void setPercentageBasedPrice(double percentageBasedPrice) { + this.percentageBasedPrice = percentageBasedPrice; + } } 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 e49fc624a6..d43e236f51 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 @@ -106,7 +106,7 @@ public class CreateOfferView extends ActivatableViewAndModel amountFocusedListener; private ChangeListener minAmountFocusedListener; - private ChangeListener priceFocusedListener; + private ChangeListener priceFocusedListener, priceAsPercentageFocusedListener; private ChangeListener volumeFocusedListener; private ChangeListener showWarningInvalidBtcDecimalPlacesListener; private ChangeListener showWarningInvalidFiatDecimalPlacesPlacesListener; @@ -286,6 +286,9 @@ public class CreateOfferView extends ActivatableViewAndModel model.tradeCurrencyCode.get() + "/" + model.btcCode.get(), model.btcCode, model.tradeCurrencyCode)); priceTextField.disableProperty().bind(model.dataModel.usePercentageBasedPrice); + priceCurrencyLabel.disableProperty().bind(model.dataModel.usePercentageBasedPrice); priceAsPercentageTextField.disableProperty().bind(model.dataModel.usePercentageBasedPrice.not()); + priceAsPercentageLabel.disableProperty().bind(model.dataModel.usePercentageBasedPrice.not()); priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty()); volumeCurrencyLabel.textProperty().bind(model.tradeCurrencyCode); minAmountBtcLabel.textProperty().bind(model.btcCode); @@ -423,6 +428,7 @@ public class CreateOfferView extends ActivatableViewAndModel { + model.onFocusOutPriceAsPercentageTextField(oldValue, newValue, priceAsPercentageTextField.getText()); + priceAsPercentageTextField.setText(model.priceAsPercentage.get()); + }; volumeFocusedListener = (o, oldValue, newValue) -> { model.onFocusOutVolumeTextField(oldValue, newValue, volumeTextField.getText()); volumeTextField.setText(model.volume.get()); @@ -595,6 +608,7 @@ public class CreateOfferView extends ActivatableViewAndModel { priceTextField.clear(); + priceAsPercentageTextField.clear(); volumeTextField.clear(); }; @@ -633,6 +647,7 @@ public class CreateOfferView extends ActivatableViewAndModel amountValueCurrencyBoxTuple = getValueCurrencyBox(BSResources.get("createOffer.amount.prompt")); HBox amountValueCurrencyBox = amountValueCurrencyBoxTuple.first; minAmountTextField = amountValueCurrencyBoxTuple.second; diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java index 874befc5c1..01376879da 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java @@ -71,6 +71,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel amountListener; private ChangeListener minAmountListener; - private ChangeListener priceListener; + private ChangeListener priceListener, priceAsPercentageListener; private ChangeListener volumeListener; private ChangeListener amountAsCoinListener; private ChangeListener minAmountAsCoinListener; @@ -114,6 +115,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel errorMessageListener; private Offer offer; private Timer timeoutTimer; + private PriceFeed.Type priceFeedType; /////////////////////////////////////////////////////////////////////////////////////////// @@ -235,9 +237,61 @@ class CreateOfferViewModel extends ActivatableWithDataModel { + try { + if (!newValue.isEmpty() && !newValue.equals("-")) { + double percentageBasedPrice = formatter.parsePercentStringToDouble(newValue); + if (percentageBasedPrice >= 1 || percentageBasedPrice <= -1) { + dataModel.setPercentageBasedPrice(0); + UserThread.execute(() -> priceAsPercentage.set("0")); + new Popup().warning("You cannot set a percentage of 100% or larger. Please enter a percentage number like \"5.4\" for 5.4%") + .show(); + } else { + MarketPrice marketPrice = priceFeed.getMarketPrice(dataModel.tradeCurrencyCode.get()); + if (marketPrice != null) { + percentageBasedPrice = formatter.roundDouble(percentageBasedPrice, 4); + dataModel.setPercentageBasedPrice(percentageBasedPrice); + double marketPriceAsDouble = marketPrice.getPrice(priceFeedType); + double factor = dataModel.getDirection() == Offer.Direction.BUY ? 1 - percentageBasedPrice : 1 + percentageBasedPrice; + double targetPrice = marketPriceAsDouble * factor; + price.set(formatter.formatToNumberString(targetPrice, 2)); + setPriceToModel(); + calculateVolume(); + dataModel.calculateTotalToPay(); + updateButtonDisableState(); + } else { + new Popup().warning("There is no price feed available for that currency. You cannot use percent based price.") + .show(); + } + } + } else { + dataModel.setPercentageBasedPrice(0); + } + } catch (Throwable t) { + dataModel.setPercentageBasedPrice(0); + UserThread.execute(() -> priceAsPercentage.set("0")); + new Popup().warning("Your input is not a valid number. Please enter a percentage number like \"5.4\" for 5.4%") + .show(); + } + }; volumeListener = (ov, oldValue, newValue) -> { if (isFiatInputValid(newValue).isValid) { setVolumeToModel(); @@ -266,6 +320,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel preferences.getMaxPriceDistanceInPercent()) { - Popup popup = new Popup(); - popup.warning("The price you have entered is outside the max. allowed deviation from the market price.\n" + - "The max. allowed deviation is " + - formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent()) + - " and can be adjusted in the preferences.") - .actionButtonText("Change price") - .onAction(() -> popup.hide()) - .closeButtonText("Go to \"Preferences\"") - .onClose(() -> navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class)) - .show(); + displayPriceOutofRangePopup(); return false; } else { return true; @@ -516,6 +571,19 @@ class CreateOfferViewModel extends ActivatableWithDataModel popup.hide()) + .closeButtonText("Go to \"Preferences\"") + .onClose(() -> navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class)) + .show(); + } + BSFormatter getFormatter() { return formatter; } diff --git a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java b/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java index 39f48471d7..7501d845aa 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java @@ -296,19 +296,16 @@ public class PreferencesView extends ActivatableViewAndModel { try { - String input = newValue.replace("%", ""); - input = input.replace(",", "."); - input = input.replace(" ", ""); - double value = Double.parseDouble(input); - preferences.setMaxPriceDistanceInPercent(value / 100); - } catch (Throwable t) { + double value = formatter.parsePercentStringToDouble(newValue); + preferences.setMaxPriceDistanceInPercent(value); + } catch (NumberFormatException t) { log.error("Exception at parseDouble deviation: " + t.toString()); - UserThread.runAfter(() -> deviationInputTextField.setText(formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent())), 100, TimeUnit.MILLISECONDS); + UserThread.runAfter(() -> deviationInputTextField.setText(formatter.formatToPercentWithSymbol(preferences.getMaxPriceDistanceInPercent())), 100, TimeUnit.MILLISECONDS); } }; deviationFocusedListener = (observable1, oldValue1, newValue1) -> { if (oldValue1 && !newValue1) - UserThread.runAfter(() -> deviationInputTextField.setText(formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent())), 100, TimeUnit.MILLISECONDS); + UserThread.runAfter(() -> deviationInputTextField.setText(formatter.formatToPercentWithSymbol(preferences.getMaxPriceDistanceInPercent())), 100, TimeUnit.MILLISECONDS); }; transactionFeeInputTextField = addLabelInputTextField(root, ++gridRow, "Withdrawal transaction fee (satoshi/byte):").second; @@ -427,7 +424,7 @@ public class PreferencesView extends ActivatableViewAndModel preferences.setBlockChainExplorer(blockChainExplorerComboBox.getSelectionModel().getSelectedItem())); - deviationInputTextField.setText(formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent())); + deviationInputTextField.setText(formatter.formatToPercentWithSymbol(preferences.getMaxPriceDistanceInPercent())); deviationInputTextField.textProperty().addListener(deviationListener); deviationInputTextField.focusedProperty().addListener(deviationFocusedListener); diff --git a/gui/src/main/java/io/bitsquare/gui/util/BSFormatter.java b/gui/src/main/java/io/bitsquare/gui/util/BSFormatter.java index 411e199b73..f0530c0ff3 100644 --- a/gui/src/main/java/io/bitsquare/gui/util/BSFormatter.java +++ b/gui/src/main/java/io/bitsquare/gui/util/BSFormatter.java @@ -325,11 +325,58 @@ public class BSFormatter { } public String formatToPercent(double value) { + return formatToPercent(value, 1); + } + + public String formatToPercent(double value, int digits) { DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(locale); - decimalFormat.setMinimumFractionDigits(1); - decimalFormat.setMaximumFractionDigits(1); + decimalFormat.setMinimumFractionDigits(digits); + decimalFormat.setMaximumFractionDigits(digits); decimalFormat.setGroupingUsed(false); - return decimalFormat.format(value * 100.0) + " %"; + return decimalFormat.format(value * 100.0); + } + + public String formatToNumberString(double value, int digits) { + DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(locale); + decimalFormat.setMinimumFractionDigits(digits); + decimalFormat.setMaximumFractionDigits(digits); + decimalFormat.setGroupingUsed(false); + return decimalFormat.format(value); + } + + public double parseNumberStringToDouble(String percentString) throws NumberFormatException { + try { + String input = percentString.replace(",", "."); + input = input.replace(" ", ""); + return Double.parseDouble(input); + } catch (NumberFormatException e) { + throw e; + } + } + + public String formatToPercentWithSymbol(double value) { + return formatToPercent(value) + " %"; + } + + public double parsePercentStringToDouble(String percentString) throws NumberFormatException { + try { + String input = percentString.replace("%", ""); + input = input.replace(",", "."); + input = input.replace(" ", ""); + double value = Double.parseDouble(input); + return value / 100; + } catch (NumberFormatException e) { + throw e; + } + } + + public double roundDouble(double value, int places) { + if (places < 0) throw new IllegalArgumentException(); + + long factor = (long) Math.pow(10, places); + value = value * factor; + long tmp = Math.round(value); + return (double) tmp / factor; } private String cleanInput(String input) { From c43575073d502b01cf8e025b5c18e7ca6cd8f026 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 14 Apr 2016 22:28:47 +0200 Subject: [PATCH 3/9] display trade amount in table instead of payout amount. refactor method in trade --- .../java/io/bitsquare/trade/BuyerAsOffererTrade.java | 12 ------------ .../java/io/bitsquare/trade/BuyerAsTakerTrade.java | 9 --------- .../src/main/java/io/bitsquare/trade/BuyerTrade.java | 8 ++++++++ .../main/java/io/bitsquare/trade/SellerTrade.java | 6 ++++++ core/src/main/java/io/bitsquare/trade/Trade.java | 5 +---- .../portfolio/pendingtrades/PendingTradesView.java | 2 +- 6 files changed, 16 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/io/bitsquare/trade/BuyerAsOffererTrade.java b/core/src/main/java/io/bitsquare/trade/BuyerAsOffererTrade.java index cf2cda9812..8cb852b864 100644 --- a/core/src/main/java/io/bitsquare/trade/BuyerAsOffererTrade.java +++ b/core/src/main/java/io/bitsquare/trade/BuyerAsOffererTrade.java @@ -18,22 +18,18 @@ package io.bitsquare.trade; import io.bitsquare.app.Version; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.p2p.NodeAddress; import io.bitsquare.storage.Storage; import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.protocol.trade.BuyerAsOffererProtocol; import io.bitsquare.trade.protocol.trade.OffererProtocol; import io.bitsquare.trade.protocol.trade.messages.TradeMessage; -import org.bitcoinj.core.Coin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.ObjectInputStream; -import static com.google.common.base.Preconditions.checkNotNull; - public final class BuyerAsOffererTrade extends BuyerTrade implements OffererTrade { // That object is saved to disc. We need to take care of changes to not break deserialization. private static final long serialVersionUID = Version.LOCAL_DB_VERSION; @@ -73,12 +69,4 @@ public final class BuyerAsOffererTrade extends BuyerTrade implements OffererTrad public void handleTakeOfferRequest(TradeMessage message, NodeAddress taker) { ((OffererProtocol) tradeProtocol).handleTakeOfferRequest(message, taker); } - - @Override - public Coin getPayoutAmount() { - checkNotNull(getTradeAmount(), "Invalid state: getTradeAmount() = null"); - - return FeePolicy.getSecurityDeposit().add(getTradeAmount()); - } - } diff --git a/core/src/main/java/io/bitsquare/trade/BuyerAsTakerTrade.java b/core/src/main/java/io/bitsquare/trade/BuyerAsTakerTrade.java index 938d9812ea..4a1bee811f 100644 --- a/core/src/main/java/io/bitsquare/trade/BuyerAsTakerTrade.java +++ b/core/src/main/java/io/bitsquare/trade/BuyerAsTakerTrade.java @@ -18,7 +18,6 @@ package io.bitsquare.trade; import io.bitsquare.app.Version; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.p2p.NodeAddress; import io.bitsquare.storage.Storage; import io.bitsquare.trade.offer.Offer; @@ -32,7 +31,6 @@ import java.io.IOException; import java.io.ObjectInputStream; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade { // That object is saved to disc. We need to take care of changes to not break deserialization. @@ -74,11 +72,4 @@ public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade { checkArgument(tradeProtocol instanceof TakerProtocol, "tradeProtocol NOT instanceof TakerProtocol"); ((TakerProtocol) tradeProtocol).takeAvailableOffer(); } - - @Override - public Coin getPayoutAmount() { - checkNotNull(getTradeAmount(), "Invalid state: getTradeAmount() = null"); - - return FeePolicy.getSecurityDeposit().add(getTradeAmount()); - } } diff --git a/core/src/main/java/io/bitsquare/trade/BuyerTrade.java b/core/src/main/java/io/bitsquare/trade/BuyerTrade.java index 3732863963..59fd993420 100644 --- a/core/src/main/java/io/bitsquare/trade/BuyerTrade.java +++ b/core/src/main/java/io/bitsquare/trade/BuyerTrade.java @@ -18,6 +18,7 @@ package io.bitsquare.trade; import io.bitsquare.app.Version; +import io.bitsquare.btc.FeePolicy; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.p2p.NodeAddress; @@ -29,6 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; public abstract class BuyerTrade extends Trade { // That object is saved to disc. We need to take care of changes to not break deserialization. @@ -63,7 +65,13 @@ public abstract class BuyerTrade extends Trade { log::warn); } } + + @Override + public Coin getPayoutAmount() { + checkNotNull(getTradeAmount(), "Invalid state: getTradeAmount() = null"); + return FeePolicy.getSecurityDeposit().add(getTradeAmount()); + } /////////////////////////////////////////////////////////////////////////////////////////// // Setter for Mutable objects diff --git a/core/src/main/java/io/bitsquare/trade/SellerTrade.java b/core/src/main/java/io/bitsquare/trade/SellerTrade.java index 9c60dc19c5..e49b269912 100644 --- a/core/src/main/java/io/bitsquare/trade/SellerTrade.java +++ b/core/src/main/java/io/bitsquare/trade/SellerTrade.java @@ -18,6 +18,7 @@ package io.bitsquare.trade; import io.bitsquare.app.Version; +import io.bitsquare.btc.FeePolicy; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.p2p.NodeAddress; @@ -64,6 +65,11 @@ public abstract class SellerTrade extends Trade { } } + @Override + public Coin getPayoutAmount() { + return FeePolicy.getSecurityDeposit(); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Setter for Mutable objects /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/io/bitsquare/trade/Trade.java b/core/src/main/java/io/bitsquare/trade/Trade.java index e49a79122d..b34f0ee666 100644 --- a/core/src/main/java/io/bitsquare/trade/Trade.java +++ b/core/src/main/java/io/bitsquare/trade/Trade.java @@ -24,7 +24,6 @@ import com.google.common.util.concurrent.ListenableFuture; import io.bitsquare.app.Log; import io.bitsquare.app.Version; import io.bitsquare.arbitration.ArbitratorManager; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; import io.bitsquare.common.crypto.KeyRing; @@ -374,9 +373,7 @@ public abstract class Trade implements Tradable, Model { return offer; } - public Coin getPayoutAmount() { - return FeePolicy.getSecurityDeposit(); - } + abstract public Coin getPayoutAmount(); public ProcessModel getProcessModel() { return processModel; 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 c2ec3176ca..bc7a5b6ba1 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 @@ -336,7 +336,7 @@ public class PendingTradesView extends ActivatableViewAndModel Date: Thu, 14 Apr 2016 22:57:21 +0200 Subject: [PATCH 4/9] remove min amount warning -> change amounts it not valid. only update price if perc. price is input source --- .../createoffer/CreateOfferViewModel.java | 75 ++++++++++--------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java index 01376879da..14d1a009d6 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java @@ -116,6 +116,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel { - try { - if (!newValue.isEmpty() && !newValue.equals("-")) { - double percentageBasedPrice = formatter.parsePercentStringToDouble(newValue); - if (percentageBasedPrice >= 1 || percentageBasedPrice <= -1) { - dataModel.setPercentageBasedPrice(0); - UserThread.execute(() -> priceAsPercentage.set("0")); - new Popup().warning("You cannot set a percentage of 100% or larger. Please enter a percentage number like \"5.4\" for 5.4%") - .show(); - } else { - MarketPrice marketPrice = priceFeed.getMarketPrice(dataModel.tradeCurrencyCode.get()); - if (marketPrice != null) { - percentageBasedPrice = formatter.roundDouble(percentageBasedPrice, 4); - dataModel.setPercentageBasedPrice(percentageBasedPrice); - double marketPriceAsDouble = marketPrice.getPrice(priceFeedType); - double factor = dataModel.getDirection() == Offer.Direction.BUY ? 1 - percentageBasedPrice : 1 + percentageBasedPrice; - double targetPrice = marketPriceAsDouble * factor; - price.set(formatter.formatToNumberString(targetPrice, 2)); - setPriceToModel(); - calculateVolume(); - dataModel.calculateTotalToPay(); - updateButtonDisableState(); - } else { - new Popup().warning("There is no price feed available for that currency. You cannot use percent based price.") + if (priceAsPercentageIsInput) { + try { + if (!newValue.isEmpty() && !newValue.equals("-")) { + double percentageBasedPrice = formatter.parsePercentStringToDouble(newValue); + if (percentageBasedPrice >= 1 || percentageBasedPrice <= -1) { + dataModel.setPercentageBasedPrice(0); + UserThread.execute(() -> priceAsPercentage.set("0")); + new Popup().warning("You cannot set a percentage of 100% or larger. Please enter a percentage number like \"5.4\" for 5.4%") .show(); + } else { + MarketPrice marketPrice = priceFeed.getMarketPrice(dataModel.tradeCurrencyCode.get()); + if (marketPrice != null) { + percentageBasedPrice = formatter.roundDouble(percentageBasedPrice, 4); + dataModel.setPercentageBasedPrice(percentageBasedPrice); + double marketPriceAsDouble = marketPrice.getPrice(priceFeedType); + double factor = dataModel.getDirection() == Offer.Direction.BUY ? 1 - percentageBasedPrice : 1 + percentageBasedPrice; + double targetPrice = marketPriceAsDouble * factor; + price.set(formatter.formatToNumberString(targetPrice, 2)); + setPriceToModel(); + calculateVolume(); + dataModel.calculateTotalToPay(); + updateButtonDisableState(); + } else { + new Popup().warning("There is no price feed available for that currency. You cannot use percent based price.") + .show(); + } } + } else { + dataModel.setPercentageBasedPrice(0); } - } else { + } catch (Throwable t) { dataModel.setPercentageBasedPrice(0); + UserThread.execute(() -> priceAsPercentage.set("0")); + new Popup().warning("Your input is not a valid number. Please enter a percentage number like \"5.4\" for 5.4%") + .show(); } - } catch (Throwable t) { - dataModel.setPercentageBasedPrice(0); - UserThread.execute(() -> priceAsPercentage.set("0")); - new Popup().warning("Your input is not a valid number. Please enter a percentage number like \"5.4\" for 5.4%") - .show(); } }; volumeListener = (ov, oldValue, newValue) -> { @@ -475,8 +478,9 @@ class CreateOfferViewModel extends ActivatableWithDataModel Date: Fri, 15 Apr 2016 00:35:31 +0200 Subject: [PATCH 5/9] Add pricefeed to offer, display offerprice with perc. (WIP) --- .../java/io/bitsquare/trade/TradeManager.java | 7 ++- .../trade/closed/ClosedTradableManager.java | 4 +- .../trade/failed/FailedTradesManager.java | 4 +- .../java/io/bitsquare/trade/offer/Offer.java | 52 +++++++++++++++---- .../trade/offer/OfferBookService.java | 18 +++++-- .../trade/offer/OpenOfferManager.java | 5 +- .../createoffer/CreateOfferDataModel.java | 3 +- .../createoffer/CreateOfferViewModel.java | 39 +++++++++----- .../offer/offerbook/OfferBookViewModel.java | 13 ++++- .../offer/takeoffer/TakeOfferViewModel.java | 5 +- .../offerbook/OfferBookViewModelTest.java | 3 +- 11 files changed, 115 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/io/bitsquare/trade/TradeManager.java b/core/src/main/java/io/bitsquare/trade/TradeManager.java index 53b6aec5f9..f2f321b843 100644 --- a/core/src/main/java/io/bitsquare/trade/TradeManager.java +++ b/core/src/main/java/io/bitsquare/trade/TradeManager.java @@ -24,6 +24,7 @@ import io.bitsquare.btc.AddressEntry; import io.bitsquare.btc.AddressEntryException; import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; +import io.bitsquare.btc.pricefeed.PriceFeed; import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.ResultHandler; @@ -97,6 +98,7 @@ public class TradeManager { FailedTradesManager failedTradesManager, ArbitratorManager arbitratorManager, P2PService p2PService, + PriceFeed priceFeed, @Named("storage.dir") File storageDir) { this.user = user; this.keyRing = keyRing; @@ -109,8 +111,9 @@ public class TradeManager { this.p2PService = p2PService; tradableListStorage = new Storage<>(storageDir); - this.trades = new TradableList<>(tradableListStorage, "PendingTrades"); - + trades = new TradableList<>(tradableListStorage, "PendingTrades"); + trades.forEach(e -> e.getOffer().setPriceFeed(priceFeed)); + p2PService.addDecryptedDirectMessageListener(new DecryptedDirectMessageListener() { @Override public void onDirectMessage(DecryptedMsgWithPubKey decryptedMsgWithPubKey, NodeAddress peerNodeAddress) { diff --git a/core/src/main/java/io/bitsquare/trade/closed/ClosedTradableManager.java b/core/src/main/java/io/bitsquare/trade/closed/ClosedTradableManager.java index c738c798b5..cd5dbb6eb7 100644 --- a/core/src/main/java/io/bitsquare/trade/closed/ClosedTradableManager.java +++ b/core/src/main/java/io/bitsquare/trade/closed/ClosedTradableManager.java @@ -18,6 +18,7 @@ package io.bitsquare.trade.closed; import com.google.inject.Inject; +import io.bitsquare.btc.pricefeed.PriceFeed; import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.storage.Storage; import io.bitsquare.trade.Tradable; @@ -37,9 +38,10 @@ public class ClosedTradableManager { private final KeyRing keyRing; @Inject - public ClosedTradableManager(KeyRing keyRing, @Named("storage.dir") File storageDir) { + public ClosedTradableManager(KeyRing keyRing, PriceFeed priceFeed, @Named("storage.dir") File storageDir) { this.keyRing = keyRing; this.closedTrades = new TradableList<>(new Storage<>(storageDir), "ClosedTrades"); + closedTrades.forEach(e -> e.getOffer().setPriceFeed(priceFeed)); } public void add(Tradable tradable) { diff --git a/core/src/main/java/io/bitsquare/trade/failed/FailedTradesManager.java b/core/src/main/java/io/bitsquare/trade/failed/FailedTradesManager.java index 200eb98b0b..e0b3d931ff 100644 --- a/core/src/main/java/io/bitsquare/trade/failed/FailedTradesManager.java +++ b/core/src/main/java/io/bitsquare/trade/failed/FailedTradesManager.java @@ -18,6 +18,7 @@ package io.bitsquare.trade.failed; import com.google.inject.Inject; +import io.bitsquare.btc.pricefeed.PriceFeed; import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.storage.Storage; import io.bitsquare.trade.TradableList; @@ -37,9 +38,10 @@ public class FailedTradesManager { private final KeyRing keyRing; @Inject - public FailedTradesManager(KeyRing keyRing, @Named("storage.dir") File storageDir) { + public FailedTradesManager(KeyRing keyRing, PriceFeed priceFeed, @Named("storage.dir") File storageDir) { this.keyRing = keyRing; this.failedTrades = new TradableList<>(new Storage<>(storageDir), "FailedTrades"); + failedTrades.forEach(e -> e.getOffer().setPriceFeed(priceFeed)); } public void add(Trade trade) { diff --git a/core/src/main/java/io/bitsquare/trade/offer/Offer.java b/core/src/main/java/io/bitsquare/trade/offer/Offer.java index 6fc326b289..ae5caeeff7 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/Offer.java +++ b/core/src/main/java/io/bitsquare/trade/offer/Offer.java @@ -19,6 +19,8 @@ package io.bitsquare.trade.offer; import io.bitsquare.app.Version; import io.bitsquare.btc.Restrictions; +import io.bitsquare.btc.pricefeed.MarketPrice; +import io.bitsquare.btc.pricefeed.PriceFeed; import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.PubKeyRing; import io.bitsquare.common.handlers.ResultHandler; @@ -106,7 +108,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload private final long date; private final long protocolVersion; private final long fiatPrice; - private final double percentageBasedPrice; + private final double marketPriceMargin; private final boolean usePercentageBasedPrice; private final long amount; private final long minAmount; @@ -129,6 +131,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload transient private OfferAvailabilityProtocol availabilityProtocol; @JsonExclude transient private StringProperty errorMessageProperty = new SimpleStringProperty(); + transient private PriceFeed priceFeed; /////////////////////////////////////////////////////////////////////////////////////////// @@ -140,7 +143,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload PubKeyRing pubKeyRing, Direction direction, long fiatPrice, - double percentageBasedPrice, + double marketPriceMargin, boolean usePercentageBasedPrice, long amount, long minAmount, @@ -151,13 +154,14 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload @Nullable String countryCode, @Nullable ArrayList acceptedCountryCodes, @Nullable String bankId, - @Nullable ArrayList acceptedBankIds) { + @Nullable ArrayList acceptedBankIds, + PriceFeed priceFeed) { this.id = id; this.offererNodeAddress = offererNodeAddress; this.pubKeyRing = pubKeyRing; this.direction = direction; this.fiatPrice = fiatPrice; - this.percentageBasedPrice = percentageBasedPrice; + this.marketPriceMargin = marketPriceMargin; this.usePercentageBasedPrice = usePercentageBasedPrice; this.amount = amount; this.minAmount = minAmount; @@ -169,6 +173,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload this.acceptedCountryCodes = acceptedCountryCodes; this.bankId = bankId; this.acceptedBankIds = acceptedBankIds; + this.priceFeed = priceFeed; protocolVersion = Version.TRADE_PROTOCOL_VERSION; @@ -269,6 +274,10 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload // Setters /////////////////////////////////////////////////////////////////////////////////////////// + public void setPriceFeed(PriceFeed priceFeed) { + this.priceFeed = priceFeed; + } + public void setState(State state) { this.state = state; stateProperty().set(state); @@ -318,11 +327,34 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload } public Fiat getPrice() { - return Fiat.valueOf(currencyCode, fiatPrice); + Fiat priceAsFiat = Fiat.valueOf(currencyCode, fiatPrice); + if (usePercentageBasedPrice && priceFeed != null) { + MarketPrice marketPrice = priceFeed.getMarketPrice(currencyCode); + if (marketPrice != null) { + PriceFeed.Type priceFeedType = direction == Direction.SELL ? PriceFeed.Type.ASK : PriceFeed.Type.BID; + double marketPriceAsDouble = marketPrice.getPrice(priceFeedType); + double factor = direction == Offer.Direction.BUY ? 1 - marketPriceMargin : 1 + marketPriceMargin; + double targetPrice = marketPriceAsDouble * factor; + try { + return Fiat.parseFiat(currencyCode, String.valueOf(targetPrice)); + } catch (Exception e) { + log.warn("Exception at parseToFiat: " + e.toString()); + log.warn("We use the static price."); + return priceAsFiat; + } + } else { + log.warn("We don't have a market price. We use the static price instead."); + return priceAsFiat; + } + } else { + if (priceFeed == null) + log.warn("priceFeed must not be null"); + return priceAsFiat; + } } - public double getPercentageBasedPrice() { - return percentageBasedPrice; + public double getMarketPriceMargin() { + return marketPriceMargin; } public boolean isUsePercentageBasedPrice() { @@ -408,7 +440,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload Offer offer = (Offer) o; if (date != offer.date) return false; if (fiatPrice != offer.fiatPrice) return false; - if (Double.compare(offer.percentageBasedPrice, percentageBasedPrice) != 0) return false; + if (Double.compare(offer.marketPriceMargin, marketPriceMargin) != 0) return false; if (usePercentageBasedPrice != offer.usePercentageBasedPrice) return false; if (amount != offer.amount) return false; if (minAmount != offer.minAmount) return false; @@ -441,7 +473,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload result = 31 * result + (currencyCode != null ? currencyCode.hashCode() : 0); result = 31 * result + (int) (date ^ (date >>> 32)); result = 31 * result + (int) (fiatPrice ^ (fiatPrice >>> 32)); - long temp = Double.doubleToLongBits(percentageBasedPrice); + long temp = Double.doubleToLongBits(marketPriceMargin); result = 31 * result + (int) (temp ^ (temp >>> 32)); result = 31 * result + (usePercentageBasedPrice ? 1 : 0); result = 31 * result + (int) (amount ^ (amount >>> 32)); @@ -467,7 +499,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload "\n\tcurrencyCode='" + currencyCode + '\'' + "\n\tdate=" + date + "\n\tfiatPrice=" + fiatPrice + - "\n\tpercentagePrice=" + percentageBasedPrice + + "\n\tmarketPriceMargin=" + marketPriceMargin + "\n\tusePercentageBasedPrice=" + usePercentageBasedPrice + "\n\tamount=" + amount + "\n\tminAmount=" + minAmount + diff --git a/core/src/main/java/io/bitsquare/trade/offer/OfferBookService.java b/core/src/main/java/io/bitsquare/trade/offer/OfferBookService.java index 810447fe79..c3650bee08 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/OfferBookService.java +++ b/core/src/main/java/io/bitsquare/trade/offer/OfferBookService.java @@ -17,6 +17,7 @@ package io.bitsquare.trade.offer; +import io.bitsquare.btc.pricefeed.PriceFeed; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.p2p.P2PService; @@ -45,6 +46,7 @@ public class OfferBookService { } private final P2PService p2PService; + private PriceFeed priceFeed; private final List offerBookChangedListeners = new LinkedList<>(); @@ -53,15 +55,19 @@ public class OfferBookService { /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public OfferBookService(P2PService p2PService) { + public OfferBookService(P2PService p2PService, PriceFeed priceFeed) { this.p2PService = p2PService; + this.priceFeed = priceFeed; p2PService.addHashSetChangedListener(new HashMapChangedListener() { @Override public void onAdded(ProtectedStorageEntry data) { offerBookChangedListeners.stream().forEach(listener -> { - if (data.getStoragePayload() instanceof Offer) - listener.onAdded((Offer) data.getStoragePayload()); + if (data.getStoragePayload() instanceof Offer) { + Offer offer = (Offer) data.getStoragePayload(); + offer.setPriceFeed(priceFeed); + listener.onAdded(offer); + } }); } @@ -118,7 +124,11 @@ public class OfferBookService { public List getOffers() { return p2PService.getDataMap().values().stream() .filter(data -> data.getStoragePayload() instanceof Offer) - .map(data -> (Offer) data.getStoragePayload()) + .map(data -> { + Offer offer = (Offer) data.getStoragePayload(); + offer.setPriceFeed(priceFeed); + return offer; + }) .collect(Collectors.toList()); } diff --git a/core/src/main/java/io/bitsquare/trade/offer/OpenOfferManager.java b/core/src/main/java/io/bitsquare/trade/offer/OpenOfferManager.java index e5fca72a65..7b8f89fce7 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/OpenOfferManager.java +++ b/core/src/main/java/io/bitsquare/trade/offer/OpenOfferManager.java @@ -22,6 +22,7 @@ import io.bitsquare.app.Log; import io.bitsquare.btc.AddressEntry; import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; +import io.bitsquare.btc.pricefeed.PriceFeed; import io.bitsquare.common.Timer; import io.bitsquare.common.UserThread; import io.bitsquare.common.crypto.KeyRing; @@ -95,6 +96,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe TradeWalletService tradeWalletService, OfferBookService offerBookService, ClosedTradableManager closedTradableManager, + PriceFeed priceFeed, @Named("storage.dir") File storageDir) { this.keyRing = keyRing; this.user = user; @@ -105,7 +107,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe this.closedTradableManager = closedTradableManager; openOffersStorage = new Storage<>(storageDir); - this.openOffers = new TradableList<>(openOffersStorage, "OpenOffers"); + openOffers = new TradableList<>(openOffersStorage, "OpenOffers"); + openOffers.forEach(e -> e.getOffer().setPriceFeed(priceFeed)); // In case the app did get killed the shutDown from the modules is not called, so we use a shutdown hook Runtime.getRuntime().addShutdownHook(new Thread(OpenOfferManager.this::shutDown, 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 b7b464fc14..23c73acf0b 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 @@ -301,7 +301,8 @@ class CreateOfferDataModel extends ActivatableDataModel { countryCode, acceptedCountryCodes, bankId, - acceptedBanks); + acceptedBanks, + priceFeed); } void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java index 14d1a009d6..be6cbf61aa 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java @@ -117,6 +117,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel usePercentageBasedPriceListener; /////////////////////////////////////////////////////////////////////////////////////////// @@ -239,18 +240,20 @@ class CreateOfferViewModel extends ActivatableWithDataModel { + if (newValue) + priceValidationResult.set(new InputValidator.ValidationResult(true)); + }; + volumeListener = (ov, oldValue, newValue) -> { if (isFiatInputValid(newValue).isValid) { setVolumeToModel(); @@ -324,6 +332,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel preferences.getMaxPriceDistanceInPercent()) { + double percentage = Math.abs(1 - (offerPrice / marketPriceAsDouble)); + if (marketPriceAsDouble != 0 && percentage > preferences.getMaxPriceDistanceInPercent()) { displayPriceOutofRangePopup(); return false; } else { diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java index 62b52e990e..b908e49af5 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java @@ -257,10 +257,19 @@ class OfferBookViewModel extends ActivatableViewModel { } String getPrice(OfferBookListItem item) { + if ((item == null)) + return ""; + + Offer offer = item.getOffer(); + Fiat price = offer.getPrice(); + String postFix = ""; + if (offer.isUsePercentageBasedPrice()) { + postFix = " (" + formatter.formatToPercentWithSymbol(offer.getMarketPriceMargin()) + ")"; + } if (showAllTradeCurrenciesProperty.get()) - return (item != null) ? formatter.formatFiatWithCode(item.getOffer().getPrice()) : ""; + return formatter.formatPriceWithCode(price) + postFix; else - return (item != null) ? formatter.formatFiat(item.getOffer().getPrice()) : ""; + return formatter.formatFiat(price) + postFix; } String getVolume(OfferBookListItem item) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java index ed850c58d8..11d2f17c48 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java @@ -18,6 +18,7 @@ package io.bitsquare.gui.main.offer.takeoffer; import io.bitsquare.arbitration.Arbitrator; +import io.bitsquare.btc.pricefeed.PriceFeed; import io.bitsquare.gui.Navigation; import io.bitsquare.gui.common.model.ActivatableWithDataModel; import io.bitsquare.gui.common.model.ViewModel; @@ -52,6 +53,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel im final TakeOfferDataModel dataModel; private final BtcValidator btcValidator; private final P2PService p2PService; + private PriceFeed priceFeed; private final Navigation navigation; final BSFormatter formatter; @@ -101,13 +103,14 @@ class TakeOfferViewModel extends ActivatableWithDataModel im /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public TakeOfferViewModel(TakeOfferDataModel dataModel, BtcValidator btcValidator, P2PService p2PService, + public TakeOfferViewModel(TakeOfferDataModel dataModel, BtcValidator btcValidator, P2PService p2PService, PriceFeed priceFeed, Navigation navigation, BSFormatter formatter) { super(dataModel); this.dataModel = dataModel; this.btcValidator = btcValidator; this.p2PService = p2PService; + this.priceFeed = priceFeed; this.navigation = navigation; this.formatter = formatter; diff --git a/gui/src/test/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModelTest.java b/gui/src/test/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModelTest.java index 40c259c712..21e92daeb3 100644 --- a/gui/src/test/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModelTest.java +++ b/gui/src/test/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModelTest.java @@ -277,6 +277,7 @@ public class OfferBookViewModelTest { countryCode, acceptedCountryCodes, bankId, - acceptedBanks); + acceptedBanks, + null); } } From bc07df2d7aad8909de0e85c7dc5959938cd67be0 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 15 Apr 2016 02:07:58 +0200 Subject: [PATCH 6/9] put trade price into trade and contract --- .../io/bitsquare/trade/BuyerAsTakerTrade.java | 4 +- .../java/io/bitsquare/trade/BuyerTrade.java | 6 +-- .../java/io/bitsquare/trade/Contract.java | 11 +++++ .../bitsquare/trade/SellerAsTakerTrade.java | 4 +- .../java/io/bitsquare/trade/SellerTrade.java | 6 +-- .../main/java/io/bitsquare/trade/Trade.java | 17 +++++-- .../java/io/bitsquare/trade/TradeManager.java | 10 ++-- .../java/io/bitsquare/trade/offer/Offer.java | 9 +++- .../trade/messages/PayDepositRequest.java | 3 ++ .../tasks/offerer/CreateAndSignContract.java | 1 + .../offerer/ProcessPayDepositRequest.java | 3 +- .../tasks/taker/SendPayDepositRequest.java | 1 + .../tasks/taker/VerifyAndSignContract.java | 1 + .../offer/createoffer/CreateOfferView.java | 6 +-- .../createoffer/CreateOfferViewModel.java | 5 +- .../offer/takeoffer/TakeOfferDataModel.java | 5 +- .../main/offer/takeoffer/TakeOfferView.java | 49 ++++++++++++++----- .../offer/takeoffer/TakeOfferViewModel.java | 4 +- .../main/overlays/windows/ContractWindow.java | 2 +- .../windows/DisputeSummaryWindow.java | 5 +- .../overlays/windows/OfferDetailsWindow.java | 10 +++- .../overlays/windows/TradeDetailsWindow.java | 2 +- .../closedtrades/ClosedTradesView.java | 8 ++- .../closedtrades/ClosedTradesViewModel.java | 9 +++- .../failedtrades/FailedTradesView.java | 2 +- .../failedtrades/FailedTradesViewModel.java | 2 +- .../pendingtrades/PendingTradesListItem.java | 2 +- .../pendingtrades/PendingTradesView.java | 2 +- .../io/bitsquare/gui/util/BSFormatter.java | 1 - 29 files changed, 139 insertions(+), 51 deletions(-) diff --git a/core/src/main/java/io/bitsquare/trade/BuyerAsTakerTrade.java b/core/src/main/java/io/bitsquare/trade/BuyerAsTakerTrade.java index 4a1bee811f..76879390ad 100644 --- a/core/src/main/java/io/bitsquare/trade/BuyerAsTakerTrade.java +++ b/core/src/main/java/io/bitsquare/trade/BuyerAsTakerTrade.java @@ -43,8 +43,8 @@ public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade { // Constructor, initialization /////////////////////////////////////////////////////////////////////////////////////////// - public BuyerAsTakerTrade(Offer offer, Coin tradeAmount, NodeAddress tradingPeerNodeAddress, Storage storage) { - super(offer, tradeAmount, tradingPeerNodeAddress, storage); + public BuyerAsTakerTrade(Offer offer, Coin tradeAmount, long tradePrice, NodeAddress tradingPeerNodeAddress, Storage storage) { + super(offer, tradeAmount, tradePrice, tradingPeerNodeAddress, storage); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { diff --git a/core/src/main/java/io/bitsquare/trade/BuyerTrade.java b/core/src/main/java/io/bitsquare/trade/BuyerTrade.java index 59fd993420..24a2d4e170 100644 --- a/core/src/main/java/io/bitsquare/trade/BuyerTrade.java +++ b/core/src/main/java/io/bitsquare/trade/BuyerTrade.java @@ -38,8 +38,8 @@ public abstract class BuyerTrade extends Trade { private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererTrade.class); - BuyerTrade(Offer offer, Coin tradeAmount, NodeAddress tradingPeerNodeAddress, Storage storage) { - super(offer, tradeAmount, tradingPeerNodeAddress, storage); + BuyerTrade(Offer offer, Coin tradeAmount, long tradePrice, NodeAddress tradingPeerNodeAddress, Storage storage) { + super(offer, tradeAmount, tradePrice, tradingPeerNodeAddress, storage); } BuyerTrade(Offer offer, Storage storage) { @@ -65,7 +65,7 @@ public abstract class BuyerTrade extends Trade { log::warn); } } - + @Override public Coin getPayoutAmount() { checkNotNull(getTradeAmount(), "Invalid state: getTradeAmount() = null"); diff --git a/core/src/main/java/io/bitsquare/trade/Contract.java b/core/src/main/java/io/bitsquare/trade/Contract.java index affee0f842..3de8d18a1c 100644 --- a/core/src/main/java/io/bitsquare/trade/Contract.java +++ b/core/src/main/java/io/bitsquare/trade/Contract.java @@ -25,6 +25,7 @@ import io.bitsquare.p2p.NodeAddress; import io.bitsquare.payment.PaymentAccountContractData; import io.bitsquare.trade.offer.Offer; import org.bitcoinj.core.Coin; +import org.bitcoinj.utils.Fiat; import javax.annotation.concurrent.Immutable; import java.util.Arrays; @@ -40,6 +41,7 @@ public final class Contract implements Payload { public final Offer offer; private final long tradeAmount; + private final long tradePrice; public final String takeOfferFeeTxID; public final NodeAddress arbitratorNodeAddress; private final boolean isBuyerOffererAndSellerTaker; @@ -64,6 +66,7 @@ public final class Contract implements Payload { public Contract(Offer offer, Coin tradeAmount, + Fiat tradePrice, String takeOfferFeeTxID, NodeAddress buyerNodeAddress, NodeAddress sellerNodeAddress, @@ -80,6 +83,7 @@ public final class Contract implements Payload { byte[] offererBtcPubKey, byte[] takerBtcPubKey) { this.offer = offer; + this.tradePrice = tradePrice.value; this.buyerNodeAddress = buyerNodeAddress; this.sellerNodeAddress = sellerNodeAddress; this.tradeAmount = tradeAmount.value; @@ -154,6 +158,10 @@ public final class Contract implements Payload { return Coin.valueOf(tradeAmount); } + public Fiat getTradePrice() { + return Fiat.valueOf(offer.getCurrencyCode(), tradePrice); + } + public NodeAddress getBuyerNodeAddress() { return buyerNodeAddress; } @@ -171,6 +179,7 @@ public final class Contract implements Payload { Contract contract = (Contract) o; if (tradeAmount != contract.tradeAmount) return false; + if (tradePrice != contract.tradePrice) return false; if (isBuyerOffererAndSellerTaker != contract.isBuyerOffererAndSellerTaker) return false; if (offer != null ? !offer.equals(contract.offer) : contract.offer != null) return false; if (takeOfferFeeTxID != null ? !takeOfferFeeTxID.equals(contract.takeOfferFeeTxID) : contract.takeOfferFeeTxID != null) @@ -206,6 +215,7 @@ public final class Contract implements Payload { public int hashCode() { int result = offer != null ? offer.hashCode() : 0; result = 31 * result + (int) (tradeAmount ^ (tradeAmount >>> 32)); + result = 31 * result + (int) (tradePrice ^ (tradePrice >>> 32)); result = 31 * result + (takeOfferFeeTxID != null ? takeOfferFeeTxID.hashCode() : 0); result = 31 * result + (arbitratorNodeAddress != null ? arbitratorNodeAddress.hashCode() : 0); result = 31 * result + (isBuyerOffererAndSellerTaker ? 1 : 0); @@ -229,6 +239,7 @@ public final class Contract implements Payload { return "Contract{" + "\n\toffer=" + offer + "\n\ttradeAmount=" + tradeAmount + + "\n\ttradePrice=" + tradePrice + "\n\ttakeOfferFeeTxID='" + takeOfferFeeTxID + '\'' + "\n\tarbitratorAddress=" + arbitratorNodeAddress + "\n\tisBuyerOffererAndSellerTaker=" + isBuyerOffererAndSellerTaker + diff --git a/core/src/main/java/io/bitsquare/trade/SellerAsTakerTrade.java b/core/src/main/java/io/bitsquare/trade/SellerAsTakerTrade.java index b46c05b2d5..9c45e1559f 100644 --- a/core/src/main/java/io/bitsquare/trade/SellerAsTakerTrade.java +++ b/core/src/main/java/io/bitsquare/trade/SellerAsTakerTrade.java @@ -42,8 +42,8 @@ public final class SellerAsTakerTrade extends SellerTrade implements TakerTrade // Constructor, initialization /////////////////////////////////////////////////////////////////////////////////////////// - public SellerAsTakerTrade(Offer offer, Coin tradeAmount, NodeAddress tradingPeerNodeAddress, Storage storage) { - super(offer, tradeAmount, tradingPeerNodeAddress, storage); + public SellerAsTakerTrade(Offer offer, Coin tradeAmount, long tradePrice, NodeAddress tradingPeerNodeAddress, Storage storage) { + super(offer, tradeAmount, tradePrice, tradingPeerNodeAddress, storage); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { diff --git a/core/src/main/java/io/bitsquare/trade/SellerTrade.java b/core/src/main/java/io/bitsquare/trade/SellerTrade.java index e49b269912..ecdb97336f 100644 --- a/core/src/main/java/io/bitsquare/trade/SellerTrade.java +++ b/core/src/main/java/io/bitsquare/trade/SellerTrade.java @@ -37,8 +37,8 @@ public abstract class SellerTrade extends Trade { private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerTrade.class); - SellerTrade(Offer offer, Coin tradeAmount, NodeAddress tradingPeerNodeAddress, Storage storage) { - super(offer, tradeAmount, tradingPeerNodeAddress, storage); + SellerTrade(Offer offer, Coin tradeAmount, long tradePrice, NodeAddress tradingPeerNodeAddress, Storage storage) { + super(offer, tradeAmount, tradePrice, tradingPeerNodeAddress, storage); } SellerTrade(Offer offer, Storage storage) { @@ -69,7 +69,7 @@ public abstract class SellerTrade extends Trade { public Coin getPayoutAmount() { return FeePolicy.getSecurityDeposit(); } - + /////////////////////////////////////////////////////////////////////////////////////////// // Setter for Mutable objects /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/io/bitsquare/trade/Trade.java b/core/src/main/java/io/bitsquare/trade/Trade.java index b34f0ee666..b5dfc23233 100644 --- a/core/src/main/java/io/bitsquare/trade/Trade.java +++ b/core/src/main/java/io/bitsquare/trade/Trade.java @@ -41,6 +41,7 @@ import javafx.beans.property.*; import org.bitcoinj.core.Coin; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; +import org.bitcoinj.utils.ExchangeRate; import org.bitcoinj.utils.Fiat; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -167,6 +168,7 @@ public abstract class Trade implements Tradable, Model { transient private ObjectProperty tradeVolumeProperty; @Nullable private String takeOfferFeeTxId; + private long tradePrice; /////////////////////////////////////////////////////////////////////////////////////////// @@ -189,11 +191,12 @@ public abstract class Trade implements Tradable, Model { } // taker - protected Trade(Offer offer, Coin tradeAmount, NodeAddress tradingPeerNodeAddress, + protected Trade(Offer offer, Coin tradeAmount, long tradePrice, NodeAddress tradingPeerNodeAddress, Storage storage) { this(offer, storage); this.tradeAmount = tradeAmount; + this.tradePrice = tradePrice; this.tradingPeerNodeAddress = tradingPeerNodeAddress; tradeAmountProperty.set(tradeAmount); tradeVolumeProperty.set(getTradeVolume()); @@ -381,8 +384,8 @@ public abstract class Trade implements Tradable, Model { @Nullable public Fiat getTradeVolume() { - if (tradeAmount != null) - return offer.getVolumeByAmount(tradeAmount); + if (tradeAmount != null && getTradePrice() != null) + return new ExchangeRate(getTradePrice()).coinToFiat(tradeAmount); else return null; } @@ -452,6 +455,14 @@ public abstract class Trade implements Tradable, Model { tradeVolumeProperty.set(getTradeVolume()); } + public void setTradePrice(long tradePrice) { + this.tradePrice = tradePrice; + } + + public Fiat getTradePrice() { + return Fiat.valueOf(offer.getCurrencyCode(), tradePrice); + } + @Nullable public Coin getTradeAmount() { return tradeAmount; diff --git a/core/src/main/java/io/bitsquare/trade/TradeManager.java b/core/src/main/java/io/bitsquare/trade/TradeManager.java index f2f321b843..f98c4e7124 100644 --- a/core/src/main/java/io/bitsquare/trade/TradeManager.java +++ b/core/src/main/java/io/bitsquare/trade/TradeManager.java @@ -113,7 +113,7 @@ public class TradeManager { tradableListStorage = new Storage<>(storageDir); trades = new TradableList<>(tradableListStorage, "PendingTrades"); trades.forEach(e -> e.getOffer().setPriceFeed(priceFeed)); - + p2PService.addDecryptedDirectMessageListener(new DecryptedDirectMessageListener() { @Override public void onDirectMessage(DecryptedMsgWithPubKey decryptedMsgWithPubKey, NodeAddress peerNodeAddress) { @@ -264,6 +264,7 @@ public class TradeManager { // First we check if offer is still available then we create the trade with the protocol public void onTakeOffer(Coin amount, + long tradePrice, Coin fundsNeededForTrade, Offer offer, String paymentAccountId, @@ -273,11 +274,12 @@ public class TradeManager { offer.checkOfferAvailability(model, () -> { if (offer.getState() == Offer.State.AVAILABLE) - createTrade(amount, fundsNeededForTrade, offer, paymentAccountId, useSavingsWallet, model, tradeResultHandler); + createTrade(amount, tradePrice, fundsNeededForTrade, offer, paymentAccountId, useSavingsWallet, model, tradeResultHandler); }); } private void createTrade(Coin amount, + long tradePrice, Coin fundsNeededForTrade, Offer offer, String paymentAccountId, @@ -286,9 +288,9 @@ public class TradeManager { TradeResultHandler tradeResultHandler) { Trade trade; if (offer.getDirection() == Offer.Direction.BUY) - trade = new SellerAsTakerTrade(offer, amount, model.getPeerNodeAddress(), tradableListStorage); + trade = new SellerAsTakerTrade(offer, amount, tradePrice, model.getPeerNodeAddress(), tradableListStorage); else - trade = new BuyerAsTakerTrade(offer, amount, model.getPeerNodeAddress(), tradableListStorage); + trade = new BuyerAsTakerTrade(offer, amount, tradePrice, model.getPeerNodeAddress(), tradableListStorage); trade.setTakerPaymentAccountId(paymentAccountId); diff --git a/core/src/main/java/io/bitsquare/trade/offer/Offer.java b/core/src/main/java/io/bitsquare/trade/offer/Offer.java index ae5caeeff7..e99b8cb2b7 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/Offer.java +++ b/core/src/main/java/io/bitsquare/trade/offer/Offer.java @@ -229,7 +229,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload public Fiat getVolumeByAmount(Coin amount) { if (fiatPrice != 0 && amount != null && !amount.isZero()) - return new ExchangeRate(Fiat.valueOf(currencyCode, fiatPrice)).coinToFiat(amount); + return new ExchangeRate(getPrice()).coinToFiat(amount); else return null; } @@ -335,6 +335,13 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload double marketPriceAsDouble = marketPrice.getPrice(priceFeedType); double factor = direction == Offer.Direction.BUY ? 1 - marketPriceMargin : 1 + marketPriceMargin; double targetPrice = marketPriceAsDouble * factor; + + // round + long factor1 = (long) Math.pow(10, 2); + targetPrice = targetPrice * factor1; + long tmp = Math.round(targetPrice); + targetPrice = (double) tmp / factor1; + try { return Fiat.parseFiat(currencyCode, String.valueOf(targetPrice)); } catch (Exception e) { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/PayDepositRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/PayDepositRequest.java index 8ca2a81770..503b97ed8d 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/PayDepositRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/PayDepositRequest.java @@ -35,6 +35,7 @@ public final class PayDepositRequest extends TradeMessage implements MailboxMess private static final long serialVersionUID = Version.P2P_NETWORK_VERSION; public final long tradeAmount; + public final long tradePrice; public final byte[] takerMultiSigPubKey; public final ArrayList rawTransactionInputs; public final long changeOutputValue; @@ -52,6 +53,7 @@ public final class PayDepositRequest extends TradeMessage implements MailboxMess public PayDepositRequest(NodeAddress senderNodeAddress, String tradeId, long tradeAmount, + long tradePrice, ArrayList rawTransactionInputs, long changeOutputValue, String changeOutputAddress, @@ -66,6 +68,7 @@ public final class PayDepositRequest extends TradeMessage implements MailboxMess super(tradeId); this.senderNodeAddress = senderNodeAddress; this.tradeAmount = tradeAmount; + this.tradePrice = tradePrice; this.rawTransactionInputs = rawTransactionInputs; this.changeOutputValue = changeOutputValue; this.changeOutputAddress = changeOutputAddress; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/CreateAndSignContract.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/CreateAndSignContract.java index b0219d9798..ecfa140814 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/CreateAndSignContract.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/CreateAndSignContract.java @@ -60,6 +60,7 @@ public class CreateAndSignContract extends TradeTask { Contract contract = new Contract( processModel.getOffer(), trade.getTradeAmount(), + trade.getTradePrice(), trade.getTakeOfferFeeTxId(), buyerNodeAddress, sellerNodeAddress, diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java index 2e56649499..bddb6451d7 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java @@ -77,13 +77,14 @@ public class ProcessPayDepositRequest extends TradeTask { failed("acceptedArbitratorNames size must be at least 1"); trade.setArbitratorNodeAddress(checkNotNull(payDepositRequest.arbitratorNodeAddress)); checkArgument(payDepositRequest.tradeAmount > 0); + trade.setTradePrice(payDepositRequest.tradePrice); trade.setTradeAmount(Coin.valueOf(payDepositRequest.tradeAmount)); // update to the latest peer address of our peer if the payDepositRequest is correct trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress()); removeMailboxMessageAfterProcessing(); - + complete(); } catch (Throwable t) { failed(t); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java index 161d80f0b7..c669c1c118 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java @@ -49,6 +49,7 @@ public class SendPayDepositRequest extends TradeTask { processModel.getMyAddress(), processModel.getId(), trade.getTradeAmount().value, + trade.getTradePrice().value, processModel.getRawTransactionInputs(), processModel.getChangeOutputValue(), processModel.getChangeOutputAddress(), diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/VerifyAndSignContract.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/VerifyAndSignContract.java index 96edc32936..1f21cac3e3 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/VerifyAndSignContract.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/VerifyAndSignContract.java @@ -61,6 +61,7 @@ public class VerifyAndSignContract extends TradeTask { Contract contract = new Contract( processModel.getOffer(), trade.getTradeAmount(), + trade.getTradePrice(), trade.getTakeOfferFeeTxId(), buyerNodeAddress, sellerNodeAddress, 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 d43e236f51..66dcce3b95 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 @@ -93,14 +93,12 @@ public class CreateOfferView extends ActivatableViewAndModel paymentAccountsComboBox; private ComboBox currencyComboBox; private PopOver totalToPayInfoPopover; - private Label priceAsPercentageLabel; - private ToggleButton fixedPriceButton; - private ToggleButton percentagePriceButton; + private ToggleButton fixedPriceButton, percentagePriceButton; private OfferView.CloseHandler closeHandler; diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java index be6cbf61aa..e3981b2d13 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java @@ -576,8 +576,9 @@ class CreateOfferViewModel extends ActivatableWithDataModel preferences.getMaxPriceDistanceInPercent()) { - displayPriceOutofRangePopup(); + displayPriceOutOfRangePopup(); return false; } else { return true; @@ -587,7 +588,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel model.dataModel.getCurrencyCode() + "/" + model.btcCode.get(), model.btcCode)); + priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty()); amountRangeBtcLabel.textProperty().bind(model.btcCode); nextButton.disableProperty().bind(model.isNextButtonDisabled); @@ -414,6 +417,7 @@ public class TakeOfferView extends ActivatableViewAndModel priceAsPercentageTuple = getValueCurrencyBox(); + HBox priceAsPercentageValueCurrencyBox = priceAsPercentageTuple.first; + priceAsPercentageTextField = priceAsPercentageTuple.second; + priceAsPercentageLabel = priceAsPercentageTuple.third; + + Tuple2 priceAsPercentageInputBoxTuple = getTradeInputBox(priceAsPercentageValueCurrencyBox, "Distance in % from market price"); + priceAsPercentageInputBoxTuple.first.setPrefWidth(200); + VBox priceAsPercentageInputBox = priceAsPercentageInputBoxTuple.second; + + priceAsPercentageTextField.setPromptText("Enter % value"); + priceAsPercentageLabel.setText("% dist."); + priceAsPercentageLabel.setStyle("-fx-alignment: center;"); + + Tuple3 amountValueCurrencyBoxTuple = getValueCurrencyBox(); HBox amountValueCurrencyBox = amountValueCurrencyBoxTuple.first; amountRangeTextField = amountValueCurrencyBoxTuple.second; amountRangeBtcLabel = amountValueCurrencyBoxTuple.third; Tuple2 amountInputBoxTuple = getTradeInputBox(amountValueCurrencyBox, BSResources.get("takeOffer.amountPriceBox.amountRangeDescription")); - VBox box = amountInputBoxTuple.second; - GridPane.setRowIndex(box, ++gridRow); - GridPane.setColumnIndex(box, 1); - GridPane.setMargin(box, new Insets(5, 10, 5, 0)); - gridPane.getChildren().add(box); + + Label xLabel = new Label("x"); + xLabel.setFont(Font.font("Helvetica-Bold", 20)); + xLabel.setPadding(new Insets(14, 3, 0, 3)); + xLabel.setVisible(false); // we just use it to get the same layout as the upper row + + HBox hBox = new HBox(); + hBox.setSpacing(5); + hBox.setAlignment(Pos.CENTER_LEFT); + hBox.getChildren().addAll(amountInputBoxTuple.second, xLabel, priceAsPercentageInputBox); + GridPane.setRowIndex(hBox, ++gridRow); + GridPane.setColumnIndex(hBox, 1); + GridPane.setMargin(hBox, new Insets(5, 10, 5, 0)); + GridPane.setColumnSpan(hBox, 2); + gridPane.getChildren().add(hBox); } diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java index 11d2f17c48..4dab1ea3c7 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java @@ -96,6 +96,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel im private ConnectionListener connectionListener; // private Subscription isFeeSufficientSubscription; private Runnable takeOfferSucceededHandler; + String marketPriceMargin; /////////////////////////////////////////////////////////////////////////////////////////// @@ -162,7 +163,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel im } amountRange = formatter.formatCoin(offer.getMinAmount()) + " - " + formatter.formatCoin(offer.getAmount()); - price = formatter.formatFiat(offer.getPrice()); + price = formatter.formatFiat(dataModel.tradePrice); + marketPriceMargin = formatter.formatToPercentWithSymbol(offer.getMarketPriceMargin()); paymentLabel = BSResources.get("takeOffer.fundsBox.paymentLabel", offer.getId()); checkNotNull(dataModel.getAddressEntry(), "dataModel.getAddressEntry() must not be null"); diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/ContractWindow.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/ContractWindow.java index 330059194b..ec1dff2078 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/ContractWindow.java +++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/ContractWindow.java @@ -123,7 +123,7 @@ public class ContractWindow extends Overlay { addLabelTextField(gridPane, ++rowIndex, "Offer date:", formatter.formatDateTime(offer.getDate())); addLabelTextField(gridPane, ++rowIndex, "Trade date:", formatter.formatDateTime(dispute.getTradeDate())); addLabelTextField(gridPane, ++rowIndex, "Trade type:", formatter.getDirectionBothSides(offer.getDirection())); - addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(offer.getPrice()) + " " + offer.getCurrencyCode()); + addLabelTextField(gridPane, ++rowIndex, "Trade price:", formatter.formatFiat(contract.getTradePrice()) + " " + offer.getCurrencyCode()); addLabelTextField(gridPane, ++rowIndex, "Trade amount:", formatter.formatCoinWithCode(contract.getTradeAmount())); addLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, "Buyer bitcoin address:", contract.getBuyerPayoutAddressString()).second.setMouseTransparent(false); diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/DisputeSummaryWindow.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/DisputeSummaryWindow.java index f8658b0e8b..6c91b6e3d0 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/DisputeSummaryWindow.java +++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/DisputeSummaryWindow.java @@ -47,6 +47,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.Coin; +import org.bitcoinj.utils.ExchangeRate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -232,8 +233,8 @@ public class DisputeSummaryWindow extends Overlay { } addLabelTextField(gridPane, ++rowIndex, "Traders role:", role); addLabelTextField(gridPane, ++rowIndex, "Trade amount:", formatter.formatCoinWithCode(contract.getTradeAmount())); - addLabelTextField(gridPane, ++rowIndex, "Trade volume:", formatter.formatFiatWithCode(contract.offer.getVolumeByAmount(contract.getTradeAmount()))); - addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiatWithCode(contract.offer.getPrice())); + addLabelTextField(gridPane, ++rowIndex, "Trade price:", formatter.formatFiatWithCode(contract.getTradePrice())); + addLabelTextField(gridPane, ++rowIndex, "Trade volume:", formatter.formatFiatWithCode(new ExchangeRate(contract.getTradePrice()).coinToFiat(contract.getTradeAmount()))); } private void addCheckboxes() { diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java index 1aed3ea396..4a8ede823c 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java +++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java @@ -41,6 +41,7 @@ import javafx.geometry.Insets; import javafx.scene.control.*; import javafx.scene.image.ImageView; import org.bitcoinj.core.Coin; +import org.bitcoinj.utils.Fiat; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,6 +62,7 @@ public class OfferDetailsWindow extends Overlay { private final Navigation navigation; private Offer offer; private Coin tradeAmount; + private Fiat tradePrice; private Optional placeOfferHandlerOptional = Optional.empty(); private Optional takeOfferHandlerOptional = Optional.empty(); private ProgressIndicator spinner; @@ -80,9 +82,10 @@ public class OfferDetailsWindow extends Overlay { type = Type.Confirmation; } - public void show(Offer offer, Coin tradeAmount) { + public void show(Offer offer, Coin tradeAmount, Fiat tradePrice) { this.offer = offer; this.tradeAmount = tradeAmount; + this.tradePrice = tradePrice; rowIndex = -1; width = 900; @@ -172,7 +175,10 @@ public class OfferDetailsWindow extends Overlay { addLabelTextField(gridPane, ++rowIndex, CurrencyUtil.getNameByCode(offer.getCurrencyCode()) + " amount" + fiatDirectionInfo, formatter.formatFiatWithCode(offer.getVolumeByAmount(offer.getAmount()))); } - addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(offer.getPrice()) + " " + offer.getCurrencyCode() + "/" + "BTC"); + if (takeOfferHandlerOptional.isPresent()) + addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(tradePrice) + " " + offer.getCurrencyCode() + "/" + "BTC"); + else + addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(offer.getPrice()) + " " + offer.getCurrencyCode() + "/" + "BTC"); if (offer.isMyOffer(keyRing) && user.getPaymentAccount(offer.getOffererPaymentAccountId()) != null) addLabelTextField(gridPane, ++rowIndex, "Payment account:", user.getPaymentAccount(offer.getOffererPaymentAccountId()).getAccountName()); diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/TradeDetailsWindow.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/TradeDetailsWindow.java index c795d6b50f..25fc1fdb58 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/TradeDetailsWindow.java +++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/TradeDetailsWindow.java @@ -127,7 +127,7 @@ public class TradeDetailsWindow extends Overlay { addLabelTextField(gridPane, ++rowIndex, "Bitcoin amount" + btcDirectionInfo, formatter.formatCoinWithCode(trade.getTradeAmount())); addLabelTextField(gridPane, ++rowIndex, CurrencyUtil.getNameByCode(offer.getCurrencyCode()) + " amount" + fiatDirectionInfo, formatter.formatFiatWithCode(trade.getTradeVolume())); - addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatPriceWithCode(offer.getPrice())); + addLabelTextField(gridPane, ++rowIndex, "Trade price:", formatter.formatPriceWithCode(trade.getTradePrice())); addLabelTextField(gridPane, ++rowIndex, "Payment method:", BSResources.get(offer.getPaymentMethod().getId())); // second group diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesView.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesView.java index 93d504a998..969f99ac04 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesView.java @@ -81,7 +81,13 @@ public class ClosedTradesView extends ActivatableViewAndModel o1.getTradable().getId().compareTo(o2.getTradable().getId())); dateColumn.setComparator((o1, o2) -> o1.getTradable().getDate().compareTo(o2.getTradable().getDate())); directionColumn.setComparator((o1, o2) -> o1.getTradable().getOffer().getDirection().compareTo(o2.getTradable().getOffer().getDirection())); - priceColumn.setComparator((o1, o2) -> o1.getTradable().getOffer().getPrice().compareTo(o2.getTradable().getOffer().getPrice())); + priceColumn.setComparator((o1, o2) -> { + Tradable tradable = o1.getTradable(); + if (tradable instanceof Trade) + return ((Trade) o1.getTradable()).getTradePrice().compareTo(((Trade) o2.getTradable()).getTradePrice()); + else + return o1.getTradable().getOffer().getPrice().compareTo(o2.getTradable().getOffer().getPrice()); + }); volumeColumn.setComparator((o1, o2) -> { if (o1.getTradable() instanceof Trade && o2.getTradable() instanceof Trade) { Fiat tradeVolume1 = ((Trade) o1.getTradable()).getTradeVolume(); diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesViewModel.java index 59162b78b9..74fa7c375c 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesViewModel.java @@ -21,6 +21,7 @@ import com.google.inject.Inject; import io.bitsquare.gui.common.model.ActivatableWithDataModel; import io.bitsquare.gui.common.model.ViewModel; import io.bitsquare.gui.util.BSFormatter; +import io.bitsquare.trade.Tradable; import io.bitsquare.trade.Trade; import io.bitsquare.trade.offer.OpenOffer; import javafx.collections.ObservableList; @@ -54,7 +55,13 @@ class ClosedTradesViewModel extends ActivatableWithDataModel o1.getTrade().getId().compareTo(o2.getTrade().getId())); dateColumn.setComparator((o1, o2) -> o1.getTrade().getDate().compareTo(o2.getTrade().getDate())); - priceColumn.setComparator((o1, o2) -> o1.getTrade().getOffer().getPrice().compareTo(o2.getTrade().getOffer().getPrice())); + priceColumn.setComparator((o1, o2) -> o1.getTrade().getTradePrice().compareTo(o2.getTrade().getTradePrice())); volumeColumn.setComparator((o1, o2) -> o1.getTrade().getTradeVolume().compareTo(o2.getTrade().getTradeVolume())); amountColumn.setComparator((o1, o2) -> o1.getTrade().getTradeAmount().compareTo(o2.getTrade().getTradeAmount())); stateColumn.setComparator((o1, o2) -> model.getState(o1).compareTo(model.getState(o2))); diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/failedtrades/FailedTradesViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/failedtrades/FailedTradesViewModel.java index 5b0087b528..2440a26f9e 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/failedtrades/FailedTradesViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/failedtrades/FailedTradesViewModel.java @@ -51,7 +51,7 @@ class FailedTradesViewModel extends ActivatableWithDataModel Date: Fri, 15 Apr 2016 02:31:20 +0200 Subject: [PATCH 7/9] Hide perc box if not perc based price used, fix toggle button --- core/src/main/java/io/bitsquare/trade/offer/Offer.java | 2 +- .../gui/main/offer/createoffer/CreateOfferView.java | 2 ++ .../gui/main/offer/offerbook/OfferBookViewModel.java | 2 +- .../bitsquare/gui/main/offer/takeoffer/TakeOfferView.java | 7 +++++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/io/bitsquare/trade/offer/Offer.java b/core/src/main/java/io/bitsquare/trade/offer/Offer.java index e99b8cb2b7..6c29fb63b2 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/Offer.java +++ b/core/src/main/java/io/bitsquare/trade/offer/Offer.java @@ -364,7 +364,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload return marketPriceMargin; } - public boolean isUsePercentageBasedPrice() { + public boolean getUsePercentageBasedPrice() { return usePercentageBasedPrice; } 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 66dcce3b95..20706721bf 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 @@ -938,6 +938,7 @@ public class CreateOfferView extends ActivatableViewAndModel { model.dataModel.setUsePercentageBasedPrice(!newValue); + percentagePriceButton.setSelected(!newValue); }); percentagePriceButton = new ToggleButton("Percentage"); @@ -945,6 +946,7 @@ public class CreateOfferView extends ActivatableViewAndModel { model.dataModel.setUsePercentageBasedPrice(newValue); + fixedPriceButton.setSelected(!newValue); }); HBox toggleButtons = new HBox(); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java index b908e49af5..d64215bb42 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookViewModel.java @@ -263,7 +263,7 @@ class OfferBookViewModel extends ActivatableViewModel { Offer offer = item.getOffer(); Fiat price = offer.getPrice(); String postFix = ""; - if (offer.isUsePercentageBasedPrice()) { + if (offer.getUsePercentageBasedPrice()) { postFix = " (" + formatter.formatToPercentWithSymbol(offer.getMarketPriceMargin()) + ")"; } if (showAllTradeCurrenciesProperty.get()) 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 b644c84dbc..f149b9d0f6 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 @@ -116,6 +116,7 @@ public class TakeOfferView extends ActivatableViewAndModel noSufficientFeeBinding; private Subscription cancelButton2StyleSubscription; + private VBox priceAsPercentageInputBox; /////////////////////////////////////////////////////////////////////////////////////////// @@ -195,6 +196,8 @@ public class TakeOfferView extends ActivatableViewAndModel 1; paymentAccountsLabel.setVisible(showComboBox); paymentAccountsLabel.setManaged(showComboBox); @@ -850,7 +852,7 @@ public class TakeOfferView extends ActivatableViewAndModel priceAsPercentageInputBoxTuple = getTradeInputBox(priceAsPercentageValueCurrencyBox, "Distance in % from market price"); priceAsPercentageInputBoxTuple.first.setPrefWidth(200); - VBox priceAsPercentageInputBox = priceAsPercentageInputBoxTuple.second; + priceAsPercentageInputBox = priceAsPercentageInputBoxTuple.second; priceAsPercentageTextField.setPromptText("Enter % value"); priceAsPercentageLabel.setText("% dist."); @@ -873,6 +875,7 @@ public class TakeOfferView extends ActivatableViewAndModel Date: Fri, 15 Apr 2016 12:03:30 +0200 Subject: [PATCH 8/9] Add check at offerer for trade price --- .../trade/protocol/trade/ProcessModel.java | 1 - .../offerer/ProcessPayDepositRequest.java | 22 ++++++++++++++++++- .../java/io/bitsquare/user/Preferences.java | 12 ++++++++++ .../java/io/bitsquare/app/BitsquareApp.java | 2 +- .../io/bitsquare/gui/main/MainViewModel.java | 20 +++++++++-------- .../createoffer/CreateOfferDataModel.java | 6 ++--- .../offer/createoffer/CreateOfferView.java | 2 +- .../main/offer/takeoffer/TakeOfferView.java | 2 +- 8 files changed, 50 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java index 1f4c8b804e..42f4f1a1e2 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java @@ -68,7 +68,6 @@ public class ProcessModel implements Model, Serializable { transient private KeyRing keyRing; transient private P2PService p2PService; - // Mutable public final TradingPeer tradingPeer; transient private TradeMessage tradeMessage; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java index bddb6451d7..15b42ef113 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java @@ -26,6 +26,7 @@ import io.bitsquare.trade.Trade; import io.bitsquare.trade.protocol.trade.messages.PayDepositRequest; import io.bitsquare.trade.protocol.trade.tasks.TradeTask; import org.bitcoinj.core.Coin; +import org.bitcoinj.utils.Fiat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,8 +77,27 @@ public class ProcessPayDepositRequest extends TradeTask { if (payDepositRequest.acceptedArbitratorNodeAddresses.size() < 1) failed("acceptedArbitratorNames size must be at least 1"); trade.setArbitratorNodeAddress(checkNotNull(payDepositRequest.arbitratorNodeAddress)); + + long takersTradePrice = payDepositRequest.tradePrice; + checkArgument(takersTradePrice > 0); + Fiat tradePriceAsFiat = Fiat.valueOf(trade.getOffer().getCurrencyCode(), takersTradePrice); + Fiat offerPriceAsFiat = trade.getOffer().getPrice(); + double factor = (double) takersTradePrice / (double) offerPriceAsFiat.value; + // We allow max. 2 % difference between own offer price calculation and takers calculation. + // Market price might be different at offerers and takers side so we need a bit of tolerance. + // The tolerance will get smaller once we have multiple price feeds avoiding fast price fluctuations + // from one provider. + if (Math.abs(1 - factor) > 0.02) { + String msg = "Takers tradePrice is outside our market price tolerance.\n" + + "tradePriceAsFiat=" + tradePriceAsFiat.toFriendlyString() + "\n" + + "offerPriceAsFiat=" + offerPriceAsFiat.toFriendlyString(); + log.warn(msg); + failed(msg); + } + trade.setTradePrice(takersTradePrice); + + checkArgument(payDepositRequest.tradeAmount > 0); - trade.setTradePrice(payDepositRequest.tradePrice); trade.setTradeAmount(Coin.valueOf(payDepositRequest.tradeAmount)); // update to the latest peer address of our peer if the payDepositRequest is correct diff --git a/core/src/main/java/io/bitsquare/user/Preferences.java b/core/src/main/java/io/bitsquare/user/Preferences.java index 9bdd2ac2cc..991a6d363f 100644 --- a/core/src/main/java/io/bitsquare/user/Preferences.java +++ b/core/src/main/java/io/bitsquare/user/Preferences.java @@ -110,6 +110,7 @@ public final class Preferences implements Persistable { private double maxPriceDistanceInPercent; private boolean useInvertedMarketPrice; private boolean useStickyMarketPrice = false; + private boolean usePercentageBasedPrice = false; // Observable wrappers transient private final StringProperty btcDenominationProperty = new SimpleStringProperty(btcDenomination); @@ -162,6 +163,7 @@ public final class Preferences implements Persistable { // useTorForBitcoinJ = persisted.getUseTorForBitcoinJ(); useTorForBitcoinJ = false; useStickyMarketPrice = persisted.getUseStickyMarketPrice(); + usePercentageBasedPrice = persisted.getUsePercentageBasedPrice(); showOwnOffersInOfferBook = persisted.getShowOwnOffersInOfferBook(); maxPriceDistanceInPercent = persisted.getMaxPriceDistanceInPercent(); // Backward compatible to version 0.3.6. Can be removed after a while @@ -368,6 +370,12 @@ public final class Preferences implements Persistable { storage.queueUpForSave(); } + public void setUsePercentageBasedPrice(boolean usePercentageBasedPrice) { + this.usePercentageBasedPrice = usePercentageBasedPrice; + storage.queueUpForSave(); + } + + /////////////////////////////////////////////////////////////////////////////////////////// // Getter /////////////////////////////////////////////////////////////////////////////////////////// @@ -488,6 +496,10 @@ public final class Preferences implements Persistable { return useStickyMarketPrice; } + public boolean getUsePercentageBasedPrice() { + return usePercentageBasedPrice; + } + /////////////////////////////////////////////////////////////////////////////////////////// // Private diff --git a/gui/src/main/java/io/bitsquare/app/BitsquareApp.java b/gui/src/main/java/io/bitsquare/app/BitsquareApp.java index 2a4348d9a5..cf61f7da3f 100644 --- a/gui/src/main/java/io/bitsquare/app/BitsquareApp.java +++ b/gui/src/main/java/io/bitsquare/app/BitsquareApp.java @@ -76,7 +76,7 @@ import static io.bitsquare.app.BitsquareEnvironment.APP_NAME_KEY; public class BitsquareApp extends Application { private static final Logger log = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(BitsquareApp.class); - public static final boolean DEV_MODE = false; + public static final boolean DEV_MODE = true; public static final boolean IS_RELEASE_VERSION = !DEV_MODE && true; private static Environment env; diff --git a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java index efb4140b77..ce19f066e8 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java @@ -497,7 +497,11 @@ public class MainViewModel implements ViewModel { setupBtcNumPeersWatcher(); setupP2PNumPeersWatcher(); updateBalance(); - setupDevDummyPaymentAccount(); + if (BitsquareApp.DEV_MODE) { + preferences.setShowOwnOffersInOfferBook(true); + if (user.getPaymentAccounts().isEmpty()) + setupDevDummyPaymentAccount(); + } setupMarketPriceFeed(); swapPendingOfferFundingEntries(); fillPriceFeedComboBoxItems(); @@ -713,7 +717,7 @@ public class MainViewModel implements ViewModel { marketPriceBinding.subscribe((observable, oldValue, newValue) -> { if (newValue != null && !newValue.equals(oldValue)) { setMarketPriceInItems(); - + String code = preferences.getUseStickyMarketPrice() ? preferences.getPreferredTradeCurrency().getCode() : priceFeed.currencyCodeProperty().get(); @@ -896,12 +900,10 @@ public class MainViewModel implements ViewModel { } private void setupDevDummyPaymentAccount() { - if (BitsquareApp.DEV_MODE && user.getPaymentAccounts().isEmpty()) { - OKPayAccount okPayAccount = new OKPayAccount(); - okPayAccount.setAccountNr("dummy"); - okPayAccount.setAccountName("OKPay dummy"); - okPayAccount.setSelectedTradeCurrency(CurrencyUtil.getDefaultTradeCurrency()); - user.addPaymentAccount(okPayAccount); - } + OKPayAccount okPayAccount = new OKPayAccount(); + okPayAccount.setAccountNr("dummy"); + okPayAccount.setAccountName("OKPay dummy"); + okPayAccount.setSelectedTradeCurrency(CurrencyUtil.getDefaultTradeCurrency()); + user.addPaymentAccount(okPayAccount); } } 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 23c73acf0b..e56e1f68ae 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 @@ -140,6 +140,8 @@ class CreateOfferDataModel extends ActivatableDataModel { networkFeeAsCoin = FeePolicy.getFixedTxFeeForTrades(); securityDepositAsCoin = FeePolicy.getSecurityDeposit(); + usePercentageBasedPrice.set(preferences.getUsePercentageBasedPrice()); + balanceListener = new BalanceListener(getAddressEntry().getAddress()) { @Override public void onBalanceChanged(Coin balance, Transaction tx) { @@ -282,9 +284,6 @@ class CreateOfferDataModel extends ActivatableDataModel { String countryCode = paymentAccount instanceof CountryBasedPaymentAccount ? ((CountryBasedPaymentAccount) paymentAccount).getCountry().code : null; checkNotNull(p2PService.getAddress(), "Address must not be null"); - log.error("fiatPrice " + fiatPrice); - log.error("percentageBasedPrice " + percentageBasedPrice); - log.error("usePercentageBasedPrice " + usePercentageBasedPrice.get()); return new Offer(offerId, p2PService.getAddress(), keyRing.getPubKeyRing(), @@ -389,6 +388,7 @@ class CreateOfferDataModel extends ActivatableDataModel { public void setUsePercentageBasedPrice(boolean usePercentageBasedPrice) { this.usePercentageBasedPrice.set(usePercentageBasedPrice); + preferences.setUsePercentageBasedPrice(usePercentageBasedPrice); } /*boolean isFeeFromFundingTxSufficient() { 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 20706721bf..0e15b05c5c 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 @@ -989,7 +989,7 @@ public class CreateOfferView extends ActivatableViewAndModel amountValueCurrencyBoxTuple = getValueCurrencyBox(BSResources.get("createOffer.amount.prompt")); 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 f149b9d0f6..742c947ca7 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 @@ -855,7 +855,7 @@ public class TakeOfferView extends ActivatableViewAndModel Date: Fri, 15 Apr 2016 19:10:05 +0200 Subject: [PATCH 9/9] Deactivate focus traversal when input fields are deactivated --- .../io/bitsquare/btc/pricefeed/PriceFeed.java | 7 ++-- .../pricefeed/providers/PriceProvider.java | 1 - .../offer/createoffer/CreateOfferView.java | 39 ++++++++++++++----- .../main/offer/takeoffer/TakeOfferView.java | 1 + 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/io/bitsquare/btc/pricefeed/PriceFeed.java b/core/src/main/java/io/bitsquare/btc/pricefeed/PriceFeed.java index 5efb81bc81..a029fa0be0 100644 --- a/core/src/main/java/io/bitsquare/btc/pricefeed/PriceFeed.java +++ b/core/src/main/java/io/bitsquare/btc/pricefeed/PriceFeed.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; +// TODO use https://github.com/timmolter/XChange public class PriceFeed { private static final Logger log = LoggerFactory.getLogger(PriceFeed.class); @@ -44,9 +45,9 @@ public class PriceFeed { } } - private static final long PERIOD_FIAT_SEC = 2 * 60; - private static final long PERIOD_ALL_FIAT_SEC = 60 * 5; - private static final long PERIOD_ALL_CRYPTO_SEC = 60 * 5; + private static final long PERIOD_FIAT_SEC = 90; + private static final long PERIOD_ALL_FIAT_SEC = 60 * 3; + private static final long PERIOD_ALL_CRYPTO_SEC = 60 * 3; private final Map cache = new HashMap<>(); private final PriceProvider fiatPriceProvider = new BitcoinAveragePriceProvider(); diff --git a/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PriceProvider.java b/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PriceProvider.java index 6d564a4d14..36a9e618a0 100644 --- a/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PriceProvider.java +++ b/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PriceProvider.java @@ -7,7 +7,6 @@ import java.io.IOException; import java.io.Serializable; import java.util.Map; -// https://api.bitfinex.com/v1/pubticker/BTCUSD public interface PriceProvider extends Serializable { Map getAllPrices() throws IOException, HttpException; 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 0e15b05c5c..26ce8eb611 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 @@ -52,6 +52,7 @@ import javafx.collections.FXCollections; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.*; +import javafx.scene.Node; import javafx.scene.control.*; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -70,6 +71,8 @@ import org.jetbrains.annotations.NotNull; import javax.inject.Inject; import java.io.ByteArrayInputStream; import java.net.URI; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; import static io.bitsquare.gui.util.FormBuilder.*; @@ -123,6 +126,7 @@ public class CreateOfferView extends ActivatableViewAndModel editOfferElements = new ArrayList<>(); /////////////////////////////////////////////////////////////////////////////////////////// @@ -281,15 +285,10 @@ public class CreateOfferView extends ActivatableViewAndModel { + node.setMouseTransparent(true); + node.setFocusTraversable(false); + }); balanceTextField.setTargetAmount(model.dataModel.totalToPayAsCoin.get()); @@ -728,11 +727,14 @@ public class CreateOfferView extends ActivatableViewAndModel currencyComboBoxTuple = addLabelComboBox(gridPane, ++gridRow, "Currency:"); currencyComboBoxLabel = currencyComboBoxTuple.first; + editOfferElements.add(currencyComboBoxLabel); currencyComboBox = currencyComboBoxTuple.second; + editOfferElements.add(currencyComboBox); currencyComboBox.setPromptText("Select currency"); currencyComboBox.setConverter(new StringConverter() { @Override @@ -748,7 +750,9 @@ public class CreateOfferView extends ActivatableViewAndModel currencyTextFieldTuple = addLabelTextField(gridPane, gridRow, "Currency:", "", 5); currencyTextFieldLabel = currencyTextFieldTuple.first; + editOfferElements.add(currencyTextFieldLabel); currencyTextField = currencyTextFieldTuple.second; + editOfferElements.add(currencyTextField); } private void addAmountPriceGroup() { @@ -775,9 +779,11 @@ public class CreateOfferView extends ActivatableViewAndModel tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, BSResources.get("createOffer.amountPriceBox.next"), BSResources.get("shared.cancel")); nextButton = tuple.first; + editOfferElements.add(nextButton); nextButton.disableProperty().bind(model.isNextButtonDisabled); //UserThread.runAfter(() -> nextButton.requestFocus(), 100, TimeUnit.MILLISECONDS); cancelButton1 = tuple.second; + editOfferElements.add(cancelButton1); cancelButton1.setDefaultButton(false); cancelButton1.setOnAction(e -> { close(); @@ -912,9 +918,12 @@ public class CreateOfferView extends ActivatableViewAndModel amountValueCurrencyBoxTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.amount.prompt")); HBox amountValueCurrencyBox = amountValueCurrencyBoxTuple.first; amountTextField = amountValueCurrencyBoxTuple.second; + editOfferElements.add(amountTextField); amountBtcLabel = amountValueCurrencyBoxTuple.third; + editOfferElements.add(amountBtcLabel); Tuple2 amountInputBoxTuple = getTradeInputBox(amountValueCurrencyBox, model.getAmountDescription()); amountDescriptionLabel = amountInputBoxTuple.first; + editOfferElements.add(amountDescriptionLabel); VBox amountBox = amountInputBoxTuple.second; // x @@ -926,14 +935,18 @@ public class CreateOfferView extends ActivatableViewAndModel priceValueCurrencyBoxTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.price.prompt")); HBox priceValueCurrencyBox = priceValueCurrencyBoxTuple.first; priceTextField = priceValueCurrencyBoxTuple.second; + editOfferElements.add(priceTextField); priceCurrencyLabel = priceValueCurrencyBoxTuple.third; + editOfferElements.add(priceCurrencyLabel); Tuple2 priceInputBoxTuple = getTradeInputBox(priceValueCurrencyBox, BSResources.get("createOffer.amountPriceBox.priceDescription")); priceDescriptionLabel = priceInputBoxTuple.first; + editOfferElements.add(priceDescriptionLabel); VBox priceBox = priceInputBoxTuple.second; // Fixed/Percentage toggle ToggleGroup toggleGroup = new ToggleGroup(); fixedPriceButton = new ToggleButton("Fixed"); + editOfferElements.add(fixedPriceButton); fixedPriceButton.setId("toggle-price-left"); fixedPriceButton.setToggleGroup(toggleGroup); fixedPriceButton.selectedProperty().addListener((ov, oldValue, newValue) -> { @@ -942,6 +955,7 @@ public class CreateOfferView extends ActivatableViewAndModel { @@ -962,9 +976,12 @@ public class CreateOfferView extends ActivatableViewAndModel volumeValueCurrencyBoxTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.volume.prompt")); HBox volumeValueCurrencyBox = volumeValueCurrencyBoxTuple.first; volumeTextField = volumeValueCurrencyBoxTuple.second; + editOfferElements.add(volumeTextField); volumeCurrencyLabel = volumeValueCurrencyBoxTuple.third; + editOfferElements.add(volumeCurrencyLabel); Tuple2 volumeInputBoxTuple = getTradeInputBox(volumeValueCurrencyBox, model.volumeDescriptionLabel.get()); volumeDescriptionLabel = volumeInputBoxTuple.first; + editOfferElements.add(volumeDescriptionLabel); VBox volumeBox = volumeInputBoxTuple.second; HBox hBox = new HBox(); @@ -982,7 +999,9 @@ public class CreateOfferView extends ActivatableViewAndModel priceAsPercentageTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.price.prompt")); HBox priceAsPercentageValueCurrencyBox = priceAsPercentageTuple.first; priceAsPercentageTextField = priceAsPercentageTuple.second; + editOfferElements.add(priceAsPercentageTextField); priceAsPercentageLabel = priceAsPercentageTuple.third; + editOfferElements.add(priceAsPercentageLabel); Tuple2 priceAsPercentageInputBoxTuple = getTradeInputBox(priceAsPercentageValueCurrencyBox, "Distance in % from market price"); priceAsPercentageInputBoxTuple.first.setPrefWidth(200); @@ -995,7 +1014,9 @@ public class CreateOfferView extends ActivatableViewAndModel amountValueCurrencyBoxTuple = getValueCurrencyBox(BSResources.get("createOffer.amount.prompt")); HBox amountValueCurrencyBox = amountValueCurrencyBoxTuple.first; minAmountTextField = amountValueCurrencyBoxTuple.second; + editOfferElements.add(minAmountTextField); minAmountBtcLabel = amountValueCurrencyBoxTuple.third; + editOfferElements.add(minAmountBtcLabel); Tuple2 amountInputBoxTuple = getTradeInputBox(amountValueCurrencyBox, BSResources.get("createOffer.amountPriceBox" + ".minAmountDescription")); 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 742c947ca7..b746f89f27 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 @@ -287,6 +287,7 @@ public class TakeOfferView extends ActivatableViewAndModel