Document and tidy up TakeBestPricedOfferToBuyXmr bot

This commit is contained in:
ghubstan 2022-06-30 13:17:46 -03:00
parent d59dc725ee
commit fdc96bc455
No known key found for this signature in database
GPG Key ID: E35592D6800A861E
2 changed files with 68 additions and 31 deletions

View File

@ -32,23 +32,54 @@ import static java.math.RoundingMode.HALF_UP;
import static protobuf.OfferDirection.SELL; 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.: * This bot's general use case is to sell your XMR for BTC at a high BTC price. It periodically checks the
* <pre> * Buy XMR (Sell BTC) market, and takes a configured maximum number of offers to buy XMR from you according to criteria
* Take an offer to buy XMR from you for BTC, at no less than #.##% above or below current market price if: * you define in the bot's configuration file: <b>TakeBestPricedOfferToBuyXmr.properties</b> (located in project's
* the offer maker is a preferred trading peer, * src/main/resources directory). You will need to replace the default values in the configuration file for your
* and the offer's BTC amount is between 0.50 and 1.00 BTC, * use cases.
* and the current transaction mining fee rate is below 15 sats / byte. * <p><br/>
* * After the maximum number of offers have been taken (good to start with 1), the bot will shut down the API daemon,
* Usage: TakeBestPricedOfferToBuyXmr --password=api-password --port=api-port \ * then itself. You have to send XMR payment to the offer maker(s) outside Bisq, then complete the trade(s) in the
* [--conf=take-best-priced-offer-to-buy-xmr.conf] \ * <a href="https://bisq.network">Bisq Desktop</a> application.
* [--dryrun=true|false]
* [--simulate-regtest-payment=true|false]
* </pre>
* <p> * <p>
* The criteria for determining which offers to take are defined in the bot's configuration file * Here is one possible use case:
* TakeBestPricedOfferToBuyXmr.properties (located in project's src/main/resources directory). The individual * <pre>
* configurations are commented in the existing TakeBestPricedOfferToBuyXmr.properties, which should be used as a * Take 2 offers to buy your XMR for BTC, priced no lower than -1.50% above or below current market price if:
* template for your own use case. *
* 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
* </pre>
* <b>Usage</b>
* <p><br/>
* You must encrypt your wallet password before running this bot. If it is not already, you can use the CLI:
* <pre>
* $ ./bisq-cli --password=xyz --port=9998 setwalletpassword --wallet-password="be careful"
* </pre>
* 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 <String>`. The bot will prompt you for your wallet-password in the console.
* <p><br/>
* You can pass the '--dryrun=true' option to the program to see what offers your bot <i>would take</i> with a given
* configuration. This will help you avoid taking offers by mistake.
* <pre>
* TakeBestPricedOfferToBuyXmr --password=api-password --port=api-port [--dryrun=true|false]
* </pre>
* 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.
* <pre>
* TakeBestPricedOfferToBuyXmr --password=api-password --port=api-port [--simulate-regtest-payment=true|false]
* </pre>
*/ */
@Slf4j @Slf4j
@Getter @Getter
@ -143,7 +174,6 @@ public class TakeBestPricedOfferToBuyXmr extends AbstractBot {
takeCriteria.printOfferAgainstCriteria(highestPricedOffer); takeCriteria.printOfferAgainstCriteria(highestPricedOffer);
}); });
printDryRunProgress();
runCountdown(log, pollingInterval); runCountdown(log, pollingInterval);
pingDaemon(startTime); pingDaemon(startTime);
} }
@ -157,16 +187,21 @@ public class TakeBestPricedOfferToBuyXmr extends AbstractBot {
private void takeOffer(TakeCriteria takeCriteria, OfferInfo offer) { private void takeOffer(TakeCriteria takeCriteria, OfferInfo offer) {
log.info("Will attempt to take offer '{}'.", offer.getId()); log.info("Will attempt to take offer '{}'.", offer.getId());
takeCriteria.printOfferAgainstCriteria(offer); 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) { if (isDryRun) {
addToOffersTaken(offer); addToOffersTaken(offer);
numOffersTaken++; numOffersTaken++;
maybeShutdownAfterSuccessfulTradeCreation(numOffersTaken, maxTakeOffers);
} else { } 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 { try {
unlockWallet(walletPassword, 600);
printBTCBalances("BTC Balances Before Take Offer Attempt"); printBTCBalances("BTC Balances Before Take Offer Attempt");
// Blocks until new trade is prepared, or times out. // Blocks until new trade is prepared, or times out.
takeV1ProtocolOffer(offer, paymentAccount, bisqTradeFeeCurrency, pollingInterval); takeV1ProtocolOffer(offer, paymentAccount, bisqTradeFeeCurrency, pollingInterval);
@ -181,13 +216,13 @@ public class TakeBestPricedOfferToBuyXmr extends AbstractBot {
printBTCBalances("BTC Balances After Simulated Trade Completion"); printBTCBalances("BTC Balances After Simulated Trade Completion");
} }
numOffersTaken++; numOffersTaken++;
maybeShutdownAfterSuccessfulTradeCreation(numOffersTaken, maxTakeOffers);
} catch (NonFatalException nonFatalException) { } catch (NonFatalException nonFatalException) {
handleNonFatalException(nonFatalException, pollingInterval); handleNonFatalException(nonFatalException, pollingInterval);
} catch (StatusRuntimeException fatalException) { } catch (StatusRuntimeException fatalException) {
shutdownAfterTakeOfferFailure(fatalException); shutdownAfterTakeOfferFailure(fatalException);
} }
} }
maybeShutdownAfterSuccessfulTradeCreation(numOffersTaken, maxTakeOffers);
} }
/** /**
@ -205,6 +240,8 @@ public class TakeBestPricedOfferToBuyXmr extends AbstractBot {
configsByLabel.put("Bot OS:", getOSName() + " " + getOSVersion()); configsByLabel.put("Bot OS:", getOSName() + " " + getOSVersion());
var network = getNetwork(); var network = getNetwork();
configsByLabel.put("BTC Network:", network); 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("My Payment Account:", "");
configsByLabel.put("\tPayment Account Id:", paymentAccount.getId()); configsByLabel.put("\tPayment Account Id:", paymentAccount.getId());
configsByLabel.put("\tAccount Name:", paymentAccount.getAccountName()); configsByLabel.put("\tAccount Name:", paymentAccount.getAccountName());
@ -309,7 +346,7 @@ public class TakeBestPricedOfferToBuyXmr extends AbstractBot {
var marginPriceLabel = format("Is offer's price margin (%s%%) >= bot's min market price margin (%s%%)?", var marginPriceLabel = format("Is offer's price margin (%s%%) >= bot's min market price margin (%s%%)?",
offer.getMarketPriceMarginPct(), offer.getMarketPriceMarginPct(),
minMarketPriceMargin); minMarketPriceMargin);
filterResultsByLabel.put(marginPriceLabel, isMarginLEMaxMarketPriceMargin.test(offer, minMarketPriceMargin)); filterResultsByLabel.put(marginPriceLabel, isMarginGEMinMarketPriceMargin.test(offer, minMarketPriceMargin));
} else { } else {
var fixedPriceLabel = format("Is offer's fixed-price (%s) >= bot's target price (%s)?", var fixedPriceLabel = format("Is offer's fixed-price (%s) >= bot's target price (%s)?",
offer.getPrice() + " BTC", offer.getPrice() + " BTC",

View File

@ -1,21 +1,21 @@
# Maximum # of offers to take during one bot session. When reached, bot will shut down (but not the API daemon). # 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. # 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. # 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. # Taker bot's min BTC amount to sell. The candidate SELL BTC offer's amount must be >= minAmount BTC.
minAmount=0.01 minAmount=0.01
# #
# Taker bot's max BTC amount to sell. The candidate SELL BTC offer's amount must be <= maxAmount BTC. # 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). # Taker bot's max acceptable transaction fee rate (sats / byte).
# Regtest fee rates are from https://price.bisq.wiz.biz/getFees # Regtest fee rates are from https://price.bisq.wiz.biz/getFees
maxTxFeeRate=25 maxTxFeeRate=50
# #
# Bisq trade fee currency code (BSQ or BTC). # Bisq trade fee currency code (BSQ or BTC).
bisqTradeFeeCurrency=BSQ bisqTradeFeeCurrency=BSQ
@ -25,4 +25,4 @@ bisqTradeFeeCurrency=BSQ
preferredTradingPeers=localhost:8888 preferredTradingPeers=localhost:8888
# #
# Offer polling frequency must be >= 1s (1000ms) between each getoffers request. # Offer polling frequency must be >= 1s (1000ms) between each getoffers request.
pollingInterval=30000 pollingInterval=10000