Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bugs related to margin config #7

Merged
merged 5 commits into from
Jun 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion java-examples/scripts/create-bot-jars.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ extractdistribution
./create-runnable-jar.sh "$GRADLE_DIST_NAME" bisq.bots.TakeBestPricedOfferToSellBsq

rm -r "$GRADLE_DIST_TARBALL"
echo "Done"
echo "Done"
21 changes: 21 additions & 0 deletions java-examples/src/main/java/bisq/bots/AbstractBot.java
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,27 @@ protected void unlockWallet(String walletPassword, long timeoutInSeconds) throws
}
}

/**
* Attempt to unlock the wallet for 1 second using the given password, and shut down the bot if the
* password check fails for any reason.
*
* @param walletPassword String API daemon's wallet password
*/
protected void validateWalletPassword(String walletPassword) {
try {
var request = UnlockWalletRequest.newBuilder()
.setPassword(walletPassword)
.setTimeout(1)
.build();
//noinspection ResultOfMethodCallIgnored
grpcStubs.walletsService.unlockWallet(request);
} catch (StatusRuntimeException grpcException) {
log.error("Wallet password check failed.");
log.error((toCleanErrorMessage.apply(grpcException)));
exit(1);
}
}

/**
* Returns PaymentAccount for given paymentAccountId, else throws an IllegalArgumentException.
*
Expand Down
13 changes: 7 additions & 6 deletions java-examples/src/main/java/bisq/bots/BotUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public class BotUtils {
public static BigDecimal calcTargetPrice(BigDecimal targetMarketPriceMargin,
BigDecimal currentMarketPrice,
String currencyCode) {
if (!isZero.test(targetMarketPriceMargin) && targetMarketPriceMargin.precision() <= 2)
if (!isZero.test(targetMarketPriceMargin) && targetMarketPriceMargin.precision() < 2)
throw new IllegalArgumentException(
format("Price margin percent literal argument %s is invalid;"
+ " it must have a precision of at least 2 decimal places.",
Expand Down Expand Up @@ -133,12 +133,13 @@ public static BigDecimal calcTargetBsqPrice(BigDecimal targetMarketPriceMargin,
return diff.subtract(factor);
};


/**
* Return true if the margin price based offer's market price margin (%) >= minMarketPriceMargin (%).
* Return true if the offer's margin based price >= target price.
*/
public static final BiPredicate<OfferInfo, BigDecimal> isMarginGEMinMarketPriceMargin =
(offer, minMarketPriceMargin) -> offer.getUseMarketBasedPrice()
&& offer.getMarketPriceMarginPct() >= minMarketPriceMargin.doubleValue();
public static final BiPredicate<OfferInfo, BigDecimal> isMarginBasedPriceGETargetPrice =
(offer, targetPrice) -> offer.getUseMarketBasedPrice()
&& new BigDecimal(offer.getPrice()).compareTo(targetPrice) >= 0;

/**
* Return true if the margin price based offer's market price margin (%) <= maxMarketPriceMargin (%).
Expand Down Expand Up @@ -253,7 +254,7 @@ public static void sleep(long ms) {
*/
public static String readWalletPassword(String prompt) {
String walletPassword;
var console = console(); // Returns null in IDE console!
var console = console();
// System.console() returns null if you do not launch your java application with a real console.
if (console == null) {
// Have to read it in a less secure way in the IDE's virtual console.
Expand Down
2 changes: 1 addition & 1 deletion java-examples/src/main/java/bisq/bots/OfferTaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ void waitForBsqSwapCompletion() {
+ " Shut down the API bot and server, then check the server log."));
}
}

/**
* Wait and block until a new trade is fully initialized, with a trade contract and the user's trade role.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,8 @@ private void maybeShutdownAfterSuccessfulSwap() {
public static void main(String[] args) {
@SuppressWarnings("unused")
String prompt = "An encrypted wallet must be unlocked before any offer can be taken.\n"
+ " Please enter your wallet password:";
String walletPassword = "be careful"; // readWalletPassword(prompt);
log.info("Your wallet password is {}", walletPassword.isBlank() ? "blank" : walletPassword);
+ "Please enter your wallet password:";
String walletPassword = readWalletPassword(prompt);
TakeBestPricedOfferToBuyBsq bot = new TakeBestPricedOfferToBuyBsq(appendWalletPasswordOpt(args, walletPassword));
bot.run();
}
Expand Down Expand Up @@ -308,12 +307,19 @@ Optional<OfferInfo> findTakeableOffer(List<OfferInfo> offers) {
}

void printCriteriaSummary() {
log.info("Looking for offers to {}, with a fixed-price at or greater than"
+ " {}% {} the 30-day average BSQ trade price of {} BTC.",
MARKET_DESCRIPTION,
minMarketPriceMargin.abs(), // Hide the sign, text explains target price % "above or below".
aboveOrBelowMarketPrice.apply(minMarketPriceMargin),
avgBsqPrice);
if (isZero.test(minMarketPriceMargin)) {
log.info("Looking for offers to {}, with a fixed-price at or greater than"
+ " the 30-day average BSQ trade price of {} BTC.",
MARKET_DESCRIPTION,
avgBsqPrice);
} else {
log.info("Looking for offers to {}, with a fixed-price at or greater than"
+ " {}% {} the 30-day average BSQ trade price of {} BTC.",
MARKET_DESCRIPTION,
minMarketPriceMargin.abs(), // Hide the sign, text explains target price % "above or below".
aboveOrBelowMarketPrice.apply(minMarketPriceMargin),
avgBsqPrice);
}
}

void printOffersAgainstCriteria(List<OfferInfo> offers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

/**
* The TakeBestPricedOfferToBuyBtc bot waits for attractively priced BUY BTC offers to appear, takes the offers
* (up to a maximum of configured {@link #maxTakeOffers}, then shuts down both the API daemon and itself (the bot),
* (up to a maximum of configured {@link #maxTakeOffers}), then shuts down both the API daemon and itself (the bot),
* to allow the user to start the desktop UI application and complete the trades.
* <p>
* The benefit this bot provides is freeing up the user time spent watching the offer book in the UI, waiting for the
Expand Down Expand Up @@ -133,6 +133,7 @@ public TakeBestPricedOfferToBuyBtc(String[] args) {
public void run() {
var startTime = new Date().getTime();
validatePollingInterval(pollingInterval);
validateWalletPassword(walletPassword);
validateTradeFeeCurrencyCode(bisqTradeFeeCurrency);
validatePaymentAccount(paymentAccount);
printBotConfiguration();
Expand Down Expand Up @@ -329,9 +330,8 @@ private void printBotConfiguration() {
public static void main(String[] args) {
@SuppressWarnings("unused")
String prompt = "An encrypted wallet must be unlocked before any offer can be taken.\n"
+ " Please enter your wallet password:";
String walletPassword = "be careful"; // readWalletPassword(prompt);
log.info("Your wallet password is {}", walletPassword.isBlank() ? "blank" : walletPassword);
+ "Please enter your wallet password:";
String walletPassword = readWalletPassword(prompt);
TakeBestPricedOfferToBuyBtc bot = new TakeBestPricedOfferToBuyBtc(appendWalletPasswordOpt(args, walletPassword));
bot.run();
}
Expand Down Expand Up @@ -368,26 +368,33 @@ Optional<OfferInfo> findTakeableOffer(List<OfferInfo> offers) {
return offers.stream()
.filter(o -> usesSamePaymentMethod.test(o, getPaymentAccount()))
.filter(isMakerPreferredTradingPeer)
.filter(o -> isMarginGEMinMarketPriceMargin.test(o, minMarketPriceMargin)
.filter(o -> isMarginBasedPriceGETargetPrice.test(o, targetPrice)
|| isFixedPriceGEMinMarketPriceMargin.test(o, currentMarketPrice))
.filter(isWithinBTCAmountBounds)
.findFirst();
else
return offers.stream()
.filter(o -> usesSamePaymentMethod.test(o, getPaymentAccount()))
.filter(o -> isMarginGEMinMarketPriceMargin.test(o, minMarketPriceMargin)
.filter(o -> isMarginBasedPriceGETargetPrice.test(o, targetPrice)
|| isFixedPriceGEMinMarketPriceMargin.test(o, currentMarketPrice))
.filter(isWithinBTCAmountBounds)
.findFirst();
}

void printCriteriaSummary() {
log.info("Looking for offers to {}, priced at or more than {}% {} the current market price {} {}.",
marketDescription.get(),
minMarketPriceMargin.abs(), // Hide the sign, text explains target price % "above or below".
aboveOrBelowMarketPrice.apply(minMarketPriceMargin),
currentMarketPrice,
isXmr.test(currencyCode) ? "BTC" : currencyCode);
if (isZero.test(minMarketPriceMargin)) {
log.info("Looking for offers to {}, priced at or higher than the current market price {} {}.",
marketDescription.get(),
currentMarketPrice,
isXmr.test(currencyCode) ? "BTC" : currencyCode);
} else {
log.info("Looking for offers to {}, priced at or more than {}% {} the current market price {} {}.",
marketDescription.get(),
minMarketPriceMargin.abs(), // Hide the sign, text explains target price % "above or below".
aboveOrBelowMarketPrice.apply(minMarketPriceMargin),
currentMarketPrice,
isXmr.test(currencyCode) ? "BTC" : currencyCode);
}
}

void printOffersAgainstCriteria(List<OfferInfo> offers) {
Expand All @@ -404,20 +411,20 @@ void printOfferAgainstCriteria(OfferInfo offer) {

var filterResultsByLabel = new LinkedHashMap<String, Object>();
filterResultsByLabel.put("Current Market Price:", currentMarketPrice + " " + currencyCode);
filterResultsByLabel.put("Target Price (Max):", targetPrice + " " + currencyCode);
filterResultsByLabel.put("Target Price (Min):", targetPrice + " " + currencyCode);
filterResultsByLabel.put("Offer Price:", offer.getPrice() + " " + currencyCode);
filterResultsByLabel.put("Offer maker used same payment method?",
usesSamePaymentMethod.test(offer, getPaymentAccount()));
filterResultsByLabel.put("Is offer maker a preferred trading peer?",
iHavePreferredTradingPeers.get()
? isMakerPreferredTradingPeer.test(offer) ? "YES" : "NO"
: "N/A");
var marginPriceLabel = format("Is offer's price margin (%s%%) >= bot's min market price margin (%s%%)?",
offer.getMarketPriceMarginPct(),
minMarketPriceMargin);
var marginPriceLabel = format("Is offer's margin based price (%s) >= bot's target price (%s)?",
offer.getUseMarketBasedPrice() ? offer.getPrice() : "N/A",
offer.getUseMarketBasedPrice() ? targetPrice : "N/A");
filterResultsByLabel.put(marginPriceLabel,
offer.getUseMarketBasedPrice()
? isMarginGEMinMarketPriceMargin.test(offer, minMarketPriceMargin)
? isMarginBasedPriceGETargetPrice.test(offer, targetPrice)
: "N/A");
var fixedPriceLabel = format("Is offer's fixed-price (%s) >= bot's target price (%s)?",
offer.getUseMarketBasedPrice() ? "N/A" : offer.getPrice() + " " + currencyCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,8 @@ private void maybeShutdownAfterSuccessfulSwap() {
public static void main(String[] args) {
@SuppressWarnings("unused")
String prompt = "An encrypted wallet must be unlocked before any offer can be taken.\n"
+ " Please enter your wallet password:";
String walletPassword = "be careful"; // readWalletPassword(prompt);
log.info("Your wallet password is {}", walletPassword.isBlank() ? "blank" : walletPassword);
+ "Please enter your wallet password:";
String walletPassword = readWalletPassword(prompt);
TakeBestPricedOfferToSellBsq bot = new TakeBestPricedOfferToSellBsq(appendWalletPasswordOpt(args, walletPassword));
bot.run();
}
Expand Down Expand Up @@ -309,15 +308,21 @@ Optional<OfferInfo> findTakeableOffer(List<OfferInfo> offers) {
}

void printCriteriaSummary() {
log.info("Looking for offers to {}, with a fixed-price at or less than"
+ " {}% {} the 30-day average BSQ trade price of {} BTC.",
MARKET_DESCRIPTION,
maxMarketPriceMargin.abs(), // Hide the sign, text explains target price % "above or below".
aboveOrBelowMarketPrice.apply(maxMarketPriceMargin),
avgBsqPrice);
if (isZero.test(maxMarketPriceMargin)) {
log.info("Looking for offers to {}, with a fixed-price at or less than"
+ " the 30-day average BSQ trade price of {} BTC.",
MARKET_DESCRIPTION,
avgBsqPrice);
} else {
log.info("Looking for offers to {}, with a fixed-price at or less than"
+ " {}% {} the 30-day average BSQ trade price of {} BTC.",
MARKET_DESCRIPTION,
maxMarketPriceMargin.abs(), // Hide the sign, text explains target price % "above or below".
aboveOrBelowMarketPrice.apply(maxMarketPriceMargin),
avgBsqPrice);
}
}


void printOffersAgainstCriteria(List<OfferInfo> offers) {
log.info("Currently available {} offers -- want to take BSQ swap offer with fixed-price <= {} BTC.",
MARKET_DESCRIPTION,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

/**
* The TakeBestPricedOfferToSellBtc bot waits for attractively priced SELL BTC offers to appear, takes the offers
* (up to a maximum of configured {@link #maxTakeOffers}, then shuts down both the API daemon and itself (the bot),
* (up to a maximum of configured {@link #maxTakeOffers}), then shuts down both the API daemon and itself (the bot),
* to allow the user to start the desktop UI application and complete the trades.
* <p>
* The benefit this bot provides is freeing up the user time spent watching the offer book in the UI, waiting for the
Expand Down Expand Up @@ -330,9 +330,8 @@ private void printBotConfiguration() {
public static void main(String[] args) {
@SuppressWarnings("unused")
String prompt = "An encrypted wallet must be unlocked before any offer can be taken.\n"
+ " Please enter your wallet password:";
String walletPassword = "be careful"; // readWalletPassword(prompt);
log.info("Your wallet password is {}", walletPassword.isBlank() ? "blank" : walletPassword);
+ "Please enter your wallet password:";
String walletPassword = readWalletPassword(prompt);
TakeBestPricedOfferToSellBtc bot = new TakeBestPricedOfferToSellBtc(appendWalletPasswordOpt(args, walletPassword));
bot.run();
}
Expand Down Expand Up @@ -383,12 +382,19 @@ Optional<OfferInfo> findTakeableOffer(List<OfferInfo> offers) {
}

void printCriteriaSummary() {
log.info("Looking for offers to {}, priced at or less than {}% {} the current market price {} {}.",
marketDescription.get(),
maxMarketPriceMargin.abs(), // Hide the sign, text explains target price % "above or below".
aboveOrBelowMarketPrice.apply(maxMarketPriceMargin),
currentMarketPrice,
isXmr.test(currencyCode) ? "BTC" : currencyCode);
if (isZero.test(maxMarketPriceMargin)) {
log.info("Looking for offers to {}, priced at or lower than the current market price {} {}.",
marketDescription.get(),
currentMarketPrice,
isXmr.test(currencyCode) ? "BTC" : currencyCode);
} else {
log.info("Looking for offers to {}, priced at or less than {}% {} the current market price {} {}.",
marketDescription.get(),
maxMarketPriceMargin.abs(), // Hide the sign, text explains target price % "above or below".
aboveOrBelowMarketPrice.apply(maxMarketPriceMargin),
currentMarketPrice,
isXmr.test(currencyCode) ? "BTC" : currencyCode);
}
}

void printOffersAgainstCriteria(List<OfferInfo> offers) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#
# Maximum # of offers to take during one bot session. When reached, bot will shut down API daemon then itself.
maxTakeOffers=10
maxTakeOffers=1
#
# Taker bot's payment account id. Only BUY BTC offers using the same payment method will be considered for taking.
paymentAccountId=9ad3cc7a-7d32-453c-b9db-a3714b5b8f61
paymentAccountId=28030c83-f07d-4f0b-b824-019529279630
#
# Taker bot's min market price margin. A candidate BUY BTC offer's price margin must be >= minMarketPriceMargin.
#
minMarketPriceMargin=1.00
minMarketPriceMargin=0
#
# Taker bot's min BTC amount to sell. The candidate BUY offer's amount must be >= minAmount BTC.
minAmount=0.01
Expand Down