use list instead of set for trade statistics due to jfx bug

This commit is contained in:
woodser 2025-12-07 14:27:27 -05:00 committed by woodser
parent 156bd3bdd8
commit 6141c9000d
11 changed files with 35 additions and 36 deletions

View file

@ -337,7 +337,7 @@ public class CoreApi {
} }
public List<TradeStatistics3> getTradeStatistics() { public List<TradeStatistics3> getTradeStatistics() {
return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet()); return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsList());
} }
public int getNumConfirmationsForMostRecentTransaction(String addressString) { public int getNumConfirmationsForMostRecentTransaction(String addressString) {

View file

@ -62,7 +62,7 @@ public class DisputeAgentSelection {
DisputeAgentManager<T> disputeAgentManager, DisputeAgentManager<T> disputeAgentManager,
Set<NodeAddress> excludedDisputeAgents) { Set<NodeAddress> excludedDisputeAgents) {
// We take last 100 entries from trade statistics // We take last 100 entries from trade statistics
List<TradeStatistics3> list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet()); List<TradeStatistics3> list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsList());
list.sort(Comparator.comparing(TradeStatistics3::getDateAsLong)); list.sort(Comparator.comparing(TradeStatistics3::getDateAsLong));
Collections.reverse(list); Collections.reverse(list);
if (!list.isEmpty()) { if (!list.isEmpty()) {

View file

@ -54,7 +54,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -346,10 +345,10 @@ public class PriceFeedService {
return new Date(epochInMillisAtLastRequest); return new Date(epochInMillisAtLastRequest);
} }
public void applyLatestHavenoMarketPrice(Set<TradeStatistics3> tradeStatisticsSet) { public void applyLatestHavenoMarketPrice(List<TradeStatistics3> tradeStatisticsList) {
// takes about 10 ms for 5000 items // takes about 10 ms for 5000 items
Map<String, List<TradeStatistics3>> mapByCurrencyCode = new HashMap<>(); Map<String, List<TradeStatistics3>> mapByCurrencyCode = new HashMap<>();
tradeStatisticsSet.forEach(e -> { tradeStatisticsList.forEach(e -> {
List<TradeStatistics3> list; List<TradeStatistics3> list;
String currencyCode = e.getCurrency(); String currencyCode = e.getCurrency();
if (mapByCurrencyCode.containsKey(currencyCode)) { if (mapByCurrencyCode.containsKey(currencyCode)) {

View file

@ -43,7 +43,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableSet; import javafx.collections.ObservableList;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -55,7 +55,7 @@ public class TradeStatisticsManager {
private final TradeStatistics3StorageService tradeStatistics3StorageService; private final TradeStatistics3StorageService tradeStatistics3StorageService;
private final File storageDir; private final File storageDir;
private final boolean dumpStatistics; private final boolean dumpStatistics;
private final ObservableSet<TradeStatistics3> observableTradeStatisticsSet = FXCollections.observableSet(); private final ObservableList<TradeStatistics3> observableTradeStatisticsList = FXCollections.observableArrayList();
private JsonFileManager jsonFileManager; private JsonFileManager jsonFileManager;
public static final int PUBLISH_STATS_RANDOM_DELAY_HOURS = 24; public static final int PUBLISH_STATS_RANDOM_DELAY_HOURS = 24;
@ -89,9 +89,9 @@ public class TradeStatisticsManager {
if (!tradeStatistics.isValid()) { if (!tradeStatistics.isValid()) {
return; return;
} }
synchronized (observableTradeStatisticsSet) { synchronized (observableTradeStatisticsList) {
observableTradeStatisticsSet.add(tradeStatistics); observableTradeStatisticsList.add(tradeStatistics);
priceFeedService.applyLatestHavenoMarketPrice(observableTradeStatisticsSet); priceFeedService.applyLatestHavenoMarketPrice(observableTradeStatisticsList);
} }
maybeDumpStatistics(); maybeDumpStatistics();
} }
@ -107,9 +107,9 @@ public class TradeStatisticsManager {
// remove duplicates in early trade stats due to bugs // remove duplicates in early trade stats due to bugs
removeDuplicateStats(set); removeDuplicateStats(set);
synchronized (observableTradeStatisticsSet) { synchronized (observableTradeStatisticsList) {
observableTradeStatisticsSet.addAll(set); observableTradeStatisticsList.addAll(set);
priceFeedService.applyLatestHavenoMarketPrice(observableTradeStatisticsSet); priceFeedService.applyLatestHavenoMarketPrice(observableTradeStatisticsList);
} }
maybeDumpStatistics(); maybeDumpStatistics();
} }
@ -195,8 +195,8 @@ public class TradeStatisticsManager {
return isWithinFuzzedHours && isWithinFuzzedAmount; return isWithinFuzzedHours && isWithinFuzzedAmount;
} }
public ObservableSet<TradeStatistics3> getObservableTradeStatisticsSet() { public ObservableList<TradeStatistics3> getObservableTradeStatisticsList() {
return observableTradeStatisticsSet; return observableTradeStatisticsList;
} }
private void maybeDumpStatistics() { private void maybeDumpStatistics() {
@ -220,7 +220,7 @@ public class TradeStatisticsManager {
jsonFileManager.writeToDiscThreaded(JsonUtil.objectToJson(cryptoCurrencyList), "crypto_currency_list"); jsonFileManager.writeToDiscThreaded(JsonUtil.objectToJson(cryptoCurrencyList), "crypto_currency_list");
Instant yearAgo = Instant.ofEpochSecond(Instant.now().getEpochSecond() - TimeUnit.DAYS.toSeconds(365)); Instant yearAgo = Instant.ofEpochSecond(Instant.now().getEpochSecond() - TimeUnit.DAYS.toSeconds(365));
Set<String> activeCurrencies = observableTradeStatisticsSet.stream() Set<String> activeCurrencies = observableTradeStatisticsList.stream()
.filter(e -> e.getDate().toInstant().isAfter(yearAgo)) .filter(e -> e.getDate().toInstant().isAfter(yearAgo))
.map(p -> p.getCurrency()) .map(p -> p.getCurrency())
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@ -238,7 +238,7 @@ public class TradeStatisticsManager {
jsonFileManager.writeToDiscThreaded(JsonUtil.objectToJson(activeCryptoCurrencyList), "active_crypto_currency_list"); jsonFileManager.writeToDiscThreaded(JsonUtil.objectToJson(activeCryptoCurrencyList), "active_crypto_currency_list");
} }
List<TradeStatisticsForJson> list = observableTradeStatisticsSet.stream() List<TradeStatisticsForJson> list = observableTradeStatisticsList.stream()
.map(TradeStatisticsForJson::new) .map(TradeStatisticsForJson::new)
.sorted((o1, o2) -> (Long.compare(o2.tradeDate, o1.tradeDate))) .sorted((o1, o2) -> (Long.compare(o2.tradeDate, o1.tradeDate)))
.collect(Collectors.toList()); .collect(Collectors.toList());

View file

@ -42,7 +42,7 @@ public class AveragePriceUtil {
int days) { int days) {
double percentToTrim = Math.max(0, Math.min(49, preferences.getBsqAverageTrimThreshold() * 100)); double percentToTrim = Math.max(0, Math.min(49, preferences.getBsqAverageTrimThreshold() * 100));
Date pastXDays = getPastDate(days); Date pastXDays = getPastDate(days);
List<TradeStatistics3> bsqAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() List<TradeStatistics3> bsqAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
.filter(e -> e.getCurrency().equals("BSQ")) .filter(e -> e.getCurrency().equals("BSQ"))
.filter(e -> e.getDate().after(pastXDays)) .filter(e -> e.getDate().after(pastXDays))
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -50,7 +50,7 @@ public class AveragePriceUtil {
removeOutliers(bsqAllTradePastXDays, percentToTrim) : removeOutliers(bsqAllTradePastXDays, percentToTrim) :
bsqAllTradePastXDays; bsqAllTradePastXDays;
List<TradeStatistics3> usdAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() List<TradeStatistics3> usdAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
.filter(e -> e.getCurrency().equals("USD")) .filter(e -> e.getCurrency().equals("USD"))
.filter(e -> e.getDate().after(pastXDays)) .filter(e -> e.getDate().after(pastXDays))
.collect(Collectors.toList()); .collect(Collectors.toList());

View file

@ -59,7 +59,7 @@ public class ChartCalculations {
// Async // Async
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
static CompletableFuture<Map<TradesChartsViewModel.TickUnit, Map<Long, Long>>> getUsdAveragePriceMapsPerTickUnit(Set<TradeStatistics3> tradeStatisticsSet) { static CompletableFuture<Map<TradesChartsViewModel.TickUnit, Map<Long, Long>>> getUsdAveragePriceMapsPerTickUnit(List<TradeStatistics3> tradeStatisticsList) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
Map<TradesChartsViewModel.TickUnit, Map<Long, Long>> usdAveragePriceMapsPerTickUnit = new HashMap<>(); Map<TradesChartsViewModel.TickUnit, Map<Long, Long>> usdAveragePriceMapsPerTickUnit = new HashMap<>();
Map<TradesChartsViewModel.TickUnit, Map<Long, List<TradeStatistics3>>> dateMapsPerTickUnit = new HashMap<>(); Map<TradesChartsViewModel.TickUnit, Map<Long, List<TradeStatistics3>>> dateMapsPerTickUnit = new HashMap<>();
@ -67,7 +67,7 @@ public class ChartCalculations {
dateMapsPerTickUnit.put(tick, new HashMap<>()); dateMapsPerTickUnit.put(tick, new HashMap<>());
} }
tradeStatisticsSet.stream() tradeStatisticsList.stream()
.filter(e -> e.getCurrency().equals("USD")) .filter(e -> e.getCurrency().equals("USD"))
.forEach(tradeStatistics -> { .forEach(tradeStatistics -> {
for (TradesChartsViewModel.TickUnit tick : TradesChartsViewModel.TickUnit.values()) { for (TradesChartsViewModel.TickUnit tick : TradesChartsViewModel.TickUnit.values()) {
@ -80,18 +80,18 @@ public class ChartCalculations {
dateMapsPerTickUnit.forEach((tick, map) -> { dateMapsPerTickUnit.forEach((tick, map) -> {
HashMap<Long, Long> priceMap = new HashMap<>(); HashMap<Long, Long> 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); usdAveragePriceMapsPerTickUnit.put(tick, priceMap);
}); });
return usdAveragePriceMapsPerTickUnit; return usdAveragePriceMapsPerTickUnit;
}); });
} }
static CompletableFuture<List<TradeStatistics3>> getTradeStatisticsForCurrency(Set<TradeStatistics3> tradeStatisticsSet, static CompletableFuture<List<TradeStatistics3>> getTradeStatisticsForCurrency(List<TradeStatistics3> tradeStatisticsList,
String currencyCode, String currencyCode,
boolean showAllTradeCurrencies) { boolean showAllTradeCurrencies) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
return tradeStatisticsSet.stream() return tradeStatisticsList.stream()
.filter(e -> showAllTradeCurrencies || e.getCurrency().equals(currencyCode)) .filter(e -> showAllTradeCurrencies || e.getCurrency().equals(currencyCode))
.collect(Collectors.toList()); .collect(Collectors.toList());
}); });

View file

@ -41,8 +41,8 @@ import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.collections.SetChangeListener;
import javafx.scene.chart.XYChart; import javafx.scene.chart.XYChart;
import javafx.util.Pair; import javafx.util.Pair;
@ -79,7 +79,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
private final PriceFeedService priceFeedService; private final PriceFeedService priceFeedService;
private final Navigation navigation; private final Navigation navigation;
private final SetChangeListener<TradeStatistics3> setChangeListener; private final ListChangeListener<TradeStatistics3> listChangeListener;
final ObjectProperty<TradeCurrency> selectedTradeCurrencyProperty = new SimpleObjectProperty<>(); final ObjectProperty<TradeCurrency> selectedTradeCurrencyProperty = new SimpleObjectProperty<>();
final BooleanProperty showAllTradeCurrenciesProperty = new SimpleBooleanProperty(false); final BooleanProperty showAllTradeCurrenciesProperty = new SimpleBooleanProperty(false);
private final CurrencyList currencyListItems; private final CurrencyList currencyListItems;
@ -109,7 +109,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
this.priceFeedService = priceFeedService; this.priceFeedService = priceFeedService;
this.navigation = navigation; this.navigation = navigation;
setChangeListener = change -> { listChangeListener = change -> {
applyAsyncTradeStatisticsForCurrency(getCurrencyCode()) applyAsyncTradeStatisticsForCurrency(getCurrencyCode())
.whenComplete((result, throwable) -> { .whenComplete((result, throwable) -> {
if (deactivateCalled) { if (deactivateCalled) {
@ -141,7 +141,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
deactivateCalled = false; deactivateCalled = false;
tradeStatisticsManager.getObservableTradeStatisticsSet().addListener(setChangeListener); tradeStatisticsManager.getObservableTradeStatisticsList().addListener(listChangeListener);
if (!fillTradeCurrenciesOnActivateCalled) { if (!fillTradeCurrenciesOnActivateCalled) {
fillTradeCurrencies(); fillTradeCurrencies();
fillTradeCurrenciesOnActivateCalled = true; fillTradeCurrenciesOnActivateCalled = true;
@ -179,7 +179,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
@Override @Override
protected void deactivate() { protected void deactivate() {
deactivateCalled = true; 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 // We want to avoid to trigger listeners in the view so we delay a bit. Deactivate on model is called before
// deactivate on view. // deactivate on view.
@ -200,7 +200,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
private void applyAsyncUsdAveragePriceMapsPerTickUnit(CompletableFuture<Boolean> completeFuture) { private void applyAsyncUsdAveragePriceMapsPerTickUnit(CompletableFuture<Boolean> completeFuture) {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
ChartCalculations.getUsdAveragePriceMapsPerTickUnit(tradeStatisticsManager.getObservableTradeStatisticsSet()) ChartCalculations.getUsdAveragePriceMapsPerTickUnit(tradeStatisticsManager.getObservableTradeStatisticsList())
.whenComplete((usdAveragePriceMapsPerTickUnit, throwable) -> { .whenComplete((usdAveragePriceMapsPerTickUnit, throwable) -> {
if (deactivateCalled) { if (deactivateCalled) {
return; return;
@ -227,7 +227,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
@Nullable CompletableFuture<Boolean> completeFuture) { @Nullable CompletableFuture<Boolean> completeFuture) {
CompletableFuture<Boolean> future = new CompletableFuture<>(); CompletableFuture<Boolean> future = new CompletableFuture<>();
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
ChartCalculations.getTradeStatisticsForCurrency(tradeStatisticsManager.getObservableTradeStatisticsSet(), ChartCalculations.getTradeStatisticsForCurrency(tradeStatisticsManager.getObservableTradeStatisticsList(),
currencyCode, currencyCode,
showAllTradeCurrenciesProperty.get()) showAllTradeCurrenciesProperty.get())
.whenComplete((list, throwable) -> { .whenComplete((list, throwable) -> {
@ -359,7 +359,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
private void fillTradeCurrencies() { private void fillTradeCurrencies() {
// Don't use a set as we need all entries // Don't use a set as we need all entries
List<TradeCurrency> tradeCurrencyList = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() List<TradeCurrency> tradeCurrencyList = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
.flatMap(e -> CurrencyUtil.getTradeCurrency(e.getCurrency()).stream()) .flatMap(e -> CurrencyUtil.getTradeCurrency(e.getCurrency()).stream())
.collect(Collectors.toList()); .collect(Collectors.toList());
currencyListItems.updateWithCurrencies(tradeCurrencyList, showAllCurrencyListItem); currencyListItems.updateWithCurrencies(tradeCurrencyList, showAllCurrencyListItem);

View file

@ -347,7 +347,7 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
// Get average historic prices over for the prior trade period equaling the lock time // Get average historic prices over for the prior trade period equaling the lock time
var blocksRange = Restrictions.getLockTime(paymentAccount.getPaymentMethod().isBlockchain()); var blocksRange = Restrictions.getLockTime(paymentAccount.getPaymentMethod().isBlockchain());
var startDate = new Date(System.currentTimeMillis() - blocksRange * 10L * 60000); 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.getCurrency().equals(getTradeCurrency().getCode()))
.filter(e -> e.getDate().compareTo(startDate) >= 0) .filter(e -> e.getDate().compareTo(startDate) >= 0)
.sorted(Comparator.comparing(TradeStatistics3::getDate)) .sorted(Comparator.comparing(TradeStatistics3::getDate))

View file

@ -55,7 +55,7 @@ public class CreateOfferDataModelTest {
when(preferences.isUsePercentageBasedPrice()).thenReturn(true); when(preferences.isUsePercentageBasedPrice()).thenReturn(true);
when(preferences.getSecurityDepositAsPercent(null)).thenReturn(0.01); when(preferences.getSecurityDepositAsPercent(null)).thenReturn(0.01);
when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString()); when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString());
when(tradeStats.getObservableTradeStatisticsSet()).thenReturn(FXCollections.observableSet()); when(tradeStats.getObservableTradeStatisticsList()).thenReturn(FXCollections.observableArrayList());
model = new CreateOfferDataModel(createOfferService, model = new CreateOfferDataModel(createOfferService,
null, null,

View file

@ -103,7 +103,7 @@ public class CreateOfferViewModelTest {
when(accountAgeWitnessService.getMyTradeLimit(any(), any(), any(), anyBoolean())).thenReturn(100000000L); when(accountAgeWitnessService.getMyTradeLimit(any(), any(), any(), anyBoolean())).thenReturn(100000000L);
when(preferences.getUserCountry()).thenReturn(new Country("ES", "Spain", null)); when(preferences.getUserCountry()).thenReturn(new Country("ES", "Spain", null));
when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString()); when(createOfferService.getRandomOfferId()).thenReturn(UUID.randomUUID().toString());
when(tradeStats.getObservableTradeStatisticsSet()).thenReturn(FXCollections.observableSet()); when(tradeStats.getObservableTradeStatisticsList()).thenReturn(FXCollections.observableArrayList());
CreateOfferDataModel dataModel = new CreateOfferDataModel(createOfferService, CreateOfferDataModel dataModel = new CreateOfferDataModel(createOfferService,
null, null,

View file

@ -99,7 +99,7 @@ public class OfferBookViewModelTest {
private PriceUtil getPriceUtil() { private PriceUtil getPriceUtil() {
PriceFeedService priceFeedService = mock(PriceFeedService.class); PriceFeedService priceFeedService = mock(PriceFeedService.class);
TradeStatisticsManager tradeStatisticsManager = mock(TradeStatisticsManager.class); TradeStatisticsManager tradeStatisticsManager = mock(TradeStatisticsManager.class);
when(tradeStatisticsManager.getObservableTradeStatisticsSet()).thenReturn(FXCollections.observableSet()); when(tradeStatisticsManager.getObservableTradeStatisticsList()).thenReturn(FXCollections.observableArrayList());
return new PriceUtil(priceFeedService, tradeStatisticsManager, empty); return new PriceUtil(priceFeedService, tradeStatisticsManager, empty);
} }