Skip to content

Commit

Permalink
Add bsq price trend indicator (#3226)
Browse files Browse the repository at this point in the history
* Change text field for median to average 30

The average 30 days price is more relevant information than the small
different between average and median price. To add a second tuple of
both would be another option would overload the screen with not that
relevant information.

In a follow up commit we want to add an ip/down indication so see
price trend.

Fixes also handling of 0 values.

* Apply reformat (no code change)

* Add support for columnIndex

* Add price trend indicator

If average 30 days price is > av. 90 days price we show a green
arrow up, otherwise a red arrow down. If same we don't show an arrow.

* Fix dev test change

Forgot to change back a small change to test directions...

* Fix padding issues
  • Loading branch information
chimp1984 authored and ripcurlx committed Sep 9, 2019
1 parent 622cf8b commit bb806cf
Show file tree
Hide file tree
Showing 5 changed files with 417 additions and 110 deletions.
2 changes: 1 addition & 1 deletion core/src/main/resources/i18n/displayStrings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2056,7 +2056,7 @@ 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.avgPrice30=30 days average BSQ/BTC trade price
dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on trade price)
dao.factsAndFigures.dashboard.availableAmount=Total available BSQ

Expand Down
12 changes: 11 additions & 1 deletion desktop/src/main/java/bisq/desktop/bisq.css
Original file line number Diff line number Diff line change
Expand Up @@ -1298,7 +1298,7 @@ textfield */
#address-text-field:hover {
-fx-text-fill: -bs-text-color;
}

/* Account setup */
#wizard-item-background-deactivated {
-fx-body-color: linear-gradient(to bottom, -bs-content-background-gray, -bs-color-gray-aaa);
Expand Down Expand Up @@ -1990,6 +1990,16 @@ textfield */
-fx-fill: -bs-rd-green;
}

.price-trend-up {
-fx-text-fill: -bs-color-primary;
-fx-padding: 2 0 0 0;
}

.price-trend-down {
-fx-text-fill: -bs-red;
-fx-padding: 2 0 0 0;
}

/********************************************************************************************************************
* *
* Notifications *
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class TextFieldWithIcon extends AnchorPane {
public static final Logger log = LoggerFactory.getLogger(TextFieldWithIcon.class);
@Getter
private final Label iconLabel;
@Getter
private final TextField textField;
private final Label dummyTextField;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.TextFieldWithIcon;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.GUIUtil;

Expand All @@ -36,13 +37,14 @@
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;

import javax.inject.Inject;

import de.jensd.fx.fontawesome.AwesomeIcon;

import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
Expand All @@ -57,6 +59,8 @@

import javafx.beans.value.ChangeListener;

import javafx.collections.ObservableList;

import javafx.util.StringConverter;

import java.time.LocalDate;
Expand All @@ -83,6 +87,7 @@

import static bisq.desktop.util.FormBuilder.addLabelWithSubText;
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;
import static bisq.desktop.util.FormBuilder.addTopLabelTextFieldWithIcon;


@FxmlView
Expand All @@ -102,7 +107,8 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
private AreaChart bsqPriceChart;
private XYChart.Series<Number, Number> seriesBSQPrice;

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

private Coin availableAmount;
Expand Down Expand Up @@ -136,13 +142,13 @@ public void initialize() {

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

private void createKPIs() {

Tuple3<Label, Label, VBox> marketPriceBox = addLabelWithSubText(root, gridRow++, "0.004000 BSQ/BTC", "Latest BSQ/BTC trade price (in Bisq)");
Tuple3<Label, Label, VBox> marketPriceBox = addLabelWithSubText(root, gridRow++, "", "");
marketPriceLabel = marketPriceBox.first;
marketPriceLabel.getStyleClass().add("dao-kpi-big");

Expand All @@ -151,15 +157,15 @@ private void createKPIs() {
avgPrice90TextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.factsAndFigures.dashboard.avgPrice90")).second;

medianPrice90TextField = addTopLabelReadOnlyTextField(root, gridRow, 1,
Res.get("dao.factsAndFigures.dashboard.medianPrice90")).second;
avgPrice30TextField = addTopLabelTextFieldWithIcon(root, gridRow, 1,
Res.get("dao.factsAndFigures.dashboard.avgPrice30"), -15).second;
AnchorPane.setRightAnchor(avgPrice30TextField.getIconLabel(), 10d);

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

availableAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, 1,
Res.get("dao.factsAndFigures.dashboard.availableAmount")).second;

}


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


Expand Down Expand Up @@ -243,7 +249,7 @@ public Number fromString(String string) {
bsqPriceChart.setLegendVisible(false);
bsqPriceChart.setAnimated(false);
bsqPriceChart.setId("charts-dao");
bsqPriceChart.setMinHeight(335);
bsqPriceChart.setMinHeight(320);
bsqPriceChart.setPrefHeight(bsqPriceChart.getMinHeight());
bsqPriceChart.setCreateSymbols(true);
bsqPriceChart.setPadding(new Insets(0));
Expand Down Expand Up @@ -329,27 +335,45 @@ private void updatePrice() {
}
}

private void updateAverageAndMedianPrice() {
Date past90 = getPastDate(90);
private void updateAveragePriceFields() {
long average90 = updateAveragePriceField(avgPrice90TextField, 90);
long average30 = updateAveragePriceField(avgPrice30TextField.getTextField(), 30);
boolean trendUp = average30 > average90;
boolean trendDown = average30 < average90;

Label iconLabel = avgPrice30TextField.getIconLabel();
ObservableList<String> styleClass = iconLabel.getStyleClass();
if (trendUp) {
avgPrice30TextField.setVisible(true);
avgPrice30TextField.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);
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()
.filter(e -> e.getCurrencyCode().equals("BSQ"))
.filter(e -> e.getTradeDate().after(past90))
.collect(Collectors.toList());
Tuple2<Long, Long> averageAndMedian = getAverageAndMedian(bsqTradePast90Days);
long average = getAverage(bsqTradePast90Days);
Coin oneBsq = Coin.valueOf(100);

Price avgPrice = Price.valueOf("BSQ", averageAndMedian.first);
Price avgPrice = Price.valueOf("BSQ", average);
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 + ")");
String bsqInUsdAvg = average > 0 ? GUIUtil.getBsqInUsd(avgPrice, oneBsq, priceFeedService, bsqFormatter) : Res.get("shared.na");
textField.setText(avg + " BSQ/BTC (" + "1 BSQ = " + bsqInUsdAvg + ")");
return average;
}

private Tuple2<Long, Long> getAverageAndMedian(List<TradeStatistics2> list) {
private long getAverage(List<TradeStatistics2> list) {
long accumulatedVolume = 0;
long accumulatedAmount = 0;
List<Long> tradePrices = new ArrayList<>(list.size());
Expand All @@ -366,11 +390,10 @@ private Tuple2<Long, Long> getAverageAndMedian(List<TradeStatistics2> list) {
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);
averagePrice = accumulatedVolume > 0 ? MathUtils.roundDoubleToLong(accumulatedAmountAsDouble / (double) accumulatedVolume) : 0;

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

private Date getPastDate(int days) {
Expand Down
Loading

0 comments on commit bb806cf

Please sign in to comment.