Add null checks or price and volume

This commit is contained in:
Manfred Karrer 2016-04-16 14:08:39 +02:00
parent a45f8d7325
commit 3b94eda701
12 changed files with 142 additions and 85 deletions

View File

@ -118,7 +118,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
// E.g. Buy offer with market price 400.- leads to a 360.- price.
// Sell offer with market price 400.- leads to a 440.- price.
private final double marketPriceMargin;
private final long amount;
private final long minAmount;
private final NodeAddress offererNodeAddress;
@ -236,19 +236,27 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
return getPubKeyRing().equals(keyRing.getPubKeyRing());
}
@Nullable
public Fiat getVolumeByAmount(Coin amount) {
try {
return new ExchangeRate(getPrice()).coinToFiat(amount);
} catch (Throwable t) {
log.error("getVolumeByAmount failed. Error=" + t.getMessage());
return Fiat.valueOf(currencyCode, 0);
Fiat price = getPrice();
if (price != null && amount != null) {
try {
return new ExchangeRate(price).coinToFiat(amount);
} catch (Throwable t) {
log.error("getVolumeByAmount failed. Error=" + t.getMessage());
return null;
}
} else {
return null;
}
}
@Nullable
public Fiat getOfferVolume() {
return getVolumeByAmount(getAmount());
}
@Nullable
public Fiat getMinOfferVolume() {
return getVolumeByAmount(getMinAmount());
}
@ -337,6 +345,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
return pubKeyRing;
}
@Nullable
public Fiat getPrice() {
if (useMarketBasedPrice) {
checkNotNull(priceFeed, "priceFeed must not be null");
@ -357,14 +366,13 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
return Fiat.parseFiat(currencyCode, String.valueOf(targetPrice));
} catch (Exception e) {
log.error("Exception at getPrice / parseToFiat: " + e.toString() + "\n" +
"We use an inaccessible price to avoid null pointers.\n" +
"That case should never happen.");
return Fiat.valueOf(currencyCode, direction == Direction.BUY ? Long.MIN_VALUE : Long.MAX_VALUE);
return null;
}
} else {
log.warn("We don't have a market price. We use an inaccessible price to avoid null pointers.\n" +
log.warn("We don't have a market price./n" +
"That case could only happen if you don't get a price feed.");
return Fiat.valueOf(currencyCode, direction == Direction.BUY ? Long.MIN_VALUE : Long.MAX_VALUE);
return null;
}
} else {
return Fiat.valueOf(currencyCode, fiatPrice);

View File

@ -82,6 +82,7 @@ public class ProcessPayDepositRequest extends TradeTask {
checkArgument(takersTradePrice > 0);
Fiat tradePriceAsFiat = Fiat.valueOf(trade.getOffer().getCurrencyCode(), takersTradePrice);
Fiat offerPriceAsFiat = trade.getOffer().getPrice();
checkArgument(offerPriceAsFiat != null, "offerPriceAsFiat must not be null");
double factor = (double) takersTradePrice / (double) offerPriceAsFiat.value;
// We allow max. 2 % difference between own offer price calculation and takers calculation.
// Market price might be different at offerers and takers side so we need a bit of tolerance.
@ -95,8 +96,8 @@ public class ProcessPayDepositRequest extends TradeTask {
failed(msg);
}
trade.setTradePrice(takersTradePrice);
checkArgument(payDepositRequest.tradeAmount > 0);
trade.setTradeAmount(Coin.valueOf(payDepositRequest.tradeAmount));

View File

@ -47,6 +47,7 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
import javafx.util.StringConverter;
import org.bitcoinj.utils.Fiat;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
@ -184,9 +185,13 @@ public class MarketsChartsView extends ActivatableViewAndModel<VBox, MarketsChar
@Override
public void updateItem(final Offer offer, boolean empty) {
super.updateItem(offer, empty);
if (offer != null && !empty)
setText(formatter.formatFiat(offer.getPrice()));
else
if (offer != null && !empty) {
Fiat offerPrice = offer.getPrice();
if (offerPrice != null)
setText(formatter.formatFiat(offerPrice));
else
setText("");
} else
setText("");
}
};

View File

@ -33,6 +33,7 @@ import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.chart.XYChart;
import org.bitcoinj.utils.Fiat;
import java.util.ArrayList;
import java.util.List;
@ -90,8 +91,8 @@ class MarketsChartsViewModel extends ActivatableViewModel {
.filter(e -> e.getCurrencyCode().equals(tradeCurrency.get().getCode())
&& e.getDirection().equals(Offer.Direction.BUY))
.sorted((o1, o2) -> {
long a = o1.getPrice().value;
long b = o2.getPrice().value;
long a = o1.getPrice() != null ? o1.getPrice().value : 0;
long b = o2.getPrice() != null ? o2.getPrice().value : 0;
if (a != b)
return a < b ? 1 : -1;
return 0;
@ -105,8 +106,8 @@ class MarketsChartsViewModel extends ActivatableViewModel {
.filter(e -> e.getCurrencyCode().equals(tradeCurrency.get().getCode())
&& e.getDirection().equals(Offer.Direction.SELL))
.sorted((o1, o2) -> {
long a = o1.getPrice().value;
long b = o2.getPrice().value;
long a = o1.getPrice() != null ? o1.getPrice().value : 0;
long b = o2.getPrice() != null ? o2.getPrice().value : 0;
if (a != b)
return a > b ? 1 : -1;
return 0;
@ -120,13 +121,16 @@ class MarketsChartsViewModel extends ActivatableViewModel {
data.clear();
double accumulatedAmount = 0;
for (Offer offer : sortedList) {
double price = (double) offer.getPrice().value / LongMath.pow(10, offer.getPrice().smallestUnitExponent());
double amount = (double) offer.getAmount().value / LongMath.pow(10, offer.getAmount().smallestUnitExponent());
accumulatedAmount += amount;
if (direction.equals(Offer.Direction.BUY))
data.add(0, new XYChart.Data(price, accumulatedAmount));
else
data.add(new XYChart.Data(price, accumulatedAmount));
Fiat priceAsFiat = offer.getPrice();
if (priceAsFiat != null) {
double price = (double) priceAsFiat.value / LongMath.pow(10, priceAsFiat.smallestUnitExponent());
double amount = (double) offer.getAmount().value / LongMath.pow(10, offer.getAmount().smallestUnitExponent());
accumulatedAmount += amount;
if (direction.equals(Offer.Direction.BUY))
data.add(0, new XYChart.Data(price, accumulatedAmount));
else
data.add(new XYChart.Data(price, accumulatedAmount));
}
}
}

View File

@ -82,29 +82,30 @@ class MarketsStatisticViewModel extends ActivatableViewModel {
.stream()
.filter(e -> e.getDirection().equals(Offer.Direction.BUY))
.sorted((o1, o2) -> {
long a = o1.getPrice().value;
long b = o2.getPrice().value;
long a = o1.getPrice() != null ? o1.getPrice().value : 0;
long b = o2.getPrice() != null ? o2.getPrice().value : 0;
if (a != b)
return a < b ? 1 : -1;
return 0;
})
.collect(Collectors.toList());
Fiat bestBuyOfferPrice = buyOffers.isEmpty() ? null : buyOffers.get(0).getPrice();
List<Offer> sellOffers = offers
.stream()
.filter(e -> e.getDirection().equals(Offer.Direction.SELL))
.sorted((o1, o2) -> {
long a = o1.getPrice().value;
long b = o2.getPrice().value;
long a = o1.getPrice() != null ? o1.getPrice().value : 0;
long b = o2.getPrice() != null ? o2.getPrice().value : 0;
if (a != b)
return a > b ? 1 : -1;
return 0;
})
.collect(Collectors.toList());
Fiat bestSellOfferPrice = sellOffers.isEmpty() ? null : sellOffers.get(0).getPrice();
Fiat spread = null;
if (bestBuyOfferPrice != null && bestSellOfferPrice != null)
Fiat bestSellOfferPrice = sellOffers.isEmpty() ? null : sellOffers.get(0).getPrice();
Fiat bestBuyOfferPrice = buyOffers.isEmpty() ? null : buyOffers.get(0).getPrice();
if (bestBuyOfferPrice != null && bestSellOfferPrice != null)
spread = bestSellOfferPrice.subtract(bestBuyOfferPrice);
Coin totalAmount = Coin.valueOf(offers.stream().mapToLong(offer -> offer.getAmount().getValue()).sum());

View File

@ -51,6 +51,7 @@ import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.util.Callback;
import javafx.util.StringConverter;
import org.bitcoinj.utils.Fiat;
import javax.inject.Inject;
@ -164,9 +165,17 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
placeholder.setWrapText(true);
tableView.setPlaceholder(placeholder);
priceColumn.setComparator((o1, o2) -> o1.getOffer().getPrice().compareTo(o2.getOffer().getPrice()));
priceColumn.setComparator((o1, o2) -> {
Fiat price1 = o1.getOffer().getPrice();
Fiat price2 = o2.getOffer().getPrice();
return price1 != null && price2 != null ? price1.compareTo(price2) : 0;
});
amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount()));
volumeColumn.setComparator((o1, o2) -> o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume()));
volumeColumn.setComparator((o1, o2) -> {
Fiat offerVolume1 = o1.getOffer().getOfferVolume();
Fiat offerVolume2 = o2.getOffer().getOfferVolume();
return offerVolume1 != null && offerVolume2 != null ? offerVolume1.compareTo(offerVolume2) : 0;
});
paymentMethodColumn.setComparator((o1, o2) -> o1.getOffer().getPaymentMethod().compareTo(o2.getOffer().getPaymentMethod()));
avatarColumn.setComparator((o1, o2) -> o1.getOffer().getOwnerNodeAddress().hostName.compareTo(o2.getOffer().getOwnerNodeAddress().hostName));

View File

@ -262,14 +262,18 @@ class OfferBookViewModel extends ActivatableViewModel {
Offer offer = item.getOffer();
Fiat price = offer.getPrice();
String postFix = "";
if (offer.getUseMarketBasedPrice()) {
postFix = " (" + formatter.formatToPercentWithSymbol(offer.getMarketPriceMargin()) + ")";
if (price != null) {
String postFix = "";
if (offer.getUseMarketBasedPrice()) {
postFix = " (" + formatter.formatToPercentWithSymbol(offer.getMarketPriceMargin()) + ")";
}
if (showAllTradeCurrenciesProperty.get())
return formatter.formatPriceWithCode(price) + postFix;
else
return formatter.formatFiat(price) + postFix;
} else {
return "";
}
if (showAllTradeCurrenciesProperty.get())
return formatter.formatPriceWithCode(price) + postFix;
else
return formatter.formatFiat(price) + postFix;
}
String getVolume(OfferBookListItem item) {

View File

@ -160,7 +160,8 @@ class TakeOfferDataModel extends ActivatableDataModel {
void initWithData(Offer offer) {
this.offer = offer;
tradePrice = offer.getPrice();
// we check at the view class and close in case we dont get a price
checkNotNull(tradePrice, "tradePrice must not be null");
addressEntry = walletService.getOrCreateAddressEntry(offer.getId(), AddressEntry.Context.OFFER_FUNDING);
checkNotNull(addressEntry, "addressEntry must not be null");

View File

@ -194,45 +194,51 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
///////////////////////////////////////////////////////////////////////////////////////////
public void initWithData(Offer offer) {
model.initWithData(offer);
if (offer.getPrice() != null) {
model.initWithData(offer);
priceAsPercentageInputBox.setVisible(offer.getUseMarketBasedPrice());
priceAsPercentageInputBox.setVisible(offer.getUseMarketBasedPrice());
if (model.getOffer().getDirection() == Offer.Direction.SELL) {
imageView.setId("image-buy-large");
directionLabel.setId("direction-icon-label-buy");
if (model.getOffer().getDirection() == Offer.Direction.SELL) {
imageView.setId("image-buy-large");
directionLabel.setId("direction-icon-label-buy");
takeOfferButton.setId("buy-button-big");
takeOfferButton.setText("Review offer for buying bitcoin");
nextButton.setId("buy-button");
} else {
imageView.setId("image-sell-large");
directionLabel.setId("direction-icon-label-sell");
takeOfferButton.setId("buy-button-big");
takeOfferButton.setText("Review offer for buying bitcoin");
nextButton.setId("buy-button");
takeOfferButton.setId("sell-button-big");
nextButton.setId("sell-button");
takeOfferButton.setText("Review offer for selling bitcoin");
}
boolean showComboBox = model.getPossiblePaymentAccounts().size() > 1;
paymentAccountsLabel.setVisible(showComboBox);
paymentAccountsLabel.setManaged(showComboBox);
paymentAccountsComboBox.setVisible(showComboBox);
paymentAccountsComboBox.setManaged(showComboBox);
paymentMethodTextField.setVisible(!showComboBox);
paymentMethodTextField.setManaged(!showComboBox);
paymentMethodLabel.setVisible(!showComboBox);
paymentMethodLabel.setManaged(!showComboBox);
if (!showComboBox)
paymentMethodTextField.setText(BSResources.get(model.getPaymentMethod().getId()));
currencyTextField.setText(model.dataModel.getCurrencyNameAndCode());
directionLabel.setText(model.getDirectionLabel());
amountDescriptionLabel.setText(model.getAmountDescription());
amountRangeTextField.setText(model.getAmountRange());
priceTextField.setText(model.getPrice());
priceAsPercentageTextField.setText(model.marketPriceMargin);
addressTextField.setPaymentLabel(model.getPaymentLabel());
addressTextField.setAddress(model.dataModel.getAddressEntry().getAddressString());
} else {
imageView.setId("image-sell-large");
directionLabel.setId("direction-icon-label-sell");
takeOfferButton.setId("sell-button-big");
nextButton.setId("sell-button");
takeOfferButton.setText("Review offer for selling bitcoin");
new Popup().warning("You cannot take that offer as it uses a percentage price based on the " +
"market price but there is no price feed available.")
.onClose(this::close)
.show();
}
boolean showComboBox = model.getPossiblePaymentAccounts().size() > 1;
paymentAccountsLabel.setVisible(showComboBox);
paymentAccountsLabel.setManaged(showComboBox);
paymentAccountsComboBox.setVisible(showComboBox);
paymentAccountsComboBox.setManaged(showComboBox);
paymentMethodTextField.setVisible(!showComboBox);
paymentMethodTextField.setManaged(!showComboBox);
paymentMethodLabel.setVisible(!showComboBox);
paymentMethodLabel.setManaged(!showComboBox);
if (!showComboBox)
paymentMethodTextField.setText(BSResources.get(model.getPaymentMethod().getId()));
currencyTextField.setText(model.dataModel.getCurrencyNameAndCode());
directionLabel.setText(model.getDirectionLabel());
amountDescriptionLabel.setText(model.getAmountDescription());
amountRangeTextField.setText(model.getAmountRange());
priceTextField.setText(model.getPrice());
priceAsPercentageTextField.setText(model.marketPriceMargin);
addressTextField.setPaymentLabel(model.getPaymentLabel());
addressTextField.setAddress(model.dataModel.getAddressEntry().getAddressString());
}
public void setCloseHandler(OfferView.CloseHandler closeHandler) {

View File

@ -175,11 +175,17 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
addLabelTextField(gridPane, ++rowIndex, CurrencyUtil.getNameByCode(offer.getCurrencyCode()) + " amount" + fiatDirectionInfo, formatter.formatFiatWithCode(offer.getVolumeByAmount(offer.getAmount())));
}
if (takeOfferHandlerOptional.isPresent())
if (takeOfferHandlerOptional.isPresent()) {
addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(tradePrice) + " " + offer.getCurrencyCode() + "/" + "BTC");
else
addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatFiat(offer.getPrice()) + " " + offer.getCurrencyCode() + "/" + "BTC");
} else {
Fiat price = offer.getPrice();
if (offer.getUseMarketBasedPrice()) {
addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatPriceWithCode(price) +
" (" + formatter.formatToPercentWithSymbol(offer.getMarketPriceMargin()) + ")");
} else {
addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatPriceWithCode(price));
}
}
if (offer.isMyOffer(keyRing) && user.getPaymentAccount(offer.getOffererPaymentAccountId()) != null)
addLabelTextField(gridPane, ++rowIndex, "Payment account:", user.getPaymentAccount(offer.getOffererPaymentAccountId()).getAccountName());
else

View File

@ -85,8 +85,11 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
Tradable tradable = o1.getTradable();
if (tradable instanceof Trade)
return ((Trade) o1.getTradable()).getTradePrice().compareTo(((Trade) o2.getTradable()).getTradePrice());
else
return o1.getTradable().getOffer().getPrice().compareTo(o2.getTradable().getOffer().getPrice());
else {
Fiat price1 = o1.getTradable().getOffer().getPrice();
Fiat price2 = o2.getTradable().getOffer().getPrice();
return price1 != null && price2 != null ? price1.compareTo(price2) : 0;
}
});
volumeColumn.setComparator((o1, o2) -> {
if (o1.getTradable() instanceof Trade && o2.getTradable() instanceof Trade) {

View File

@ -34,6 +34,7 @@ import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
import org.bitcoinj.utils.Fiat;
import javax.inject.Inject;
@ -72,8 +73,16 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
offerIdColumn.setComparator((o1, o2) -> o1.getOffer().getId().compareTo(o2.getOffer().getId()));
directionColumn.setComparator((o1, o2) -> o1.getOffer().getDirection().compareTo(o2.getOffer().getDirection()));
amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount()));
priceColumn.setComparator((o1, o2) -> o1.getOffer().getPrice().compareTo(o2.getOffer().getPrice()));
volumeColumn.setComparator((o1, o2) -> o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume()));
priceColumn.setComparator((o1, o2) -> {
Fiat price1 = o1.getOffer().getPrice();
Fiat price2 = o2.getOffer().getPrice();
return price1 != null && price2 != null ? price1.compareTo(price2) : 0;
});
volumeColumn.setComparator((o1, o2) -> {
Fiat offerVolume1 = o1.getOffer().getOfferVolume();
Fiat offerVolume2 = o2.getOffer().getOfferVolume();
return offerVolume1 != null && offerVolume2 != null ? offerVolume1.compareTo(offerVolume2) : 0;
});
dateColumn.setComparator((o1, o2) -> o1.getOffer().getDate().compareTo(o2.getOffer().getDate()));
dateColumn.setSortType(TableColumn.SortType.DESCENDING);