Skip to content

Commit

Permalink
Merge pull request #3212 from chimp1984/add-average-bsq-price
Browse files Browse the repository at this point in the history
Add average bsq price
  • Loading branch information
ripcurlx committed Sep 6, 2019
2 parents 372ff50 + 69d70f3 commit 7c034a4
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 34 deletions.
11 changes: 11 additions & 0 deletions common/src/main/java/bisq/common/util/MathUtils.java
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
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
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
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() {
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();
}
}

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
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,
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);
}
}

0 comments on commit 7c034a4

Please sign in to comment.