Merge pull request #3212 from chimp1984/add-average-bsq-price

Add average bsq price
This commit is contained in:
Christoph Atteneder 2019-09-06 17:10:25 +02:00 committed by GitHub
commit 7c034a49c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 129 additions and 34 deletions

View file

@ -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;
}
}

View file

@ -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));

View file

@ -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

View file

@ -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();
}
}

View file

@ -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();

View file

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