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
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()); |
|
} |
|
} |
|
}
|
|
|