diff --git a/java-examples/src/main/java/bisq/bots/TakeBestPricedOfferToBuyXmr.java b/java-examples/src/main/java/bisq/bots/TakeBestPricedOfferToBuyXmr.java index 1d5ea49..d9cd71c 100644 --- a/java-examples/src/main/java/bisq/bots/TakeBestPricedOfferToBuyXmr.java +++ b/java-examples/src/main/java/bisq/bots/TakeBestPricedOfferToBuyXmr.java @@ -32,23 +32,54 @@ import static java.math.RoundingMode.HALF_UP; import static protobuf.OfferDirection.SELL; /** - * The use case for the TakeBestPricedOfferToBuyXmr bot is to sell XMR for BTC at a high BTC price, e.g.: - *
- * Take an offer to buy XMR from you for BTC, at no less than #.##% above or below current market price if: - * the offer maker is a preferred trading peer, - * and the offer's BTC amount is between 0.50 and 1.00 BTC, - * and the current transaction mining fee rate is below 15 sats / byte. - * - * Usage: TakeBestPricedOfferToBuyXmr --password=api-password --port=api-port \ - * [--conf=take-best-priced-offer-to-buy-xmr.conf] \ - * [--dryrun=true|false] - * [--simulate-regtest-payment=true|false] - *+ * This bot's general use case is to sell your XMR for BTC at a high BTC price. It periodically checks the + * Buy XMR (Sell BTC) market, and takes a configured maximum number of offers to buy XMR from you according to criteria + * you define in the bot's configuration file: TakeBestPricedOfferToBuyXmr.properties (located in project's + * src/main/resources directory). You will need to replace the default values in the configuration file for your + * use cases. + *
+ * After the maximum number of offers have been taken (good to start with 1), the bot will shut down the API daemon,
+ * then itself. You have to send XMR payment to the offer maker(s) outside Bisq, then complete the trade(s) in the
+ * Bisq Desktop application.
*
- * The criteria for determining which offers to take are defined in the bot's configuration file - * TakeBestPricedOfferToBuyXmr.properties (located in project's src/main/resources directory). The individual - * configurations are commented in the existing TakeBestPricedOfferToBuyXmr.properties, which should be used as a - * template for your own use case. + * Here is one possible use case: + *
+ * Take 2 offers to buy your XMR for BTC, priced no lower than -1.50% above or below current market price if: + * + * the offer's BTC amount is between 0.50 and 1.00 BTC + * the offer maker is one of two preferred trading peers + * the current transaction mining fee rate is below 20 sats / byte + * + * The bot configurations for these rules are set in TakeBestPricedOfferToBuyXmr.properties as follows: + * + * maxTakeOffers=2 + * minMarketPriceMargin=-1.50 + * minAmount=0.50 + * maxAmount=1.00 + * preferredTradingPeers=preferred-address-1.onion:9999,preferred-address-2.onion:9999 + * maxTxFeeRate=20 + *+ * Usage + *
+ * You must encrypt your wallet password before running this bot. If it is not already, you can use the CLI:
+ *
+ * $ ./bisq-cli --password=xyz --port=9998 setwalletpassword --wallet-password="be careful" + *+ * There are some {@link bisq.bots.Config program options} common to all the Java bot examples, passed on the command + * line. The only one you must provide (no default value) is your API daemon's password option: + * `--password
+ * You can pass the '--dryrun=true' option to the program to see what offers your bot would take with a given
+ * configuration. This will help you avoid taking offers by mistake.
+ *
+ * TakeBestPricedOfferToBuyXmr --password=api-password --port=api-port [--dryrun=true|false] + *+ * If your API daemon is running on a local regtest network (with a trading peer), you can pass the + * '--simulate-regtest-payment=true' option to the program to simulate the full trade protocol. The bot will print + * your regtest trading peer's CLI commands will be printed on the console, for you to copy/paste into another terminal. + *
+ * TakeBestPricedOfferToBuyXmr --password=api-password --port=api-port [--simulate-regtest-payment=true|false] + **/ @Slf4j @Getter @@ -143,7 +174,6 @@ public class TakeBestPricedOfferToBuyXmr extends AbstractBot { takeCriteria.printOfferAgainstCriteria(highestPricedOffer); }); - printDryRunProgress(); runCountdown(log, pollingInterval); pingDaemon(startTime); } @@ -157,16 +187,21 @@ public class TakeBestPricedOfferToBuyXmr extends AbstractBot { private void takeOffer(TakeCriteria takeCriteria, OfferInfo offer) { log.info("Will attempt to take offer '{}'.", offer.getId()); takeCriteria.printOfferAgainstCriteria(offer); + + // An encrypted wallet must be unlocked before calling takeoffer and gettrade(s). + // Unlock the wallet for 5 minutes. If the wallet is already unlocked, this request + // will override the timeout of the previous unlock request. + try { + unlockWallet(walletPassword, 300); + } catch (NonFatalException nonFatalException) { + handleNonFatalException(nonFatalException, pollingInterval); + } + if (isDryRun) { addToOffersTaken(offer); numOffersTaken++; - maybeShutdownAfterSuccessfulTradeCreation(numOffersTaken, maxTakeOffers); } else { - // An encrypted wallet must be unlocked before calling takeoffer and gettrade. - // Unlock the wallet for 5 minutes. If the wallet is already unlocked, - // this command will override the timeout of the previous unlock command. try { - unlockWallet(walletPassword, 600); printBTCBalances("BTC Balances Before Take Offer Attempt"); // Blocks until new trade is prepared, or times out. takeV1ProtocolOffer(offer, paymentAccount, bisqTradeFeeCurrency, pollingInterval); @@ -181,13 +216,13 @@ public class TakeBestPricedOfferToBuyXmr extends AbstractBot { printBTCBalances("BTC Balances After Simulated Trade Completion"); } numOffersTaken++; - maybeShutdownAfterSuccessfulTradeCreation(numOffersTaken, maxTakeOffers); } catch (NonFatalException nonFatalException) { handleNonFatalException(nonFatalException, pollingInterval); } catch (StatusRuntimeException fatalException) { shutdownAfterTakeOfferFailure(fatalException); } } + maybeShutdownAfterSuccessfulTradeCreation(numOffersTaken, maxTakeOffers); } /** @@ -205,6 +240,8 @@ public class TakeBestPricedOfferToBuyXmr extends AbstractBot { configsByLabel.put("Bot OS:", getOSName() + " " + getOSVersion()); var network = getNetwork(); configsByLabel.put("BTC Network:", network); + configsByLabel.put("Dry Run?", isDryRun ? "YES" : "NO"); + configsByLabel.put("Simulate Regtest Trade?", canSimulatePaymentSteps ? "YES" : "NO"); configsByLabel.put("My Payment Account:", ""); configsByLabel.put("\tPayment Account Id:", paymentAccount.getId()); configsByLabel.put("\tAccount Name:", paymentAccount.getAccountName()); @@ -309,14 +346,14 @@ public class TakeBestPricedOfferToBuyXmr extends AbstractBot { var marginPriceLabel = format("Is offer's price margin (%s%%) >= bot's min market price margin (%s%%)?", offer.getMarketPriceMarginPct(), minMarketPriceMargin); - filterResultsByLabel.put(marginPriceLabel, isMarginLEMaxMarketPriceMargin.test(offer, minMarketPriceMargin)); + filterResultsByLabel.put(marginPriceLabel, isMarginGEMinMarketPriceMargin.test(offer, minMarketPriceMargin)); } else { var fixedPriceLabel = format("Is offer's fixed-price (%s) >= bot's target price (%s)?", offer.getPrice() + " BTC", targetPrice + " BTC"); filterResultsByLabel.put(fixedPriceLabel, isFixedPriceGEMinMarketPriceMargin.test(offer, currentMarketPrice)); } - + String btcAmountBounds = format("%s BTC - %s BTC", minAmount, maxAmount); filterResultsByLabel.put("Is offer's BTC amount within bot amount bounds (" + btcAmountBounds + ")?", isWithinBTCAmountBounds(offer, getMinAmount(), getMaxAmount())); diff --git a/java-examples/src/main/resources/TakeBestPricedOfferToBuyXmr.properties b/java-examples/src/main/resources/TakeBestPricedOfferToBuyXmr.properties index 365074d..d7ef4f4 100644 --- a/java-examples/src/main/resources/TakeBestPricedOfferToBuyXmr.properties +++ b/java-examples/src/main/resources/TakeBestPricedOfferToBuyXmr.properties @@ -1,21 +1,21 @@ # Maximum # of offers to take during one bot session. When reached, bot will shut down (but not the API daemon). -maxTakeOffers=50 +maxTakeOffers=5 # # Taker bot's payment account id. Only SELL BTC offers using the same payment method will be considered for taking. -paymentAccountId=a15d0f15-e355-4b89-8322-e262097623ae +paymentAccountId=f32546cd-bb47-4bce-acc8-5227a13e2516 # # Taker bot's min market price margin. A candidate sell BTC offer's price margin must be >= minMarketPriceMargin. -minMarketPriceMargin=0 +minMarketPriceMargin=-5.00 # # Taker bot's min BTC amount to sell. The candidate SELL BTC offer's amount must be >= minAmount BTC. minAmount=0.01 # # Taker bot's max BTC amount to sell. The candidate SELL BTC offer's amount must be <= maxAmount BTC. -maxAmount=0.90 +maxAmount=1.00 # # Taker bot's max acceptable transaction fee rate (sats / byte). # Regtest fee rates are from https://price.bisq.wiz.biz/getFees -maxTxFeeRate=25 +maxTxFeeRate=50 # # Bisq trade fee currency code (BSQ or BTC). bisqTradeFeeCurrency=BSQ @@ -25,4 +25,4 @@ bisqTradeFeeCurrency=BSQ preferredTradingPeers=localhost:8888 # # Offer polling frequency must be >= 1s (1000ms) between each getoffers request. -pollingInterval=30000 +pollingInterval=10000