mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-23 06:55:08 +01:00
Merge pull request #3212 from chimp1984/add-average-bsq-price
Add average bsq price
This commit is contained in:
commit
7c034a49c6
6 changed files with 129 additions and 34 deletions
|
@ -92,4 +92,15 @@ public class MathUtils {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,10 +278,21 @@ public class OfferUtil {
|
|||
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));
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -20,21 +20,23 @@ package bisq.desktop.main.dao.economy.dashboard;
|
|||
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;
|
||||
|
@ -67,7 +69,12 @@ import java.time.format.FormatStyle;
|
|||
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;
|
||||
|
@ -78,9 +85,6 @@ import static bisq.desktop.util.FormBuilder.addLabelWithSubText;
|
|||
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;
|
||||
|
||||
|
||||
|
||||
import java.sql.Date;
|
||||
|
||||
@FxmlView
|
||||
public class BsqDashboardView extends ActivatableView<GridPane, Void> implements DaoStateListener {
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -137,7 +134,10 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
|||
createKPIs();
|
||||
createChart();
|
||||
|
||||
priceChangeListener = (observable, oldValue, newValue) -> updatePrice();
|
||||
priceChangeListener = (observable, oldValue, newValue) -> {
|
||||
updatePrice();
|
||||
updateAverageAndMedianPrice();
|
||||
};
|
||||
}
|
||||
|
||||
private void createKPIs() {
|
||||
|
@ -148,6 +148,12 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
|||
|
||||
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;
|
||||
|
||||
|
@ -165,6 +171,7 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
|||
updateWithBsqBlockChainData();
|
||||
updatePrice();
|
||||
updateChartData();
|
||||
updateAverageAndMedianPrice();
|
||||
}
|
||||
|
||||
|
||||
|
@ -236,8 +243,8 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
|||
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);
|
||||
|
@ -260,16 +267,16 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
|||
}
|
||||
|
||||
private void updateChartData() {
|
||||
updateBSQPriceData();
|
||||
updateBsqPriceData();
|
||||
}
|
||||
|
||||
private void updateBSQPriceData() {
|
||||
private void updateBsqPriceData() {
|
||||
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()
|
||||
|
@ -321,5 +328,56 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -330,7 +330,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
|||
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;
|
||||
|
@ -351,17 +351,6 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
|||
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());
|
||||
LocalDateTime tradeLocal = zdt.toLocalDateTime();
|
||||
|
|
|
@ -31,10 +31,14 @@ import bisq.core.locale.CountryUtil;
|
|||
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;
|
||||
|
@ -51,6 +55,7 @@ import bisq.common.proto.persistable.PersistenceProtoResolver;
|
|||
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;
|
||||
|
@ -59,6 +64,7 @@ import org.bitcoinj.core.Address;
|
|||
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;
|
||||
|
@ -1022,4 +1028,22 @@ public class GUIUtil {
|
|||
if (txId != null)
|
||||
GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + txId, false);
|
||||
}
|
||||
|
||||
public static String getBsqInUsd(Price bsqPrice,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue