mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-23 15:00:30 +01:00
Merge pull request #5315 from chimp1984/improve-portfolio-history
Improve portfolio history
This commit is contained in:
commit
64ab053b1b
13 changed files with 443 additions and 68 deletions
|
@ -17,8 +17,15 @@
|
||||||
|
|
||||||
package bisq.core.util;
|
package bisq.core.util;
|
||||||
|
|
||||||
|
import bisq.core.monetary.Altcoin;
|
||||||
|
import bisq.core.monetary.AltcoinExchangeRate;
|
||||||
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.monetary.Volume;
|
import bisq.core.monetary.Volume;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.bitcoinj.utils.ExchangeRate;
|
||||||
|
import org.bitcoinj.utils.Fiat;
|
||||||
|
|
||||||
public class VolumeUtil {
|
public class VolumeUtil {
|
||||||
|
|
||||||
public static Volume getRoundedFiatVolume(Volume volumeByAmount) {
|
public static Volume getRoundedFiatVolume(Volume volumeByAmount) {
|
||||||
|
@ -47,4 +54,12 @@ public class VolumeUtil {
|
||||||
roundedVolume = Math.max(factor, roundedVolume);
|
roundedVolume = Math.max(factor, roundedVolume);
|
||||||
return Volume.parse(String.valueOf(roundedVolume), volumeByAmount.getCurrencyCode());
|
return Volume.parse(String.valueOf(roundedVolume), volumeByAmount.getCurrencyCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Volume getVolume(Coin amount, Price price) {
|
||||||
|
if (price.getMonetary() instanceof Altcoin) {
|
||||||
|
return new Volume(new AltcoinExchangeRate((Altcoin) price.getMonetary()).coinToAltcoin(amount));
|
||||||
|
} else {
|
||||||
|
return new Volume(new ExchangeRate((Fiat) price.getMonetary()).coinToFiat(amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,6 @@ import java.util.Locale;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Singleton
|
@Singleton
|
||||||
public class BsqFormatter implements CoinFormatter {
|
public class BsqFormatter implements CoinFormatter {
|
||||||
|
@ -224,10 +222,15 @@ public class BsqFormatter implements CoinFormatter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String formatCoin(Coin coin) {
|
public String formatCoin(Coin coin) {
|
||||||
return immutableCoinFormatter.formatCoin(coin);
|
return formatCoin(coin, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String formatCoin(Coin coin, boolean appendCode) {
|
||||||
|
return appendCode ?
|
||||||
|
immutableCoinFormatter.formatCoinWithCode(coin) :
|
||||||
|
immutableCoinFormatter.formatCoin(coin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String formatCoin(Coin coin, int decimalPlaces) {
|
public String formatCoin(Coin coin, int decimalPlaces) {
|
||||||
return immutableCoinFormatter.formatCoin(coin, decimalPlaces);
|
return immutableCoinFormatter.formatCoin(coin, decimalPlaces);
|
||||||
}
|
}
|
||||||
|
@ -240,7 +243,7 @@ public class BsqFormatter implements CoinFormatter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String formatCoinWithCode(Coin coin) {
|
public String formatCoinWithCode(Coin coin) {
|
||||||
return immutableCoinFormatter.formatCoinWithCode(coin);
|
return formatCoin(coin, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String formatCoinWithCode(long value) {
|
public String formatCoinWithCode(long value) {
|
||||||
|
|
|
@ -2,12 +2,11 @@ package bisq.core.util.coin;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public interface CoinFormatter {
|
public interface CoinFormatter {
|
||||||
String formatCoin(Coin coin);
|
String formatCoin(Coin coin);
|
||||||
|
|
||||||
@NotNull
|
String formatCoin(Coin coin, boolean appendCode);
|
||||||
|
|
||||||
String formatCoin(Coin coin, int decimalPlaces);
|
String formatCoin(Coin coin, int decimalPlaces);
|
||||||
|
|
||||||
String formatCoin(Coin coin, int decimalPlaces, boolean decimalAligned, int maxNumberOfDigits);
|
String formatCoin(Coin coin, int decimalPlaces, boolean decimalAligned, int maxNumberOfDigits);
|
||||||
|
|
|
@ -27,8 +27,6 @@ import javax.inject.Inject;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ImmutableCoinFormatter implements CoinFormatter {
|
public class ImmutableCoinFormatter implements CoinFormatter {
|
||||||
|
|
||||||
|
@ -56,7 +54,11 @@ public class ImmutableCoinFormatter implements CoinFormatter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
public String formatCoin(Coin coin, boolean appendCode) {
|
||||||
|
return appendCode ? formatCoinWithCode(coin) : formatCoin(coin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String formatCoin(Coin coin, int decimalPlaces) {
|
public String formatCoin(Coin coin, int decimalPlaces) {
|
||||||
return formatCoin(coin, decimalPlaces, false, 0);
|
return formatCoin(coin, decimalPlaces, false, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,7 @@ shared.sendingConfirmation=Sending confirmation...
|
||||||
shared.sendingConfirmationAgain=Please send confirmation again
|
shared.sendingConfirmationAgain=Please send confirmation again
|
||||||
shared.exportCSV=Export to CSV
|
shared.exportCSV=Export to CSV
|
||||||
shared.exportJSON=Export to JSON
|
shared.exportJSON=Export to JSON
|
||||||
|
shared.summary=Show summary
|
||||||
shared.noDateAvailable=No date available
|
shared.noDateAvailable=No date available
|
||||||
shared.noDetailsAvailable=No details available
|
shared.noDetailsAvailable=No details available
|
||||||
shared.notUsedYet=Not used yet
|
shared.notUsedYet=Not used yet
|
||||||
|
@ -2722,6 +2723,17 @@ txDetailsWindow.bsq.note=You have sent BSQ funds. \
|
||||||
txDetailsWindow.sentTo=Sent to
|
txDetailsWindow.sentTo=Sent to
|
||||||
txDetailsWindow.txId=TxId
|
txDetailsWindow.txId=TxId
|
||||||
|
|
||||||
|
closedTradesSummaryWindow.headline=Trade history summary
|
||||||
|
closedTradesSummaryWindow.totalAmount.title=Total trade amount
|
||||||
|
closedTradesSummaryWindow.totalAmount.value={0} ({1} with current market price)
|
||||||
|
closedTradesSummaryWindow.totalVolume.title=Total amount traded in {0}
|
||||||
|
closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees
|
||||||
|
closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount)
|
||||||
|
closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC
|
||||||
|
closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount)
|
||||||
|
closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ
|
||||||
|
closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount)
|
||||||
|
|
||||||
walletPasswordWindow.headline=Enter password to unlock
|
walletPasswordWindow.headline=Enter password to unlock
|
||||||
|
|
||||||
torNetworkSettingWindow.header=Tor networks settings
|
torNetworkSettingWindow.header=Tor networks settings
|
||||||
|
|
|
@ -106,6 +106,17 @@ public class PriceUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Price marketPriceToPrice(MarketPrice marketPrice) {
|
||||||
|
String currencyCode = marketPrice.getCurrencyCode();
|
||||||
|
double priceAsDouble = marketPrice.getPrice();
|
||||||
|
int precision = CurrencyUtil.isCryptoCurrency(currencyCode) ?
|
||||||
|
Altcoin.SMALLEST_UNIT_EXPONENT :
|
||||||
|
Fiat.SMALLEST_UNIT_EXPONENT;
|
||||||
|
double scaled = MathUtils.scaleUpByPowerOf10(priceAsDouble, precision);
|
||||||
|
long roundedToLong = MathUtils.roundDoubleToLong(scaled);
|
||||||
|
return Price.valueOf(currencyCode, roundedToLong);
|
||||||
|
}
|
||||||
|
|
||||||
public void recalculateBsq30DayAveragePrice() {
|
public void recalculateBsq30DayAveragePrice() {
|
||||||
bsq30DayAveragePrice = null;
|
bsq30DayAveragePrice = null;
|
||||||
bsq30DayAveragePrice = getBsq30DayAveragePrice();
|
bsq30DayAveragePrice = getBsq30DayAveragePrice();
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.desktop.main.overlays.windows;
|
||||||
|
|
||||||
|
import bisq.desktop.main.overlays.Overlay;
|
||||||
|
import bisq.desktop.main.portfolio.closedtrades.ClosedTradesViewModel;
|
||||||
|
import bisq.desktop.util.Layout;
|
||||||
|
|
||||||
|
import bisq.core.locale.Res;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static bisq.desktop.util.FormBuilder.addConfirmationLabelLabel;
|
||||||
|
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
|
||||||
|
|
||||||
|
public class ClosedTradesSummaryWindow extends Overlay<ClosedTradesSummaryWindow> {
|
||||||
|
private final ClosedTradesViewModel model;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ClosedTradesSummaryWindow(ClosedTradesViewModel model) {
|
||||||
|
this.model = model;
|
||||||
|
type = Type.Information;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
rowIndex = 0;
|
||||||
|
width = 900;
|
||||||
|
createGridPane();
|
||||||
|
addContent();
|
||||||
|
addButtons();
|
||||||
|
display();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Protected
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createGridPane() {
|
||||||
|
super.createGridPane();
|
||||||
|
gridPane.setPadding(new Insets(35, 40, 30, 40));
|
||||||
|
gridPane.getStyleClass().add("grid-pane");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addContent() {
|
||||||
|
Map<String, String> totalVolumeByCurrency = model.getTotalVolumeByCurrency();
|
||||||
|
int rowSpan = totalVolumeByCurrency.size() + 4;
|
||||||
|
addTitledGroupBg(gridPane, rowIndex, rowSpan, Res.get("closedTradesSummaryWindow.headline"));
|
||||||
|
Coin totalTradeAmount = model.getTotalTradeAmount();
|
||||||
|
addConfirmationLabelLabel(gridPane, rowIndex,
|
||||||
|
Res.get("closedTradesSummaryWindow.totalAmount.title"),
|
||||||
|
model.getTotalAmountWithVolume(totalTradeAmount), Layout.TWICE_FIRST_ROW_DISTANCE);
|
||||||
|
totalVolumeByCurrency.entrySet().forEach(entry -> {
|
||||||
|
addConfirmationLabelLabel(gridPane, ++rowIndex,
|
||||||
|
Res.get("closedTradesSummaryWindow.totalVolume.title", entry.getKey()), entry.getValue());
|
||||||
|
});
|
||||||
|
addConfirmationLabelLabel(gridPane, ++rowIndex,
|
||||||
|
Res.get("closedTradesSummaryWindow.totalMinerFee.title"),
|
||||||
|
model.getTotalTxFee(totalTradeAmount));
|
||||||
|
addConfirmationLabelLabel(gridPane, ++rowIndex,
|
||||||
|
Res.get("closedTradesSummaryWindow.totalTradeFeeInBtc.title"),
|
||||||
|
model.getTotalTradeFeeInBtc(totalTradeAmount));
|
||||||
|
addConfirmationLabelLabel(gridPane, ++rowIndex,
|
||||||
|
Res.get("closedTradesSummaryWindow.totalTradeFeeInBsq.title") + " ", // lets give some extra space
|
||||||
|
model.getTotalTradeFeeInBsq(totalTradeAmount));
|
||||||
|
}
|
||||||
|
}
|
|
@ -228,7 +228,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
||||||
|
|
||||||
String txFee = Res.get("shared.makerTxFee", formatter.formatCoinWithCode(offer.getTxFee())) +
|
String txFee = Res.get("shared.makerTxFee", formatter.formatCoinWithCode(offer.getTxFee())) +
|
||||||
" / " +
|
" / " +
|
||||||
Res.get("shared.takerTxFee", formatter.formatCoinWithCode(offer.getTxFee().multiply(3L)));
|
Res.get("shared.takerTxFee", formatter.formatCoinWithCode(trade.getTxFee().multiply(3)));
|
||||||
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.txFee"), txFee);
|
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.txFee"), txFee);
|
||||||
|
|
||||||
NodeAddress arbitratorNodeAddress = trade.getArbitratorNodeAddress();
|
NodeAddress arbitratorNodeAddress = trade.getArbitratorNodeAddress();
|
||||||
|
|
|
@ -18,11 +18,27 @@
|
||||||
package bisq.desktop.main.portfolio.closedtrades;
|
package bisq.desktop.main.portfolio.closedtrades;
|
||||||
|
|
||||||
import bisq.desktop.common.model.ActivatableDataModel;
|
import bisq.desktop.common.model.ActivatableDataModel;
|
||||||
|
import bisq.desktop.main.PriceUtil;
|
||||||
|
|
||||||
|
import bisq.core.btc.wallet.BsqWalletService;
|
||||||
|
import bisq.core.monetary.Price;
|
||||||
|
import bisq.core.monetary.Volume;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferPayload;
|
import bisq.core.offer.OfferPayload;
|
||||||
|
import bisq.core.provider.price.MarketPrice;
|
||||||
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.Tradable;
|
import bisq.core.trade.Tradable;
|
||||||
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.closed.ClosedTradableManager;
|
import bisq.core.trade.closed.ClosedTradableManager;
|
||||||
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
|
import bisq.core.user.Preferences;
|
||||||
|
import bisq.core.util.AveragePriceUtil;
|
||||||
|
import bisq.core.util.VolumeUtil;
|
||||||
|
|
||||||
|
import bisq.common.util.Tuple2;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.bitcoinj.utils.Fiat;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
@ -30,17 +46,33 @@ import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
class ClosedTradesDataModel extends ActivatableDataModel {
|
class ClosedTradesDataModel extends ActivatableDataModel {
|
||||||
|
|
||||||
final ClosedTradableManager closedTradableManager;
|
final ClosedTradableManager closedTradableManager;
|
||||||
|
private final BsqWalletService bsqWalletService;
|
||||||
|
private final Preferences preferences;
|
||||||
|
private final TradeStatisticsManager tradeStatisticsManager;
|
||||||
|
private final PriceFeedService priceFeedService;
|
||||||
private final ObservableList<ClosedTradableListItem> list = FXCollections.observableArrayList();
|
private final ObservableList<ClosedTradableListItem> list = FXCollections.observableArrayList();
|
||||||
private final ListChangeListener<Tradable> tradesListChangeListener;
|
private final ListChangeListener<Tradable> tradesListChangeListener;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ClosedTradesDataModel(ClosedTradableManager closedTradableManager) {
|
public ClosedTradesDataModel(ClosedTradableManager closedTradableManager,
|
||||||
|
BsqWalletService bsqWalletService,
|
||||||
|
Preferences preferences,
|
||||||
|
TradeStatisticsManager tradeStatisticsManager,
|
||||||
|
PriceFeedService priceFeedService) {
|
||||||
this.closedTradableManager = closedTradableManager;
|
this.closedTradableManager = closedTradableManager;
|
||||||
|
this.bsqWalletService = bsqWalletService;
|
||||||
|
this.preferences = preferences;
|
||||||
|
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||||
|
this.priceFeedService = priceFeedService;
|
||||||
|
|
||||||
tradesListChangeListener = change -> applyList();
|
tradesListChangeListener = change -> applyList();
|
||||||
}
|
}
|
||||||
|
@ -73,4 +105,112 @@ class ClosedTradesDataModel extends ActivatableDataModel {
|
||||||
list.sort((o1, o2) -> o2.getTradable().getDate().compareTo(o1.getTradable().getDate()));
|
list.sort((o1, o2) -> o2.getTradable().getDate().compareTo(o1.getTradable().getDate()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean wasMyOffer(Tradable tradable) {
|
||||||
|
return closedTradableManager.wasMyOffer(tradable.getOffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
Coin getTotalAmount() {
|
||||||
|
return Coin.valueOf(getList().stream()
|
||||||
|
.map(ClosedTradableListItem::getTradable)
|
||||||
|
.filter(e -> e instanceof Trade)
|
||||||
|
.map(e -> (Trade) e)
|
||||||
|
.mapToLong(Trade::getTradeAmountAsLong)
|
||||||
|
.sum());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Long> getTotalVolumeByCurrency() {
|
||||||
|
Map<String, Long> map = new HashMap<>();
|
||||||
|
getList().stream()
|
||||||
|
.map(ClosedTradableListItem::getTradable)
|
||||||
|
.filter(e -> e instanceof Trade)
|
||||||
|
.map(e -> (Trade) e)
|
||||||
|
.map(Trade::getTradeVolume)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(volume -> {
|
||||||
|
String currencyCode = volume.getCurrencyCode();
|
||||||
|
map.putIfAbsent(currencyCode, 0L);
|
||||||
|
map.put(currencyCode, volume.getValue() + map.get(currencyCode));
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Volume> getVolumeInUserFiatCurrency(Coin amount) {
|
||||||
|
return getVolume(amount, preferences.getPreferredTradeCurrency().getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Volume> getVolume(Coin amount, String currencyCode) {
|
||||||
|
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
|
||||||
|
if (marketPrice == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Price price = PriceUtil.marketPriceToPrice(marketPrice);
|
||||||
|
return Optional.of(VolumeUtil.getVolume(amount, price));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Volume getBsqVolumeInUsdWithAveragePrice(Coin amount) {
|
||||||
|
Tuple2<Price, Price> tuple = AveragePriceUtil.getAveragePriceTuple(preferences, tradeStatisticsManager, 30);
|
||||||
|
Price usdPrice = tuple.first;
|
||||||
|
long value = Math.round(amount.value * usdPrice.getValue() / 100d);
|
||||||
|
return new Volume(Fiat.valueOf("USD", value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coin getTotalTxFee() {
|
||||||
|
return Coin.valueOf(getList().stream()
|
||||||
|
.map(ClosedTradableListItem::getTradable)
|
||||||
|
.mapToLong(tradable -> {
|
||||||
|
if (wasMyOffer(tradable)) {
|
||||||
|
return tradable.getOffer().getTxFee().value;
|
||||||
|
} else {
|
||||||
|
// taker pays for 3 transactions
|
||||||
|
return ((Trade) tradable).getTxFee().multiply(3).value;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coin getTotalTradeFee(boolean expectBtcFee) {
|
||||||
|
return Coin.valueOf(getList().stream()
|
||||||
|
.map(ClosedTradableListItem::getTradable)
|
||||||
|
.mapToLong(tradable -> getTradeFee(tradable, expectBtcFee))
|
||||||
|
.sum());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getTradeFee(Tradable tradable, boolean expectBtcFee) {
|
||||||
|
Offer offer = tradable.getOffer();
|
||||||
|
if (wasMyOffer(tradable)) {
|
||||||
|
String makerFeeTxId = offer.getOfferFeePaymentTxId();
|
||||||
|
boolean notInBsqWallet = bsqWalletService.getTransaction(makerFeeTxId) == null;
|
||||||
|
if (expectBtcFee) {
|
||||||
|
if (notInBsqWallet) {
|
||||||
|
return offer.getMakerFee().value;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (notInBsqWallet) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return offer.getMakerFee().value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Trade trade = (Trade) tradable;
|
||||||
|
String takerFeeTxId = trade.getTakerFeeTxId();
|
||||||
|
boolean notInBsqWallet = bsqWalletService.getTransaction(takerFeeTxId) == null;
|
||||||
|
if (expectBtcFee) {
|
||||||
|
if (notInBsqWallet) {
|
||||||
|
return trade.getTakerFee().value;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (notInBsqWallet) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return trade.getTakerFee().value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
<HBox spacing="10">
|
<HBox spacing="10">
|
||||||
<Label fx:id="numItems"/>
|
<Label fx:id="numItems"/>
|
||||||
<Region fx:id="footerSpacer"/>
|
<Region fx:id="footerSpacer"/>
|
||||||
|
<AutoTooltipButton fx:id="summaryButton"/>
|
||||||
<AutoTooltipButton fx:id="exportButton"/>
|
<AutoTooltipButton fx:id="exportButton"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
</VBox>
|
</VBox>
|
||||||
|
|
|
@ -25,6 +25,7 @@ import bisq.desktop.components.AutoTooltipTableColumn;
|
||||||
import bisq.desktop.components.HyperlinkWithIcon;
|
import bisq.desktop.components.HyperlinkWithIcon;
|
||||||
import bisq.desktop.components.InputTextField;
|
import bisq.desktop.components.InputTextField;
|
||||||
import bisq.desktop.components.PeerInfoIcon;
|
import bisq.desktop.components.PeerInfoIcon;
|
||||||
|
import bisq.desktop.main.overlays.windows.ClosedTradesSummaryWindow;
|
||||||
import bisq.desktop.main.overlays.windows.OfferDetailsWindow;
|
import bisq.desktop.main.overlays.windows.OfferDetailsWindow;
|
||||||
import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
|
import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
|
||||||
import bisq.desktop.util.GUIUtil;
|
import bisq.desktop.util.GUIUtil;
|
||||||
|
@ -68,7 +69,6 @@ import javafx.geometry.Insets;
|
||||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.collections.transformation.FilteredList;
|
import javafx.collections.transformation.FilteredList;
|
||||||
import javafx.collections.transformation.SortedList;
|
import javafx.collections.transformation.SortedList;
|
||||||
|
|
||||||
|
@ -89,8 +89,10 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
DEVIATION(Res.get("shared.deviation")),
|
DEVIATION(Res.get("shared.deviation")),
|
||||||
AMOUNT(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode())),
|
AMOUNT(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode())),
|
||||||
VOLUME(Res.get("shared.amount")),
|
VOLUME(Res.get("shared.amount")),
|
||||||
|
VOLUME_CURRENCY(Res.get("shared.currency")),
|
||||||
TX_FEE(Res.get("shared.txFee")),
|
TX_FEE(Res.get("shared.txFee")),
|
||||||
TRADE_FEE(Res.get("shared.tradeFee")),
|
TRADE_FEE_BTC(Res.get("shared.tradeFee") + " BTC"),
|
||||||
|
TRADE_FEE_BSQ(Res.get("shared.tradeFee") + " BSQ"),
|
||||||
BUYER_SEC(Res.get("shared.buyerSecurityDeposit")),
|
BUYER_SEC(Res.get("shared.buyerSecurityDeposit")),
|
||||||
SELLER_SEC(Res.get("shared.sellerSecurityDeposit")),
|
SELLER_SEC(Res.get("shared.sellerSecurityDeposit")),
|
||||||
OFFER_TYPE(Res.get("shared.offerType")),
|
OFFER_TYPE(Res.get("shared.offerType")),
|
||||||
|
@ -123,7 +125,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
@FXML
|
@FXML
|
||||||
Pane searchBoxSpacer;
|
Pane searchBoxSpacer;
|
||||||
@FXML
|
@FXML
|
||||||
AutoTooltipButton exportButton;
|
AutoTooltipButton exportButton, summaryButton;
|
||||||
@FXML
|
@FXML
|
||||||
Label numItems;
|
Label numItems;
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -157,7 +159,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
widthListener = (observable, oldValue, newValue) -> onWidthChange((double) newValue);
|
widthListener = (observable, oldValue, newValue) -> onWidthChange((double) newValue);
|
||||||
txFeeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TX_FEE.toString()));
|
txFeeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TX_FEE.toString()));
|
||||||
tradeFeeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TRADE_FEE.toString()));
|
tradeFeeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TRADE_FEE_BTC.toString().replace(" BTC", "")));
|
||||||
buyerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.BUYER_SEC.toString()));
|
buyerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.BUYER_SEC.toString()));
|
||||||
sellerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.SELLER_SEC.toString()));
|
sellerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.SELLER_SEC.toString()));
|
||||||
priceColumn.setGraphic(new AutoTooltipLabel(ColumnNames.PRICE.toString()));
|
priceColumn.setGraphic(new AutoTooltipLabel(ColumnNames.PRICE.toString()));
|
||||||
|
@ -211,7 +213,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
|
|
||||||
//
|
//
|
||||||
tradeFeeColumn.setComparator(Comparator.comparing(item -> {
|
tradeFeeColumn.setComparator(Comparator.comparing(item -> {
|
||||||
String tradeFee = model.getTradeFee(item);
|
String tradeFee = model.getTradeFee(item, true);
|
||||||
// We want to separate BSQ and BTC fees so we use a prefix
|
// We want to separate BSQ and BTC fees so we use a prefix
|
||||||
if (item.getTradable().getOffer().isCurrencyForMakerFeeBtc()) {
|
if (item.getTradable().getOffer().isCurrencyForMakerFeeBtc()) {
|
||||||
return "BTC" + tradeFee;
|
return "BTC" + tradeFee;
|
||||||
|
@ -241,6 +243,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
HBox.setHgrow(footerSpacer, Priority.ALWAYS);
|
HBox.setHgrow(footerSpacer, Priority.ALWAYS);
|
||||||
HBox.setMargin(exportButton, new Insets(0, 10, 0, 0));
|
HBox.setMargin(exportButton, new Insets(0, 10, 0, 0));
|
||||||
exportButton.updateText(Res.get("shared.exportCSV"));
|
exportButton.updateText(Res.get("shared.exportCSV"));
|
||||||
|
summaryButton.updateText(Res.get("shared.summary"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -254,7 +257,6 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
|
|
||||||
numItems.setText(Res.get("shared.numItemsLabel", sortedList.size()));
|
numItems.setText(Res.get("shared.numItemsLabel", sortedList.size()));
|
||||||
exportButton.setOnAction(event -> {
|
exportButton.setOnAction(event -> {
|
||||||
final ObservableList<TableColumn<ClosedTradableListItem, ?>> tableColumns = tableView.getColumns();
|
|
||||||
CSVEntryConverter<ClosedTradableListItem> headerConverter = item -> {
|
CSVEntryConverter<ClosedTradableListItem> headerConverter = item -> {
|
||||||
String[] columns = new String[ColumnNames.values().length];
|
String[] columns = new String[ColumnNames.values().length];
|
||||||
for (ColumnNames m : ColumnNames.values()) {
|
for (ColumnNames m : ColumnNames.values()) {
|
||||||
|
@ -270,9 +272,16 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
columns[ColumnNames.PRICE.ordinal()] = model.getPrice(item);
|
columns[ColumnNames.PRICE.ordinal()] = model.getPrice(item);
|
||||||
columns[ColumnNames.DEVIATION.ordinal()] = model.getPriceDeviation(item);
|
columns[ColumnNames.DEVIATION.ordinal()] = model.getPriceDeviation(item);
|
||||||
columns[ColumnNames.AMOUNT.ordinal()] = model.getAmount(item);
|
columns[ColumnNames.AMOUNT.ordinal()] = model.getAmount(item);
|
||||||
columns[ColumnNames.VOLUME.ordinal()] = model.getVolume(item);
|
columns[ColumnNames.VOLUME.ordinal()] = model.getVolume(item, false);
|
||||||
|
columns[ColumnNames.VOLUME_CURRENCY.ordinal()] = model.getVolumeCurrency(item);
|
||||||
columns[ColumnNames.TX_FEE.ordinal()] = model.getTxFee(item);
|
columns[ColumnNames.TX_FEE.ordinal()] = model.getTxFee(item);
|
||||||
columns[ColumnNames.TRADE_FEE.ordinal()] = model.getTradeFee(item);
|
if (model.isCurrencyForTradeFeeBtc(item)) {
|
||||||
|
columns[ColumnNames.TRADE_FEE_BTC.ordinal()] = model.getTradeFee(item, false);
|
||||||
|
columns[ColumnNames.TRADE_FEE_BSQ.ordinal()] = "";
|
||||||
|
} else {
|
||||||
|
columns[ColumnNames.TRADE_FEE_BTC.ordinal()] = "";
|
||||||
|
columns[ColumnNames.TRADE_FEE_BSQ.ordinal()] = model.getTradeFee(item, false);
|
||||||
|
}
|
||||||
columns[ColumnNames.BUYER_SEC.ordinal()] = model.getBuyerSecurityDeposit(item);
|
columns[ColumnNames.BUYER_SEC.ordinal()] = model.getBuyerSecurityDeposit(item);
|
||||||
columns[ColumnNames.SELLER_SEC.ordinal()] = model.getSellerSecurityDeposit(item);
|
columns[ColumnNames.SELLER_SEC.ordinal()] = model.getSellerSecurityDeposit(item);
|
||||||
columns[ColumnNames.OFFER_TYPE.ordinal()] = model.getDirectionLabel(item);
|
columns[ColumnNames.OFFER_TYPE.ordinal()] = model.getDirectionLabel(item);
|
||||||
|
@ -284,6 +293,8 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
new ClosedTradableListItem(null), sortedList, (Stage) root.getScene().getWindow());
|
new ClosedTradableListItem(null), sortedList, (Stage) root.getScene().getWindow());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
summaryButton.setOnAction(event -> new ClosedTradesSummaryWindow(model).show());
|
||||||
|
|
||||||
filterTextField.textProperty().addListener(filterTextFieldListener);
|
filterTextField.textProperty().addListener(filterTextFieldListener);
|
||||||
applyFilteredListPredicate(filterTextField.getText());
|
applyFilteredListPredicate(filterTextField.getText());
|
||||||
root.widthProperty().addListener(widthListener);
|
root.widthProperty().addListener(widthListener);
|
||||||
|
@ -294,6 +305,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
protected void deactivate() {
|
protected void deactivate() {
|
||||||
sortedList.comparatorProperty().unbind();
|
sortedList.comparatorProperty().unbind();
|
||||||
exportButton.setOnAction(null);
|
exportButton.setOnAction(null);
|
||||||
|
summaryButton.setOnAction(null);
|
||||||
|
|
||||||
filterTextField.textProperty().removeListener(filterTextFieldListener);
|
filterTextField.textProperty().removeListener(filterTextFieldListener);
|
||||||
root.widthProperty().removeListener(widthListener);
|
root.widthProperty().removeListener(widthListener);
|
||||||
|
@ -343,13 +355,13 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.getVolume(item).contains(filterString)) {
|
if (model.getVolume(item, true).contains(filterString)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (model.getAmount(item).contains(filterString)) {
|
if (model.getAmount(item).contains(filterString)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (model.getTradeFee(item).contains(filterString)) {
|
if (model.getTradeFee(item, true).contains(filterString)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (model.getTxFee(item).contains(filterString)) {
|
if (model.getTxFee(item).contains(filterString)) {
|
||||||
|
@ -607,7 +619,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
public void updateItem(final ClosedTradableListItem item, boolean empty) {
|
public void updateItem(final ClosedTradableListItem item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
setGraphic(new AutoTooltipLabel(model.getVolume(item)));
|
setGraphic(new AutoTooltipLabel(model.getVolume(item, true)));
|
||||||
else
|
else
|
||||||
setGraphic(null);
|
setGraphic(null);
|
||||||
}
|
}
|
||||||
|
@ -663,7 +675,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||||
@Override
|
@Override
|
||||||
public void updateItem(final ClosedTradableListItem item, boolean empty) {
|
public void updateItem(final ClosedTradableListItem item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
setGraphic(new AutoTooltipLabel(model.getTradeFee(item)));
|
setGraphic(new AutoTooltipLabel(model.getTradeFee(item, true)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,10 @@ import bisq.desktop.util.DisplayUtils;
|
||||||
|
|
||||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||||
import bisq.core.btc.wallet.BsqWalletService;
|
import bisq.core.btc.wallet.BsqWalletService;
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
|
||||||
import bisq.core.locale.CurrencyUtil;
|
import bisq.core.locale.CurrencyUtil;
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
|
import bisq.core.monetary.Altcoin;
|
||||||
|
import bisq.core.monetary.Volume;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OpenOffer;
|
import bisq.core.offer.OpenOffer;
|
||||||
import bisq.core.trade.Tradable;
|
import bisq.core.trade.Tradable;
|
||||||
|
@ -36,11 +37,9 @@ import bisq.core.util.coin.CoinFormatter;
|
||||||
|
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.bitcoinj.core.Monetary;
|
||||||
import org.bitcoinj.core.Address;
|
import org.bitcoinj.utils.Fiat;
|
||||||
import org.bitcoinj.core.Transaction;
|
|
||||||
import org.bitcoinj.core.TransactionOutput;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
@ -48,10 +47,10 @@ import javax.inject.Named;
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataModel> implements ViewModel {
|
public class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataModel> implements ViewModel {
|
||||||
private final BtcWalletService btcWalletService;
|
|
||||||
private final BsqWalletService bsqWalletService;
|
private final BsqWalletService bsqWalletService;
|
||||||
private final BsqFormatter bsqFormatter;
|
private final BsqFormatter bsqFormatter;
|
||||||
private final CoinFormatter btcFormatter;
|
private final CoinFormatter btcFormatter;
|
||||||
|
@ -60,13 +59,11 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
|
||||||
@Inject
|
@Inject
|
||||||
public ClosedTradesViewModel(ClosedTradesDataModel dataModel,
|
public ClosedTradesViewModel(ClosedTradesDataModel dataModel,
|
||||||
AccountAgeWitnessService accountAgeWitnessService,
|
AccountAgeWitnessService accountAgeWitnessService,
|
||||||
BtcWalletService btcWalletService,
|
|
||||||
BsqWalletService bsqWalletService,
|
BsqWalletService bsqWalletService,
|
||||||
BsqFormatter bsqFormatter,
|
BsqFormatter bsqFormatter,
|
||||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter) {
|
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter) {
|
||||||
super(dataModel);
|
super(dataModel);
|
||||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||||
this.btcWalletService = btcWalletService;
|
|
||||||
this.bsqWalletService = bsqWalletService;
|
this.bsqWalletService = bsqWalletService;
|
||||||
this.bsqFormatter = bsqFormatter;
|
this.bsqFormatter = bsqFormatter;
|
||||||
this.btcFormatter = btcFormatter;
|
this.btcFormatter = btcFormatter;
|
||||||
|
@ -83,8 +80,6 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
|
||||||
String getAmount(ClosedTradableListItem item) {
|
String getAmount(ClosedTradableListItem item) {
|
||||||
if (item != null && item.getTradable() instanceof Trade)
|
if (item != null && item.getTradable() instanceof Trade)
|
||||||
return btcFormatter.formatCoin(((Trade) item.getTradable()).getTradeAmount());
|
return btcFormatter.formatCoin(((Trade) item.getTradable()).getTradeAmount());
|
||||||
else if (item != null && item.getTradable() instanceof OpenOffer)
|
|
||||||
return "-";
|
|
||||||
else
|
else
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -110,52 +105,84 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getVolume(ClosedTradableListItem item) {
|
String getVolume(ClosedTradableListItem item, boolean appendCode) {
|
||||||
if (item != null && item.getTradable() instanceof Trade)
|
if (item == null) {
|
||||||
return DisplayUtils.formatVolumeWithCode(((Trade) item.getTradable()).getTradeVolume());
|
|
||||||
else if (item != null && item.getTradable() instanceof OpenOffer)
|
|
||||||
return "-";
|
|
||||||
else
|
|
||||||
return "";
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.getTradable() instanceof OpenOffer) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Trade trade = (Trade) item.getTradable();
|
||||||
|
return DisplayUtils.formatVolume(trade.getTradeVolume(), appendCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getVolumeCurrency(ClosedTradableListItem item) {
|
||||||
|
if (item == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
Volume volume;
|
||||||
|
if (item.getTradable() instanceof OpenOffer) {
|
||||||
|
OpenOffer openOffer = (OpenOffer) item.getTradable();
|
||||||
|
volume = openOffer.getOffer().getVolume();
|
||||||
|
} else {
|
||||||
|
Trade trade = (Trade) item.getTradable();
|
||||||
|
volume = trade.getTradeVolume();
|
||||||
|
}
|
||||||
|
return volume != null ? volume.getCurrencyCode() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
String getTxFee(ClosedTradableListItem item) {
|
String getTxFee(ClosedTradableListItem item) {
|
||||||
if (item == null)
|
if (item == null)
|
||||||
return "";
|
return "";
|
||||||
Tradable tradable = item.getTradable();
|
Tradable tradable = item.getTradable();
|
||||||
if (!wasMyOffer(tradable) && (tradable instanceof Trade))
|
if (!wasMyOffer(tradable) && (tradable instanceof Trade)) {
|
||||||
return btcFormatter.formatCoin(((Trade) tradable).getTxFee());
|
// taker pays for 3 transactions
|
||||||
else
|
return btcFormatter.formatCoin(((Trade) tradable).getTxFee().multiply(3));
|
||||||
|
} else {
|
||||||
return btcFormatter.formatCoin(tradable.getOffer().getTxFee());
|
return btcFormatter.formatCoin(tradable.getOffer().getTxFee());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getTradeFee(ClosedTradableListItem item) {
|
boolean isCurrencyForTradeFeeBtc(ClosedTradableListItem item) {
|
||||||
|
if (item == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tradable tradable = item.getTradable();
|
||||||
|
Offer offer = tradable.getOffer();
|
||||||
|
if (wasMyOffer(tradable)) {
|
||||||
|
// I was maker so we use offer
|
||||||
|
return offer.isCurrencyForMakerFeeBtc();
|
||||||
|
} else {
|
||||||
|
Trade trade = (Trade) tradable;
|
||||||
|
String takerFeeTxId = trade.getTakerFeeTxId();
|
||||||
|
// If we find our tx in the bsq wallet its a BSQ trade fee tx
|
||||||
|
return bsqWalletService.getTransaction(takerFeeTxId) == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getTradeFee(ClosedTradableListItem item, boolean appendCode) {
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Tradable tradable = item.getTradable();
|
Tradable tradable = item.getTradable();
|
||||||
Offer offer = tradable.getOffer();
|
Offer offer = tradable.getOffer();
|
||||||
|
if (wasMyOffer(tradable)) {
|
||||||
if (!wasMyOffer(tradable) && (tradable instanceof Trade)) {
|
|
||||||
Trade trade = (Trade) tradable;
|
|
||||||
Transaction takerFeeTx = btcWalletService.getTransaction(trade.getTakerFeeTxId());
|
|
||||||
if (takerFeeTx != null && takerFeeTx.getOutputs().size() > 1) {
|
|
||||||
// First output is fee receiver address. If its a BSQ (change) address of our own wallet its a BSQ fee
|
|
||||||
TransactionOutput output = takerFeeTx.getOutput(0);
|
|
||||||
Address address = output.getScriptPubKey().getToAddress(Config.baseCurrencyNetworkParameters());
|
|
||||||
if (bsqWalletService.getWallet().findKeyFromAddress(address) != null) {
|
|
||||||
return bsqFormatter.formatCoinWithCode(trade.getTakerFee());
|
|
||||||
} else {
|
|
||||||
return btcFormatter.formatCoinWithCode(trade.getTakerFee());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.warn("takerFeeTx is null or has invalid structure. takerFeeTx={}", takerFeeTx);
|
|
||||||
return Res.get("shared.na");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CoinFormatter formatter = offer.isCurrencyForMakerFeeBtc() ? btcFormatter : bsqFormatter;
|
CoinFormatter formatter = offer.isCurrencyForMakerFeeBtc() ? btcFormatter : bsqFormatter;
|
||||||
return formatter.formatCoinWithCode(offer.getMakerFee());
|
return formatter.formatCoin(offer.getMakerFee(), appendCode);
|
||||||
|
} else {
|
||||||
|
Trade trade = (Trade) tradable;
|
||||||
|
String takerFeeTxId = trade.getTakerFeeTxId();
|
||||||
|
if (bsqWalletService.getTransaction(takerFeeTxId) == null) {
|
||||||
|
// Was BTC fee
|
||||||
|
return btcFormatter.formatCoin(trade.getTakerFee(), appendCode);
|
||||||
|
} else {
|
||||||
|
// BSQ fee
|
||||||
|
return bsqFormatter.formatCoin(trade.getTakerFee(), appendCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,6 +277,66 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean wasMyOffer(Tradable tradable) {
|
boolean wasMyOffer(Tradable tradable) {
|
||||||
return dataModel.closedTradableManager.wasMyOffer(tradable.getOffer());
|
return dataModel.wasMyOffer(tradable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coin getTotalTradeAmount() {
|
||||||
|
return dataModel.getTotalAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTotalAmountWithVolume(Coin totalTradeAmount) {
|
||||||
|
return dataModel.getVolumeInUserFiatCurrency(totalTradeAmount)
|
||||||
|
.map(volume -> {
|
||||||
|
return Res.get("closedTradesSummaryWindow.totalAmount.value",
|
||||||
|
btcFormatter.formatCoin(totalTradeAmount, true),
|
||||||
|
DisplayUtils.formatVolumeWithCode(volume));
|
||||||
|
})
|
||||||
|
.orElse("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getTotalVolumeByCurrency() {
|
||||||
|
return dataModel.getTotalVolumeByCurrency().entrySet().stream()
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey,
|
||||||
|
entry -> {
|
||||||
|
String currencyCode = entry.getKey();
|
||||||
|
Monetary monetary;
|
||||||
|
if (CurrencyUtil.isCryptoCurrency(currencyCode)) {
|
||||||
|
monetary = Altcoin.valueOf(currencyCode, entry.getValue());
|
||||||
|
} else {
|
||||||
|
monetary = Fiat.valueOf(currencyCode, entry.getValue());
|
||||||
|
}
|
||||||
|
return DisplayUtils.formatVolumeWithCode(new Volume(monetary));
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTotalTxFee(Coin totalTradeAmount) {
|
||||||
|
Coin totalTxFee = dataModel.getTotalTxFee();
|
||||||
|
double percentage = ((double) totalTxFee.value) / totalTradeAmount.value;
|
||||||
|
return Res.get("closedTradesSummaryWindow.totalMinerFee.value",
|
||||||
|
btcFormatter.formatCoin(totalTxFee, true),
|
||||||
|
FormattingUtils.formatToPercentWithSymbol(percentage));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTotalTradeFeeInBtc(Coin totalTradeAmount) {
|
||||||
|
Coin totalTradeFee = dataModel.getTotalTradeFee(true);
|
||||||
|
double percentage = ((double) totalTradeFee.value) / totalTradeAmount.value;
|
||||||
|
return Res.get("closedTradesSummaryWindow.totalTradeFeeInBtc.value",
|
||||||
|
btcFormatter.formatCoin(totalTradeFee, true),
|
||||||
|
FormattingUtils.formatToPercentWithSymbol(percentage));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTotalTradeFeeInBsq(Coin totalTradeAmount) {
|
||||||
|
return dataModel.getVolume(totalTradeAmount, "USD")
|
||||||
|
.filter(v -> v.getValue() > 0)
|
||||||
|
.map(tradeAmountVolume -> {
|
||||||
|
Coin totalTradeFee = dataModel.getTotalTradeFee(false);
|
||||||
|
Volume bsqVolumeInUsd = dataModel.getBsqVolumeInUsdWithAveragePrice(totalTradeFee); // with 4 decimal
|
||||||
|
double percentage = ((double) bsqVolumeInUsd.getValue()) / tradeAmountVolume.getValue();
|
||||||
|
return Res.get("closedTradesSummaryWindow.totalTradeFeeInBsq.value",
|
||||||
|
bsqFormatter.formatCoin(totalTradeFee, true),
|
||||||
|
FormattingUtils.formatToPercentWithSymbol(percentage));
|
||||||
|
})
|
||||||
|
.orElse("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,11 @@ public class DisplayUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String formatVolumeWithCode(Volume volume) {
|
public static String formatVolumeWithCode(Volume volume) {
|
||||||
return formatVolume(volume, FIAT_VOLUME_FORMAT, true);
|
return formatVolume(volume, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatVolume(Volume volume, boolean appendCode) {
|
||||||
|
return formatVolume(volume, FIAT_VOLUME_FORMAT, appendCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String formatAverageVolumeWithCode(Volume volume) {
|
public static String formatAverageVolumeWithCode(Volume volume) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue