mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-04-19 23:36:00 -04:00
Add check for deviation from market price at create offer
This commit is contained in:
parent
007a71cc29
commit
32e1a5a3ac
@ -60,7 +60,7 @@ public class Log {
|
||||
appender.start();
|
||||
|
||||
logbackLogger = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
|
||||
logbackLogger.setLevel(useDetailedLogging ? Level.TRACE : Level.INFO);
|
||||
logbackLogger.setLevel(useDetailedLogging ? Level.TRACE : Level.WARN);
|
||||
logbackLogger.addAppender(appender);
|
||||
|
||||
// log errors in separate file
|
||||
|
@ -87,6 +87,13 @@ public class PriceFeed {
|
||||
requestAllPrices(cryptoCurrenciesPriceProvider, this::applyPrice);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MarketPrice getMarketPrice(String currencyCode) {
|
||||
if (cache.containsKey(currencyCode))
|
||||
return cache.get(currencyCode);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Setter
|
||||
|
@ -106,6 +106,7 @@ public final class Preferences implements Persistable {
|
||||
private Locale preferredLocale;
|
||||
private TradeCurrency preferredTradeCurrency;
|
||||
private long txFeePerKB = FeePolicy.getFeePerKb().value;
|
||||
private double maxPriceDistanceInPercent;
|
||||
|
||||
// Observable wrappers
|
||||
transient private final StringProperty btcDenominationProperty = new SimpleStringProperty(btcDenomination);
|
||||
@ -155,6 +156,10 @@ public final class Preferences implements Persistable {
|
||||
defaultTradeCurrency = preferredTradeCurrency;
|
||||
useTorForBitcoinJ = persisted.getUseTorForBitcoinJ();
|
||||
showOwnOffersInOfferBook = persisted.getShowOwnOffersInOfferBook();
|
||||
maxPriceDistanceInPercent = persisted.getMaxPriceDistanceInPercent();
|
||||
// Backward compatible to version 0.3.6. Can be removed after a while
|
||||
if (maxPriceDistanceInPercent == 0d)
|
||||
maxPriceDistanceInPercent = 0.2;
|
||||
|
||||
try {
|
||||
setTxFeePerKB(persisted.getTxFeePerKB());
|
||||
@ -174,6 +179,7 @@ public final class Preferences implements Persistable {
|
||||
dontShowAgainMap = new HashMap<>();
|
||||
preferredLocale = getDefaultLocale();
|
||||
preferredTradeCurrency = getDefaultTradeCurrency();
|
||||
maxPriceDistanceInPercent = 0.2;
|
||||
|
||||
storage.queueUpForSave();
|
||||
}
|
||||
@ -459,4 +465,12 @@ public final class Preferences implements Persistable {
|
||||
public void setShowOwnOffersInOfferBook(boolean showOwnOffersInOfferBook) {
|
||||
this.showOwnOffersInOfferBook = showOwnOffersInOfferBook;
|
||||
}
|
||||
|
||||
public double getMaxPriceDistanceInPercent() {
|
||||
return maxPriceDistanceInPercent;
|
||||
}
|
||||
|
||||
public void setMaxPriceDistanceInPercent(double maxPriceDistanceInPercent) {
|
||||
this.maxPriceDistanceInPercent = maxPriceDistanceInPercent;
|
||||
}
|
||||
}
|
||||
|
@ -702,7 +702,10 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
cancelButton1.setId("cancel-button");
|
||||
|
||||
GridPane.setMargin(nextButton, new Insets(-35, 0, 0, 0));
|
||||
nextButton.setOnAction(e -> onShowPayFundsScreen());
|
||||
nextButton.setOnAction(e -> {
|
||||
if (model.isPriceInRange())
|
||||
onShowPayFundsScreen();
|
||||
});
|
||||
}
|
||||
|
||||
private void addFundingGroup() {
|
||||
|
@ -19,10 +19,17 @@ package io.bitsquare.gui.main.offer.createoffer;
|
||||
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.btc.pricefeed.MarketPrice;
|
||||
import io.bitsquare.btc.pricefeed.PriceFeed;
|
||||
import io.bitsquare.common.Timer;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
import io.bitsquare.gui.main.settings.SettingsView;
|
||||
import io.bitsquare.gui.main.settings.preferences.PreferencesView;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.validation.BtcValidator;
|
||||
import io.bitsquare.gui.util.validation.FiatValidator;
|
||||
@ -32,6 +39,7 @@ import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.p2p.P2PService;
|
||||
import io.bitsquare.payment.PaymentAccount;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
@ -41,11 +49,15 @@ import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static com.google.common.math.LongMath.checkedPow;
|
||||
import static javafx.beans.binding.Bindings.createStringBinding;
|
||||
|
||||
class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel> implements ViewModel {
|
||||
private final BtcValidator btcValidator;
|
||||
private final P2PService p2PService;
|
||||
private PriceFeed priceFeed;
|
||||
private Preferences preferences;
|
||||
private Navigation navigation;
|
||||
final BSFormatter formatter;
|
||||
private final FiatValidator fiatValidator;
|
||||
|
||||
@ -109,13 +121,16 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
|
||||
@Inject
|
||||
public CreateOfferViewModel(CreateOfferDataModel dataModel, FiatValidator fiatValidator, BtcValidator btcValidator,
|
||||
P2PService p2PService,
|
||||
P2PService p2PService, PriceFeed priceFeed, Preferences preferences, Navigation navigation,
|
||||
BSFormatter formatter) {
|
||||
super(dataModel);
|
||||
|
||||
this.fiatValidator = fiatValidator;
|
||||
this.btcValidator = btcValidator;
|
||||
this.p2PService = p2PService;
|
||||
this.priceFeed = priceFeed;
|
||||
this.preferences = preferences;
|
||||
this.navigation = navigation;
|
||||
this.formatter = formatter;
|
||||
|
||||
paymentLabel = BSResources.get("createOffer.fundsBox.paymentLabel", dataModel.getOfferId());
|
||||
@ -378,6 +393,33 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
dataModel.onCurrencySelected(tradeCurrency);
|
||||
}
|
||||
|
||||
public boolean isPriceInRange() {
|
||||
MarketPrice marketPrice = priceFeed.getMarketPrice(getTradeCurrency().getCode());
|
||||
if (marketPrice != null) {
|
||||
double marketPriceAsDouble = marketPrice.getPrice(PriceFeed.Type.LAST);
|
||||
Fiat priceAsFiat = dataModel.priceAsFiat.get();
|
||||
long shiftDivisor = checkedPow(10, priceAsFiat.smallestUnitExponent());
|
||||
double offerPrice = ((double) priceAsFiat.longValue()) / ((double) shiftDivisor);
|
||||
if (marketPriceAsDouble != 0 && Math.abs(1 - (offerPrice / marketPriceAsDouble)) > preferences.getMaxPriceDistanceInPercent()) {
|
||||
Popup popup = new Popup();
|
||||
popup.warning("The price you have entered is outside the max. allowed deviation from the market price.\n" +
|
||||
"The max. allowed deviation is " +
|
||||
formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent()) +
|
||||
" and can be adjusted in the preferences.")
|
||||
.actionButtonText("Change price")
|
||||
.onAction(() -> popup.hide())
|
||||
.closeButtonText("Go to \"Preferences\"")
|
||||
.onClose(() -> navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class))
|
||||
.show();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void onShowPayFundsScreen() {
|
||||
showPayFundsScreenDisplayed = true;
|
||||
updateSpinnerInfo();
|
||||
|
@ -22,8 +22,10 @@ import io.bitsquare.common.util.Tuple2;
|
||||
import io.bitsquare.gui.common.model.Activatable;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import io.bitsquare.gui.components.TitledGroupBg;
|
||||
import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.ImageUtil;
|
||||
import io.bitsquare.gui.util.Layout;
|
||||
import io.bitsquare.locale.*;
|
||||
@ -44,6 +46,7 @@ import javafx.util.Callback;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static io.bitsquare.gui.util.FormBuilder.*;
|
||||
|
||||
@ -61,6 +64,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
|
||||
//private InputTextField transactionFeeInputTextField;
|
||||
private ChangeListener<Boolean> transactionFeeFocusedListener;
|
||||
private final Preferences preferences;
|
||||
private BSFormatter formatter;
|
||||
|
||||
private ListView<FiatCurrency> fiatCurrenciesListView;
|
||||
private ComboBox<FiatCurrency> fiatCurrenciesComboBox;
|
||||
@ -77,6 +81,9 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
|
||||
public final ObservableList<CryptoCurrency> cryptoCurrencies;
|
||||
public final ObservableList<CryptoCurrency> allCryptoCurrencies;
|
||||
public final ObservableList<TradeCurrency> tradeCurrencies;
|
||||
private InputTextField deviationInputTextField;
|
||||
private ChangeListener<String> deviationListener;
|
||||
private ChangeListener<Boolean> deviationFocusedListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -84,9 +91,10 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public PreferencesView(Preferences preferences) {
|
||||
public PreferencesView(Preferences preferences, BSFormatter formatter) {
|
||||
super();
|
||||
this.preferences = preferences;
|
||||
this.formatter = formatter;
|
||||
|
||||
blockExplorers = FXCollections.observableArrayList(preferences.getBlockChainExplorers());
|
||||
languageCodes = FXCollections.observableArrayList(LanguageUtil.getAllLanguageCodes());
|
||||
@ -280,13 +288,31 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
|
||||
}
|
||||
|
||||
private void initializeOtherOptions() {
|
||||
TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 2, "General preferences", Layout.GROUP_DISTANCE);
|
||||
TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 3, "General preferences", Layout.GROUP_DISTANCE);
|
||||
GridPane.setColumnSpan(titledGroupBg, 4);
|
||||
// userLanguageComboBox = addLabelComboBox(root, gridRow, "Language:", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
|
||||
// btcDenominationComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin denomination:").second;
|
||||
blockChainExplorerComboBox = addLabelComboBox(root, gridRow, "Bitcoin block explorer:", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
|
||||
deviationInputTextField = addLabelInputTextField(root, ++gridRow, "Max. deviation from market price:").second;
|
||||
autoSelectArbitratorsCheckBox = addLabelCheckBox(root, ++gridRow, "Auto select arbitrators:", "").second;
|
||||
|
||||
deviationListener = (observable, oldValue, newValue) -> {
|
||||
try {
|
||||
String input = newValue.replace("%", "");
|
||||
input = input.replace(",", ".");
|
||||
input = input.replace(" ", "");
|
||||
double value = Double.parseDouble(input);
|
||||
preferences.setMaxPriceDistanceInPercent(value / 100);
|
||||
} catch (Throwable t) {
|
||||
log.error("Exception at parseDouble deviation: " + t.toString());
|
||||
UserThread.runAfter(() -> deviationInputTextField.setText(formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent())), 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
};
|
||||
deviationFocusedListener = (observable1, oldValue1, newValue1) -> {
|
||||
if (oldValue1 && !newValue1)
|
||||
UserThread.runAfter(() -> deviationInputTextField.setText(formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent())), 100, TimeUnit.MILLISECONDS);
|
||||
};
|
||||
|
||||
// TODO need a bit extra work to separate trade and non trade tx fees before it can be used
|
||||
/*transactionFeeInputTextField = addLabelInputTextField(root, ++gridRow, "Transaction fee (satoshi/byte):").second;
|
||||
transactionFeeFocusedListener = (o, oldValue, newValue) -> {
|
||||
@ -393,6 +419,10 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
|
||||
});
|
||||
blockChainExplorerComboBox.setOnAction(e -> preferences.setBlockChainExplorer(blockChainExplorerComboBox.getSelectionModel().getSelectedItem()));
|
||||
|
||||
deviationInputTextField.setText(formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent()));
|
||||
deviationInputTextField.textProperty().addListener(deviationListener);
|
||||
deviationInputTextField.focusedProperty().addListener(deviationFocusedListener);
|
||||
|
||||
// transactionFeeInputTextField.textProperty().bindBidirectional(transactionFeePerByte);
|
||||
// transactionFeeInputTextField.focusedProperty().addListener(transactionFeeFocusedListener);
|
||||
}
|
||||
@ -422,6 +452,8 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
|
||||
//btcDenominationComboBox.setOnAction(null);
|
||||
// userLanguageComboBox.setOnAction(null);
|
||||
blockChainExplorerComboBox.setOnAction(null);
|
||||
deviationInputTextField.textProperty().removeListener(deviationListener);
|
||||
deviationInputTextField.focusedProperty().removeListener(deviationFocusedListener);
|
||||
// transactionFeeInputTextField.textProperty().unbind();
|
||||
/// transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user