Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show BSQ trade fee in % per day #3357

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion core/src/main/resources/i18n/displayStrings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ market.spread.spreadColumn=Spread

# TradesChartsView
market.trades.nrOfTrades=Trades: {0}
market.trades.tooltip.volumeBar=Volume: {0}\nNo. of trades: {1}\nDate: {2}
market.trades.tooltip.volumeBar=Volume: {0}\nNo. of trades: {1}\nBisq Burn Ratio: {2}%\nDate: {3}
market.trades.tooltip.candle.open=Open:
market.trades.tooltip.candle.close=Close:
market.trades.tooltip.candle.high=High:
Expand Down Expand Up @@ -1784,6 +1784,7 @@ dao.proofOfBurn.verify=Verify
dao.proofOfBurn.verify.header=Verify message with key from proof of burn transaction
dao.proofOfBurn.verificationResult.ok=Verification succeeded
dao.proofOfBurn.verificationResult.failed=Verification failed
dao.proofOfBurn.volumeRatio=Bisq Burn/Traded {0} Volume

# suppress inspection "UnusedProperty"
dao.phase.UNDEFINED=Undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@

import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.SingleSelectionModel;
Expand Down Expand Up @@ -108,6 +109,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
private NumberAxis priceAxisX, priceAxisY, volumeAxisY, volumeAxisX;
private XYChart.Series<Number, Number> priceSeries;
private XYChart.Series<Number, Number> volumeSeries;
private XYChart.Series<Number, Number> bisqBurnSeries;
private ChangeListener<Number> priceAxisYWidthListener;
private ChangeListener<Number> volumeAxisYWidthListener;
private double priceAxisYWidth;
Expand Down Expand Up @@ -327,6 +329,7 @@ private CurrencyListItem specialShowAllItem() {
// Chart
///////////////////////////////////////////////////////////////////////////////////////////

@SuppressWarnings("unchecked")
private void createCharts() {
priceSeries = new XYChart.Series<>();

Expand Down Expand Up @@ -394,6 +397,10 @@ public Number fromString(String string) {
priceChartPane.getChildren().add(priceChart);

volumeSeries = new XYChart.Series<>();
volumeSeries.setName(Res.get("shared.volumeWithCur", model.getCurrencyCode()));

bisqBurnSeries = new XYChart.Series<>();
bisqBurnSeries.setName(Res.get("dao.proofOfBurn.volumeRatio", model.getCurrencyCode()));

volumeAxisX = new NumberAxis(0, model.maxTicks + 1, 1);
volumeAxisX.setTickUnit(1);
Expand Down Expand Up @@ -430,11 +437,12 @@ public Number fromString(String string) {
});
//noinspection unchecked
volumeChart.setId("volume-chart");
volumeChart.setData(FXCollections.observableArrayList(volumeSeries));
//TODO bisqBurnSeries should only be added when model.getCurrencyCode() == "BSQ" ???
volumeChart.setData(FXCollections.observableArrayList(volumeSeries, bisqBurnSeries));
volumeChart.setMinHeight(128);
volumeChart.setPrefHeight(128);
volumeChart.setMaxHeight(200);
volumeChart.setLegendVisible(false);
volumeChart.setLegendVisible(true);
volumeChart.setPadding(new Insets(0));

volumeChartPane = new AnchorPane();
Expand All @@ -450,6 +458,7 @@ public Number fromString(String string) {

private void updateChartData() {
volumeSeries.getData().setAll(model.volumeItems);
bisqBurnSeries.getData().setAll(model.burntBsqRatioItems);

// At price chart we need to set the priceSeries new otherwise the lines are not rendered correctly
// TODO should be fixed in candle chart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
import bisq.desktop.util.CurrencyListItem;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.GUIUtil;

import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Tx;
import bisq.core.locale.CryptoCurrency;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.GlobalSettings;
Expand All @@ -38,7 +39,7 @@
import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.user.Preferences;
import bisq.core.util.BSFormatter;

import bisq.core.util.BsqFormatter;
import bisq.common.util.MathUtils;

import org.bitcoinj.core.Coin;
Expand All @@ -60,21 +61,27 @@

import javafx.util.Pair;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import javax.annotation.Nullable;

Expand All @@ -98,6 +105,8 @@ public enum TickUnit {
private final TradeStatisticsManager tradeStatisticsManager;
final Preferences preferences;
private PriceFeedService priceFeedService;
private final DaoStateService daoStateService;
private final BsqFormatter bsqFormatter;
private Navigation navigation;
private BSFormatter formatter;

Expand All @@ -107,9 +116,12 @@ public enum TickUnit {
private final CurrencyList currencyListItems;
private final CurrencyListItem showAllCurrencyListItem = new CurrencyListItem(new CryptoCurrency(GUIUtil.SHOW_ALL_FLAG, ""), -1);
final ObservableList<TradeStatistics2> tradeStatisticsByCurrency = FXCollections.observableArrayList();
private final ObservableList<Tx> burntBsqStatistics = FXCollections.observableArrayList();
final ObservableList<XYChart.Data<Number, Number>> priceItems = FXCollections.observableArrayList();
final ObservableList<XYChart.Data<Number, Number>> volumeItems = FXCollections.observableArrayList();
final ObservableList<XYChart.Data<Number, Number>> burntBsqRatioItems = FXCollections.observableArrayList();
private Map<Long, Pair<Date, Set<TradeStatistics2>>> itemsPerInterval;
private Map<Long, Pair<Date, Set<Tx>>> burntBsqPerInterval;

TickUnit tickUnit = TickUnit.DAY;
final int maxTicks = 90;
Expand All @@ -121,12 +133,16 @@ public enum TickUnit {

@SuppressWarnings("WeakerAccess")
@Inject
public TradesChartsViewModel(TradeStatisticsManager tradeStatisticsManager, Preferences preferences, PriceFeedService priceFeedService, Navigation navigation, BSFormatter formatter) {
public TradesChartsViewModel(TradeStatisticsManager tradeStatisticsManager, Preferences preferences,
PriceFeedService priceFeedService, Navigation navigation, BSFormatter formatter,
DaoStateService daoStateService, BsqFormatter bsqFormatter) {
this.tradeStatisticsManager = tradeStatisticsManager;
this.preferences = preferences;
this.priceFeedService = priceFeedService;
this.navigation = navigation;
this.formatter = formatter;
this.daoStateService = daoStateService;
this.bsqFormatter = bsqFormatter;

setChangeListener = change -> {
updateChartData();
Expand Down Expand Up @@ -256,6 +272,8 @@ private void updateChartData() {
tradeStatisticsByCurrency.setAll(tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
.filter(e -> showAllTradeCurrenciesProperty.get() || e.getCurrencyCode().equals(getCurrencyCode()))
.collect(Collectors.toList()));

burntBsqStatistics.setAll(daoStateService.getBurntFeeTxs());

// Generate date range and create sets for all ticks
itemsPerInterval = new HashMap<>();
Expand All @@ -279,10 +297,31 @@ private void updateChartData() {
}
});

burntBsqPerInterval = new HashMap<>();
time = new Date();
for (long i = maxTicks + 1; i >= 0; --i) {
Set<Tx> set = new HashSet<>();
Pair<Date, Set<Tx>> pair = new Pair<>((Date) time.clone(), set);
burntBsqPerInterval.put(i, pair);
time.setTime(time.getTime() - 1);
time = roundToTick(time, tickUnit);
}

// Get all entries for the defined time interval
burntBsqStatistics.stream().forEach(e -> {
for (long i = maxTicks; i > 0; --i) {
Pair<Date, Set<Tx>> p = burntBsqPerInterval.get(i);
if (Date.from(Instant.ofEpochMilli(e.getTime())).after(p.getKey())) {
p.getValue().add(e);
break;
}
}
});

// create CandleData for defined time interval
List<CandleData> candleDataList = itemsPerInterval.entrySet().stream()
.filter(entry -> entry.getKey() >= 0 && !entry.getValue().getValue().isEmpty())
.map(entry -> getCandleData(entry.getKey(), entry.getValue().getValue()))
.map(entry -> getCandleData(entry.getKey(), entry.getValue().getValue(), burntBsqPerInterval.get(entry.getKey()).getValue()))
.collect(Collectors.toList());
candleDataList.sort((o1, o2) -> (o1.tick < o2.tick ? -1 : (o1.tick == o2.tick ? 0 : 1)));

Expand All @@ -295,16 +334,24 @@ private void updateChartData() {
volumeItems.setAll(candleDataList.stream()
.map(e -> new XYChart.Data<Number, Number>(e.tick, e.accumulatedAmount, e))
.collect(Collectors.toList()));

//noinspection Convert2Diamond
burntBsqRatioItems.setAll(candleDataList.stream()
.map(e -> new XYChart.Data<Number, Number>(e.tick, e.bisqBurnVolumeRatio, e))
.collect(Collectors.toList()));

}

@VisibleForTesting
CandleData getCandleData(long tick, Set<TradeStatistics2> set) {
CandleData getCandleData(long tick, Set<TradeStatistics2> set, Set<Tx> burntBsqSet) {
long open = 0;
long close = 0;
long high = 0;
long low = 0;
long accumulatedVolume = 0;
long accumulatedAmount = 0;
long accumulatedBisqBurn = 0;
double bisqBurnVolumeRatio = 0;
long numTrades = set.size();
List<Long> tradePrices = new ArrayList<>(set.size());

Expand Down Expand Up @@ -333,13 +380,16 @@ CandleData getCandleData(long tick, Set<TradeStatistics2> set) {
tradePrices.toArray(prices);
long medianPrice = MathUtils.getMedian(prices);
boolean isBullish;
double accumulatedMetricAsDouble;
if (CurrencyUtil.isCryptoCurrency(getCurrencyCode())) {
isBullish = close < open;
double accumulatedAmountAsDouble = MathUtils.scaleUpByPowerOf10((double) accumulatedAmount, Altcoin.SMALLEST_UNIT_EXPONENT);
accumulatedMetricAsDouble = accumulatedAmount;
averagePrice = MathUtils.roundDoubleToLong(accumulatedAmountAsDouble / (double) accumulatedVolume);
} else {
isBullish = close > open;
double accumulatedVolumeAsDouble = MathUtils.scaleUpByPowerOf10((double) accumulatedVolume, Coin.SMALLEST_UNIT_EXPONENT);
accumulatedMetricAsDouble = accumulatedVolume;
averagePrice = MathUtils.roundDoubleToLong(accumulatedVolumeAsDouble / (double) accumulatedAmount);
}

Expand All @@ -348,8 +398,17 @@ CandleData getCandleData(long tick, Set<TradeStatistics2> set) {
String dateString = tickUnit.ordinal() > TickUnit.DAY.ordinal() ?
DisplayUtils.formatDateTimeSpan(dateFrom, dateTo) :
DisplayUtils.formatDate(dateFrom) + " - " + DisplayUtils.formatDate(dateTo);

for (Tx item : burntBsqSet) {
accumulatedBisqBurn += item.getBurntBsq();
}

//TODO compute bisqBurnVolumeRatio
double accumulatedBisqBurnAsDouble = (double) accumulatedBisqBurn;
bisqBurnVolumeRatio = accumulatedMetricAsDouble != 0 ? accumulatedBisqBurnAsDouble * 100.0 / accumulatedMetricAsDouble : 0l;//Percentage calculation

return new CandleData(tick, open, close, high, low, averagePrice, medianPrice, accumulatedAmount, accumulatedVolume,
numTrades, isBullish, dateString);
numTrades, bisqBurnVolumeRatio, isBullish, dateString);
}

Date roundToTick(Date time, TickUnit tickUnit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ public class CandleData {
public final long accumulatedAmount;
public final long accumulatedVolume;
public final long numTrades;
public final double bisqBurnVolumeRatio;
public final boolean isBullish;
public final String date;

public CandleData(long tick, long open, long close, long high, long low, long average, long median,
long accumulatedAmount, long accumulatedVolume, long numTrades,
long accumulatedAmount, long accumulatedVolume, long numTrades, double bisqBurnVolumeRatio,
boolean isBullish, String date) {
this.tick = tick;
this.open = open;
Expand All @@ -44,6 +45,7 @@ public CandleData(long tick, long open, long close, long high, long low, long av
this.accumulatedAmount = accumulatedAmount;
this.accumulatedVolume = accumulatedVolume;
this.numTrades = numTrades;
this.bisqBurnVolumeRatio = bisqBurnVolumeRatio;
this.isBullish = isBullish;
this.date = date;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void setSeriesAndDataStyleClasses(String seriesStyleClass, String dataSty
public void update(double height, double candleWidth, CandleData candleData) {
bar.resizeRelocate(-candleWidth / 2, 0, candleWidth, height);
String vol = volumeStringConverter.toString(candleData.accumulatedAmount);
tooltip.setText(Res.get("market.trades.tooltip.volumeBar", vol, candleData.numTrades, candleData.date));
tooltip.setText(Res.get("market.trades.tooltip.volumeBar", vol, candleData.numTrades, candleData.bisqBurnVolumeRatio, candleData.date));
}

private void updateStyleClasses() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
import bisq.desktop.Navigation;
import bisq.desktop.main.market.trades.charts.CandleData;

import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Tx;

import bisq.core.locale.FiatCurrency;
import bisq.core.monetary.Price;
import bisq.core.offer.OfferPayload;
Expand All @@ -28,6 +31,7 @@
import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.user.Preferences;
import bisq.core.util.BSFormatter;
import bisq.core.util.BsqFormatter;

import bisq.common.crypto.KeyRing;
import bisq.common.crypto.KeyStorage;
Expand Down Expand Up @@ -111,7 +115,7 @@ public class TradesChartsViewModelTest {
public void setup() throws IOException {
tradeStatisticsManager = mock(TradeStatisticsManager.class);
model = new TradesChartsViewModel(tradeStatisticsManager, mock(Preferences.class), mock(PriceFeedService.class),
mock(Navigation.class), mock(BSFormatter.class));
mock(Navigation.class), mock(BSFormatter.class), mock(DaoStateService.class), mock(BsqFormatter.class));
dir = File.createTempFile("temp_tests1", "");
//noinspection ResultOfMethodCallIgnored
dir.delete();
Expand Down Expand Up @@ -143,7 +147,9 @@ public void testGetCandleData() {
set.add(new TradeStatistics2(offer, Price.parse("EUR", "600"), Coin.parseCoin("1"), new Date(now.getTime() + 200), null, null));
set.add(new TradeStatistics2(offer, Price.parse("EUR", "580"), Coin.parseCoin("1"), new Date(now.getTime() + 300), null, null));

CandleData candleData = model.getCandleData(model.roundToTick(now, TradesChartsViewModel.TickUnit.DAY).getTime(), set);
Set<Tx> set2 = new HashSet<>();

CandleData candleData = model.getCandleData(model.roundToTick(now, TradesChartsViewModel.TickUnit.DAY).getTime(), set, set2);
assertEquals(open, candleData.open);
assertEquals(close, candleData.close);
assertEquals(high, candleData.high);
Expand Down