trigger price handled as string, same as price

This commit is contained in:
woodser 2022-04-26 15:19:20 -04:00
parent 9d4ec0a532
commit c2f5adac9b
23 changed files with 46 additions and 54 deletions

View file

@ -1,214 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.main;
import bisq.desktop.util.validation.AltcoinValidator;
import bisq.desktop.util.validation.FiatPriceValidator;
import bisq.desktop.util.validation.MonetaryValidator;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.monetary.Altcoin;
import bisq.core.monetary.Price;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferPayload;
import bisq.core.provider.price.MarketPrice;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.ParsingUtils;
import bisq.core.util.validation.InputValidator;
import bisq.common.util.MathUtils;
import org.bitcoinj.utils.Fiat;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Optional;
import javax.annotation.Nullable;
import static bisq.desktop.main.shared.ChatView.log;
import static com.google.common.base.Preconditions.checkNotNull;
@Singleton
public class PriceUtil {
private final PriceFeedService priceFeedService;
@Inject
public PriceUtil(PriceFeedService priceFeedService,
TradeStatisticsManager tradeStatisticsManager,
Preferences preferences) {
this.priceFeedService = priceFeedService;
}
public static MonetaryValidator getPriceValidator(boolean isFiatCurrency) {
return isFiatCurrency ?
new FiatPriceValidator() :
new AltcoinValidator();
}
public static InputValidator.ValidationResult isTriggerPriceValid(String triggerPriceAsString,
Price price,
boolean isSellOffer,
boolean isFiatCurrency) {
if (triggerPriceAsString == null || triggerPriceAsString.isEmpty()) {
return new InputValidator.ValidationResult(true);
}
InputValidator.ValidationResult result = getPriceValidator(isFiatCurrency).validate(triggerPriceAsString);
if (!result.isValid) {
return result;
}
long triggerPriceAsLong = PriceUtil.getMarketPriceAsLong(triggerPriceAsString, price.getCurrencyCode());
long priceAsLong = price.getValue();
String priceAsString = FormattingUtils.formatPrice(price);
if ((isSellOffer && isFiatCurrency) || (!isSellOffer && !isFiatCurrency)) {
if (triggerPriceAsLong >= priceAsLong) {
return new InputValidator.ValidationResult(false,
Res.get("createOffer.triggerPrice.invalid.tooHigh", priceAsString));
} else {
return new InputValidator.ValidationResult(true);
}
} else {
if (triggerPriceAsLong <= priceAsLong) {
return new InputValidator.ValidationResult(false,
Res.get("createOffer.triggerPrice.invalid.tooLow", priceAsString));
} else {
return new InputValidator.ValidationResult(true);
}
}
}
public static Price marketPriceToPrice(MarketPrice marketPrice) {
String currencyCode = marketPrice.getCurrencyCode();
double priceAsDouble = marketPrice.getPrice();
int precision = CurrencyUtil.isCryptoCurrency(currencyCode) ?
Altcoin.SMALLEST_UNIT_EXPONENT :
Fiat.SMALLEST_UNIT_EXPONENT;
double scaled = MathUtils.scaleUpByPowerOf10(priceAsDouble, precision);
long roundedToLong = MathUtils.roundDoubleToLong(scaled);
return Price.valueOf(currencyCode, roundedToLong);
}
public boolean hasMarketPrice(Offer offer) {
String currencyCode = offer.getCurrencyCode();
checkNotNull(priceFeedService, "priceFeed must not be null");
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
Price price = offer.getPrice();
return price != null && marketPrice != null && marketPrice.isRecentExternalPriceAvailable();
}
public Optional<Double> getMarketBasedPrice(Offer offer,
OfferPayload.Direction direction) {
if (offer.isUseMarketBasedPrice()) {
return Optional.of(offer.getMarketPriceMargin());
}
if (!hasMarketPrice(offer)) {
log.trace("We don't have a market price. " +
"That case could only happen if you don't have a price feed.");
return Optional.empty();
}
String currencyCode = offer.getCurrencyCode();
checkNotNull(priceFeedService, "priceFeed must not be null");
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
double marketPriceAsDouble = checkNotNull(marketPrice).getPrice();
return calculatePercentage(offer, marketPriceAsDouble, direction);
}
public Optional<Double> calculatePercentage(Offer offer,
double marketPrice,
OfferPayload.Direction direction) {
// If the offer did not use % price we calculate % from current market price
String currencyCode = offer.getCurrencyCode();
Price price = offer.getPrice();
int precision = CurrencyUtil.isCryptoCurrency(currencyCode) ?
Altcoin.SMALLEST_UNIT_EXPONENT :
Fiat.SMALLEST_UNIT_EXPONENT;
long priceAsLong = checkNotNull(price).getValue();
double scaled = MathUtils.scaleDownByPowerOf10(priceAsLong, precision);
double value;
if (direction == OfferPayload.Direction.SELL) {
if (CurrencyUtil.isFiatCurrency(currencyCode)) {
if (marketPrice == 0) {
return Optional.empty();
}
value = 1 - scaled / marketPrice;
} else {
if (marketPrice == 1) {
return Optional.empty();
}
value = scaled / marketPrice - 1;
}
} else {
if (CurrencyUtil.isFiatCurrency(currencyCode)) {
if (marketPrice == 1) {
return Optional.empty();
}
value = scaled / marketPrice - 1;
} else {
if (marketPrice == 0) {
return Optional.empty();
}
value = 1 - scaled / marketPrice;
}
}
return Optional.of(value);
}
public static long getMarketPriceAsLong(String inputValue, String currencyCode) {
if (inputValue == null || inputValue.isEmpty() || currencyCode == null) {
return 0;
}
try {
int precision = getMarketPricePrecision(currencyCode);
String stringValue = reformatMarketPrice(inputValue, currencyCode);
return ParsingUtils.parsePriceStringToLong(currencyCode, stringValue, precision);
} catch (Throwable t) {
return 0;
}
}
public static String reformatMarketPrice(String inputValue, String currencyCode) {
if (inputValue == null || inputValue.isEmpty() || currencyCode == null) {
return "";
}
double priceAsDouble = ParsingUtils.parseNumberStringToDouble(inputValue);
int precision = getMarketPricePrecision(currencyCode);
return FormattingUtils.formatRoundedDoubleWithPrecision(priceAsDouble, precision);
}
public static String formatMarketPrice(long price, String currencyCode) {
int marketPricePrecision = getMarketPricePrecision(currencyCode);
double scaled = MathUtils.scaleDownByPowerOf10(price, marketPricePrecision);
return FormattingUtils.formatMarketPrice(scaled, marketPricePrecision);
}
public static int getMarketPricePrecision(String currencyCode) {
return CurrencyUtil.isCryptoCurrency(currencyCode) ?
Altcoin.SMALLEST_UNIT_EXPONENT : Fiat.SMALLEST_UNIT_EXPONENT;
}
}

View file

@ -21,13 +21,10 @@ import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.InfoInputTextField;
import bisq.desktop.components.InputTextField;
import bisq.desktop.main.PriceUtil;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.Layout;
import bisq.desktop.util.validation.AltcoinValidator;
import bisq.desktop.util.validation.FiatPriceValidator;
import bisq.desktop.util.validation.PercentageNumberValidator;
import bisq.core.locale.CurrencyUtil;
@ -48,6 +45,9 @@ import bisq.core.user.Preferences;
import bisq.core.user.User;
import bisq.core.util.FormattingUtils;
import bisq.core.util.ParsingUtils;
import bisq.core.util.PriceUtil;
import bisq.core.util.validation.AltcoinValidator;
import bisq.core.util.validation.FiatPriceValidator;
import bisq.core.util.validation.InputValidator;
import bisq.common.UserThread;

View file

@ -20,7 +20,6 @@ package bisq.desktop.main.offer;
import bisq.desktop.Navigation;
import bisq.desktop.common.model.ActivatableWithDataModel;
import bisq.desktop.main.MainView;
import bisq.desktop.main.PriceUtil;
import bisq.desktop.main.funds.FundsView;
import bisq.desktop.main.funds.deposit.DepositView;
import bisq.desktop.main.overlays.popups.Popup;
@ -28,11 +27,8 @@ import bisq.desktop.main.settings.SettingsView;
import bisq.desktop.main.settings.preferences.PreferencesView;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.validation.AltcoinValidator;
import bisq.desktop.util.validation.BtcValidator;
import bisq.desktop.util.validation.FiatPriceValidator;
import bisq.desktop.util.validation.FiatVolumeValidator;
import bisq.desktop.util.validation.MonetaryValidator;
import bisq.desktop.util.validation.SecurityDepositValidator;
import bisq.core.account.witness.AccountAgeWitnessService;
@ -55,11 +51,14 @@ import bisq.core.provider.price.PriceFeedService;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.ParsingUtils;
import bisq.core.util.PriceUtil;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.CoinFormatter;
import bisq.core.util.coin.CoinUtil;
import bisq.core.util.validation.AltcoinValidator;
import bisq.core.util.validation.FiatPriceValidator;
import bisq.core.util.validation.InputValidator;
import bisq.core.util.validation.MonetaryValidator;
import bisq.common.Timer;
import bisq.common.UserThread;
import bisq.common.app.DevEnv;

View file

@ -20,9 +20,7 @@ package bisq.desktop.main.offer.createoffer;
import bisq.desktop.Navigation;
import bisq.desktop.common.model.ViewModel;
import bisq.desktop.main.offer.MutableOfferViewModel;
import bisq.desktop.util.validation.AltcoinValidator;
import bisq.desktop.util.validation.BtcValidator;
import bisq.desktop.util.validation.FiatPriceValidator;
import bisq.desktop.util.validation.FiatVolumeValidator;
import bisq.desktop.util.validation.SecurityDepositValidator;
@ -32,7 +30,8 @@ import bisq.core.provider.price.PriceFeedService;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import bisq.core.util.validation.AltcoinValidator;
import bisq.core.util.validation.FiatPriceValidator;
import com.google.inject.Inject;
import javax.inject.Named;

View file

@ -20,7 +20,6 @@ package bisq.desktop.main.offer.offerbook;
import bisq.desktop.Navigation;
import bisq.desktop.common.model.ActivatableViewModel;
import bisq.desktop.main.MainView;
import bisq.desktop.main.PriceUtil;
import bisq.desktop.main.settings.SettingsView;
import bisq.desktop.main.settings.preferences.PreferencesView;
import bisq.desktop.util.DisplayUtils;
@ -50,6 +49,7 @@ import bisq.core.trade.closed.ClosedTradableManager;
import bisq.core.user.Preferences;
import bisq.core.user.User;
import bisq.core.util.FormattingUtils;
import bisq.core.util.PriceUtil;
import bisq.core.util.coin.CoinFormatter;
import bisq.network.p2p.NodeAddress;

View file

@ -18,8 +18,6 @@
package bisq.desktop.main.portfolio.closedtrades;
import bisq.desktop.common.model.ActivatableDataModel;
import bisq.desktop.main.PriceUtil;
import bisq.core.monetary.Price;
import bisq.core.monetary.Volume;
import bisq.core.offer.Offer;
@ -32,6 +30,7 @@ import bisq.core.trade.Trade;
import bisq.core.trade.closed.ClosedTradableManager;
import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.user.Preferences;
import bisq.core.util.PriceUtil;
import bisq.core.util.VolumeUtil;
import bisq.common.util.Tuple2;

View file

@ -19,9 +19,7 @@ package bisq.desktop.main.portfolio.duplicateoffer;
import bisq.desktop.Navigation;
import bisq.desktop.main.offer.MutableOfferViewModel;
import bisq.desktop.util.validation.AltcoinValidator;
import bisq.desktop.util.validation.BtcValidator;
import bisq.desktop.util.validation.FiatPriceValidator;
import bisq.desktop.util.validation.FiatVolumeValidator;
import bisq.desktop.util.validation.SecurityDepositValidator;
@ -33,7 +31,8 @@ import bisq.core.provider.price.PriceFeedService;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import bisq.core.util.validation.AltcoinValidator;
import bisq.core.util.validation.FiatPriceValidator;
import com.google.inject.Inject;
import javax.inject.Named;

View file

@ -18,11 +18,8 @@
package bisq.desktop.main.portfolio.editoffer;
import bisq.desktop.Navigation;
import bisq.desktop.main.PriceUtil;
import bisq.desktop.main.offer.MutableOfferViewModel;
import bisq.desktop.util.validation.AltcoinValidator;
import bisq.desktop.util.validation.BtcValidator;
import bisq.desktop.util.validation.FiatPriceValidator;
import bisq.desktop.util.validation.FiatVolumeValidator;
import bisq.desktop.util.validation.SecurityDepositValidator;
@ -32,8 +29,10 @@ import bisq.core.offer.OpenOffer;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.PriceUtil;
import bisq.core.util.coin.CoinFormatter;
import bisq.core.util.validation.AltcoinValidator;
import bisq.core.util.validation.FiatPriceValidator;
import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ResultHandler;

View file

@ -19,7 +19,6 @@ package bisq.desktop.main.portfolio.openoffer;
import bisq.desktop.common.model.ActivatableWithDataModel;
import bisq.desktop.common.model.ViewModel;
import bisq.desktop.main.PriceUtil;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.GUIUtil;
@ -29,6 +28,7 @@ import bisq.core.monetary.Price;
import bisq.core.offer.Offer;
import bisq.core.offer.OpenOffer;
import bisq.core.util.FormattingUtils;
import bisq.core.util.PriceUtil;
import bisq.core.util.coin.CoinFormatter;
import bisq.network.p2p.P2PService;

View file

@ -1,37 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.util.validation;
import javax.inject.Inject;
public class AltcoinValidator extends MonetaryValidator {
@Override
protected double getMinValue() {
return 0.00000001;
}
@Override
protected double getMaxValue() {
// hard to say what the max value should be with altcoins
return 100_000_000;
}
@Inject
public AltcoinValidator() {
}
}

View file

@ -21,7 +21,7 @@ import bisq.core.btc.wallet.Restrictions;
import bisq.core.locale.Res;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import bisq.core.util.validation.NumberValidator;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;

View file

@ -1,38 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.util.validation;
import javax.inject.Inject;
public class FiatPriceValidator extends MonetaryValidator {
@Override
protected double getMinValue() {
return 0.0001;
}
@Override
protected double getMaxValue() {
// Hard to say what the max value should be (zimbabwe dollar....)?
// Lets set it to Double.MAX_VALUE until we find some reasonable number
return Double.MAX_VALUE;
}
@Inject
public FiatPriceValidator() {
}
}

View file

@ -17,6 +17,7 @@
package bisq.desktop.util.validation;
import bisq.core.util.validation.MonetaryValidator;
import javax.inject.Inject;
public class FiatVolumeValidator extends MonetaryValidator {

View file

@ -1,69 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.util.validation;
import bisq.core.locale.Res;
import javax.inject.Inject;
public abstract class MonetaryValidator extends NumberValidator {
protected abstract double getMinValue();
@SuppressWarnings("SameReturnValue")
protected abstract double getMaxValue();
@Inject
public MonetaryValidator() {
}
@Override
public ValidationResult validate(String input) {
ValidationResult result = validateIfNotEmpty(input);
if (result.isValid) {
input = cleanInput(input);
result = validateIfNumber(input);
}
if (result.isValid) {
result = result.andValidation(input,
this::validateIfNotZero,
this::validateIfNotNegative,
this::validateIfNotExceedsMinValue,
this::validateIfNotExceedsMaxValue);
}
return result;
}
protected ValidationResult validateIfNotExceedsMinValue(String input) {
double d = Double.parseDouble(input);
if (d < getMinValue())
return new ValidationResult(false, Res.get("validation.fiat.toSmall"));
else
return new ValidationResult(true);
}
protected ValidationResult validateIfNotExceedsMaxValue(String input) {
double d = Double.parseDouble(input);
if (d > getMaxValue())
return new ValidationResult(false, Res.get("validation.fiat.toLarge"));
else
return new ValidationResult(true);
}
}

View file

@ -1,59 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.util.validation;
import bisq.core.locale.Res;
import bisq.core.util.ParsingUtils;
import bisq.core.util.validation.InputValidator;
/**
* NumberValidator for validating basic number values.
* Localisation not supported at the moment
* The decimal mark can be either "." or ",". Thousand separators are not supported yet,
* but might be added alter with Local support.
*/
public abstract class NumberValidator extends InputValidator {
protected String cleanInput(String input) {
return ParsingUtils.convertCharsForNumber(input);
}
protected ValidationResult validateIfNumber(String input) {
try {
//noinspection ResultOfMethodCallIgnored
Double.parseDouble(input);
return new ValidationResult(true);
} catch (Exception e) {
return new ValidationResult(false, Res.get("validation.NaN"));
}
}
protected ValidationResult validateIfNotZero(String input) {
if (Double.parseDouble(input) == 0)
return new ValidationResult(false, Res.get("validation.zero"));
else
return new ValidationResult(true);
}
protected ValidationResult validateIfNotNegative(String input) {
if (Double.parseDouble(input) < 0)
return new ValidationResult(false, Res.get("validation.negative"));
else
return new ValidationResult(true);
}
}

View file

@ -18,7 +18,7 @@
package bisq.desktop.util.validation;
import bisq.core.locale.Res;
import bisq.core.util.validation.NumberValidator;
import lombok.Setter;
import javax.annotation.Nullable;

View file

@ -22,7 +22,7 @@ import bisq.core.locale.Res;
import bisq.core.payment.PaymentAccount;
import bisq.core.util.FormattingUtils;
import bisq.core.util.ParsingUtils;
import bisq.core.util.validation.NumberValidator;
import javax.inject.Inject;
public class SecurityDepositValidator extends NumberValidator {