From 2e52b5472f2d698f77952a8cd5cab6bf53e6d61c Mon Sep 17 00:00:00 2001 From: jmacxx <47253594+jmacxx@users.noreply.github.com> Date: Mon, 23 Nov 2020 18:23:01 -0600 Subject: [PATCH] Show price deviation in portfolio open offers and closed trades Fixes #4641 --- .../resources/i18n/displayStrings.properties | 3 + .../closedtrades/ClosedTradesView.fxml | 13 +- .../closedtrades/ClosedTradesView.java | 112 +++++++++++++----- .../closedtrades/ClosedTradesViewModel.java | 11 ++ .../portfolio/openoffer/OpenOffersView.fxml | 13 +- .../portfolio/openoffer/OpenOffersView.java | 34 +++++- .../openoffer/OpenOffersViewModel.java | 16 ++- 7 files changed, 156 insertions(+), 46 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 915030e395..71899b3558 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -71,6 +71,7 @@ shared.amountWithCur=Amount in {0} shared.volumeWithCur=Volume in {0} shared.currency=Currency shared.market=Market +shared.deviation=Deviation shared.paymentMethod=Payment method shared.tradeCurrency=Trade currency shared.offerType=Offer type @@ -567,6 +568,8 @@ portfolio.tab.history=History portfolio.tab.failed=Failed portfolio.tab.editOpenOffer=Edit offer +portfolio.closedTrades.deviation.help=Percentage price deviation from market + portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\n\ Please do NOT send the fiat or altcoin payment. Contact Bisq \ developers on Keybase [HYPERLINK:https://keybase.io/team/bisq] or on the \ diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml index 25900ef61a..4395e2e548 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml @@ -34,17 +34,18 @@ - - - + + + - - + + + - + diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java index c282e86db0..87407bdbc9 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java @@ -21,6 +21,7 @@ import bisq.desktop.common.view.ActivatableViewAndModel; import bisq.desktop.common.view.FxmlView; import bisq.desktop.components.AutoTooltipButton; import bisq.desktop.components.AutoTooltipLabel; +import bisq.desktop.components.AutoTooltipTableColumn; import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.components.InputTextField; import bisq.desktop.components.PeerInfoIcon; @@ -79,10 +80,37 @@ import java.util.function.Function; public class ClosedTradesView extends ActivatableViewAndModel { private final boolean useDevPrivilegeKeys; + private enum ColumnNames { + TRADE_ID(Res.get("shared.tradeId")), + DATE(Res.get("shared.dateTime")), + MARKET(Res.get("shared.market")), + PRICE(Res.get("shared.price")), + DEVIATION(Res.get("shared.deviation")), + AMOUNT(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode())), + VOLUME(Res.get("shared.amount")), + TX_FEE(Res.get("shared.txFee")), + TRADE_FEE(Res.get("shared.tradeFee")), + BUYER_SEC(Res.get("shared.buyerSecurityDeposit")), + SELLER_SEC(Res.get("shared.sellerSecurityDeposit")), + OFFER_TYPE(Res.get("shared.offerType")), + STATUS(Res.get("shared.state")); + + private final String text; + + ColumnNames(String text) { + this.text = text; + } + @Override + public String toString() { + return text; + } + } + @FXML TableView tableView; @FXML - TableColumn priceColumn, amountColumn, volumeColumn, txFeeColumn, tradeFeeColumn, buyerSecurityDepositColumn, sellerSecurityDepositColumn, + TableColumn priceColumn, deviationColumn, amountColumn, volumeColumn, + txFeeColumn, tradeFeeColumn, buyerSecurityDepositColumn, sellerSecurityDepositColumn, marketColumn, directionColumn, dateColumn, tradeIdColumn, stateColumn, avatarColumn; @FXML HBox footerBox; @@ -120,18 +148,20 @@ public class ClosedTradesView extends ActivatableViewAndModel(ColumnNames.DEVIATION.toString(), + Res.get("portfolio.closedTrades.deviation.help")).getGraphic()); + amountColumn.setGraphic(new AutoTooltipLabel(ColumnNames.AMOUNT.toString())); + volumeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.VOLUME.toString())); + marketColumn.setGraphic(new AutoTooltipLabel(ColumnNames.MARKET.toString())); + directionColumn.setGraphic(new AutoTooltipLabel(ColumnNames.OFFER_TYPE.toString())); + dateColumn.setGraphic(new AutoTooltipLabel(ColumnNames.DATE.toString())); + tradeIdColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TRADE_ID.toString())); + stateColumn.setGraphic(new AutoTooltipLabel(ColumnNames.STATUS.toString())); avatarColumn.setText(""); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); @@ -145,6 +175,7 @@ public class ClosedTradesView extends ActivatableViewAndModel o instanceof Trade ? ((Trade) o).getTradePrice() : o.getOffer().getPrice() )); + deviationColumn.setComparator(Comparator.comparing(o -> + o.getTradable().getOffer().isUseMarketBasedPrice() ? o.getTradable().getOffer().getMarketPriceMargin() : 1, + Comparator.nullsFirst(Comparator.naturalOrder()))); volumeColumn.setComparator(nullsFirstComparingAsTrade(Trade::getTradeVolume)); amountColumn.setComparator(nullsFirstComparingAsTrade(Trade::getTradeAmount)); avatarColumn.setComparator(nullsFirstComparingAsTrade(o -> @@ -217,25 +251,27 @@ public class ClosedTradesView extends ActivatableViewAndModel { final ObservableList> tableColumns = tableView.getColumns(); CSVEntryConverter headerConverter = transactionsListItem -> { - String[] columns = new String[12]; - for (int i = 0; i < columns.length; i++) - columns[i] = ((AutoTooltipLabel) tableColumns.get(i).getGraphic()).getText(); + String[] columns = new String[ColumnNames.values().length]; + for (ColumnNames m : ColumnNames.values()) { + columns[m.ordinal()] = m.toString(); + } return columns; }; CSVEntryConverter contentConverter = item -> { - String[] columns = new String[12]; - columns[0] = model.getTradeId(item); - columns[1] = model.getDate(item); - columns[2] = model.getMarketLabel(item); - columns[3] = model.getPrice(item); - columns[4] = model.getAmount(item); - columns[5] = model.getVolume(item); - columns[6] = model.getTxFee(item); - columns[7] = model.getMakerFee(item); - columns[8] = model.getBuyerSecurityDeposit(item); - columns[9] = model.getSellerSecurityDeposit(item); - columns[10] = model.getDirectionLabel(item); - columns[11] = model.getState(item); + String[] columns = new String[ColumnNames.values().length]; + columns[ColumnNames.TRADE_ID.ordinal()] = model.getTradeId(item); + columns[ColumnNames.DATE.ordinal()] = model.getDate(item); + columns[ColumnNames.MARKET.ordinal()] = model.getMarketLabel(item); + columns[ColumnNames.PRICE.ordinal()] = model.getPrice(item); + columns[ColumnNames.DEVIATION.ordinal()] = model.getPriceDeviation(item); + columns[ColumnNames.AMOUNT.ordinal()] = model.getAmount(item); + columns[ColumnNames.VOLUME.ordinal()] = model.getVolume(item); + columns[ColumnNames.TX_FEE.ordinal()] = model.getTxFee(item); + columns[ColumnNames.TRADE_FEE.ordinal()] = model.getMakerFee(item); + columns[ColumnNames.BUYER_SEC.ordinal()] = model.getBuyerSecurityDeposit(item); + columns[ColumnNames.SELLER_SEC.ordinal()] = model.getSellerSecurityDeposit(item); + columns[ColumnNames.OFFER_TYPE.ordinal()] = model.getDirectionLabel(item); + columns[ColumnNames.STATUS.ordinal()] = model.getState(item); return columns; }; @@ -461,6 +497,24 @@ public class ClosedTradesView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(offer.getValue())); + deviationColumn.setCellFactory( + new Callback<>() { + @Override + public TableCell call( + TableColumn column) { + return new TableCell<>() { + @Override + public void updateItem(final ClosedTradableListItem item, boolean empty) { + super.updateItem(item, empty); + setGraphic(new AutoTooltipLabel(model.getPriceDeviation(item))); + } + }; + } + }); + } + private void setVolumeColumnCellFactory() { volumeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); volumeColumn.setCellFactory( diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java index 6e023f0036..23487d6650 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java @@ -78,6 +78,17 @@ class ClosedTradesViewModel extends ActivatableWithDataModel - - + + - - - + + + + - + diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java index ecdee3ca5e..8dc987efaf 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java @@ -22,6 +22,7 @@ import bisq.desktop.common.view.ActivatableViewAndModel; import bisq.desktop.common.view.FxmlView; import bisq.desktop.components.AutoTooltipCheckBox; import bisq.desktop.components.AutoTooltipLabel; +import bisq.desktop.components.AutoTooltipTableColumn; import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.main.MainView; import bisq.desktop.main.funds.FundsView; @@ -67,7 +68,7 @@ public class OpenOffersView extends ActivatableViewAndModel tableView; @FXML - TableColumn priceColumn, amountColumn, volumeColumn, + TableColumn priceColumn, deviationColumn, amountColumn, volumeColumn, marketColumn, directionColumn, dateColumn, offerIdColumn, deactivateItemColumn, removeItemColumn, editItemColumn, paymentMethodColumn; private final Navigation navigation; @@ -86,6 +87,8 @@ public class OpenOffersView extends ActivatableViewAndModel(Res.get("shared.deviation"), + Res.get("portfolio.closedTrades.deviation.help")).getGraphic()); amountColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.BTCMinMax"))); volumeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amountMinMax"))); marketColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.market"))); @@ -100,6 +103,7 @@ public class OpenOffersView extends ActivatableViewAndModel o.getOffer().getAmount())); priceColumn.setComparator(Comparator.comparing(o -> o.getOffer().getPrice(), Comparator.nullsFirst(Comparator.naturalOrder()))); + deviationColumn.setComparator(Comparator.comparing(o -> + o.getOffer().isUseMarketBasedPrice() ? o.getOffer().getMarketPriceMargin() : 1, + Comparator.nullsFirst(Comparator.naturalOrder()))); volumeColumn.setComparator(Comparator.comparing(o -> o.getOffer().getVolume(), Comparator.nullsFirst(Comparator.naturalOrder()))); dateColumn.setComparator(Comparator.comparing(o -> o.getOffer().getDate())); paymentMethodColumn.setComparator(Comparator.comparing(o -> o.getOffer().getPaymentMethod().getId())); @@ -308,6 +315,31 @@ public class OpenOffersView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(offer.getValue())); + deviationColumn.setCellFactory( + new Callback<>() { + @Override + public TableCell call( + TableColumn column) { + return new TableCell<>() { + @Override + public void updateItem(final OpenOfferListItem item, boolean empty) { + super.updateItem(item, empty); + getStyleClass().removeAll("offer-disabled"); + + if (item != null) { + if (model.isDeactivated(item)) getStyleClass().add("offer-disabled"); + setGraphic(new AutoTooltipLabel(model.getPriceDeviation(item))); + } else { + setGraphic(null); + } + } + }; + } + }); + } + private void setVolumeColumnCellFactory() { volumeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); volumeColumn.setCellFactory( diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java index 1e3b2884f0..94b4533935 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java @@ -93,10 +93,18 @@ class OpenOffersViewModel extends ActivatableWithDataModel Offer offer = item.getOffer(); Price price = offer.getPrice(); if (price != null) { - String postFix = ""; - if (offer.isUseMarketBasedPrice()) - postFix = " (" + FormattingUtils.formatPercentagePrice(offer.getMarketPriceMargin()) + ")"; - return FormattingUtils.formatPrice(price) + postFix; + return FormattingUtils.formatPrice(price); + } else { + return Res.get("shared.na"); + } + } + + String getPriceDeviation(OpenOfferListItem item) { + if ((item == null)) + return ""; + Offer offer = item.getOffer(); + if (offer.isUseMarketBasedPrice()) { + return FormattingUtils.formatPercentagePrice(offer.getMarketPriceMargin()); } else { return Res.get("shared.na"); }