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.

438 lines
20 KiB

package com.sigmaflare.binancej;
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.BinanceServiceException;
import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException;
import com.sigmaflare.binancej.exceptions.UnexpectedErrorException;
import lombok.Builder;
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.ArrayList;
import java.util.Arrays;
import java.util.List;
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;
import static com.sigmaflare.binancej.HttpRequests.buildGetRequestFromEndpoint;
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";
private static final ArrayList<Integer> LIMITS = new ArrayList<>(Arrays.asList(5, 10, 20, 50, 100, 500, 1000));
private static final String LIMIT_EXCEPTION_MESSAGE = "Limit must be in [5, 10, 20, 50, 100, 500, 1000]";
@Builder
MarketData(String apiKey, String secretKey) {
super(apiKey, secretKey);
}
MarketData(String apiKey, String secretKey, CloseableHttpClient closeableHttpClient) {
super(apiKey, secretKey, closeableHttpClient);
}
private void checkCandlestickLimit(int limit) {
if(limit < 0 || limit > 500) {
throw new IllegalArgumentException("Limit must be greater than 0 and less than or equal to 500");
}
}
/**
* 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 com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
*/
public OrderBookDepth getOrderBookDepth(String symbol) throws BinanceServiceException {
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
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws UnexpectedErrorException If an unexpected exception occurs
*/
public OrderBookDepth getOrderBookDepth(String symbol, int limit) throws BinanceServiceException {
if(!LIMITS.contains(limit)) {
throw new IllegalArgumentException(LIMIT_EXCEPTION_MESSAGE);
}
String urlWithParams = String.format("%s?symbol=%s&limit=%d", ORDER_BOOK_URL, symbol, limit);
final HttpGet request = buildGetRequestFromEndpoint(urlWithParams, apiKey);
try {
try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) {
StatusLine sl = closeableHttpResponse.getStatusLine();
HttpEntity httpEntity = closeableHttpResponse.getEntity();
if (httpEntity == null) {
throw new BinanceServiceUnreachableException(
String.format(NO_RESPONSE_TEXT_FORMATTED, request.getURI().toASCIIString()), null);
}
String response = EntityUtils.toString(httpEntity);
if (!HttpRequests.statusCodeIsOk(sl.getStatusCode())) {
ServiceError error = mapper.readValue(response, ServiceError.class);
throw HttpRequests.buildHttpException(sl.getStatusCode(), error);
}
return mapper.readValue(response, OrderBookDepth.class);
}
} catch (IOException e) {
throw new UnexpectedErrorException(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
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public List<Candlestick> getCandleStickData(String symbol, Interval interval)
throws BinanceServiceException {
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
}
String url = String.format(
"%s?symbol=%s&interval=%s",
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 com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public List<Candlestick> getCandleStickData(String symbol, Interval interval, int limit)
throws BinanceServiceException {
checkCandlestickLimit(limit);
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
}
String url = String.format(
"%s?symbol=%s&interval=%s&limit=%d",
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
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public List<Candlestick>
getCandleStickData(String symbol, Interval interval, long time, boolean isStartTime)
throws BinanceServiceException {
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
}
String url;
if (isStartTime) {
url = String.format(
"%s?symbol=%s&interval=%s&startTime=%d",
CANDLESTICK_URL,
symbol,
interval.toString(),
time);
} else {
url = String.format(
"%s?symbol=%s&interval=%s&endTime=%d",
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
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public List<Candlestick>
getCandleStickData(String symbol, Interval interval, int limit, long time, boolean isStartTime)
throws BinanceServiceException {
checkCandlestickLimit(limit);
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
}
String url;
if (isStartTime) {
url = String.format(
"%s?symbol=%s&interval=%s&limit=%d&startTime=%d",
CANDLESTICK_URL,
symbol,
interval.toString(),
limit,
time);
} else {
url = String.format(
"%s?symbol=%s&interval=%s&limit=%d&endTime=%d",
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
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public List<Candlestick>
getCandleStickData(String symbol, Interval interval, int limit, long startTime, long endTime)
throws BinanceServiceException {
checkCandlestickLimit(limit);
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
}
String url;
url = String.format(
"%s?symbol=%s&interval=%s&limit=%d&startTime=%d&endTime=%d",
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 populated list of TickerPrice objects
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws UnexpectedErrorException If an unexpected exception occurs
*/
public TickerPrice getTickerPriceForSymbol(String symbol)
throws BinanceServiceException {
if (symbol == null) {
throw new IllegalArgumentException("Symbol must not be null");
}
String urlWithParams = String.format("%s?symbol=%s", TICKER_PRICE_URL, symbol);
final HttpGet request = buildGetRequestFromEndpoint(urlWithParams, apiKey);
try {
try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) {
StatusLine sl = closeableHttpResponse.getStatusLine();
HttpEntity httpEntity = closeableHttpResponse.getEntity();
if (httpEntity == null) {
throw new BinanceServiceUnreachableException(
String.format(NO_RESPONSE_TEXT_FORMATTED, request.getURI().toASCIIString()), null);
}
String response = EntityUtils.toString(httpEntity);
if (!HttpRequests.statusCodeIsOk(sl.getStatusCode())) {
ServiceError error = mapper.readValue(response, ServiceError.class);
throw HttpRequests.buildHttpException(sl.getStatusCode(), error);
}
return mapper.readValue(response, TickerPrice.class);
}
} catch (IOException e) {
throw new UnexpectedErrorException(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 A populated list of TickerPrice objects
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws UnexpectedErrorException If an unexpected exception occurs
*/
public List<TickerPrice> getTickerPrices() throws BinanceServiceException {
final HttpGet request = buildGetRequestFromEndpoint(TICKER_PRICE_URL, apiKey);
try {
try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) {
StatusLine sl = closeableHttpResponse.getStatusLine();
HttpEntity httpEntity = closeableHttpResponse.getEntity();
if (httpEntity == null) {
throw new BinanceServiceUnreachableException(
String.format(NO_RESPONSE_TEXT_FORMATTED, request.getURI().toASCIIString()), null);
}
String response = EntityUtils.toString(httpEntity);
if (!HttpRequests.statusCodeIsOk(sl.getStatusCode())) {
ServiceError error = mapper.readValue(response, ServiceError.class);
throw HttpRequests.buildHttpException(sl.getStatusCode(), error);
}
JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, TickerPrice.class);
return mapper.readValue(response, type);
}
} catch (IOException e) {
throw new UnexpectedErrorException(e.getMessage(), e.getCause());
}
}
/**
* Gets candlestick data from the provided URL, allowing all of the getCandleStickData functions to act
* as glorified URL builders.
*
* @param endpoint The endpoint to use that has the associated parameters populated
* @return A list of candlesticks
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws UnexpectedErrorException If an unexpected exception occurs
*/
private List<Candlestick>
getCandleStickDataFromUrl(String endpoint) throws BinanceServiceException {
final HttpGet request = buildGetRequestFromEndpoint(endpoint, apiKey);
try {
try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) {
StatusLine sl = closeableHttpResponse.getStatusLine();
HttpEntity httpEntity = closeableHttpResponse.getEntity();
if (httpEntity == null) {
throw new BinanceServiceUnreachableException(
String.format(NO_RESPONSE_TEXT_FORMATTED, request.getURI().toASCIIString()), null);
}
String response = EntityUtils.toString(httpEntity);
if (!HttpRequests.statusCodeIsOk(sl.getStatusCode())) {
ServiceError error = mapper.readValue(response, ServiceError.class);
throw HttpRequests.buildHttpException(sl.getStatusCode(), error);
}
JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, Candlestick.class);
return mapper.readValue(response, type);
}
} catch (IOException e) {
throw new UnexpectedErrorException(e.getMessage(), e.getCause());
}
}
}