From c31dbd7697e2fa936f10738401a38a75753b4f9b Mon Sep 17 00:00:00 2001 From: woodser <13068859+woodser@users.noreply.github.com> Date: Sun, 7 Dec 2025 14:27:27 -0500 Subject: [PATCH] use list instead of set for trade statistics due to jfx bug --- .../main/java/haveno/core/api/CoreApi.java | 2 +- .../availability/DisputeAgentSelection.java | 2 +- .../core/provider/price/PriceFeedService.java | 5 ++-- .../statistics/TradeStatisticsManager.java | 24 +++++++++---------- .../haveno/core/util/AveragePriceUtil.java | 4 ++-- .../main/market/trades/ChartCalculations.java | 10 ++++---- .../market/trades/TradesChartsViewModel.java | 16 ++++++------- .../main/offer/MutableOfferDataModel.java | 2 +- .../createoffer/CreateOfferDataModelTest.java | 2 +- .../createoffer/CreateOfferViewModelTest.java | 2 +- .../offerbook/OfferBookViewModelTest.java | 2 +- 11 files changed, 35 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/haveno/core/api/CoreApi.java b/core/src/main/java/haveno/core/api/CoreApi.java index 24e83b2b2e..aa84090648 100644 --- a/core/src/main/java/haveno/core/api/CoreApi.java +++ b/core/src/main/java/haveno/core/api/CoreApi.java @@ -337,7 +337,7 @@ public class CoreApi { } public List getTradeStatistics() { - return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet()); + return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsList()); } public int getNumConfirmationsForMostRecentTransaction(String addressString) { diff --git a/core/src/main/java/haveno/core/offer/availability/DisputeAgentSelection.java b/core/src/main/java/haveno/core/offer/availability/DisputeAgentSelection.java index b09750ead4..612d72f2c4 100644 --- a/core/src/main/java/haveno/core/offer/availability/DisputeAgentSelection.java +++ b/core/src/main/java/haveno/core/offer/availability/DisputeAgentSelection.java @@ -62,7 +62,7 @@ public class DisputeAgentSelection { DisputeAgentManager disputeAgentManager, Set excludedDisputeAgents) { // We take last 100 entries from trade statistics - List list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet()); + List list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsList()); list.sort(Comparator.comparing(TradeStatistics3::getDateAsLong)); Collections.reverse(list); if (!list.isEmpty()) { diff --git a/core/src/main/java/haveno/core/provider/price/PriceFeedService.java b/core/src/main/java/haveno/core/provider/price/PriceFeedService.java index ef4ab94e43..a1fce7a11f 100644 --- a/core/src/main/java/haveno/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/haveno/core/provider/price/PriceFeedService.java @@ -54,7 +54,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; -import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -346,10 +345,10 @@ public class PriceFeedService { return new Date(epochInMillisAtLastRequest); } - public void applyLatestHavenoMarketPrice(Set tradeStatisticsSet) { + public void applyLatestHavenoMarketPrice(List tradeStatisticsList) { // takes about 10 ms for 5000 items Map> mapByCurrencyCode = new HashMap<>(); - tradeStatisticsSet.forEach(e -> { + tradeStatisticsList.forEach(e -> { List list; String currencyCode = e.getCurrency(); if (mapByCurrencyCode.containsKey(currencyCode)) { diff --git a/core/src/main/java/haveno/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/haveno/core/trade/statistics/TradeStatisticsManager.java index c6ff8e71ee..44d68d7769 100644 --- a/core/src/main/java/haveno/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/haveno/core/trade/statistics/TradeStatisticsManager.java @@ -43,7 +43,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javafx.collections.FXCollections; -import javafx.collections.ObservableSet; +import javafx.collections.ObservableList; import javax.annotation.Nullable; import lombok.extern.slf4j.Slf4j; @@ -55,7 +55,7 @@ public class TradeStatisticsManager { private final TradeStatistics3StorageService tradeStatistics3StorageService; private final File storageDir; private final boolean dumpStatistics; - private final ObservableSet observableTradeStatisticsSet = FXCollections.observableSet(); + private final ObservableList observableTradeStatisticsList = FXCollections.observableArrayList(); private JsonFileManager jsonFileManager; public static final int PUBLISH_STATS_RANDOM_DELAY_HOURS = 24; @@ -89,9 +89,9 @@ public class TradeStatisticsManager { if (!tradeStatistics.isValid()) { return; } - synchronized (observableTradeStatisticsSet) { - observableTradeStatisticsSet.add(tradeStatistics); - priceFeedService.applyLatestHavenoMarketPrice(observableTradeStatisticsSet); + synchronized (observableTradeStatisticsList) { + observableTradeStatisticsList.add(tradeStatistics); + priceFeedService.applyLatestHavenoMarketPrice(observableTradeStatisticsList); } maybeDumpStatistics(); } @@ -107,9 +107,9 @@ public class TradeStatisticsManager { // remove duplicates in early trade stats due to bugs removeDuplicateStats(set); - synchronized (observableTradeStatisticsSet) { - observableTradeStatisticsSet.addAll(set); - priceFeedService.applyLatestHavenoMarketPrice(observableTradeStatisticsSet); + synchronized (observableTradeStatisticsList) { + observableTradeStatisticsList.addAll(set); + priceFeedService.applyLatestHavenoMarketPrice(observableTradeStatisticsList); } maybeDumpStatistics(); } @@ -195,8 +195,8 @@ public class TradeStatisticsManager { return isWithinFuzzedHours && isWithinFuzzedAmount; } - public ObservableSet getObservableTradeStatisticsSet() { - return observableTradeStatisticsSet; + public ObservableList getObservableTradeStatisticsList() { + return observableTradeStatisticsList; } private void maybeDumpStatistics() { @@ -220,7 +220,7 @@ public class TradeStatisticsManager { jsonFileManager.writeToDiscThreaded(JsonUtil.objectToJson(cryptoCurrencyList), "crypto_currency_list"); Instant yearAgo = Instant.ofEpochSecond(Instant.now().getEpochSecond() - TimeUnit.DAYS.toSeconds(365)); - Set activeCurrencies = observableTradeStatisticsSet.stream() + Set activeCurrencies = observableTradeStatisticsList.stream() .filter(e -> e.getDate().toInstant().isAfter(yearAgo)) .map(p -> p.getCurrency()) .collect(Collectors.toSet()); @@ -238,7 +238,7 @@ public class TradeStatisticsManager { jsonFileManager.writeToDiscThreaded(JsonUtil.objectToJson(activeCryptoCurrencyList), "active_crypto_currency_list"); } - List list = observableTradeStatisticsSet.stream() + List list = observableTradeStatisticsList.stream() .map(TradeStatisticsForJson::new) .sorted((o1, o2) -> (Long.compare(o2.tradeDate, o1.tradeDate))) .collect(Collectors.toList()); diff --git a/core/src/main/java/haveno/core/util/AveragePriceUtil.java b/core/src/main/java/haveno/core/util/AveragePriceUtil.java index 3e30327b85..f8f3d420b5 100644 --- a/core/src/main/java/haveno/core/util/AveragePriceUtil.java +++ b/core/src/main/java/haveno/core/util/AveragePriceUtil.java @@ -42,7 +42,7 @@ public class AveragePriceUtil { int days) { double percentToTrim = Math.max(0, Math.min(49, preferences.getBsqAverageTrimThreshold() * 100)); Date pastXDays = getPastDate(days); - List bsqAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() + List bsqAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsList().stream() .filter(e -> e.getCurrency().equals("BSQ")) .filter(e -> e.getDate().after(pastXDays)) .collect(Collectors.toList()); @@ -50,7 +50,7 @@ public class AveragePriceUtil { removeOutliers(bsqAllTradePastXDays, percentToTrim) : bsqAllTradePastXDays; - List usdAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() + List usdAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsList().stream() .filter(e -> e.getCurrency().equals("USD")) .filter(e -> e.getDate().after(pastXDays)) .collect(Collectors.toList()); diff --git a/desktop/src/main/java/haveno/desktop/main/market/trades/ChartCalculations.java b/desktop/src/main/java/haveno/desktop/main/market/trades/ChartCalculations.java index b3d8687849..0a8a77d97d 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/trades/ChartCalculations.java +++ b/desktop/src/main/java/haveno/desktop/main/market/trades/ChartCalculations.java @@ -59,7 +59,7 @@ public class ChartCalculations { // Async /////////////////////////////////////////////////////////////////////////////////////////// - static CompletableFuture>> getUsdAveragePriceMapsPerTickUnit(Set tradeStatisticsSet) { + static CompletableFuture>> getUsdAveragePriceMapsPerTickUnit(List tradeStatisticsList) { return CompletableFuture.supplyAsync(() -> { Map> usdAveragePriceMapsPerTickUnit = new HashMap<>(); Map>> dateMapsPerTickUnit = new HashMap<>(); @@ -67,7 +67,7 @@ public class ChartCalculations { dateMapsPerTickUnit.put(tick, new HashMap<>()); } - tradeStatisticsSet.stream() + tradeStatisticsList.stream() .filter(e -> e.getCurrency().equals("USD")) .forEach(tradeStatistics -> { for (TradesChartsViewModel.TickUnit tick : TradesChartsViewModel.TickUnit.values()) { @@ -80,18 +80,18 @@ public class ChartCalculations { dateMapsPerTickUnit.forEach((tick, map) -> { HashMap priceMap = new HashMap<>(); - map.forEach((date, tradeStatisticsList) -> priceMap.put(date, getAverageTraditionalPrice(tradeStatisticsList))); + map.forEach((date, tradeStatistics) -> priceMap.put(date, getAverageTraditionalPrice(tradeStatistics))); usdAveragePriceMapsPerTickUnit.put(tick, priceMap); }); return usdAveragePriceMapsPerTickUnit; }); } - static CompletableFuture> getTradeStatisticsForCurrency(Set tradeStatisticsSet, + static CompletableFuture> getTradeStatisticsForCurrency(List tradeStatisticsList, String currencyCode, boolean showAllTradeCurrencies) { return CompletableFuture.supplyAsync(() -> { - return tradeStatisticsSet.stream() + return tradeStatisticsList.stream() .filter(e -> showAllTradeCurrencies || e.getCurrency().equals(currencyCode)) .collect(Collectors.toList()); }); diff --git a/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsViewModel.java b/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsViewModel.java index 022c3cbdc6..43fc725912 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsViewModel.java @@ -41,8 +41,8 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; -import javafx.collections.SetChangeListener; import javafx.scene.chart.XYChart; import javafx.util.Pair; @@ -79,7 +79,7 @@ class TradesChartsViewModel extends ActivatableViewModel { private final PriceFeedService priceFeedService; private final Navigation navigation; - private final SetChangeListener setChangeListener; + private final ListChangeListener listChangeListener; final ObjectProperty selectedTradeCurrencyProperty = new SimpleObjectProperty<>(); final BooleanProperty showAllTradeCurrenciesProperty = new SimpleBooleanProperty(false); private final CurrencyList currencyListItems; @@ -109,7 +109,7 @@ class TradesChartsViewModel extends ActivatableViewModel { this.priceFeedService = priceFeedService; this.navigation = navigation; - setChangeListener = change -> { + listChangeListener = change -> { applyAsyncTradeStatisticsForCurrency(getCurrencyCode()) .whenComplete((result, throwable) -> { if (deactivateCalled) { @@ -141,7 +141,7 @@ class TradesChartsViewModel extends ActivatableViewModel { long ts = System.currentTimeMillis(); deactivateCalled = false; - tradeStatisticsManager.getObservableTradeStatisticsSet().addListener(setChangeListener); + tradeStatisticsManager.getObservableTradeStatisticsList().addListener(listChangeListener); if (!fillTradeCurrenciesOnActivateCalled) { fillTradeCurrencies(); fillTradeCurrenciesOnActivateCalled = true; @@ -179,7 +179,7 @@ class TradesChartsViewModel extends ActivatableViewModel { @Override protected void deactivate() { deactivateCalled = true; - tradeStatisticsManager.getObservableTradeStatisticsSet().removeListener(setChangeListener); + tradeStatisticsManager.getObservableTradeStatisticsList().removeListener(listChangeListener); // We want to avoid to trigger listeners in the view so we delay a bit. Deactivate on model is called before // deactivate on view. @@ -200,7 +200,7 @@ class TradesChartsViewModel extends ActivatableViewModel { private void applyAsyncUsdAveragePriceMapsPerTickUnit(CompletableFuture completeFuture) { long ts = System.currentTimeMillis(); - ChartCalculations.getUsdAveragePriceMapsPerTickUnit(tradeStatisticsManager.getObservableTradeStatisticsSet()) + ChartCalculations.getUsdAveragePriceMapsPerTickUnit(tradeStatisticsManager.getObservableTradeStatisticsList()) .whenComplete((usdAveragePriceMapsPerTickUnit, throwable) -> { if (deactivateCalled) { return; @@ -227,7 +227,7 @@ class TradesChartsViewModel extends ActivatableViewModel { @Nullable CompletableFuture completeFuture) { CompletableFuture future = new CompletableFuture<>(); long ts = System.currentTimeMillis(); - ChartCalculations.getTradeStatisticsForCurrency(tradeStatisticsManager.getObservableTradeStatisticsSet(), + ChartCalculations.getTradeStatisticsForCurrency(tradeStatisticsManager.getObservableTradeStatisticsList(), currencyCode, showAllTradeCurrenciesProperty.get()) .whenComplete((list, throwable) -> { @@ -359,7 +359,7 @@ class TradesChartsViewModel extends ActivatableViewModel { private void fillTradeCurrencies() { // Don't use a set as we need all entries - List tradeCurrencyList = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() + List tradeCurrencyList = tradeStatisticsManager.getObservableTradeStatisticsList().stream() .flatMap(e -> CurrencyUtil.getTradeCurrency(e.getCurrency()).stream()) .collect(Collectors.toList()); currencyListItems.updateWithCurrencies(tradeCurrencyList, showAllCurrencyListItem); diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java index dfbb07e17c..5c9bd8aa15 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java @@ -347,7 +347,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel { // Get average historic prices over for the prior trade period equaling the lock time var blocksRange = Restrictions.getLockTime(paymentAccount.getPaymentMethod().isBlockchain()); var startDate = new Date(System.currentTimeMillis() - blocksRange * 10L * 60000); - var sortedRangeData = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() + var sortedRangeData = tradeStatisticsManager.getObservableTradeStatisticsList().stream() .filter(e -> e.getCurrency().equals(getTradeCurrency().getCode())) .filter(e -> e.getDate().compareTo(startDate) >= 0) .sorted(Comparator.comparing(TradeStatistics3::getDate)) diff --git a/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferDataModelTest.java b/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferDataModelTest.java index e9d39f47bf..5c6bbe1684 100644 --- a/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferDataModelTest.java +++ b/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferDataModelTest.java @@ -55,7 +55,7 @@ public class CreateOfferDataModelTest { when(preferences.isUsePercentageBasedPrice()).thenReturn(true); when(preferences.getSecurityDepositAsPercent(null)).thenReturn(0.01); when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString()); - when(tradeStats.getObservableTradeStatisticsSet()).thenReturn(FXCollections.observableSet()); + when(tradeStats.getObservableTradeStatisticsList()).thenReturn(FXCollections.observableArrayList()); model = new CreateOfferDataModel(createOfferService, null, diff --git a/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferViewModelTest.java b/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferViewModelTest.java index 22ce86e5a4..1bd7fc54b0 100644 --- a/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferViewModelTest.java +++ b/desktop/src/test/java/haveno/desktop/main/offer/createoffer/CreateOfferViewModelTest.java @@ -103,7 +103,7 @@ public class CreateOfferViewModelTest { when(accountAgeWitnessService.getMyTradeLimit(any(), any(), any(), anyBoolean())).thenReturn(100000000L); when(preferences.getUserCountry()).thenReturn(new Country("ES", "Spain", null)); when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString()); - when(tradeStats.getObservableTradeStatisticsSet()).thenReturn(FXCollections.observableSet()); + when(tradeStats.getObservableTradeStatisticsList()).thenReturn(FXCollections.observableArrayList()); CreateOfferDataModel dataModel = new CreateOfferDataModel(createOfferService, null, diff --git a/desktop/src/test/java/haveno/desktop/main/offer/offerbook/OfferBookViewModelTest.java b/desktop/src/test/java/haveno/desktop/main/offer/offerbook/OfferBookViewModelTest.java index dfc68c4739..490a78d1ab 100644 --- a/desktop/src/test/java/haveno/desktop/main/offer/offerbook/OfferBookViewModelTest.java +++ b/desktop/src/test/java/haveno/desktop/main/offer/offerbook/OfferBookViewModelTest.java @@ -99,7 +99,7 @@ public class OfferBookViewModelTest { private PriceUtil getPriceUtil() { PriceFeedService priceFeedService = mock(PriceFeedService.class); TradeStatisticsManager tradeStatisticsManager = mock(TradeStatisticsManager.class); - when(tradeStatisticsManager.getObservableTradeStatisticsSet()).thenReturn(FXCollections.observableSet()); + when(tradeStatisticsManager.getObservableTradeStatisticsList()).thenReturn(FXCollections.observableArrayList()); return new PriceUtil(priceFeedService, tradeStatisticsManager, empty); }