mirror of
https://github.com/bisq-network/bisq.git
synced 2025-03-13 11:09:10 +01:00
Add API method 'getavgbsqprice'
Returns volume weighted average BSQ price in BTC and USD over N days. The need for this arose while creating a Java BSQ Swap bot example (PR pending). API bots can use this to determine whether or not to take swap offers based on their price distance above or below the 30 (or 90) day trade price average.
This commit is contained in:
parent
71365be48c
commit
a12dd52b81
10 changed files with 189 additions and 3 deletions
|
@ -17,6 +17,7 @@
|
|||
|
||||
package bisq.cli;
|
||||
|
||||
import bisq.proto.grpc.AverageBsqTradePrice;
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
|
@ -63,6 +64,7 @@ import bisq.cli.opts.CreateOfferOptionParser;
|
|||
import bisq.cli.opts.CreatePaymentAcctOptionParser;
|
||||
import bisq.cli.opts.EditOfferOptionParser;
|
||||
import bisq.cli.opts.GetAddressBalanceOptionParser;
|
||||
import bisq.cli.opts.GetAvgBsqPriceOptionParser;
|
||||
import bisq.cli.opts.GetBTCMarketPriceOptionParser;
|
||||
import bisq.cli.opts.GetBalanceOptionParser;
|
||||
import bisq.cli.opts.GetOffersOptionParser;
|
||||
|
@ -210,6 +212,21 @@ public class CliMain {
|
|||
new TableBuilder(ADDRESS_BALANCE_TBL, addressBalance).build().print(out);
|
||||
return;
|
||||
}
|
||||
case getavgbsqprice: {
|
||||
var opts = new GetAvgBsqPriceOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
out.println(client.getMethodHelp(method));
|
||||
return;
|
||||
}
|
||||
var days = opts.getDays();
|
||||
AverageBsqTradePrice price = client.getAverageBsqTradePrice(days);
|
||||
out.println(format("avg %d day btc price: %s avg %d day usd price: %s",
|
||||
days,
|
||||
price.getBtcPrice(),
|
||||
days,
|
||||
price.getUsdPrice()));
|
||||
return;
|
||||
}
|
||||
case getbtcprice: {
|
||||
var opts = new GetBTCMarketPriceOptionParser(args).parse();
|
||||
if (opts.isForHelp()) {
|
||||
|
@ -837,6 +854,8 @@ public class CliMain {
|
|||
stream.println();
|
||||
stream.format(rowFormat, getaddressbalance.name(), "--address=<btc-address>", "Get server wallet address balance");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getavgbsqprice.name(), "--days=<days>", "Get volume weighted average bsq trade price");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getbtcprice.name(), "--currency-code=<currency-code>", "Get current market btc price");
|
||||
stream.println();
|
||||
stream.format(rowFormat, getfundingaddresses.name(), "", "Get BTC funding addresses");
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
package bisq.cli;
|
||||
|
||||
import bisq.proto.grpc.AddressBalanceInfo;
|
||||
import bisq.proto.grpc.AverageBsqTradePrice;
|
||||
import bisq.proto.grpc.BalancesInfo;
|
||||
import bisq.proto.grpc.BsqBalanceInfo;
|
||||
import bisq.proto.grpc.BtcBalanceInfo;
|
||||
import bisq.proto.grpc.GetAverageBsqTradePriceRequest;
|
||||
import bisq.proto.grpc.GetMethodHelpRequest;
|
||||
import bisq.proto.grpc.GetTradesRequest;
|
||||
import bisq.proto.grpc.GetVersionRequest;
|
||||
|
@ -98,6 +100,13 @@ public final class GrpcClient {
|
|||
return walletsServiceRequest.getAddressBalance(address);
|
||||
}
|
||||
|
||||
public AverageBsqTradePrice getAverageBsqTradePrice(int days) {
|
||||
var request = GetAverageBsqTradePriceRequest.newBuilder()
|
||||
.setDays(days)
|
||||
.build();
|
||||
return grpcStubs.priceService.getAverageBsqTradePrice(request).getPrice();
|
||||
}
|
||||
|
||||
public double getBtcPrice(String currencyCode) {
|
||||
return walletsServiceRequest.getBtcPrice(currencyCode);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ public enum Method {
|
|||
editoffer,
|
||||
createpaymentacct,
|
||||
createcryptopaymentacct,
|
||||
getavgbsqprice,
|
||||
getaddressbalance,
|
||||
getbalance,
|
||||
getbtcprice,
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq 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.
|
||||
*
|
||||
* Bisq 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 Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.opts.OptLabel.OPT_DAYS;
|
||||
|
||||
public class GetAvgBsqPriceOptionParser extends AbstractMethodOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<Integer> daysOpt = parser.accepts(OPT_DAYS,
|
||||
"number of days in average bsq price calculation")
|
||||
.withRequiredArg()
|
||||
.ofType(Integer.class)
|
||||
.defaultsTo(30);
|
||||
|
||||
public GetAvgBsqPriceOptionParser(String[] args) {
|
||||
super(args);
|
||||
}
|
||||
|
||||
public GetAvgBsqPriceOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Short circuit opt validation if user just wants help.
|
||||
if (options.has(helpOpt))
|
||||
return this;
|
||||
|
||||
if (!options.has(daysOpt) || options.valueOf(daysOpt) <= 0)
|
||||
throw new IllegalArgumentException("no # of days specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getDays() {
|
||||
return options.valueOf(daysOpt);
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ public class OptLabel {
|
|||
public final static String OPT_AMOUNT = "amount";
|
||||
public final static String OPT_CATEGORY = "category";
|
||||
public final static String OPT_CURRENCY_CODE = "currency-code";
|
||||
public final static String OPT_DAYS = "days";
|
||||
public final static String OPT_DIRECTION = "direction";
|
||||
public final static String OPT_DISPUTE_AGENT_TYPE = "dispute-agent-type";
|
||||
public final static String OPT_ENABLE = "enable";
|
||||
|
|
|
@ -21,6 +21,7 @@ import bisq.core.api.model.AddressBalanceInfo;
|
|||
import bisq.core.api.model.BalancesInfo;
|
||||
import bisq.core.api.model.TxFeeRateInfo;
|
||||
import bisq.core.btc.wallet.TxBroadcaster;
|
||||
import bisq.core.monetary.Price;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.OpenOffer;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
|
@ -36,6 +37,7 @@ import bisq.common.app.Version;
|
|||
import bisq.common.config.Config;
|
||||
import bisq.common.handlers.ErrorMessageHandler;
|
||||
import bisq.common.handlers.ResultHandler;
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import bisq.proto.grpc.GetTradesRequest;
|
||||
|
||||
|
@ -282,6 +284,10 @@ public class CoreApi {
|
|||
corePriceService.getMarketPrice(currencyCode, resultHandler);
|
||||
}
|
||||
|
||||
public Tuple2<Price, Price> getAverageBsqTradePrice(int days) {
|
||||
return corePriceService.getAverageBsqTradePrice(days);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Trades
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -17,7 +17,14 @@
|
|||
|
||||
package bisq.core.api;
|
||||
|
||||
import bisq.core.api.exception.NotAvailableException;
|
||||
import bisq.core.monetary.Price;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.AveragePriceUtil;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
@ -38,14 +45,20 @@ class CorePriceService {
|
|||
|
||||
private final Predicate<String> isCurrencyCode = (c) -> isFiatCurrency(c) || isCryptoCurrency(c);
|
||||
|
||||
private final Preferences preferences;
|
||||
private final PriceFeedService priceFeedService;
|
||||
private final TradeStatisticsManager tradeStatisticsManager;
|
||||
|
||||
@Inject
|
||||
public CorePriceService(PriceFeedService priceFeedService) {
|
||||
public CorePriceService(Preferences preferences,
|
||||
PriceFeedService priceFeedService,
|
||||
TradeStatisticsManager tradeStatisticsManager) {
|
||||
this.preferences = preferences;
|
||||
this.priceFeedService = priceFeedService;
|
||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||
}
|
||||
|
||||
public void getMarketPrice(String currencyCode, Consumer<Double> resultHandler) {
|
||||
void getMarketPrice(String currencyCode, Consumer<Double> resultHandler) {
|
||||
String upperCaseCurrencyCode = currencyCode.toUpperCase();
|
||||
|
||||
if (!isCurrencyCode.test(upperCaseCurrencyCode))
|
||||
|
@ -72,9 +85,17 @@ class CorePriceService {
|
|||
format("%s price feed request should not return data for unsupported currency code",
|
||||
upperCaseCurrencyCode));
|
||||
} else {
|
||||
throw new IllegalStateException(format("%s price is not available", upperCaseCurrencyCode));
|
||||
throw new NotAvailableException(format("%s price is not available", upperCaseCurrencyCode));
|
||||
}
|
||||
},
|
||||
log::warn);
|
||||
}
|
||||
|
||||
Tuple2<Price, Price> getAverageBsqTradePrice(int days) {
|
||||
Tuple2<Price, Price> prices = AveragePriceUtil.getAveragePriceTuple(preferences, tradeStatisticsManager, days);
|
||||
if (prices.first.getValue() == 0 || prices.second.getValue() == 0)
|
||||
throw new NotAvailableException("average bsq price is not available");
|
||||
|
||||
return prices;
|
||||
}
|
||||
}
|
||||
|
|
19
core/src/main/resources/help/getavgbsqprice-help.txt
Normal file
19
core/src/main/resources/help/getavgbsqprice-help.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
getavgbsqprice
|
||||
|
||||
NAME
|
||||
----
|
||||
getavgbsqprice - get average BSQ price in btc and usd
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
getavgbsqprice
|
||||
--days=<30|90>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Returns the volume weighted average BSQ trade price over n days,
|
||||
in BTC and USD.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
$ ./bisq-cli --password=xyz --port=9998 getavgbsqprice --days=30
|
|
@ -18,15 +18,27 @@
|
|||
package bisq.daemon.grpc;
|
||||
|
||||
import bisq.core.api.CoreApi;
|
||||
import bisq.core.monetary.Altcoin;
|
||||
import bisq.core.monetary.Price;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import bisq.proto.grpc.AverageBsqTradePrice;
|
||||
import bisq.proto.grpc.GetAverageBsqTradePriceReply;
|
||||
import bisq.proto.grpc.GetAverageBsqTradePriceRequest;
|
||||
import bisq.proto.grpc.MarketPriceReply;
|
||||
import bisq.proto.grpc.MarketPriceRequest;
|
||||
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -34,6 +46,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
|
||||
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
||||
import static bisq.proto.grpc.PriceGrpc.PriceImplBase;
|
||||
import static bisq.proto.grpc.PriceGrpc.getGetAverageBsqTradePriceMethod;
|
||||
import static bisq.proto.grpc.PriceGrpc.getGetMarketPriceMethod;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
@ -69,6 +82,30 @@ class GrpcPriceService extends PriceImplBase {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAverageBsqTradePrice(GetAverageBsqTradePriceRequest req,
|
||||
StreamObserver<GetAverageBsqTradePriceReply> responseObserver) {
|
||||
try {
|
||||
var days = req.getDays();
|
||||
Tuple2<Price, Price> prices = coreApi.getAverageBsqTradePrice(days);
|
||||
var usdPrice = new BigDecimal(prices.first.toString())
|
||||
.setScale(Fiat.SMALLEST_UNIT_EXPONENT, RoundingMode.HALF_UP);
|
||||
var btcPrice = new BigDecimal(prices.second.toString())
|
||||
.setScale(Altcoin.SMALLEST_UNIT_EXPONENT, RoundingMode.HALF_UP);
|
||||
var proto = AverageBsqTradePrice.newBuilder()
|
||||
.setUsdPrice(usdPrice.toString())
|
||||
.setBtcPrice(btcPrice.toString())
|
||||
.build();
|
||||
var reply = GetAverageBsqTradePriceReply.newBuilder()
|
||||
.setPrice(proto)
|
||||
.build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
} catch (Throwable cause) {
|
||||
exceptionHandler.handleException(log, cause, responseObserver);
|
||||
}
|
||||
}
|
||||
|
||||
final ServerInterceptor[] interceptors() {
|
||||
Optional<ServerInterceptor> rateMeteringInterceptor = rateMeteringInterceptor();
|
||||
return rateMeteringInterceptor.map(serverInterceptor ->
|
||||
|
@ -80,6 +117,7 @@ class GrpcPriceService extends PriceImplBase {
|
|||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||
new HashMap<>() {{
|
||||
put(getGetMarketPriceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
||||
put(getGetAverageBsqTradePriceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -434,6 +434,10 @@ service Price {
|
|||
// Get the current market price for a crypto currency.
|
||||
rpc GetMarketPrice (MarketPriceRequest) returns (MarketPriceReply) {
|
||||
}
|
||||
// Get the volume weighted average trade price for BSQ, calculated over N days.
|
||||
// The response contains the average BSQ trade price in USD to 4 decimal places, and in BTC to 8 decimal places.
|
||||
rpc GetAverageBsqTradePrice (GetAverageBsqTradePriceRequest) returns (GetAverageBsqTradePriceReply) {
|
||||
}
|
||||
}
|
||||
|
||||
message MarketPriceRequest {
|
||||
|
@ -444,6 +448,21 @@ message MarketPriceReply {
|
|||
double price = 1; // The most recently available market price.
|
||||
}
|
||||
|
||||
message GetAverageBsqTradePriceRequest {
|
||||
sint32 days = 1; // The number of days used in the average BSQ trade price calculations.
|
||||
}
|
||||
|
||||
message GetAverageBsqTradePriceReply {
|
||||
// The average BSQ trade price in USD to 4 decimal places, and in BTC to 8 decimal places.
|
||||
AverageBsqTradePrice price = 1;
|
||||
}
|
||||
|
||||
// The average BSQ trade price in USD and BTC.
|
||||
message AverageBsqTradePrice {
|
||||
string usdPrice = 1; // The average BSQ trade price in USD to 4 decimal places.
|
||||
string btcPrice = 2; // The average BSQ trade price in BTC to 8 decimal places.
|
||||
}
|
||||
|
||||
service ShutdownServer {
|
||||
// Shut down a local Bisq daemon.
|
||||
rpc Stop (StopRequest) returns (StopReply) {
|
||||
|
|
Loading…
Add table
Reference in a new issue