mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 18:03:12 +01:00
Apply TradeStatistics3 to TradeStatisticsManager and some related classes
This commit is contained in:
parent
98207518d6
commit
c4a4c878b8
@ -23,7 +23,7 @@ import bisq.core.monetary.Altcoin;
|
||||
import bisq.core.monetary.Price;
|
||||
import bisq.core.provider.PriceNodeHttpClient;
|
||||
import bisq.core.provider.ProvidersRepository;
|
||||
import bisq.core.trade.statistics.TradeStatistics2;
|
||||
import bisq.core.trade.statistics.TradeStatistics3;
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
import bisq.network.http.HttpClient;
|
||||
@ -50,6 +50,7 @@ import javafx.beans.property.StringProperty;
|
||||
import java.time.Instant;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -288,12 +289,12 @@ public class PriceFeedService {
|
||||
return new Date(epochInMillisAtLastRequest);
|
||||
}
|
||||
|
||||
public void applyLatestBisqMarketPrice(Set<TradeStatistics2> tradeStatisticsSet) {
|
||||
public void applyLatestBisqMarketPrice(Set<TradeStatistics3> tradeStatisticsSet) {
|
||||
// takes about 10 ms for 5000 items
|
||||
Map<String, List<TradeStatistics2>> mapByCurrencyCode = new HashMap<>();
|
||||
Map<String, List<TradeStatistics3>> mapByCurrencyCode = new HashMap<>();
|
||||
tradeStatisticsSet.forEach(e -> {
|
||||
final List<TradeStatistics2> list;
|
||||
final String currencyCode = e.getCurrencyCode();
|
||||
List<TradeStatistics3> list;
|
||||
String currencyCode = e.getCurrency();
|
||||
if (mapByCurrencyCode.containsKey(currencyCode)) {
|
||||
list = mapByCurrencyCode.get(currencyCode);
|
||||
} else {
|
||||
@ -306,9 +307,9 @@ public class PriceFeedService {
|
||||
mapByCurrencyCode.values().stream()
|
||||
.filter(list -> !list.isEmpty())
|
||||
.forEach(list -> {
|
||||
list.sort((o1, o2) -> o1.getTradeDate().compareTo(o2.getTradeDate()));
|
||||
TradeStatistics2 tradeStatistics = list.get(list.size() - 1);
|
||||
setBisqMarketPrice(tradeStatistics.getCurrencyCode(), tradeStatistics.getTradePrice());
|
||||
list.sort(Comparator.comparing(o -> o.getTradeDate()));
|
||||
TradeStatistics3 tradeStatistics = list.get(list.size() - 1);
|
||||
setBisqMarketPrice(tradeStatistics.getCurrency(), tradeStatistics.getTradePrice());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,6 @@ import bisq.core.account.witness.AccountAgeWitnessStorageService;
|
||||
import bisq.core.trade.closed.ClosedTradableManager;
|
||||
import bisq.core.trade.failed.FailedTradesManager;
|
||||
import bisq.core.trade.statistics.ReferralIdService;
|
||||
import bisq.core.trade.statistics.TradeStatistics2StorageService;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
|
||||
import bisq.common.app.AppModule;
|
||||
import bisq.common.config.Config;
|
||||
@ -46,8 +44,6 @@ public class TradeModule extends AppModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(TradeManager.class).in(Singleton.class);
|
||||
bind(TradeStatisticsManager.class).in(Singleton.class);
|
||||
bind(TradeStatistics2StorageService.class).in(Singleton.class);
|
||||
bind(ClosedTradableManager.class).in(Singleton.class);
|
||||
bind(FailedTradesManager.class).in(Singleton.class);
|
||||
bind(AccountAgeWitnessService.class).in(Singleton.class);
|
||||
|
@ -21,7 +21,6 @@ import bisq.core.locale.CurrencyUtil;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.monetary.Price;
|
||||
import bisq.core.monetary.Volume;
|
||||
import bisq.core.offer.OfferPayload;
|
||||
|
||||
import bisq.common.util.MathUtils;
|
||||
|
||||
@ -38,65 +37,44 @@ import javax.annotation.concurrent.Immutable;
|
||||
@ToString
|
||||
@Slf4j
|
||||
public final class TradeStatisticsForJson {
|
||||
|
||||
public final String currency;
|
||||
public final OfferPayload.Direction direction;
|
||||
public final long tradePrice;
|
||||
public final long tradeAmount;
|
||||
public final long tradeDate;
|
||||
public final String paymentMethod;
|
||||
public final long offerDate;
|
||||
public final boolean useMarketBasedPrice;
|
||||
public final double marketPriceMargin;
|
||||
public final long offerAmount;
|
||||
public final long offerMinAmount;
|
||||
public final String offerId;
|
||||
public final String depositTxId;
|
||||
|
||||
// primaryMarket fields are based on industry standard where primaryMarket is always in the focus (in the app BTC is always in the focus - will be changed in a larger refactoring once)
|
||||
public String currencyPair;
|
||||
public OfferPayload.Direction primaryMarketDirection;
|
||||
|
||||
public long primaryMarketTradePrice;
|
||||
public long primaryMarketTradeAmount;
|
||||
public long primaryMarketTradeVolume;
|
||||
|
||||
public TradeStatisticsForJson(TradeStatistics2 tradeStatistics) {
|
||||
this.direction = OfferPayload.Direction.valueOf(tradeStatistics.getDirection().name());
|
||||
this.currency = tradeStatistics.getCurrencyCode();
|
||||
this.paymentMethod = tradeStatistics.getOfferPaymentMethod();
|
||||
this.offerDate = tradeStatistics.getOfferDate();
|
||||
this.useMarketBasedPrice = tradeStatistics.isOfferUseMarketBasedPrice();
|
||||
this.marketPriceMargin = tradeStatistics.getOfferMarketPriceMargin();
|
||||
this.offerAmount = tradeStatistics.getOfferAmount();
|
||||
this.offerMinAmount = tradeStatistics.getOfferMinAmount();
|
||||
this.offerId = tradeStatistics.getOfferId();
|
||||
this.tradePrice = tradeStatistics.getTradePrice().getValue();
|
||||
this.tradeAmount = tradeStatistics.getTradeAmount().getValue();
|
||||
this.tradeDate = tradeStatistics.getTradeDate().getTime();
|
||||
this.depositTxId = tradeStatistics.getDepositTxId();
|
||||
public TradeStatisticsForJson(TradeStatistics3 tradeStatistics) {
|
||||
this.currency = tradeStatistics.getCurrency();
|
||||
this.paymentMethod = tradeStatistics.getPaymentMethod();
|
||||
this.tradePrice = tradeStatistics.getPrice();
|
||||
this.tradeAmount = tradeStatistics.getAmount();
|
||||
this.tradeDate = tradeStatistics.getDate();
|
||||
|
||||
try {
|
||||
final Price tradePrice = getTradePrice();
|
||||
Price tradePrice = getTradePrice();
|
||||
if (CurrencyUtil.isCryptoCurrency(currency)) {
|
||||
primaryMarketDirection = direction == OfferPayload.Direction.BUY ? OfferPayload.Direction.SELL : OfferPayload.Direction.BUY;
|
||||
currencyPair = currency + "/" + Res.getBaseCurrencyCode();
|
||||
|
||||
primaryMarketTradePrice = tradePrice.getValue();
|
||||
|
||||
primaryMarketTradeAmount = getTradeVolume() != null ? getTradeVolume().getValue() : 0;
|
||||
primaryMarketTradeAmount = getTradeVolume() != null ?
|
||||
getTradeVolume().getValue() :
|
||||
0;
|
||||
primaryMarketTradeVolume = getTradeAmount().getValue();
|
||||
} else {
|
||||
primaryMarketDirection = direction;
|
||||
currencyPair = Res.getBaseCurrencyCode() + "/" + currency;
|
||||
|
||||
// we use precision 4 for fiat based price but on the markets api we use precision 8 so we scale up by 10000
|
||||
primaryMarketTradePrice = (long) MathUtils.scaleUpByPowerOf10(tradePrice.getValue(), 4);
|
||||
|
||||
primaryMarketTradeAmount = getTradeAmount().getValue();
|
||||
// we use precision 4 for fiat but on the markets api we use precision 8 so we scale up by 10000
|
||||
primaryMarketTradeVolume = getTradeVolume() != null ?
|
||||
(long) MathUtils.scaleUpByPowerOf10(getTradeVolume().getValue(), 4) : 0;
|
||||
(long) MathUtils.scaleUpByPowerOf10(getTradeVolume().getValue(), 4) :
|
||||
0;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.error(t.getMessage());
|
||||
@ -113,6 +91,10 @@ public final class TradeStatisticsForJson {
|
||||
}
|
||||
|
||||
public Volume getTradeVolume() {
|
||||
return getTradePrice().getVolumeByAmount(getTradeAmount());
|
||||
try {
|
||||
return getTradePrice().getVolumeByAmount(getTradeAmount());
|
||||
} catch (Throwable t) {
|
||||
return Volume.parse("0", currency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import bisq.common.util.Utilities;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableSet;
|
||||
@ -40,94 +41,75 @@ import java.io.File;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Singleton
|
||||
@Slf4j
|
||||
public class TradeStatisticsManager {
|
||||
|
||||
private final JsonFileManager jsonFileManager;
|
||||
private final P2PService p2PService;
|
||||
private final PriceFeedService priceFeedService;
|
||||
private final TradeStatistics2StorageService tradeStatistics2StorageService;
|
||||
private final TradeStatistics3StorageService tradeStatistics3StorageService;
|
||||
private final File storageDir;
|
||||
private final boolean dumpStatistics;
|
||||
private final ObservableSet<TradeStatistics2> observableTradeStatisticsSet = FXCollections.observableSet();
|
||||
private final ObservableSet<TradeStatistics3> observableTradeStatisticsSet = FXCollections.observableSet();
|
||||
private JsonFileManager jsonFileManager;
|
||||
|
||||
@Inject
|
||||
public TradeStatisticsManager(P2PService p2PService,
|
||||
PriceFeedService priceFeedService,
|
||||
TradeStatistics2StorageService tradeStatistics2StorageService,
|
||||
TradeStatistics3StorageService tradeStatistics3StorageService,
|
||||
AppendOnlyDataStoreService appendOnlyDataStoreService,
|
||||
@Named(Config.STORAGE_DIR) File storageDir,
|
||||
@Named(Config.DUMP_STATISTICS) boolean dumpStatistics) {
|
||||
this.p2PService = p2PService;
|
||||
this.priceFeedService = priceFeedService;
|
||||
this.tradeStatistics2StorageService = tradeStatistics2StorageService;
|
||||
this.tradeStatistics3StorageService = tradeStatistics3StorageService;
|
||||
this.storageDir = storageDir;
|
||||
this.dumpStatistics = dumpStatistics;
|
||||
jsonFileManager = new JsonFileManager(storageDir);
|
||||
|
||||
appendOnlyDataStoreService.addService(tradeStatistics2StorageService);
|
||||
|
||||
appendOnlyDataStoreService.addService(tradeStatistics3StorageService);
|
||||
}
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
p2PService.getP2PDataStorage().addAppendOnlyDataStoreListener(payload -> {
|
||||
if (payload instanceof TradeStatistics2)
|
||||
addToSet((TradeStatistics2) payload);
|
||||
if (payload instanceof TradeStatistics3) {
|
||||
TradeStatistics3 tradeStatistics = (TradeStatistics3) payload;
|
||||
if (!tradeStatistics.isValid()) {
|
||||
return;
|
||||
}
|
||||
observableTradeStatisticsSet.add(tradeStatistics);
|
||||
priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet);
|
||||
maybeDump();
|
||||
}
|
||||
});
|
||||
|
||||
Set<TradeStatistics2> set = tradeStatistics2StorageService.getMapOfAllData().values().stream()
|
||||
.filter(e -> e instanceof TradeStatistics2)
|
||||
.map(e -> (TradeStatistics2) e)
|
||||
.map(WrapperTradeStatistics2::new)
|
||||
.distinct()
|
||||
.map(WrapperTradeStatistics2::unwrap)
|
||||
.filter(TradeStatistics2::isValid)
|
||||
Set<TradeStatistics3> set = tradeStatistics3StorageService.getMapOfAllData().values().stream()
|
||||
.filter(e -> e instanceof TradeStatistics3)
|
||||
.map(e -> (TradeStatistics3) e)
|
||||
.filter(TradeStatistics3::isValid)
|
||||
.collect(Collectors.toSet());
|
||||
observableTradeStatisticsSet.addAll(set);
|
||||
|
||||
priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet);
|
||||
|
||||
dump();
|
||||
maybeDump();
|
||||
}
|
||||
|
||||
public ObservableSet<TradeStatistics2> getObservableTradeStatisticsSet() {
|
||||
public ObservableSet<TradeStatistics3> getObservableTradeStatisticsSet() {
|
||||
return observableTradeStatisticsSet;
|
||||
}
|
||||
|
||||
private void addToSet(TradeStatistics2 tradeStatistics) {
|
||||
if (!observableTradeStatisticsSet.contains(tradeStatistics)) {
|
||||
Optional<TradeStatistics2> duplicate = observableTradeStatisticsSet.stream().filter(
|
||||
e -> e.getOfferId().equals(tradeStatistics.getOfferId())).findAny();
|
||||
|
||||
if (duplicate.isPresent()) {
|
||||
// TODO: Can be removed as soon as everyone uses v1.2.6+
|
||||
// Removes an existing object with a trade id if the new one matches the existing except
|
||||
// for the deposit tx id
|
||||
if (tradeStatistics.getDepositTxId() == null &&
|
||||
tradeStatistics.isValid() &&
|
||||
duplicate.get().compareTo(tradeStatistics) == 0) {
|
||||
observableTradeStatisticsSet.remove(duplicate.get());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tradeStatistics.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
observableTradeStatisticsSet.add(tradeStatistics);
|
||||
priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet);
|
||||
dump();
|
||||
private void maybeDump() {
|
||||
if (!dumpStatistics) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void dump() {
|
||||
if (dumpStatistics) {
|
||||
if (jsonFileManager == null) {
|
||||
jsonFileManager = new JsonFileManager(storageDir);
|
||||
|
||||
// We only dump once the currencies as they do not change during runtime
|
||||
ArrayList<CurrencyTuple> fiatCurrencyList = CurrencyUtil.getAllSortedFiatCurrencies().stream()
|
||||
.map(e -> new CurrencyTuple(e.getCode(), e.getName(), 8))
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
@ -138,44 +120,15 @@ public class TradeStatisticsManager {
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
cryptoCurrencyList.add(0, new CurrencyTuple(Res.getBaseCurrencyCode(), Res.getBaseCurrencyName(), 8));
|
||||
jsonFileManager.writeToDisc(Utilities.objectToJson(cryptoCurrencyList), "crypto_currency_list");
|
||||
|
||||
// We store the statistics as json so it is easy for further processing (e.g. for web based services)
|
||||
// TODO This is just a quick solution for storing to one file.
|
||||
// 1 statistic entry has 500 bytes as json.
|
||||
// Need a more scalable solution later when we get more volume.
|
||||
// The flag will only be activated by dedicated nodes, so it should not be too critical for the moment, but needs to
|
||||
// get improved. Maybe a LevelDB like DB...? Could be impl. in a headless version only.
|
||||
List<TradeStatisticsForJson> list = observableTradeStatisticsSet.stream().map(TradeStatisticsForJson::new)
|
||||
.sorted((o1, o2) -> (Long.compare(o2.tradeDate, o1.tradeDate)))
|
||||
.collect(Collectors.toList());
|
||||
TradeStatisticsForJson[] array = new TradeStatisticsForJson[list.size()];
|
||||
list.toArray(array);
|
||||
jsonFileManager.writeToDisc(Utilities.objectToJson(array), "trade_statistics");
|
||||
}
|
||||
|
||||
List<TradeStatisticsForJson> list = observableTradeStatisticsSet.stream()
|
||||
.map(TradeStatisticsForJson::new)
|
||||
.sorted((o1, o2) -> (Long.compare(o2.tradeDate, o1.tradeDate)))
|
||||
.collect(Collectors.toList());
|
||||
TradeStatisticsForJson[] array = new TradeStatisticsForJson[list.size()];
|
||||
list.toArray(array);
|
||||
jsonFileManager.writeToDisc(Utilities.objectToJson(array), "trade_statistics");
|
||||
}
|
||||
|
||||
static class WrapperTradeStatistics2 {
|
||||
private final TradeStatistics2 tradeStatistics;
|
||||
|
||||
public WrapperTradeStatistics2(TradeStatistics2 tradeStatistics) {
|
||||
this.tradeStatistics = tradeStatistics;
|
||||
}
|
||||
|
||||
public TradeStatistics2 unwrap() {
|
||||
return this.tradeStatistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
var wrapper = (WrapperTradeStatistics2) obj;
|
||||
return Objects.equals(tradeStatistics.getOfferId(), wrapper.tradeStatistics.getOfferId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(tradeStatistics.getOfferId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user