Skip to content

Commit c6673c8

Browse files
authored
synchronize observable trade statistics list (#2167)
1 parent d4549eb commit c6673c8

File tree

8 files changed

+85
-49
lines changed

8 files changed

+85
-49
lines changed

core/src/main/java/haveno/core/api/CoreApi.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
import haveno.core.support.dispute.DisputeResult;
5959
import haveno.core.support.messages.ChatMessage;
6060
import haveno.core.trade.Trade;
61-
import haveno.core.trade.statistics.TradeStatistics3;
6261
import haveno.core.trade.statistics.TradeStatisticsManager;
6362
import haveno.core.xmr.XmrNodeSettings;
6463
import haveno.proto.grpc.NotificationMessage;
@@ -325,10 +324,6 @@ public void removeWalletPassword(String password) {
325324
walletsService.removeWalletPassword(password);
326325
}
327326

328-
public List<TradeStatistics3> getTradeStatistics() {
329-
return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsList());
330-
}
331-
332327
public int getNumConfirmationsForMostRecentTransaction(String addressString) {
333328
return walletsService.getNumConfirmationsForMostRecentTransaction(addressString);
334329
}

core/src/main/java/haveno/core/offer/availability/DisputeAgentSelection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ private static <T extends DisputeAgent> T getLeastUsedDisputeAgent(TradeStatisti
6262
DisputeAgentManager<T> disputeAgentManager,
6363
Set<NodeAddress> excludedDisputeAgents) {
6464
// We take last 100 entries from trade statistics
65-
List<TradeStatistics3> list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsList());
65+
List<TradeStatistics3> list = tradeStatisticsManager.getTradeStatisticsListCopy();
6666
list.sort(Comparator.comparing(TradeStatistics3::getDateAsLong));
6767
Collections.reverse(list);
6868
if (!list.isEmpty()) {

core/src/main/java/haveno/core/trade/statistics/TradeStatisticsManager.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ public ObservableList<TradeStatistics3> getObservableTradeStatisticsList() {
199199
return observableTradeStatisticsList;
200200
}
201201

202+
public List<TradeStatistics3> getTradeStatisticsListCopy() {
203+
synchronized (observableTradeStatisticsList) {
204+
return new ArrayList<>(observableTradeStatisticsList);
205+
}
206+
}
207+
202208
private void maybeDumpStatistics() {
203209
if (!dumpStatistics) {
204210
return;
@@ -220,10 +226,13 @@ private void maybeDumpStatistics() {
220226
jsonFileManager.writeToDiscThreaded(JsonUtil.objectToJson(cryptoCurrencyList), "crypto_currency_list");
221227

222228
Instant yearAgo = Instant.ofEpochSecond(Instant.now().getEpochSecond() - TimeUnit.DAYS.toSeconds(365));
223-
Set<String> activeCurrencies = observableTradeStatisticsList.stream()
224-
.filter(e -> e.getDate().toInstant().isAfter(yearAgo))
225-
.map(p -> p.getCurrency())
226-
.collect(Collectors.toSet());
229+
Set<String> activeCurrencies;
230+
synchronized (observableTradeStatisticsList) {
231+
activeCurrencies = observableTradeStatisticsList.stream()
232+
.filter(e -> e.getDate().toInstant().isAfter(yearAgo))
233+
.map(p -> p.getCurrency())
234+
.collect(Collectors.toSet());
235+
}
227236

228237
ArrayList<CurrencyTuple> activeTraditionalCurrencyList = traditionalCurrencyList.stream()
229238
.filter(e -> activeCurrencies.contains(e.code))
@@ -238,10 +247,13 @@ private void maybeDumpStatistics() {
238247
jsonFileManager.writeToDiscThreaded(JsonUtil.objectToJson(activeCryptoCurrencyList), "active_crypto_currency_list");
239248
}
240249

241-
List<TradeStatisticsForJson> list = observableTradeStatisticsList.stream()
242-
.map(TradeStatisticsForJson::new)
243-
.sorted((o1, o2) -> (Long.compare(o2.tradeDate, o1.tradeDate)))
244-
.collect(Collectors.toList());
250+
List<TradeStatisticsForJson> list;
251+
synchronized (observableTradeStatisticsList) {
252+
list = observableTradeStatisticsList.stream()
253+
.map(TradeStatisticsForJson::new)
254+
.sorted((o1, o2) -> (Long.compare(o2.tradeDate, o1.tradeDate)))
255+
.collect(Collectors.toList());
256+
}
245257
TradeStatisticsForJson[] array = new TradeStatisticsForJson[list.size()];
246258
list.toArray(array);
247259
jsonFileManager.writeToDiscThreaded(JsonUtil.objectToJson(array), "trade_statistics");

core/src/main/java/haveno/core/util/AveragePriceUtil.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,25 @@ public static Tuple2<Price, Price> getAveragePriceTuple(Preferences preferences,
4242
int days) {
4343
double percentToTrim = Math.max(0, Math.min(49, preferences.getBsqAverageTrimThreshold() * 100));
4444
Date pastXDays = getPastDate(days);
45-
List<TradeStatistics3> bsqAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
46-
.filter(e -> e.getCurrency().equals("BSQ"))
47-
.filter(e -> e.getDate().after(pastXDays))
48-
.collect(Collectors.toList());
45+
46+
List<TradeStatistics3> bsqAllTradePastXDays; // TODO: remove BSQ
47+
List<TradeStatistics3> usdAllTradePastXDays;
48+
synchronized (tradeStatisticsManager.getObservableTradeStatisticsList()) {
49+
bsqAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
50+
.filter(e -> e.getCurrency().equals("BSQ"))
51+
.filter(e -> e.getDate().after(pastXDays))
52+
.collect(Collectors.toList());
53+
54+
usdAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
55+
.filter(e -> e.getCurrency().equals("USD"))
56+
.filter(e -> e.getDate().after(pastXDays))
57+
.collect(Collectors.toList());
58+
}
59+
4960
List<TradeStatistics3> bsqTradePastXDays = percentToTrim > 0 ?
5061
removeOutliers(bsqAllTradePastXDays, percentToTrim) :
5162
bsqAllTradePastXDays;
5263

53-
List<TradeStatistics3> usdAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
54-
.filter(e -> e.getCurrency().equals("USD"))
55-
.filter(e -> e.getDate().after(pastXDays))
56-
.collect(Collectors.toList());
5764
List<TradeStatistics3> usdTradePastXDays = percentToTrim > 0 ?
5865
removeOutliers(usdAllTradePastXDays, percentToTrim) :
5966
usdAllTradePastXDays;

daemon/src/main/java/haveno/daemon/grpc/GrpcGetTradeStatisticsService.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.google.inject.Inject;
44
import haveno.core.api.CoreApi;
55
import haveno.core.trade.statistics.TradeStatistics3;
6+
import haveno.core.trade.statistics.TradeStatisticsManager;
67
import haveno.daemon.grpc.interceptor.CallRateMeteringInterceptor;
78
import haveno.daemon.grpc.interceptor.GrpcCallRateMeter;
89
import static haveno.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
@@ -13,6 +14,7 @@
1314
import io.grpc.ServerInterceptor;
1415
import io.grpc.stub.StreamObserver;
1516
import java.util.HashMap;
17+
import java.util.List;
1618
import java.util.Optional;
1719
import static java.util.concurrent.TimeUnit.SECONDS;
1820
import java.util.stream.Collectors;
@@ -22,21 +24,26 @@
2224
class GrpcGetTradeStatisticsService extends GetTradeStatisticsImplBase {
2325

2426
private final CoreApi coreApi;
27+
private final TradeStatisticsManager tradeStatisticsManager;
2528
private final GrpcExceptionHandler exceptionHandler;
2629

2730
@Inject
28-
public GrpcGetTradeStatisticsService(CoreApi coreApi, GrpcExceptionHandler exceptionHandler) {
31+
public GrpcGetTradeStatisticsService(CoreApi coreApi, TradeStatisticsManager tradeStatisticsManager, GrpcExceptionHandler exceptionHandler) {
2932
this.coreApi = coreApi;
33+
this.tradeStatisticsManager = tradeStatisticsManager;
3034
this.exceptionHandler = exceptionHandler;
3135
}
3236

3337
@Override
3438
public void getTradeStatistics(GetTradeStatisticsRequest req,
3539
StreamObserver<GetTradeStatisticsReply> responseObserver) {
3640
try {
37-
var tradeStatistics = coreApi.getTradeStatistics().stream()
38-
.map(TradeStatistics3::toProtoTradeStatistics3)
39-
.collect(Collectors.toList());
41+
List<protobuf.TradeStatistics3> tradeStatistics;
42+
synchronized (tradeStatisticsManager.getObservableTradeStatisticsList()) {
43+
tradeStatistics = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
44+
.map(TradeStatistics3::toProtoTradeStatistics3)
45+
.collect(Collectors.toList());
46+
}
4047

4148
var reply = GetTradeStatisticsReply.newBuilder().addAllTradeStatistics(tradeStatistics).build();
4249
responseObserver.onNext(reply);

desktop/src/main/java/haveno/desktop/main/market/trades/ChartCalculations.java

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,18 @@ static CompletableFuture<Map<TradesChartsViewModel.TickUnit, Map<Long, Long>>> g
6767
dateMapsPerTickUnit.put(tick, new HashMap<>());
6868
}
6969

70-
tradeStatisticsList.stream()
71-
.filter(e -> e.getCurrency().equals("USD"))
72-
.forEach(tradeStatistics -> {
73-
for (TradesChartsViewModel.TickUnit tick : TradesChartsViewModel.TickUnit.values()) {
74-
long time = roundToTick(tradeStatistics.getLocalDateTime(), tick).getTime();
75-
Map<Long, List<TradeStatistics3>> map = dateMapsPerTickUnit.get(tick);
76-
map.putIfAbsent(time, new ArrayList<>());
77-
map.get(time).add(tradeStatistics);
78-
}
79-
});
70+
synchronized (tradeStatisticsList) {
71+
tradeStatisticsList.stream()
72+
.filter(e -> e.getCurrency().equals("USD"))
73+
.forEach(tradeStatistics -> {
74+
for (TradesChartsViewModel.TickUnit tick : TradesChartsViewModel.TickUnit.values()) {
75+
long time = roundToTick(tradeStatistics.getLocalDateTime(), tick).getTime();
76+
Map<Long, List<TradeStatistics3>> map = dateMapsPerTickUnit.get(tick);
77+
map.putIfAbsent(time, new ArrayList<>());
78+
map.get(time).add(tradeStatistics);
79+
}
80+
});
81+
}
8082

8183
dateMapsPerTickUnit.forEach((tick, map) -> {
8284
HashMap<Long, Long> priceMap = new HashMap<>();
@@ -91,9 +93,11 @@ static CompletableFuture<List<TradeStatistics3>> getTradeStatisticsForCurrency(L
9193
String currencyCode,
9294
boolean showAllTradeCurrencies) {
9395
return CompletableFuture.supplyAsync(() -> {
94-
return tradeStatisticsList.stream()
95-
.filter(e -> showAllTradeCurrencies || e.getCurrency().equals(currencyCode))
96-
.collect(Collectors.toList());
96+
synchronized (tradeStatisticsList) {
97+
return tradeStatisticsList.stream()
98+
.filter(e -> showAllTradeCurrencies || e.getCurrency().equals(currencyCode))
99+
.collect(Collectors.toList());
100+
}
97101
});
98102
}
99103

desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsViewModel.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ protected void activate() {
141141
long ts = System.currentTimeMillis();
142142
deactivateCalled = false;
143143

144-
tradeStatisticsManager.getObservableTradeStatisticsList().addListener(listChangeListener);
144+
synchronized (tradeStatisticsManager.getObservableTradeStatisticsList()) {
145+
tradeStatisticsManager.getObservableTradeStatisticsList().addListener(listChangeListener);
146+
}
145147
if (!fillTradeCurrenciesOnActivateCalled) {
146148
fillTradeCurrencies();
147149
fillTradeCurrenciesOnActivateCalled = true;
@@ -179,7 +181,9 @@ protected void activate() {
179181
@Override
180182
protected void deactivate() {
181183
deactivateCalled = true;
182-
tradeStatisticsManager.getObservableTradeStatisticsList().removeListener(listChangeListener);
184+
synchronized (tradeStatisticsManager.getObservableTradeStatisticsList()) {
185+
tradeStatisticsManager.getObservableTradeStatisticsList().removeListener(listChangeListener);
186+
}
183187

184188
// We want to avoid to trigger listeners in the view so we delay a bit. Deactivate on model is called before
185189
// deactivate on view.
@@ -359,9 +363,12 @@ long getTimeFromTickIndex(long tick) {
359363

360364
private void fillTradeCurrencies() {
361365
// Don't use a set as we need all entries
362-
List<TradeCurrency> tradeCurrencyList = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
363-
.flatMap(e -> CurrencyUtil.getTradeCurrency(e.getCurrency()).stream())
364-
.collect(Collectors.toList());
366+
List<TradeCurrency> tradeCurrencyList;
367+
synchronized (tradeStatisticsManager.getObservableTradeStatisticsList()) {
368+
tradeCurrencyList = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
369+
.flatMap(e -> CurrencyUtil.getTradeCurrency(e.getCurrency()).stream())
370+
.collect(Collectors.toList());
371+
}
365372
currencyListItems.updateWithCurrencies(tradeCurrencyList, showAllCurrencyListItem);
366373
}
367374

desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import static java.util.Comparator.comparing;
6060
import java.util.Date;
6161
import java.util.HashSet;
62+
import java.util.List;
6263
import java.util.Optional;
6364
import java.util.Set;
6465
import java.util.function.Predicate;
@@ -347,11 +348,14 @@ private void setSuggestedSecurityDeposit(PaymentAccount paymentAccount) {
347348
// Get average historic prices over for the prior trade period equaling the lock time
348349
var blocksRange = Restrictions.getLockTime(paymentAccount.getPaymentMethod().isBlockchain());
349350
var startDate = new Date(System.currentTimeMillis() - blocksRange * 10L * 60000);
350-
var sortedRangeData = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
351-
.filter(e -> e.getCurrency().equals(getTradeCurrency().getCode()))
352-
.filter(e -> e.getDate().compareTo(startDate) >= 0)
353-
.sorted(Comparator.comparing(TradeStatistics3::getDate))
354-
.collect(Collectors.toList());
351+
List<TradeStatistics3> sortedRangeData;
352+
synchronized (tradeStatisticsManager.getObservableTradeStatisticsList()) {
353+
sortedRangeData = tradeStatisticsManager.getObservableTradeStatisticsList().stream()
354+
.filter(e -> e.getCurrency().equals(getTradeCurrency().getCode()))
355+
.filter(e -> e.getDate().compareTo(startDate) >= 0)
356+
.sorted(Comparator.comparing(TradeStatistics3::getDate))
357+
.collect(Collectors.toList());
358+
}
355359
var movingAverage = new MathUtils.MovingAverage(10, 0.2);
356360
double[] extremes = {Double.MAX_VALUE, Double.MIN_VALUE};
357361
sortedRangeData.forEach(e -> {

0 commit comments

Comments
 (0)