Skip to content

Commit

Permalink
Add vwap bsq price (#4098)
Browse files Browse the repository at this point in the history
* Remove unused code

* Add VWAP calculation for USD/BSQ price

* Remove unused argument
  • Loading branch information
sqrrm authored Mar 31, 2020
1 parent c62c2d8 commit 55048dc
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 35 deletions.
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 @@ -2257,6 +2257,8 @@ 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.avgPrice30=30 days average BSQ/BTC trade price
dao.factsAndFigures.dashboard.avgUSDPrice90=90 days volume weighted average USD/BSQ trade price
dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average USD/BSQ 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 @@ -21,7 +21,6 @@
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.TextFieldWithIcon;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.GUIUtil;

import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.DaoStateListener;
Expand All @@ -34,13 +33,15 @@
import bisq.core.trade.statistics.TradeStatistics2;
import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.user.Preferences;
import bisq.core.util.coin.BsqFormatter;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.BsqFormatter;

import bisq.common.util.MathUtils;
import bisq.common.util.Tuple2;
import bisq.common.util.Tuple3;

import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat;

import javax.inject.Inject;

Expand Down Expand Up @@ -76,7 +77,6 @@

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
Expand Down Expand Up @@ -108,8 +108,8 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
private AreaChart<Number, Number> bsqPriceChart;
private XYChart.Series<Number, Number> seriesBSQPrice;

private TextField avgPrice90TextField, marketCapTextField, availableAmountTextField;
private TextFieldWithIcon avgPrice30TextField;
private TextField avgPrice90TextField, avgUSDPrice90TextField, marketCapTextField, availableAmountTextField;
private TextFieldWithIcon avgPrice30TextField, avgUSDPrice30TextField;
private Label marketPriceLabel;

private Coin availableAmount;
Expand Down Expand Up @@ -143,7 +143,8 @@ public void initialize() {

priceChangeListener = (observable, oldValue, newValue) -> {
updatePrice();
updateAveragePriceFields();
updateAveragePriceFields(avgPrice90TextField, avgPrice30TextField, false);
updateAveragePriceFields(avgUSDPrice90TextField, avgUSDPrice30TextField, true);
};
}

Expand All @@ -162,6 +163,13 @@ private void createKPIs() {
Res.get("dao.factsAndFigures.dashboard.avgPrice30"), -15).second;
AnchorPane.setRightAnchor(avgPrice30TextField.getIconLabel(), 10d);

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

avgUSDPrice30TextField = addTopLabelTextFieldWithIcon(root, gridRow, 1,
Res.get("dao.factsAndFigures.dashboard.avgUSDPrice30"), -15).second;
AnchorPane.setRightAnchor(avgUSDPrice30TextField.getIconLabel(), 10d);

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

Expand All @@ -178,7 +186,8 @@ protected void activate() {
updateWithBsqBlockChainData();
updatePrice();
updateChartData();
updateAveragePriceFields();
updateAveragePriceFields(avgPrice90TextField, avgPrice30TextField, false);
updateAveragePriceFields(avgUSDPrice90TextField, avgUSDPrice30TextField, true);
}


Expand Down Expand Up @@ -336,67 +345,102 @@ private void updatePrice() {
}
}

private void updateAveragePriceFields() {
long average90 = updateAveragePriceField(avgPrice90TextField, 90);
long average30 = updateAveragePriceField(avgPrice30TextField.getTextField(), 30);
private void updateAveragePriceFields(TextField field90, TextFieldWithIcon field30, boolean isUSDField) {
long average90 = updateAveragePriceField(field90, 90, isUSDField);
long average30 = updateAveragePriceField(field30.getTextField(), 30, isUSDField);
boolean trendUp = average30 > average90;
boolean trendDown = average30 < average90;

Label iconLabel = avgPrice30TextField.getIconLabel();
Label iconLabel = field30.getIconLabel();
ObservableList<String> styleClass = iconLabel.getStyleClass();
if (trendUp) {
avgPrice30TextField.setVisible(true);
avgPrice30TextField.setIcon(AwesomeIcon.CIRCLE_ARROW_UP);
field30.setVisible(true);
field30.setIcon(AwesomeIcon.CIRCLE_ARROW_UP);
styleClass.remove("price-trend-down");
styleClass.add("price-trend-up");
} else if (trendDown) {
avgPrice30TextField.setVisible(true);
avgPrice30TextField.setIcon(AwesomeIcon.CIRCLE_ARROW_DOWN);
field30.setVisible(true);
field30.setIcon(AwesomeIcon.CIRCLE_ARROW_DOWN);
styleClass.remove("price-trend-up");
styleClass.add("price-trend-down");
} else {
iconLabel.setVisible(false);
}
}

private long updateAveragePriceField(TextField textField, int days) {
Date past90 = getPastDate(days);
List<TradeStatistics2> bsqTradePast90Days = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
private long updateAveragePriceField(TextField textField, int days, boolean isUSDField) {
Date pastXDays = getPastDate(days);
List<TradeStatistics2> bsqTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
.filter(e -> e.getCurrencyCode().equals("BSQ"))
.filter(e -> e.getTradeDate().after(past90))
.filter(e -> e.getTradeDate().after(pastXDays))
.collect(Collectors.toList());
long average = getAverage(bsqTradePast90Days);
Coin oneBsq = Coin.valueOf(100);
Price avgPrice = Price.valueOf("BSQ", average);
List<TradeStatistics2> usdTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
.filter(e -> e.getCurrencyCode().equals("USD"))
.filter(e -> e.getTradeDate().after(pastXDays))
.collect(Collectors.toList());
long average = isUSDField ? getUSDAverage(bsqTradePastXDays, usdTradePastXDays) :
getBTCAverage(bsqTradePastXDays);
Price avgPrice = isUSDField ? Price.valueOf("USD", average) :
Price.valueOf("BSQ", average);
String avg = FormattingUtils.formatPrice(avgPrice);
String bsqInUsdAvg = average > 0 ? GUIUtil.getBsqInUsd(avgPrice, oneBsq, priceFeedService, bsqFormatter) : Res.get("shared.na");
textField.setText(avg + " BSQ/BTC (" + "1 BSQ = " + bsqInUsdAvg + ")");
if (isUSDField) {
textField.setText(avg + " USD/BSQ");
} else {
textField.setText(avg + " BSQ/BTC");
}
return average;
}

private long getAverage(List<TradeStatistics2> list) {
private long getBTCAverage(List<TradeStatistics2> bsqList) {
long accumulatedVolume = 0;
long accumulatedAmount = 0;
List<Long> tradePrices = new ArrayList<>(list.size());

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

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

return averagePrice;
}

private long getUSDAverage(List<TradeStatistics2> bsqList, List<TradeStatistics2> usdList) {
// Use next USD/BTC print as price to calculate BSQ/USD rate
// Store each trade as amount of USD and amount of BSQ traded
List<Tuple2<Double, Double>> usdBsqList = new ArrayList<>(bsqList.size());
usdList.sort(Comparator.comparing(o -> o.getTradeDate().getTime()));
var usdBTCPrice = 10000d; // Default to 10000 USD per BTC if there is no USD feed at all

for (TradeStatistics2 item : bsqList) {
// Find usdprice for trade item
usdBTCPrice = usdList.stream()
.filter(usd -> usd.getTradeDate().getTime() > item.getTradeDate().getTime())
.map(usd -> MathUtils.scaleDownByPowerOf10((double) usd.getTradePrice().getValue(),
Fiat.SMALLEST_UNIT_EXPONENT))
.findFirst()
.orElse(usdBTCPrice);
var bsqAmount = MathUtils.scaleDownByPowerOf10((double) item.getTradeVolume().getValue(),
Altcoin.SMALLEST_UNIT_EXPONENT);
var btcAmount = MathUtils.scaleDownByPowerOf10((double) item.getTradeAmount().getValue(),
Altcoin.SMALLEST_UNIT_EXPONENT);
usdBsqList.add(new Tuple2<>(usdBTCPrice * btcAmount, bsqAmount));
}
long averagePrice;
var usdTraded = usdBsqList.stream()
.mapToDouble(item -> item.first)
.sum();
var bsqTraded = usdBsqList.stream()
.mapToDouble(item -> item.second)
.sum();
var averageAsDouble = bsqTraded > 0 ? usdTraded / bsqTraded : 0d;
var averageScaledUp = MathUtils.scaleUpByPowerOf10(averageAsDouble, Fiat.SMALLEST_UNIT_EXPONENT);
averagePrice = bsqTraded > 0 ? MathUtils.roundDoubleToLong(averageScaledUp) : 0;

return averagePrice;
}

private Date getPastDate(int days) {
Calendar cal = new GregorianCalendar();
cal.setTime(new Date());
Expand Down

0 comments on commit 55048dc

Please sign in to comment.