A java implementation of the Binance API Specification
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

382 lines
15 KiB

package com.sigmaflare.binancej;
import com.codepoetics.ambivalence.Either;
import com.fasterxml.jackson.databind.JavaType;
import com.sigmaflare.binancej.entities.Candlestick;
import com.sigmaflare.binancej.entities.Interval;
import com.sigmaflare.binancej.entities.OrderBookDepth;
import com.sigmaflare.binancej.entities.ServiceError;
import com.sigmaflare.binancej.entities.TickerPrice;
import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.List;
import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT;
import static com.sigmaflare.binancej.Constant.NO_RESPONSE_TEXT;
import static com.sigmaflare.binancej.Constant.NO_RESPONSE_TEXT_FORMATTED;
import static com.sigmaflare.binancej.Constant.SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED;
@Slf4j
public class MarketData extends BaseBinanceApi {
private static final String ORDER_BOOK_URL = "/api/v1/depth";
private static final String CANDLESTICK_URL = "/api/v1/klines";
private static final String TICKER_PRICE_URL = "/api/v3/ticker/price";
@Builder
MarketData(String apiKey, String secretKey) {
super(apiKey, secretKey);
}
MarketData(String apiKey, String secretKey, CloseableHttpClient closeableHttpClient) {
super(apiKey, secretKey, closeableHttpClient);
}
/**
* Overloaded version of getOrderBookDepth that uses the default limit of 100
*
* @param symbol The symbol
* @return A populated OrderBookDepth if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException In the case the service is unreachable
*/
public Either<ServiceError, OrderBookDepth> getOrderBookDepth(String symbol)
throws BinanceServiceUnreachableException {
return getOrderBookDepth(symbol, 100);
}
/**
* Retrieves orderbook depth information
*
* @param symbol The symbol
* @param limit The record limit (default: 100, maximum 1000)
* @return A populated OrderBookDepth if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException In the case the service is unreachable
*/
public Either<ServiceError, OrderBookDepth> getOrderBookDepth(String symbol, int limit)
throws BinanceServiceUnreachableException {
String url = String.format("%s%s?symbol=%s&limit=%d", BASE_ENDPOINT, ORDER_BOOK_URL, symbol, limit);
final HttpGet request = Helpers.getBuilder(url, apiKey);
try {
try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) {
StatusLine sl = closeableHttpResponse.getStatusLine();
HttpEntity httpEntity = closeableHttpResponse.getEntity();
if (httpEntity == null) {
log.error(NO_RESPONSE_TEXT, url);
throw new BinanceServiceUnreachableException(String.format(NO_RESPONSE_TEXT_FORMATTED, url), null);
}
String response = EntityUtils.toString(httpEntity);
if (!Helpers.statusCodeIsOk(sl.getStatusCode())) {
return Either.ofLeft(mapper.readValue(response, ServiceError.class));
}
return Either.ofRight(mapper.readValue(response, OrderBookDepth.class));
}
} catch (IOException e) {
throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause());
}
}
/**
* Retrieves candlestick data for the supplied symbol and interval
*
* @param symbol The symbol
* @param interval The interval
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public Either<ServiceError, List<Candlestick>> getCandleStickData(String symbol, Interval interval)
throws BinanceServiceUnreachableException {
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
}
String url = String.format(
"%s%s?symbol=%s&interval=%s",
BASE_ENDPOINT,
CANDLESTICK_URL,
symbol,
interval.toString());
return getCandleStickDataFromUrl(url);
}
/**
* Retrieves candlestick data for the supplied symbol and interval
*
* @param symbol The symbol
* @param interval The interval
* @param limit The output limit
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public Either<ServiceError, List<Candlestick>> getCandleStickData(String symbol, Interval interval, int limit)
throws BinanceServiceUnreachableException {
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
}
String url = String.format(
"%s%s?symbol=%s&interval=%s&limit=%d",
BASE_ENDPOINT,
CANDLESTICK_URL,
symbol,
interval.toString(),
limit);
return getCandleStickDataFromUrl(url);
}
/**
* Retrieves candlestick data for the supplied symbol and interval
*
* @param symbol The symbol
* @param interval The interval
* @param time The start/end time
* @param isStartTime Indicates whether the time is a start or end time
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public Either<ServiceError, List<Candlestick>>
getCandleStickData(String symbol, Interval interval, long time, boolean isStartTime)
throws BinanceServiceUnreachableException {
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
}
String url;
if (isStartTime) {
url = String.format(
"%s%s?symbol=%s&interval=%s&startTime=%d",
BASE_ENDPOINT,
CANDLESTICK_URL,
symbol,
interval.toString(),
time);
} else {
url = String.format(
"%s%s?symbol=%s&interval=%s&endTime=%d",
BASE_ENDPOINT,
CANDLESTICK_URL,
symbol,
interval.toString(),
time);
}
return getCandleStickDataFromUrl(url);
}
/**
* Retrieves candlestick data for the supplied symbol and interval
*
* @param symbol The symbol
* @param interval The interval
* @param limit The output limit
* @param time The timeframe
* @param isStartTime indicates whether time is a startTime (true) or endTime (false)
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public Either<ServiceError, List<Candlestick>>
getCandleStickData(String symbol, Interval interval, int limit, long time, boolean isStartTime)
throws BinanceServiceUnreachableException {
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
}
String url;
if (isStartTime) {
url = String.format(
"%s%s?symbol=%s&interval=%s&limit=%d&startTime=%d",
BASE_ENDPOINT,
CANDLESTICK_URL,
symbol,
interval.toString(),
limit,
time);
} else {
url = String.format(
"%s%s?symbol=%s&interval=%s&limit=%d&endTime=%d",
BASE_ENDPOINT,
CANDLESTICK_URL,
symbol,
interval.toString(),
limit,
time);
}
return getCandleStickDataFromUrl(url);
}
/**
* Retrieves candlestick data for the supplied symbol and interval
*
* @param symbol The symbol
* @param interval The interval
* @param limit The output limit
* @param startTime The start timeframe
* @param endTime The end timeframe
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public Either<ServiceError, List<Candlestick>>
getCandleStickData(String symbol, Interval interval, int limit, long startTime, long endTime)
throws BinanceServiceUnreachableException {
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
}
String url;
url = String.format(
"%s%s?symbol=%s&interval=%s&limit=%d&startTime=%d&endTime=%d",
BASE_ENDPOINT,
CANDLESTICK_URL,
symbol,
interval.toString(),
limit,
startTime,
endTime);
return getCandleStickDataFromUrl(url);
}
/**
* Retrieves the current ticker price for the specified symbol
*
* @param symbol The symbol
* @return A TickerPrice if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
*/
public Either<ServiceError, TickerPrice> getTickerPriceForSymbol(String symbol)
throws BinanceServiceUnreachableException {
if (symbol == null) {
throw new IllegalArgumentException("Symbol must not be null");
}
String url = String.format("%s%s?symbol=%s", BASE_ENDPOINT, TICKER_PRICE_URL, symbol);
final HttpGet request = Helpers.getBuilder(url, apiKey);
try {
try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) {
StatusLine sl = closeableHttpResponse.getStatusLine();
HttpEntity httpEntity = closeableHttpResponse.getEntity();
if (httpEntity == null) {
log.error(NO_RESPONSE_TEXT, url);
throw new BinanceServiceUnreachableException(String.format(NO_RESPONSE_TEXT_FORMATTED, url), null);
}
String response = EntityUtils.toString(httpEntity);
if (!Helpers.statusCodeIsOk(sl.getStatusCode())) {
return Either.ofLeft(mapper.readValue(response, ServiceError.class));
}
return Either.ofRight(mapper.readValue(response, TickerPrice.class));
}
} catch (IOException e) {
throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause());
}
}
/**
* Retrieves ticker prices for all supported Binance symbols. This is good to use in terms of cost if you need
* more than one symbol's current ticker price.
*
* @return Either a list of TickerPrice objects if successful, or an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
*/
public Either<ServiceError, List<TickerPrice>> getTickerPrices() throws BinanceServiceUnreachableException {
String url = String.format("%s%s", BASE_ENDPOINT, TICKER_PRICE_URL);
final HttpGet request = Helpers.getBuilder(url, apiKey);
try {
try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) {
StatusLine sl = closeableHttpResponse.getStatusLine();
HttpEntity httpEntity = closeableHttpResponse.getEntity();
if (httpEntity == null) {
log.error(NO_RESPONSE_TEXT, url);
throw new BinanceServiceUnreachableException(String.format(NO_RESPONSE_TEXT_FORMATTED, url), null);
}
String response = EntityUtils.toString(httpEntity);
if (!Helpers.statusCodeIsOk(sl.getStatusCode())) {
return Either.ofLeft(mapper.readValue(response, ServiceError.class));
}
JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, TickerPrice.class);
return Either.ofRight(mapper.readValue(response, type));
}
} catch (IOException e) {
throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause());
}
}
/**
* Gets candlestick data from the provided URL, allowing all of the getCandleStickData functions to act
* as glorified URL builders.
*
* @param url The URL to use
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
*/
private Either<ServiceError, List<Candlestick>>
getCandleStickDataFromUrl(String url) throws BinanceServiceUnreachableException {
final HttpGet request = Helpers.getBuilder(url, apiKey);
try {
try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) {
StatusLine sl = closeableHttpResponse.getStatusLine();
HttpEntity httpEntity = closeableHttpResponse.getEntity();
if (httpEntity == null) {
log.error(NO_RESPONSE_TEXT, url);
throw new BinanceServiceUnreachableException(String.format(NO_RESPONSE_TEXT_FORMATTED, url), null);
}
String response = EntityUtils.toString(httpEntity);
if (!Helpers.statusCodeIsOk(sl.getStatusCode())) {
return Either.ofLeft(mapper.readValue(response, ServiceError.class));
}
JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, Candlestick.class);
return Either.ofRight(mapper.readValue(response, type));
}
} catch (IOException e) {
throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause());
}
}
}