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

Add average bsq price #3212

Merged
Merged
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
11 changes: 11 additions & 0 deletions common/src/main/java/bisq/common/util/MathUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,15 @@ public static double scaleDownByPowerOf10(long value, int exponent) {
public static double exactMultiply(double value1, double value2) {
return BigDecimal.valueOf(value1).multiply(BigDecimal.valueOf(value2)).doubleValue();
}

public static Long getMedian(Long[] list) {
int middle = list.length / 2;
long median;
if (list.length % 2 == 1) {
median = list[middle];
} else {
median = MathUtils.roundDoubleToLong((list[middle - 1] + list[middle]) / 2.0);
}
return median;
}
}
15 changes: 13 additions & 2 deletions core/src/main/java/bisq/core/offer/OfferUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,21 @@ static Coin getAdjustedAmount(Coin amount, Price price, long maxTradeLimit, int
public static Optional<Volume> getFeeInUserFiatCurrency(Coin makerFee, boolean isCurrencyForMakerFeeBtc,
Preferences preferences, PriceFeedService priceFeedService,
BsqFormatter bsqFormatter) {
// We use the users currency derived from his selected country.
// We don't use the preferredTradeCurrency from preferences as that can be also set to an altcoin.
String countryCode = preferences.getUserCountry().code;
String userCurrencyCode = CurrencyUtil.getCurrencyByCountryCode(countryCode).getCode();
return getFeeInUserFiatCurrency(makerFee,
isCurrencyForMakerFeeBtc,
userCurrencyCode,
priceFeedService,
bsqFormatter);
}

public static Optional<Volume> getFeeInUserFiatCurrency(Coin makerFee, boolean isCurrencyForMakerFeeBtc,
String userCurrencyCode, PriceFeedService priceFeedService,
BsqFormatter bsqFormatter) {
// We use the users currency derived from his selected country.
// We don't use the preferredTradeCurrency from preferences as that can be also set to an altcoin.

MarketPrice marketPrice = priceFeedService.getMarketPrice(userCurrencyCode);
if (marketPrice != null && makerFee != null) {
long marketPriceAsLong = MathUtils.roundDoubleToLong(MathUtils.scaleUpByPowerOf10(marketPrice.getPrice(), Fiat.SMALLEST_UNIT_EXPONENT));
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/resources/i18n/displayStrings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2055,6 +2055,8 @@ dao.factsAndFigures.menuItem.transactions=BSQ Transactions

dao.factsAndFigures.dashboard.marketPrice=Market data
dao.factsAndFigures.dashboard.price=Latest BSQ/BTC trade price (in Bisq)
dao.factsAndFigures.dashboard.avgPrice90=90 days average BSQ/BTC trade price
dao.factsAndFigures.dashboard.medianPrice90=90 days median BSQ/BTC trade price
dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on trade price)
dao.factsAndFigures.dashboard.availableAmount=Total available BSQ

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,23 @@
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.GUIUtil;

import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.governance.IssuanceType;
import bisq.core.locale.Res;
import bisq.core.monetary.Altcoin;
import bisq.core.monetary.Price;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.TradeStatistics2;
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 bisq.common.util.Tuple2;
import bisq.common.util.Tuple3;

import org.bitcoinj.core.Coin;
Expand Down Expand Up @@ -67,7 +69,12 @@
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -78,9 +85,6 @@
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;



import java.sql.Date;

@FxmlView
public class BsqDashboardView extends ActivatableView<GridPane, Void> implements DaoStateListener {

Expand All @@ -90,18 +94,15 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
private final DaoFacade daoFacade;
private final TradeStatisticsManager tradeStatisticsManager;
private final PriceFeedService priceFeedService;
private final DaoStateService daoStateService;
private final Preferences preferences;
private final BsqFormatter bsqFormatter;
private final BSFormatter btcFormatter;

private ChangeListener<Number> priceChangeListener;

private AreaChart bsqPriceChart;
private XYChart.Series<Number, Number> seriesBSQAdded, seriesBSQBurnt;
private XYChart.Series<Number, Number> seriesBSQPrice;

private TextField marketCapTextField, availableAmountTextField;
private TextField avgPrice90TextField, medianPrice90TextField, marketCapTextField, availableAmountTextField;
private Label marketPriceLabel;

private Coin availableAmount;
Expand All @@ -116,17 +117,13 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
private BsqDashboardView(DaoFacade daoFacade,
TradeStatisticsManager tradeStatisticsManager,
PriceFeedService priceFeedService,
DaoStateService daoStateService,
Preferences preferences,
BsqFormatter bsqFormatter,
BSFormatter btcFormatter) {
BsqFormatter bsqFormatter) {
this.daoFacade = daoFacade;
this.tradeStatisticsManager = tradeStatisticsManager;
this.priceFeedService = priceFeedService;
this.daoStateService = daoStateService;
this.preferences = preferences;
this.bsqFormatter = bsqFormatter;
this.btcFormatter = btcFormatter;
}

@Override
Expand All @@ -137,7 +134,10 @@ public void initialize() {
createKPIs();
createChart();

priceChangeListener = (observable, oldValue, newValue) -> updatePrice();
priceChangeListener = (observable, oldValue, newValue) -> {
updatePrice();
updateAverageAndMedianPrice();
};
}

private void createKPIs() {
Expand All @@ -148,6 +148,12 @@ private void createKPIs() {

marketPriceBox.second.getStyleClass().add("dao-kpi-subtext");

avgPrice90TextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.factsAndFigures.dashboard.avgPrice90")).second;

medianPrice90TextField = addTopLabelReadOnlyTextField(root, gridRow, 1,
Res.get("dao.factsAndFigures.dashboard.medianPrice90")).second;

marketCapTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.factsAndFigures.dashboard.marketCap")).second;

Expand All @@ -165,6 +171,7 @@ protected void activate() {
updateWithBsqBlockChainData();
updatePrice();
updateChartData();
updateAverageAndMedianPrice();
}


Expand Down Expand Up @@ -236,8 +243,8 @@ public Number fromString(String string) {
bsqPriceChart.setLegendVisible(false);
bsqPriceChart.setAnimated(false);
bsqPriceChart.setId("charts-dao");
bsqPriceChart.setMinHeight(385);
bsqPriceChart.setPrefHeight(385);
bsqPriceChart.setMinHeight(335);
bsqPriceChart.setPrefHeight(bsqPriceChart.getMinHeight());
bsqPriceChart.setCreateSymbols(true);
bsqPriceChart.setPadding(new Insets(0));
bsqPriceChart.getData().addAll(seriesBSQPrice);
Expand All @@ -260,16 +267,16 @@ public Number fromString(String string) {
}

private void updateChartData() {
updateBSQPriceData();
updateBsqPriceData();
}

private void updateBSQPriceData() {
private void updateBsqPriceData() {
freimair marked this conversation as resolved.
Show resolved Hide resolved
seriesBSQPrice.getData().clear();

Map<LocalDate, List<TradeStatistics2>> bsqPriceByDate = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
.filter(e -> e.getCurrencyCode().equals("BSQ"))
.sorted(Comparator.comparing(TradeStatistics2::getTradeDate))
.collect(Collectors.groupingBy(item -> new Date(item.getTradeDate().getTime()).toLocalDate()
.collect(Collectors.groupingBy(item -> new java.sql.Date(item.getTradeDate().getTime()).toLocalDate()
.with(ADJUSTERS.get(DAY))));

List<XYChart.Data<Number, Number>> updatedBSQPrice = bsqPriceByDate.keySet().stream()
Expand Down Expand Up @@ -321,5 +328,56 @@ private void updatePrice() {
marketCapTextField.setText(Res.get("shared.na"));
}
}

private void updateAverageAndMedianPrice() {
Date past90 = getPastDate(90);
List<TradeStatistics2> bsqTradePast90Days = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
.filter(e -> e.getCurrencyCode().equals("BSQ"))
.filter(e -> e.getTradeDate().after(past90))
.collect(Collectors.toList());
Tuple2<Long, Long> averageAndMedian = getAverageAndMedian(bsqTradePast90Days);
Coin oneBsq = Coin.valueOf(100);

Price avgPrice = Price.valueOf("BSQ", averageAndMedian.first);
String avg = bsqFormatter.formatPrice(avgPrice);
String bsqInUsdAvg = GUIUtil.getBsqInUsd(avgPrice, oneBsq, priceFeedService, bsqFormatter);
avgPrice90TextField.setText(avg + " BSQ/BTC (" + "1 BSQ = " + bsqInUsdAvg + ")");

Price medianPrice = Price.valueOf("BSQ", averageAndMedian.second);
String median = bsqFormatter.formatPrice(medianPrice);
String bsqInUsdMedian = GUIUtil.getBsqInUsd(medianPrice, oneBsq, priceFeedService, bsqFormatter);
medianPrice90TextField.setText(median + " BSQ/BTC (" + "1 BSQ = " + bsqInUsdMedian + ")");
}

private Tuple2<Long, Long> getAverageAndMedian(List<TradeStatistics2> list) {
long accumulatedVolume = 0;
long accumulatedAmount = 0;
List<Long> tradePrices = new ArrayList<>(list.size());

for (TradeStatistics2 item : list) {
item.getTradeVolume();
accumulatedVolume += item.getTradeVolume().getValue();
accumulatedAmount += item.getTradeAmount().getValue();
tradePrices.add(item.getTradePrice().getValue());
}
Collections.sort(tradePrices);
list.sort(Comparator.comparingLong(o -> o.getTradeDate().getTime()));

long averagePrice;
Long[] prices = new Long[tradePrices.size()];
tradePrices.toArray(prices);
long medianPrice = MathUtils.getMedian(prices);
double accumulatedAmountAsDouble = MathUtils.scaleUpByPowerOf10((double) accumulatedAmount, Altcoin.SMALLEST_UNIT_EXPONENT);
averagePrice = MathUtils.roundDoubleToLong(accumulatedAmountAsDouble / (double) accumulatedVolume);

return new Tuple2<>(averagePrice, medianPrice);
}

private Date getPastDate(int days) {
Calendar cal = new GregorianCalendar();
cal.setTime(new Date());
cal.add(Calendar.DAY_OF_MONTH, -1 * days);
return cal.getTime();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ CandleData getCandleData(long tick, Set<TradeStatistics2> set) {
long averagePrice;
Long[] prices = new Long[tradePrices.size()];
tradePrices.toArray(prices);
long medianPrice = findMedian(prices);
long medianPrice = MathUtils.getMedian(prices);
boolean isBullish;
if (CurrencyUtil.isCryptoCurrency(getCurrencyCode())) {
isBullish = close < open;
Expand All @@ -350,17 +350,6 @@ CandleData getCandleData(long tick, Set<TradeStatistics2> set) {
return new CandleData(tick, open, close, high, low, averagePrice, medianPrice, accumulatedAmount, accumulatedVolume,
numTrades, isBullish, dateString);
}

Long findMedian(Long[] prices) {
int middle = prices.length / 2;
long median;
if (prices.length % 2 == 1) {
median = prices[middle];
} else {
median = MathUtils.roundDoubleToLong((prices[middle - 1] + prices[middle]) / 2.0);
}
return median;
}

Date roundToTick(Date time, TickUnit tickUnit) {
ZonedDateTime zdt = time.toInstant().atZone(ZoneId.systemDefault());
Expand Down
24 changes: 24 additions & 0 deletions desktop/src/main/java/bisq/desktop/util/GUIUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.locale.TradeCurrency;
import bisq.core.monetary.Price;
import bisq.core.monetary.Volume;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.PaymentAccountList;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.MarketPrice;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.user.DontShowAgainLookup;
import bisq.core.user.Preferences;
import bisq.core.user.User;
Expand All @@ -51,6 +55,7 @@
import bisq.common.storage.CorruptedDatabaseFilesHandler;
import bisq.common.storage.FileUtil;
import bisq.common.storage.Storage;
import bisq.common.util.MathUtils;
import bisq.common.util.Tuple2;
import bisq.common.util.Tuple3;
import bisq.common.util.Utilities;
Expand All @@ -59,6 +64,7 @@
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.uri.BitcoinURI;
import org.bitcoinj.utils.Fiat;
import org.bitcoinj.wallet.DeterministicSeed;

import com.googlecode.jcsv.CSVStrategy;
Expand Down Expand Up @@ -1022,4 +1028,22 @@ public static void openTxInBsqBlockExplorer(String txId, Preferences preferences
if (txId != null)
GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + txId, false);
}

public static String getBsqInUsd(Price bsqPrice,
chimp1984 marked this conversation as resolved.
Show resolved Hide resolved
Coin bsqAmount,
PriceFeedService priceFeedService,
BsqFormatter bsqFormatter) {
MarketPrice usdMarketPrice = priceFeedService.getMarketPrice("USD");
if (usdMarketPrice == null) {
return Res.get("shared.na");
}
long usdMarketPriceAsLong = MathUtils.roundDoubleToLong(MathUtils.scaleUpByPowerOf10(usdMarketPrice.getPrice(),
Fiat.SMALLEST_UNIT_EXPONENT));
Price usdPrice = Price.valueOf("USD", usdMarketPriceAsLong);
String bsqAmountAsString = bsqFormatter.formatCoin(bsqAmount);
Volume bsqAmountAsVolume = Volume.parse(bsqAmountAsString, "BSQ");
Coin requiredBtc = bsqPrice.getAmountByVolume(bsqAmountAsVolume);
Volume volumeByAmount = usdPrice.getVolumeByAmount(requiredBtc);
return bsqFormatter.formatVolumeWithCode(volumeByAmount);
}
}