|
|
|
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.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.Helpers.buildGetRequestFromEndpoint;
|
|
|
|
|
|
|
|
@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 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) {
|
|
|
|
log.error(NO_RESPONSE_TEXT, request.getURI().toASCIIString());
|
|
|
|
throw new BinanceServiceUnreachableException(
|
|
|
|
String.format(NO_RESPONSE_TEXT_FORMATTED, request.getURI().toASCIIString()), 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?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 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?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 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?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 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?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 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?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 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 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) {
|
|
|
|
log.error(NO_RESPONSE_TEXT, request.getURI().toASCIIString());
|
|
|
|
throw new BinanceServiceUnreachableException(
|
|
|
|
String.format(NO_RESPONSE_TEXT_FORMATTED, request.getURI().toASCIIString()), 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 {
|
|
|
|
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) {
|
|
|
|
log.error(NO_RESPONSE_TEXT, request.getURI().toASCIIString());
|
|
|
|
throw new BinanceServiceUnreachableException(
|
|
|
|
String.format(NO_RESPONSE_TEXT_FORMATTED, request.getURI().toASCIIString()), 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 endpoint The endpoint to use that has the associated parameters populated
|
|
|
|
* @return A list of candlesticks if successful, otherwise an ServiceError
|
|
|
|
* @throws BinanceServiceUnreachableException If the service is unreachable
|
|
|
|
*/
|
|
|
|
private Either<ServiceError, List<Candlestick>>
|
|
|
|
getCandleStickDataFromUrl(String endpoint) throws BinanceServiceUnreachableException {
|
|
|
|
final HttpGet request = buildGetRequestFromEndpoint(endpoint, apiKey);
|
|
|
|
|
|
|
|
try {
|
|
|
|
try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) {
|
|
|
|
StatusLine sl = closeableHttpResponse.getStatusLine();
|
|
|
|
|
|
|
|
HttpEntity httpEntity = closeableHttpResponse.getEntity();
|
|
|
|
|
|
|
|
if (httpEntity == null) {
|
|
|
|
log.error(NO_RESPONSE_TEXT, request.getURI().toASCIIString());
|
|
|
|
throw new BinanceServiceUnreachableException(
|
|
|
|
String.format(NO_RESPONSE_TEXT_FORMATTED, request.getURI().toASCIIString()), 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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|