mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 07:07:43 +01:00
Separate volume chart.
This commit is contained in:
parent
bdad86526a
commit
86273768b1
12 changed files with 290 additions and 213 deletions
|
@ -119,8 +119,11 @@ public final class Preferences implements Persistable {
|
|||
private double maxPriceDistanceInPercent;
|
||||
private boolean useInvertedMarketPrice;
|
||||
private String marketScreenCurrencyCode = CurrencyUtil.getDefaultTradeCurrency().getCode();
|
||||
private String tradeStatisticsScreenCurrencyCode = CurrencyUtil.getDefaultTradeCurrency().getCode();
|
||||
private String buyScreenCurrencyCode = CurrencyUtil.getDefaultTradeCurrency().getCode();
|
||||
private String sellScreenCurrencyCode = CurrencyUtil.getDefaultTradeCurrency().getCode();
|
||||
private int tradeStatisticsTickUnit = 0;
|
||||
|
||||
private boolean useStickyMarketPrice = false;
|
||||
private boolean usePercentageBasedPrice = false;
|
||||
private Map<String, String> peerTagMap = new HashMap<>();
|
||||
|
@ -205,6 +208,8 @@ public final class Preferences implements Persistable {
|
|||
marketScreenCurrencyCode = persisted.getMarketScreenCurrencyCode();
|
||||
buyScreenCurrencyCode = persisted.getBuyScreenCurrencyCode();
|
||||
sellScreenCurrencyCode = persisted.getSellScreenCurrencyCode();
|
||||
tradeStatisticsScreenCurrencyCode = persisted.getTradeStatisticsScreenCurrencyCode();
|
||||
tradeStatisticsTickUnit = persisted.getTradeStatisticsTickUnit();
|
||||
|
||||
if (persisted.getIgnoreTradersList() != null)
|
||||
ignoreTradersList = persisted.getIgnoreTradersList();
|
||||
|
@ -455,6 +460,16 @@ public final class Preferences implements Persistable {
|
|||
storage.queueUpForSave();
|
||||
}
|
||||
|
||||
public void setTradeStatisticsScreenCurrencyCode(String tradeStatisticsScreenCurrencyCode) {
|
||||
this.tradeStatisticsScreenCurrencyCode = tradeStatisticsScreenCurrencyCode;
|
||||
storage.queueUpForSave();
|
||||
}
|
||||
|
||||
public void setTradeStatisticsTickUnit(int tradeStatisticsTickUnit) {
|
||||
this.tradeStatisticsTickUnit = tradeStatisticsTickUnit;
|
||||
storage.queueUpForSave();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getter
|
||||
|
@ -607,6 +622,15 @@ public final class Preferences implements Persistable {
|
|||
public String getDefaultPath() {
|
||||
return defaultPath;
|
||||
}
|
||||
|
||||
public String getTradeStatisticsScreenCurrencyCode() {
|
||||
return tradeStatisticsScreenCurrencyCode;
|
||||
}
|
||||
|
||||
public int getTradeStatisticsTickUnit() {
|
||||
return tradeStatisticsTickUnit;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -636,4 +660,5 @@ public final class Preferences implements Persistable {
|
|||
this.blockChainExplorerMainNet = blockChainExplorerMainNet;
|
||||
storage.queueUpForSave(2000);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
}
|
||||
|
||||
.volume-bar.bg {
|
||||
-demo-bar-fill: #91b1cc;
|
||||
-demo-bar-fill: #70bfc6;
|
||||
}
|
||||
|
||||
.volume-bar {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<VBox fx:id="root" fx:controller="io.bitsquare.gui.main.markets.trades.TradesChartsView"
|
||||
spacing="20.0" fillWidth="true"
|
||||
spacing="10.0" fillWidth="true"
|
||||
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
|
|
@ -33,14 +33,15 @@ import javafx.beans.property.StringProperty;
|
|||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.geometry.Side;
|
||||
import javafx.scene.chart.NumberAxis;
|
||||
import javafx.scene.chart.XYChart;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.Callback;
|
||||
import javafx.util.StringConverter;
|
||||
|
@ -77,6 +78,12 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
private CandleStickChart priceChart;
|
||||
|
||||
private final ListChangeListener<XYChart.Data<Number, Number>> itemsChangeListener;
|
||||
private double priceAxisYWidth;
|
||||
private double volumeAxisYWidth;
|
||||
private ChangeListener<Number> priceAxisYWidthListener;
|
||||
private ChangeListener<Number> volumeAxisYWidthListener;
|
||||
private NumberAxis volumeAxisX;
|
||||
private SortedList<TradeStatistics> sortedList;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -94,14 +101,15 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
@Override
|
||||
public void initialize() {
|
||||
HBox currencyHBox = createCurrencyComboBox();
|
||||
HBox toggleBarHBox = createToggleBar();
|
||||
// HBox toggleBarHBox = createToggleBar();
|
||||
|
||||
createChart();
|
||||
final VBox tableVBox = getTableBox();
|
||||
createTableBox();
|
||||
|
||||
StackPane stackPane = new StackPane();
|
||||
stackPane.getChildren().addAll(priceChart, volumeChart);
|
||||
/* StackPane stackPane = new StackPane();
|
||||
stackPane.getChildren().addAll(volumeChart, priceChart);*/
|
||||
|
||||
root.getChildren().addAll(currencyHBox, toggleBarHBox, stackPane, tableVBox);
|
||||
root.getChildren().addAll(currencyHBox, priceChart, volumeChart, tableView);
|
||||
|
||||
toggleChangeListener = (observable, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
|
@ -109,6 +117,14 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
timeAxisX.setTickLabelFormatter(getTimeAxisStringConverter());
|
||||
}
|
||||
};
|
||||
priceAxisYWidthListener = (observable, oldValue, newValue) -> {
|
||||
priceAxisYWidth = (double) newValue;
|
||||
layoutChart();
|
||||
};
|
||||
volumeAxisYWidthListener = (observable, oldValue, newValue) -> {
|
||||
volumeAxisYWidth = (double) newValue;
|
||||
layoutChart();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -119,8 +135,14 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 25));
|
||||
currencyComboBox.setOnAction(e -> model.onSetTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem()));
|
||||
|
||||
toggleGroup.getToggles().get(model.tickUnit.ordinal()).setSelected(true);
|
||||
|
||||
model.priceItems.addListener(itemsChangeListener);
|
||||
tradeCurrencySubscriber = EasyBind.subscribe(model.tradeCurrency,
|
||||
toggleGroup.selectedToggleProperty().addListener(toggleChangeListener);
|
||||
priceAxisY.widthProperty().addListener(priceAxisYWidthListener);
|
||||
volumeAxisY.widthProperty().addListener(volumeAxisYWidthListener);
|
||||
|
||||
tradeCurrencySubscriber = EasyBind.subscribe(model.tradeCurrencyProperty,
|
||||
tradeCurrency -> {
|
||||
String code = tradeCurrency.getCode();
|
||||
String tradeCurrencyName = tradeCurrency.getName();
|
||||
|
@ -135,8 +157,10 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
volumeAxisY.setLabel(volumeColumnLabel.get());
|
||||
});
|
||||
|
||||
tableView.setItems(model.tradeStatistics);
|
||||
toggleGroup.selectedToggleProperty().addListener(toggleChangeListener);
|
||||
sortedList = new SortedList<>(model.tradeStatisticsByCurrency);
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
|
||||
updateChartData();
|
||||
timeAxisX.setTickLabelFormatter(getTimeAxisStringConverter());
|
||||
}
|
||||
|
@ -144,11 +168,15 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
@Override
|
||||
protected void deactivate() {
|
||||
model.priceItems.removeListener(itemsChangeListener);
|
||||
toggleGroup.selectedToggleProperty().removeListener(toggleChangeListener);
|
||||
priceAxisY.widthProperty().removeListener(priceAxisYWidthListener);
|
||||
volumeAxisY.widthProperty().removeListener(volumeAxisYWidthListener);
|
||||
tradeCurrencySubscriber.unsubscribe();
|
||||
currencyComboBox.setOnAction(null);
|
||||
toggleGroup.selectedToggleProperty().removeListener(toggleChangeListener);
|
||||
priceSeries.getData().clear();
|
||||
priceChart.getData().clear();
|
||||
|
||||
sortedList.comparatorProperty().unbind();
|
||||
}
|
||||
|
||||
|
||||
|
@ -161,65 +189,109 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
timeAxisX.setTickUnit(1);
|
||||
timeAxisX.setMinorTickCount(0);
|
||||
timeAxisX.setForceZeroInRange(false);
|
||||
timeAxisX.setLabel("Date/Time");
|
||||
timeAxisX.setTickLabelFormatter(getTimeAxisStringConverter());
|
||||
|
||||
volumeSeries = new XYChart.Series<>();
|
||||
|
||||
volumeAxisY = new NumberAxis();
|
||||
volumeAxisY.setForceZeroInRange(false);
|
||||
volumeAxisY.setAutoRanging(true);
|
||||
volumeAxisY.setLabel("Volume");
|
||||
volumeAxisY.setTickLabelFormatter(getVolumeStringConverter());
|
||||
volumeAxisY.setSide(Side.RIGHT);
|
||||
|
||||
NumberAxis volumeAxisX = new NumberAxis(0, model.upperBound + 1, 1);
|
||||
volumeAxisX.setTickLabelsVisible(false);
|
||||
volumeAxisX.setTickMarkVisible(false);
|
||||
volumeAxisX.lookup(".axis-minor-tick-mark").setOpacity(0);
|
||||
|
||||
volumeChart = new VolumeChart(volumeAxisX, volumeAxisY);
|
||||
volumeChart.setData(FXCollections.observableArrayList(volumeSeries));
|
||||
volumeChart.setAnimated(false);
|
||||
volumeChart.setMinHeight(300);
|
||||
volumeChart.setPadding(new Insets(0, 0, 40, 69));
|
||||
volumeChart.setLegendVisible(false);
|
||||
|
||||
priceSeries = new XYChart.Series<>();
|
||||
|
||||
priceAxisY = new NumberAxis();
|
||||
priceAxisY.setForceZeroInRange(false);
|
||||
priceAxisY.setAutoRanging(true);
|
||||
priceAxisY.setLabel(priceColumnLabel.get());
|
||||
priceAxisY.setTickLabelFormatter(getPriceStringConverter());
|
||||
priceAxisY.setTickLabelFormatter(new StringConverter<Number>() {
|
||||
@Override
|
||||
public String toString(Number object) {
|
||||
return formatter.formatFiat(Fiat.valueOf(model.getCurrencyCode(), new Double((double) object).longValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
priceChart = new CandleStickChart(timeAxisX, priceAxisY);
|
||||
priceChart.setData(FXCollections.observableArrayList(priceSeries));
|
||||
priceChart.setToolTipStringConverter(getPriceStringConverter());
|
||||
priceChart.setLegendVisible(false);
|
||||
priceChart.setToolTipStringConverter(new StringConverter<Number>() {
|
||||
@Override
|
||||
public String toString(Number object) {
|
||||
return formatter.formatFiatWithCode(Fiat.valueOf(model.getCurrencyCode(), new Double((double) object).longValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
priceChart.setAnimated(true);
|
||||
priceChart.setMinHeight(300);
|
||||
priceChart.setPadding(new Insets(0, 69, 0, 0));
|
||||
priceChart.setAlternativeRowFillVisible(false);
|
||||
priceChart.setAlternativeColumnFillVisible(false);
|
||||
priceChart.setHorizontalGridLinesVisible(false);
|
||||
priceChart.setVerticalGridLinesVisible(false);
|
||||
priceChart.getXAxis().setVisible(false);
|
||||
priceChart.getYAxis().setVisible(false);
|
||||
priceChart.setMinHeight(250);
|
||||
priceChart.setLegendVisible(false);
|
||||
|
||||
volumeSeries = new XYChart.Series<>();
|
||||
|
||||
volumeAxisY = new NumberAxis();
|
||||
volumeAxisY.setForceZeroInRange(true);
|
||||
volumeAxisY.setAutoRanging(true);
|
||||
volumeAxisY.setLabel("Volume");
|
||||
volumeAxisY.setTickLabelFormatter(new StringConverter<Number>() {
|
||||
@Override
|
||||
public String toString(Number object) {
|
||||
return formatter.formatCoin(Coin.valueOf(new Double((double) object).longValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
volumeAxisX = new NumberAxis(0, model.upperBound + 1, 1);
|
||||
volumeAxisX.setTickUnit(1);
|
||||
volumeAxisX.setMinorTickCount(0);
|
||||
volumeAxisX.setForceZeroInRange(false);
|
||||
volumeAxisX.setTickLabelFormatter(getTimeAxisStringConverter());
|
||||
|
||||
volumeChart = new VolumeChart(volumeAxisX, volumeAxisY);
|
||||
volumeChart.setData(FXCollections.observableArrayList(volumeSeries));
|
||||
volumeChart.setToolTipStringConverter(new StringConverter<Number>() {
|
||||
@Override
|
||||
public String toString(Number object) {
|
||||
return formatter.formatCoinWithCode(Coin.valueOf(new Double((double) object).longValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
volumeChart.setAnimated(true);
|
||||
volumeChart.setMinHeight(140);
|
||||
volumeChart.setLegendVisible(false);
|
||||
}
|
||||
|
||||
private void updateChartData() {
|
||||
priceSeries.getData().clear();
|
||||
priceSeries = new XYChart.Series<>();
|
||||
priceSeries.getData().setAll(model.priceItems);
|
||||
priceChart.getData().clear();
|
||||
priceChart.setData(FXCollections.observableArrayList(priceSeries));
|
||||
|
||||
volumeSeries.getData().clear();
|
||||
volumeSeries = new XYChart.Series<>();
|
||||
volumeSeries.getData().setAll(model.volumeItems);
|
||||
volumeChart.getData().clear();
|
||||
volumeChart.setData(FXCollections.observableArrayList(volumeSeries));
|
||||
|
||||
priceSeries.getData().clear();
|
||||
priceSeries = new XYChart.Series<>();
|
||||
priceSeries.getData().setAll(model.priceItems);
|
||||
priceChart.getData().clear();
|
||||
priceChart.setData(FXCollections.observableArrayList(priceSeries));
|
||||
}
|
||||
|
||||
private void layoutChart() {
|
||||
UserThread.execute(() -> {
|
||||
if (volumeAxisYWidth > priceAxisYWidth) {
|
||||
priceChart.setPadding(new Insets(0, 0, 0, volumeAxisYWidth - priceAxisYWidth));
|
||||
volumeChart.setPadding(new Insets(0, 0, 0, 0));
|
||||
} else if (volumeAxisYWidth < priceAxisYWidth) {
|
||||
priceChart.setPadding(new Insets(0, 0, 0, 0));
|
||||
volumeChart.setPadding(new Insets(0, 0, 0, priceAxisYWidth - volumeAxisYWidth));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
@ -245,44 +317,15 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private StringConverter<Number> getPriceStringConverter() {
|
||||
return new StringConverter<Number>() {
|
||||
@Override
|
||||
public String toString(Number object) {
|
||||
// comes as double
|
||||
return formatter.formatFiat(Fiat.valueOf(model.getCurrencyCode(), new Double((double) object).longValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private StringConverter<Number> getVolumeStringConverter() {
|
||||
return new StringConverter<Number>() {
|
||||
@Override
|
||||
public String toString(Number object) {
|
||||
// comes as double
|
||||
return formatter.formatCoin(Coin.valueOf(new Double((double) object).longValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CurrencyComboBox
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private HBox createCurrencyComboBox() {
|
||||
Label currencyLabel = new Label("Currency:");
|
||||
currencyLabel.setPadding(new Insets(0, 3, 0, 0));
|
||||
|
||||
currencyComboBox = new ComboBox<>();
|
||||
currencyComboBox.setPromptText("Select currency");
|
||||
currencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
|
||||
|
@ -303,30 +346,12 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
Label currencyLabel = new Label("Currency:");
|
||||
HBox currencyHBox = new HBox();
|
||||
currencyHBox.setSpacing(5);
|
||||
currencyHBox.setPadding(new Insets(10, -20, 0, 20));
|
||||
currencyHBox.setAlignment(Pos.CENTER_LEFT);
|
||||
currencyHBox.getChildren().addAll(currencyLabel, currencyComboBox);
|
||||
return currencyHBox;
|
||||
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ToggleBar
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private HBox createToggleBar() {
|
||||
HBox hBox = new HBox();
|
||||
hBox.setSpacing(0);
|
||||
hBox.setPadding(new Insets(0, 0, -26, 85));
|
||||
Pane spacer = new Pane();
|
||||
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||
|
||||
Label label = new Label("Interval:");
|
||||
label.setPadding(new Insets(5, 5, 0, 0));
|
||||
|
||||
label.setPadding(new Insets(0, 3, 0, 0));
|
||||
|
||||
toggleGroup = new ToggleGroup();
|
||||
ToggleButton month = getToggleButton("Month", TradesChartsViewModel.TickUnit.MONTH, toggleGroup, "toggle-left");
|
||||
ToggleButton week = getToggleButton("Week", TradesChartsViewModel.TickUnit.WEEK, toggleGroup, "toggle-center");
|
||||
|
@ -334,8 +359,12 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
ToggleButton hour = getToggleButton("Hour", TradesChartsViewModel.TickUnit.HOUR, toggleGroup, "toggle-center");
|
||||
ToggleButton minute10 = getToggleButton("10 Minute", TradesChartsViewModel.TickUnit.MINUTE_10, toggleGroup, "toggle-center");
|
||||
ToggleButton minute = getToggleButton("Minute", TradesChartsViewModel.TickUnit.MINUTE, toggleGroup, "toggle-right");
|
||||
minute10.setSelected(true);
|
||||
hBox.getChildren().addAll(label, month, week, day, hour, minute10, minute);
|
||||
|
||||
HBox hBox = new HBox();
|
||||
hBox.setSpacing(0);
|
||||
hBox.setPadding(new Insets(5, 20, -10, 8));
|
||||
hBox.setAlignment(Pos.CENTER_LEFT);
|
||||
hBox.getChildren().addAll(currencyLabel, currencyComboBox, spacer, label, month, week, day, hour, minute10, minute);
|
||||
return hBox;
|
||||
}
|
||||
|
||||
|
@ -353,8 +382,9 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
// Table
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private VBox getTableBox() {
|
||||
private void createTableBox() {
|
||||
tableView = new TableView<>();
|
||||
tableView.setMinHeight(120);
|
||||
|
||||
// date
|
||||
TableColumn<TradeStatistics, TradeStatistics> dateColumn = new TableColumn<>("Date/Time");
|
||||
|
@ -377,6 +407,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
};
|
||||
}
|
||||
});
|
||||
dateColumn.setComparator((o1, o2) -> o1.getTradeDate().compareTo(o2.getTradeDate()));
|
||||
tableView.getColumns().add(dateColumn);
|
||||
|
||||
|
||||
|
@ -401,6 +432,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
};
|
||||
}
|
||||
});
|
||||
amountColumn.setComparator((o1, o2) -> o1.getTradeAmount().compareTo(o2.getTradeAmount()));
|
||||
tableView.getColumns().add(amountColumn);
|
||||
|
||||
|
||||
|
@ -426,6 +458,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
};
|
||||
}
|
||||
});
|
||||
priceColumn.setComparator((o1, o2) -> o1.getTradePrice().compareTo(o2.getTradePrice()));
|
||||
tableView.getColumns().add(priceColumn);
|
||||
|
||||
|
||||
|
@ -451,6 +484,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
};
|
||||
}
|
||||
});
|
||||
volumeColumn.setComparator((o1, o2) -> o1.getTradeVolume().compareTo(o2.getTradeVolume()));
|
||||
tableView.getColumns().add(volumeColumn);
|
||||
|
||||
|
||||
|
@ -475,24 +509,15 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
};
|
||||
}
|
||||
});
|
||||
directionColumn.setComparator((o1, o2) -> o1.offer.getDirection().compareTo(o2.offer.getDirection()));
|
||||
tableView.getColumns().add(directionColumn);
|
||||
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
Label placeholder = new Label("Currently there is no data available");
|
||||
placeholder.setWrapText(true);
|
||||
tableView.setPlaceholder(placeholder);
|
||||
|
||||
Label titleLabel = new Label("Trades");
|
||||
titleLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 16; -fx-alignment: center");
|
||||
UserThread.execute(() -> titleLabel.prefWidthProperty().bind(tableView.widthProperty()));
|
||||
|
||||
VBox vBox = new VBox();
|
||||
vBox.setSpacing(10);
|
||||
vBox.setFillWidth(true);
|
||||
vBox.setMinHeight(190);
|
||||
vBox.getChildren().addAll(titleLabel, tableView);
|
||||
|
||||
return vBox;
|
||||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
tableView.getSortOrder().add(dateColumn);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ import org.slf4j.LoggerFactory;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class TradesChartsViewModel extends ActivatableViewModel {
|
||||
private static final Logger log = LoggerFactory.getLogger(TradesChartsViewModel.class);
|
||||
|
@ -57,16 +56,20 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
|||
}
|
||||
|
||||
private final Preferences preferences;
|
||||
final ObjectProperty<TradeCurrency> tradeCurrency = new SimpleObjectProperty<>();
|
||||
private P2PService p2PService;
|
||||
|
||||
private final HashMapChangedListener mapChangedListener;
|
||||
final ObjectProperty<TradeCurrency> tradeCurrencyProperty = new SimpleObjectProperty<>();
|
||||
|
||||
private final Set<TradeStatistics> allTradeStatistics = new HashSet<>();
|
||||
final ObservableList<TradeStatistics> tradeStatisticsByCurrency = FXCollections.observableArrayList();
|
||||
ObservableList<XYChart.Data<Number, Number>> priceItems = FXCollections.observableArrayList();
|
||||
ObservableList<XYChart.Data<Number, Number>> volumeItems = FXCollections.observableArrayList();
|
||||
|
||||
private P2PService p2PService;
|
||||
final ObservableList<TradeStatistics> tradeStatistics = FXCollections.observableArrayList();
|
||||
TickUnit tickUnit = TickUnit.MINUTE_10;
|
||||
TickUnit tickUnit;
|
||||
int upperBound = 30;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor, lifecycle
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -85,19 +88,21 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
|||
@Override
|
||||
public void onRemoved(ProtectedStorageEntry data) {
|
||||
final StoragePayload storagePayload = data.getStoragePayload();
|
||||
if (storagePayload instanceof TradeStatistics && tradeStatistics.contains(storagePayload)) {
|
||||
tradeStatistics.remove(storagePayload);
|
||||
if (storagePayload instanceof TradeStatistics && allTradeStatistics.contains(storagePayload)) {
|
||||
allTradeStatistics.remove(storagePayload);
|
||||
updateChartData();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Optional<TradeCurrency> tradeCurrencyOptional = CurrencyUtil.getTradeCurrency(preferences.getMarketScreenCurrencyCode());
|
||||
Optional<TradeCurrency> tradeCurrencyOptional = CurrencyUtil.getTradeCurrency(preferences.getTradeStatisticsScreenCurrencyCode());
|
||||
if (tradeCurrencyOptional.isPresent())
|
||||
tradeCurrency.set(tradeCurrencyOptional.get());
|
||||
tradeCurrencyProperty.set(tradeCurrencyOptional.get());
|
||||
else {
|
||||
tradeCurrency.set(CurrencyUtil.getDefaultTradeCurrency());
|
||||
tradeCurrencyProperty.set(CurrencyUtil.getDefaultTradeCurrency());
|
||||
}
|
||||
|
||||
tickUnit = TickUnit.values()[preferences.getTradeStatisticsTickUnit()];
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
@ -106,13 +111,6 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
|||
preferences = null;
|
||||
}
|
||||
|
||||
private void addItem(StoragePayload storagePayload, boolean doUpdate) {
|
||||
if (storagePayload instanceof TradeStatistics && !tradeStatistics.contains(storagePayload)) {
|
||||
tradeStatistics.add((TradeStatistics) storagePayload);
|
||||
if (doUpdate)
|
||||
updateChartData();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
|
@ -126,18 +124,61 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
|||
p2PService.removeHashMapChangedListener(mapChangedListener);
|
||||
}
|
||||
|
||||
public void setTickUnit(TickUnit tickUnit) {
|
||||
this.tickUnit = tickUnit;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onSetTradeCurrency(TradeCurrency tradeCurrency) {
|
||||
this.tradeCurrencyProperty.set(tradeCurrency);
|
||||
preferences.setTradeStatisticsScreenCurrencyCode(tradeCurrency.getCode());
|
||||
updateChartData();
|
||||
}
|
||||
|
||||
public void setTickUnit(TickUnit tickUnit) {
|
||||
this.tickUnit = tickUnit;
|
||||
preferences.setTradeStatisticsTickUnit(tickUnit.ordinal());
|
||||
updateChartData();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return tradeCurrencyProperty.get().getCode();
|
||||
}
|
||||
|
||||
public ObservableList<TradeCurrency> getTradeCurrencies() {
|
||||
return preferences.getTradeCurrenciesAsObservable();
|
||||
}
|
||||
|
||||
public TradeCurrency getTradeCurrency() {
|
||||
return tradeCurrencyProperty.get();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
private void addItem(StoragePayload storagePayload, boolean doUpdate) {
|
||||
if (storagePayload instanceof TradeStatistics && !allTradeStatistics.contains(storagePayload)) {
|
||||
allTradeStatistics.add((TradeStatistics) storagePayload);
|
||||
if (doUpdate)
|
||||
updateChartData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateChartData() {
|
||||
final Stream<TradeStatistics> tradeStatisticsStream = tradeStatistics.stream()
|
||||
.filter(e -> e.offer.getCurrencyCode().equals(getCurrencyCode()));
|
||||
tradeStatisticsByCurrency.setAll(allTradeStatistics.stream()
|
||||
.filter(e -> e.offer.getCurrencyCode().equals(getCurrencyCode()))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
// Get all entries for the defined time interval
|
||||
Map<Long, Set<TradeStatistics>> itemsPerInterval = new HashMap<>();
|
||||
tradeStatisticsStream.forEach(e -> {
|
||||
tradeStatisticsByCurrency.stream().forEach(e -> {
|
||||
Set<TradeStatistics> set;
|
||||
final long time = getTickFromTime(e.tradeDateAsTime, tickUnit);
|
||||
final long now = getTickFromTime(new Date().getTime(), tickUnit);
|
||||
|
@ -158,11 +199,11 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
|||
candleDataList.sort((o1, o2) -> (o1.tick < o2.tick ? -1 : (o1.tick == o2.tick ? 0 : 1)));
|
||||
|
||||
priceItems.setAll(candleDataList.stream()
|
||||
.map(e -> new XYChart.Data<Number, Number>(e.tick, e.open, new CandleStickExtraValues(e.close, e.high, e.low, e.average)))
|
||||
.map(e -> new XYChart.Data<Number, Number>(e.tick, e.open, new CandleStickExtraValues(e.close, e.high, e.low, e.average, e.accumulatedAmount)))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
volumeItems.setAll(candleDataList.stream()
|
||||
.map(e -> new XYChart.Data<Number, Number>(e.tick, e.accumulatedAmount))
|
||||
.map(e -> new XYChart.Data<Number, Number>(e.tick, e.accumulatedAmount, new CandleStickExtraValues(e.close, e.high, e.low, e.average, e.accumulatedAmount)))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
|
@ -232,31 +273,4 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onSetTradeCurrency(TradeCurrency tradeCurrency) {
|
||||
this.tradeCurrency.set(tradeCurrency);
|
||||
updateChartData();
|
||||
|
||||
//preferences.setMarketScreenCurrencyCode(tradeCurrency.getCode());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return tradeCurrency.get().getCode();
|
||||
}
|
||||
|
||||
public ObservableList<TradeCurrency> getTradeCurrencies() {
|
||||
return preferences.getTradeCurrenciesAsObservable();
|
||||
}
|
||||
|
||||
public TradeCurrency getTradeCurrency() {
|
||||
return tradeCurrency.get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,15 +54,15 @@ public class Candle extends Group {
|
|||
private Tooltip tooltip = new Tooltip();
|
||||
private double closeOffset;
|
||||
|
||||
Candle(String seriesStyleClass, String dataStyleClass, StringConverter<Number> toolTipStringConverter) {
|
||||
Candle(String seriesStyleClass, String dataStyleClass, StringConverter<Number> priceStringConverter) {
|
||||
setAutoSizeChildren(false);
|
||||
getChildren().addAll(highLowLine, bar);
|
||||
this.seriesStyleClass = seriesStyleClass;
|
||||
this.dataStyleClass = dataStyleClass;
|
||||
updateStyleClasses();
|
||||
tooltipContent = new TooltipContent(toolTipStringConverter);
|
||||
tooltipContent = new TooltipContent(priceStringConverter);
|
||||
tooltip.setGraphic(tooltipContent);
|
||||
Tooltip.install(bar, tooltip);
|
||||
Tooltip.install(this, tooltip);
|
||||
}
|
||||
|
||||
public void setSeriesAndDataStyleClasses(String seriesStyleClass, String dataStyleClass) {
|
||||
|
|
|
@ -59,7 +59,7 @@ import java.util.List;
|
|||
public class CandleStickChart extends XYChart<Number, Number> {
|
||||
private static final Logger log = LoggerFactory.getLogger(CandleStickChart.class);
|
||||
|
||||
private StringConverter<Number> toolTipStringConverter;
|
||||
private StringConverter<Number> priceStringConverter;
|
||||
private Path seriesPath;
|
||||
|
||||
// -------------- CONSTRUCTORS ----------------------------------------------
|
||||
|
@ -76,8 +76,8 @@ public class CandleStickChart extends XYChart<Number, Number> {
|
|||
|
||||
// -------------- METHODS ------------------------------------------------------------------------------------------
|
||||
|
||||
public final void setToolTipStringConverter(StringConverter<Number> toolTipStringConverter) {
|
||||
this.toolTipStringConverter = toolTipStringConverter;
|
||||
public final void setToolTipStringConverter(StringConverter<Number> priceStringConverter) {
|
||||
this.priceStringConverter = priceStringConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,7 +144,7 @@ public class CandleStickChart extends XYChart<Number, Number> {
|
|||
Node candle = createCandle(getData().indexOf(series), item, itemIndex);
|
||||
if (getPlotChildren().contains(candle))
|
||||
getPlotChildren().remove(candle);
|
||||
|
||||
|
||||
if (shouldAnimate()) {
|
||||
candle.setOpacity(0);
|
||||
getPlotChildren().add(candle);
|
||||
|
@ -239,7 +239,7 @@ public class CandleStickChart extends XYChart<Number, Number> {
|
|||
if (candle instanceof Candle) {
|
||||
((Candle) candle).setSeriesAndDataStyleClasses("series" + seriesIndex, "data" + itemIndex);
|
||||
} else {
|
||||
candle = new Candle("series" + seriesIndex, "data" + itemIndex, toolTipStringConverter);
|
||||
candle = new Candle("series" + seriesIndex, "data" + itemIndex, priceStringConverter);
|
||||
item.setNode(candle);
|
||||
}
|
||||
return candle;
|
||||
|
|
|
@ -93,7 +93,7 @@ public class CandleStickChartApp extends Application {
|
|||
XYChart.Series<Number, Number> series = new XYChart.Series<>();
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
double[] day = data[i];
|
||||
series.getData().add(new XYChart.Data<>(day[0], day[1], new CandleStickExtraValues(day[2], day[3], day[4], day[5])));
|
||||
series.getData().add(new XYChart.Data<>(day[0], day[1], new CandleStickExtraValues(day[2], day[3], day[4], day[5], day[5])));
|
||||
}
|
||||
ObservableList<XYChart.Series<Number, Number>> data = chart.getData();
|
||||
if (data == null) {
|
||||
|
|
|
@ -39,12 +39,14 @@ public class CandleStickExtraValues {
|
|||
private double high;
|
||||
private double low;
|
||||
private double average;
|
||||
private double volume;
|
||||
|
||||
public CandleStickExtraValues(double close, double high, double low, double average) {
|
||||
public CandleStickExtraValues(double close, double high, double low, double average, double volume) {
|
||||
this.close = close;
|
||||
this.high = high;
|
||||
this.low = low;
|
||||
this.average = average;
|
||||
this.volume = volume;
|
||||
}
|
||||
|
||||
public double getClose() {
|
||||
|
@ -59,12 +61,16 @@ public class CandleStickExtraValues {
|
|||
return low;
|
||||
}
|
||||
|
||||
public double getVolume() {
|
||||
return volume;
|
||||
}
|
||||
|
||||
public double getAverage() {
|
||||
return average;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CandleStickExtraValues{" + "close=" + close + ", high=" + high + ", low=" + low + ", average=" + average + '}';
|
||||
return "CandleStickExtraValues{" + "close=" + close + ", high=" + high + ", low=" + low + ", average=" + average + ", volume=" + volume + '}';
|
||||
}
|
||||
}
|
|
@ -31,7 +31,6 @@
|
|||
*/
|
||||
package io.bitsquare.gui.main.markets.trades.candlestick;
|
||||
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.util.StringConverter;
|
||||
|
@ -44,15 +43,15 @@ public class TooltipContent extends GridPane {
|
|||
private Label closeValue = new Label();
|
||||
private Label highValue = new Label();
|
||||
private Label lowValue = new Label();
|
||||
private BSFormatter formatter;
|
||||
private StringConverter<Number> toolTipStringConverter;
|
||||
private StringConverter<Number> priceStringConverter;
|
||||
|
||||
TooltipContent(StringConverter<Number> toolTipStringConverter) {
|
||||
this.toolTipStringConverter = toolTipStringConverter;
|
||||
TooltipContent(StringConverter<Number> priceStringConverter) {
|
||||
this.priceStringConverter = priceStringConverter;
|
||||
Label open = new Label("Open:");
|
||||
Label close = new Label("Close:");
|
||||
Label high = new Label("High:");
|
||||
Label low = new Label("Low:");
|
||||
Label volume = new Label("Volume:");
|
||||
/* open.getStyleClass().add("candlestick-tooltip-label");
|
||||
close.getStyleClass().add("candlestick-tooltip-label");
|
||||
high.getStyleClass().add("candlestick-tooltip-label");
|
||||
|
@ -69,11 +68,12 @@ public class TooltipContent extends GridPane {
|
|||
}
|
||||
|
||||
public void update(double open, double close, double high, double low) {
|
||||
if (toolTipStringConverter != null) {
|
||||
openValue.setText(toolTipStringConverter.toString(open));
|
||||
closeValue.setText(toolTipStringConverter.toString(close));
|
||||
highValue.setText(toolTipStringConverter.toString(high));
|
||||
lowValue.setText(toolTipStringConverter.toString(low));
|
||||
if (priceStringConverter != null) {
|
||||
openValue.setText(priceStringConverter.toString(open));
|
||||
closeValue.setText(priceStringConverter.toString(close));
|
||||
highValue.setText(priceStringConverter.toString(high));
|
||||
lowValue.setText(priceStringConverter.toString(low));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@
|
|||
package io.bitsquare.gui.main.markets.trades.candlestick;
|
||||
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.util.StringConverter;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -43,17 +44,23 @@ import org.slf4j.LoggerFactory;
|
|||
public class VolumeBar extends Group {
|
||||
private static final Logger log = LoggerFactory.getLogger(VolumeBar.class);
|
||||
|
||||
private Region bar = new Region();
|
||||
private String seriesStyleClass;
|
||||
private String dataStyleClass;
|
||||
private final StringConverter<Number> volumeStringConverter;
|
||||
|
||||
VolumeBar(String seriesStyleClass, String dataStyleClass, StringConverter<Number> toolTipStringConverter) {
|
||||
setAutoSizeChildren(false);
|
||||
bar.setOpacity(0.7);
|
||||
getChildren().add(bar);
|
||||
private final Region bar = new Region();
|
||||
private final Tooltip tooltip;
|
||||
|
||||
VolumeBar(String seriesStyleClass, String dataStyleClass, StringConverter<Number> volumeStringConverter) {
|
||||
this.seriesStyleClass = seriesStyleClass;
|
||||
this.dataStyleClass = dataStyleClass;
|
||||
this.volumeStringConverter = volumeStringConverter;
|
||||
|
||||
setAutoSizeChildren(false);
|
||||
getChildren().add(bar);
|
||||
updateStyleClasses();
|
||||
tooltip = new Tooltip();
|
||||
Tooltip.install(this, tooltip);
|
||||
}
|
||||
|
||||
public void setSeriesAndDataStyleClasses(String seriesStyleClass, String dataStyleClass) {
|
||||
|
@ -62,13 +69,9 @@ public class VolumeBar extends Group {
|
|||
updateStyleClasses();
|
||||
}
|
||||
|
||||
public void update(double volume, double candleWidth) {
|
||||
updateStyleClasses();
|
||||
if (candleWidth == -1)
|
||||
candleWidth = bar.prefWidth(-1);
|
||||
|
||||
log.error("closeOffset " + volume);
|
||||
bar.resizeRelocate(-candleWidth / 2, 0, candleWidth, Math.max(5, volume));
|
||||
public void update(double height, double candleWidth, double volume) {
|
||||
bar.resizeRelocate(-candleWidth / 2, 0, candleWidth, height);
|
||||
tooltip.setText("Accumulated volume: " + volumeStringConverter.toString(volume));
|
||||
}
|
||||
|
||||
private void updateStyleClasses() {
|
||||
|
|
|
@ -94,18 +94,23 @@ public class VolumeChart extends XYChart<Number, Number> {
|
|||
double x = getXAxis().getDisplayPosition(getCurrentDisplayedXValue(item));
|
||||
double y = getYAxis().getDisplayPosition(getCurrentDisplayedYValue(item));
|
||||
Node itemNode = item.getNode();
|
||||
if (itemNode instanceof VolumeBar) {
|
||||
CandleStickExtraValues extra = (CandleStickExtraValues) item.getExtraValue();
|
||||
if (itemNode instanceof VolumeBar && extra != null) {
|
||||
VolumeBar volumeBar = (VolumeBar) itemNode;
|
||||
double candleWidth = -1;
|
||||
if (getXAxis() instanceof NumberAxis) {
|
||||
NumberAxis xa = (NumberAxis) getXAxis();
|
||||
candleWidth = xa.getDisplayPosition(xa.getTickUnit()) * 0.90; // use 90% width between ticks
|
||||
}
|
||||
volumeBar.update(y, candleWidth);
|
||||
|
||||
// position the volumeBar
|
||||
// 97 is visible chart data height if chart height is 140.
|
||||
// So we subtract 43 form the height to get the height for the bar to the bottom.
|
||||
// Did not find a way how to request the chart data height
|
||||
final double height = getHeight() - 43;
|
||||
double upperYPos = Math.min(height - 5, y); // We want min 5px height to allow tooltips
|
||||
volumeBar.update(height - upperYPos, candleWidth, extra.getVolume());
|
||||
volumeBar.setLayoutX(x);
|
||||
volumeBar.setLayoutY(y);
|
||||
volumeBar.setLayoutY(upperYPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,11 +226,10 @@ public class VolumeChart extends XYChart<Number, Number> {
|
|||
List<Number> xData = null;
|
||||
List<Number> yData = null;
|
||||
if (xa.isAutoRanging()) {
|
||||
xData = new ArrayList<Number>();
|
||||
}
|
||||
if (ya.isAutoRanging()) {
|
||||
yData = new ArrayList<Number>();
|
||||
xData = new ArrayList<>();
|
||||
}
|
||||
if (ya.isAutoRanging())
|
||||
yData = new ArrayList<>();
|
||||
if (xData != null || yData != null) {
|
||||
for (XYChart.Series<Number, Number> series : getData()) {
|
||||
for (XYChart.Data<Number, Number> data : series.getData()) {
|
||||
|
|
Loading…
Add table
Reference in a new issue