From 859ca5da664d99633ddb70e53ad39fd103a80d72 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Wed, 8 Apr 2020 20:59:22 +0200 Subject: [PATCH 001/101] master added support for OCO orders and listening to trade stream --- .../api/client/BinanceApiRestClient.java | 17 +++ .../api/client/BinanceApiWebSocketClient.java | 10 ++ .../binance/api/client/domain/OrderType.java | 3 +- .../api/client/domain/account/NewOrder.java | 42 ++++++ .../domain/account/OcoOrderResponse.java | 130 ++++++++++++++++++ .../api/client/domain/account/Order.java | 13 ++ .../api/client/domain/event/TradeEvent.java | 110 +++++++++++++++ .../client/domain/general/SymbolFilter.java | 7 + .../client/impl/BinanceApiRestClientImpl.java | 32 ++++- .../api/client/impl/BinanceApiService.java | 42 ++++++ .../impl/BinanceApiWebSocketClientImpl.java | 10 ++ 11 files changed, 412 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java create mode 100644 src/main/java/com/binance/api/client/domain/event/TradeEvent.java diff --git a/src/main/java/com/binance/api/client/BinanceApiRestClient.java b/src/main/java/com/binance/api/client/BinanceApiRestClient.java index e405d4a7f..ab5dda293 100644 --- a/src/main/java/com/binance/api/client/BinanceApiRestClient.java +++ b/src/main/java/com/binance/api/client/BinanceApiRestClient.java @@ -3,6 +3,7 @@ import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; +import com.binance.api.client.domain.account.OcoOrderResponse; import com.binance.api.client.domain.account.NewOrder; import com.binance.api.client.domain.account.NewOrderResponse; import com.binance.api.client.domain.account.Order; @@ -164,6 +165,14 @@ public interface BinanceApiRestClient { */ NewOrderResponse newOrder(NewOrder order); + /** + * Send in a new OCO order. + * + * @param order the new order to submit. + * @return a response containing details about the newly placed order. + */ + OcoOrderResponse newOcoOrder(NewOrder order); + /** * Test new order creation and signature/recvWindow long. Creates and validates a new order but does not send it into the matching engine. * @@ -186,6 +195,13 @@ public interface BinanceApiRestClient { */ CancelOrderResponse cancelOrder(CancelOrderRequest cancelOrderRequest); + /** + * Cancel an active OCO order. Both legs will be cancelled. + * @param cancelOrderRequest order request parameters + * @return order execution status + */ + OcoOrderResponse cancelOcoOrder(CancelOrderRequest cancelOrderRequest); + /** * Get all open orders on a symbol. * @@ -295,4 +311,5 @@ public interface BinanceApiRestClient { * @param listenKey listen key that identifies a data stream */ void closeUserDataStream(String listenKey); + } diff --git a/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java b/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java index 7961efc49..be6c372cf 100644 --- a/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java +++ b/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java @@ -4,6 +4,7 @@ import com.binance.api.client.domain.event.AllMarketTickersEvent; import com.binance.api.client.domain.event.CandlestickEvent; import com.binance.api.client.domain.event.DepthEvent; +import com.binance.api.client.domain.event.TradeEvent; import com.binance.api.client.domain.event.UserDataUpdateEvent; import com.binance.api.client.domain.market.CandlestickInterval; @@ -43,6 +44,15 @@ public interface BinanceApiWebSocketClient extends Closeable { */ Closeable onAggTradeEvent(String symbols, BinanceApiCallback callback); + /** + * Open a new web socket to receive {@link AggTradeEvent aggTradeEvents} on a callback. + * + * @param symbols market (one or coma-separated) symbol(s) to subscribe to + * @param callback the callback to call on new events + * @return a {@link Closeable} that allows the underlying web socket to be closed. + */ + Closeable onTradeEvent(String symbols, BinanceApiCallback callback); + /** * Open a new web socket to receive {@link UserDataUpdateEvent userDataUpdateEvents} on a callback. * diff --git a/src/main/java/com/binance/api/client/domain/OrderType.java b/src/main/java/com/binance/api/client/domain/OrderType.java index 11d484e5e..38eff6313 100644 --- a/src/main/java/com/binance/api/client/domain/OrderType.java +++ b/src/main/java/com/binance/api/client/domain/OrderType.java @@ -13,5 +13,6 @@ public enum OrderType { STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, - LIMIT_MAKER + LIMIT_MAKER, + OCO } diff --git a/src/main/java/com/binance/api/client/domain/account/NewOrder.java b/src/main/java/com/binance/api/client/domain/account/NewOrder.java index 221c07d36..6248d202b 100644 --- a/src/main/java/com/binance/api/client/domain/account/NewOrder.java +++ b/src/main/java/com/binance/api/client/domain/account/NewOrder.java @@ -52,6 +52,21 @@ public class NewOrder { * Used with stop orders. */ private String stopPrice; + + /** + * Used with OCO orders. + */ + private String stopLimitPrice; + + /** + * Optional client id for limit leg in OCO order + */ + private String limitClientOrderId; + + /** + * Optional client id for stop leg in OCO order + */ + private String stopClientOrderId; /** * Used with iceberg orders. @@ -121,6 +136,15 @@ public NewOrder type(OrderType type) { this.type = type; return this; } + + public NewOrder stopLimitPrice(String price) { + this.stopLimitPrice = price; + return this; + } + + public String getStopLimitPrice() { + return this.stopLimitPrice; + } public TimeInForce getTimeInForce() { return timeInForce; @@ -157,6 +181,24 @@ public NewOrder newClientOrderId(String newClientOrderId) { this.newClientOrderId = newClientOrderId; return this; } + + public String getLimitClientOrderId() { + return limitClientOrderId; + } + + public NewOrder limitClientOrderId(String limitClientOrderId) { + this.limitClientOrderId = limitClientOrderId; + return this; + } + + public String getStopClientOrderId() { + return stopClientOrderId; + } + + public NewOrder stopClientOrderId(String stopClientOrderId) { + this.stopClientOrderId = stopClientOrderId; + return this; + } public String getStopPrice() { return stopPrice; diff --git a/src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java b/src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java new file mode 100644 index 000000000..82bc30092 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java @@ -0,0 +1,130 @@ +package com.binance.api.client.domain.account; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * Response returned when a new OCO order is placed. + * + * @author sergey + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class OcoOrderResponse { + + long orderListId; + String contingencyType; + String listStatusType; + String listOrderStatus; + String listClientOrderId; + long transactionTime; + String symbol; + List orders; + List orderReports; + + public static class OcoLeg { + + String symbol; + long orderId; + String clientOrderId; + + public String getSymbol() { + return symbol; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + + public long getOrderId() { + return orderId; + } + + public void setOrderId(long orderId) { + this.orderId = orderId; + } + + public String getClientOrderId() { + return clientOrderId; + } + + public void setClientOrderId(String clientOrderId) { + this.clientOrderId = clientOrderId; + } + + } + + public long getOrderListId() { + return orderListId; + } + + public void setOrderListId(long orderListId) { + this.orderListId = orderListId; + } + + public String getContingencyType() { + return contingencyType; + } + + public void setContingencyType(String contingencyType) { + this.contingencyType = contingencyType; + } + + public String getListStatusType() { + return listStatusType; + } + + public void setListStatusType(String listStatusType) { + this.listStatusType = listStatusType; + } + + public String getListOrderStatus() { + return listOrderStatus; + } + + public void setListOrderStatus(String listOrderStatus) { + this.listOrderStatus = listOrderStatus; + } + + public String getListClientOrderId() { + return listClientOrderId; + } + + public void setListClientOrderId(String listClientOrderId) { + this.listClientOrderId = listClientOrderId; + } + + public long getTransactionTime() { + return transactionTime; + } + + public void setTransactionTime(long transactionTime) { + this.transactionTime = transactionTime; + } + + public String getSymbol() { + return symbol; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + + public List getOrders() { + return orders; + } + + public void setOrders(List orders) { + this.orders = orders; + } + + public List getOrderReports() { + return orderReports; + } + + public void setOrderReports(List orderReports) { + this.orderReports = orderReports; + } + +} diff --git a/src/main/java/com/binance/api/client/domain/account/Order.java b/src/main/java/com/binance/api/client/domain/account/Order.java index 88beb54d7..1551ca518 100644 --- a/src/main/java/com/binance/api/client/domain/account/Order.java +++ b/src/main/java/com/binance/api/client/domain/account/Order.java @@ -28,6 +28,11 @@ public class Order { * Client order id. */ private String clientOrderId; + + /** + * Collective order id to which this order belongs (for OCO order legs) + */ + private String orderListId; /** * Price. @@ -196,6 +201,14 @@ public void setCummulativeQuoteQty(String cummulativeQuoteQty) { this.cummulativeQuoteQty = cummulativeQuoteQty; } + public String getOrderListId() { + return orderListId; + } + + public void setOrderListId(String orderListId) { + this.orderListId = orderListId; + } + @Override public String toString() { return new ToStringBuilder(this, BinanceApiConstants.TO_STRING_BUILDER_STYLE) diff --git a/src/main/java/com/binance/api/client/domain/event/TradeEvent.java b/src/main/java/com/binance/api/client/domain/event/TradeEvent.java new file mode 100644 index 000000000..fc7638166 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/event/TradeEvent.java @@ -0,0 +1,110 @@ +package com.binance.api.client.domain.event; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class TradeEvent { + + @JsonProperty("e") + String eventType; + @JsonProperty("s") + String symbol; + @JsonProperty("t") + String tradeId; + @JsonProperty("p") + String price; + @JsonProperty("q") + String quantity; + @JsonProperty("b") + String buyerOrderId; + @JsonProperty("a") + String sellerOrderId; + @JsonProperty("T") + long tradeTime; + + /** + * true is red (a sell) and false is green. + */ + @JsonProperty("m") + boolean marketMaker; + + public String getEventType() { + return eventType; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + public String getSymbol() { + return symbol; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + + public String getTradeId() { + return tradeId; + } + + public void setTradeId(String tradeId) { + this.tradeId = tradeId; + } + + public String getPrice() { + return price; + } + + public void setPrice(String price) { + this.price = price; + } + + public String getQuantity() { + return quantity; + } + + public void setQuantity(String quantity) { + this.quantity = quantity; + } + + public String getBuyerOrderId() { + return buyerOrderId; + } + + public void setBuyerOrderId(String buyerOrderId) { + this.buyerOrderId = buyerOrderId; + } + + public String getSellerOrderId() { + return sellerOrderId; + } + + public void setSellerOrderId(String sellerOrderId) { + this.sellerOrderId = sellerOrderId; + } + + public long getTradeTime() { + return tradeTime; + } + + public void setTradeTime(long tradeTime) { + this.tradeTime = tradeTime; + } + + public boolean isMarketMaker() { + return marketMaker; + } + + public void setMarketMaker(boolean marketMaker) { + this.marketMaker = marketMaker; + } + + @Override + public String toString() { + return "TradeEvent [symbol=" + symbol + ", price=" + price + ", quantity=" + quantity + ", tradeTime=" + tradeTime + ", marketMaker=" + + marketMaker + "]"; + } + +} diff --git a/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java b/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java index 068e0ba28..d226d3cd9 100644 --- a/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java +++ b/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java @@ -157,4 +157,11 @@ public String getLimit() { public void setLimit(String limit) { this.limit = limit; } + + @Override + public String toString() { + return "SymbolFilter [filterType=" + filterType + ", minPrice=" + minPrice + ", maxPrice=" + maxPrice + ", tickSize=" + tickSize + + ", minQty=" + minQty + ", maxQty=" + maxQty + ", stepSize=" + stepSize + ", minNotional=" + minNotional + + ", maxNumAlgoOrders=" + maxNumAlgoOrders + ", limit=" + limit + "]"; + } } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java index aceb3799a..009208517 100644 --- a/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java @@ -2,9 +2,12 @@ import com.binance.api.client.BinanceApiRestClient; import com.binance.api.client.constant.BinanceApiConstants; +import com.binance.api.client.domain.OrderType; +import com.binance.api.client.domain.TimeInForce; import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; +import com.binance.api.client.domain.account.OcoOrderResponse; import com.binance.api.client.domain.account.NewOrder; import com.binance.api.client.domain.account.NewOrderResponse; import com.binance.api.client.domain.account.Order; @@ -129,16 +132,32 @@ public List getBookTickers() { @Override public NewOrderResponse newOrder(NewOrder order) { + if (order.getType() == OrderType.OCO) { + throw new IllegalArgumentException("Please use newOcoOrder instead"); + } return executeSync(binanceApiService.newOrder(order.getSymbol(), order.getSide(), order.getType(), order.getTimeInForce(), order.getQuantity(), order.getPrice(), order.getNewClientOrderId(), order.getStopPrice(), order.getIcebergQty(), order.getNewOrderRespType(), order.getRecvWindow(), order.getTimestamp())); } + + @Override + public OcoOrderResponse newOcoOrder(NewOrder order) { + return executeSync(binanceApiService.newOcoOrder(order.getSymbol(), order.getNewClientOrderId(), order.getSide(), order.getQuantity(), order.getLimitClientOrderId(), + order.getPrice(), order.getIcebergQty(), order.getStopClientOrderId(), order.getStopPrice(), order.getStopLimitPrice(), TimeInForce.GTC, + order.getNewOrderRespType(), order.getRecvWindow(), order.getTimestamp())); + } @Override public void newOrderTest(NewOrder order) { - executeSync(binanceApiService.newOrderTest(order.getSymbol(), order.getSide(), order.getType(), - order.getTimeInForce(), order.getQuantity(), order.getPrice(), order.getNewClientOrderId(), order.getStopPrice(), - order.getIcebergQty(), order.getNewOrderRespType(), order.getRecvWindow(), order.getTimestamp())); + if (order.getType() == OrderType.OCO) { + executeSync(binanceApiService.newOcoOrderTest(order.getSymbol(), order.getNewClientOrderId(), order.getSide(), order.getQuantity(), order.getLimitClientOrderId(), + order.getPrice(), order.getIcebergQty(), order.getStopClientOrderId(), order.getStopPrice(), order.getStopLimitPrice(), TimeInForce.GTC, + order.getNewOrderRespType(), order.getRecvWindow(), order.getTimestamp())); + } else { + executeSync(binanceApiService.newOrderTest(order.getSymbol(), order.getSide(), order.getType(), + order.getTimeInForce(), order.getQuantity(), order.getPrice(), order.getNewClientOrderId(), order.getStopPrice(), + order.getIcebergQty(), order.getNewOrderRespType(), order.getRecvWindow(), order.getTimestamp())); + } } // Account endpoints @@ -157,6 +176,13 @@ public CancelOrderResponse cancelOrder(CancelOrderRequest cancelOrderRequest) { cancelOrderRequest.getRecvWindow(), cancelOrderRequest.getTimestamp())); } + @Override + public OcoOrderResponse cancelOcoOrder(CancelOrderRequest cancelOrderRequest) { + return executeSync(binanceApiService.cancelOcoOrder(cancelOrderRequest.getSymbol(), + cancelOrderRequest.getOrderId(), cancelOrderRequest.getOrigClientOrderId(), cancelOrderRequest.getNewClientOrderId(), + cancelOrderRequest.getRecvWindow(), cancelOrderRequest.getTimestamp())); + } + @Override public List getOpenOrders(OrderRequest orderRequest) { return executeSync(binanceApiService.getOpenOrders(orderRequest.getSymbol(), orderRequest.getRecvWindow(), orderRequest.getTimestamp())); diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiService.java b/src/main/java/com/binance/api/client/impl/BinanceApiService.java index 9643c8e90..2533e9b66 100644 --- a/src/main/java/com/binance/api/client/impl/BinanceApiService.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiService.java @@ -7,6 +7,7 @@ import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; +import com.binance.api.client.domain.account.OcoOrderResponse; import com.binance.api.client.domain.account.NewOrderResponse; import com.binance.api.client.domain.account.NewOrderResponseType; import com.binance.api.client.domain.account.Order; @@ -100,6 +101,7 @@ Call newOrder(@Query("symbol") String symbol, @Query("side") O @Query("icebergQty") String icebergQty, @Query("newOrderRespType") NewOrderResponseType newOrderRespType, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) @POST("/api/v3/order/test") Call newOrderTest(@Query("symbol") String symbol, @Query("side") OrderSide side, @Query("type") OrderType type, @@ -108,6 +110,40 @@ Call newOrderTest(@Query("symbol") String symbol, @Query("side") OrderSide @Query("icebergQty") String icebergQty, @Query("newOrderRespType") NewOrderResponseType newOrderRespType, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) + @POST("/api/v3/order/oco") + Call newOcoOrder(@Query("symbol") String symbol, + @Query("listClientOrderId") String listClientOrderId, + @Query("side") OrderSide side, + @Query("quantity") String quantity, + @Query("limitClientOrderId") String limitClientOrderId, + @Query("price") String price, + @Query("limitIcebergQty") String icebergQty, + @Query("stopClientOrderId") String stopClientOrderId, + @Query("stopPrice") String stopPrice, + @Query("stopLimitPrice") String stopLimitPrice, + @Query("stopLimitTimeInForce") TimeInForce stopLimitTimeInForce, + @Query("newOrderRespType") NewOrderResponseType newOrderRespType, + @Query("recvWindow") Long recvWindow, + @Query("timestamp") Long timestamp); + + @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) + @POST("/api/v3/order/oco/test") + Call newOcoOrderTest(@Query("symbol") String symbol, + @Query("listClientOrderId") String listClientOrderId, + @Query("side") OrderSide side, + @Query("quantity") String quantity, + @Query("limitClientOrderId") String limitClientOrderId, + @Query("price") String price, + @Query("limitIcebergQty") String icebergQty, + @Query("stopClientOrderId") String stopClientOrderId, + @Query("stopPrice") String stopPrice, + @Query("stopLimitPrice") String stopLimitPrice, + @Query("stopLimitTimeInForce") TimeInForce stopLimitTimeInForce, + @Query("newOrderRespType") NewOrderResponseType newOrderRespType, + @Query("recvWindow") Long recvWindow, + @Query("timestamp") Long timestamp); + @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) @GET("/api/v3/order") Call getOrderStatus(@Query("symbol") String symbol, @Query("orderId") Long orderId, @@ -120,6 +156,12 @@ Call cancelOrder(@Query("symbol") String symbol, @Query("or @Query("origClientOrderId") String origClientOrderId, @Query("newClientOrderId") String newClientOrderId, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) + @DELETE("/api/v3/orderList") + Call cancelOcoOrder(@Query("symbol") String symbol, @Query("orderListId") Long orderId, + @Query("listClientOrderId") String origClientOrderId, @Query("newClientOrderId") String newClientOrderId, + @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) @GET("/api/v3/openOrders") Call> getOpenOrders(@Query("symbol") String symbol, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java index b9e530b0a..7db17e0ce 100644 --- a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java @@ -7,6 +7,7 @@ import com.binance.api.client.domain.event.AllMarketTickersEvent; import com.binance.api.client.domain.event.CandlestickEvent; import com.binance.api.client.domain.event.DepthEvent; +import com.binance.api.client.domain.event.TradeEvent; import com.binance.api.client.domain.event.UserDataUpdateEvent; import com.binance.api.client.domain.market.CandlestickInterval; import com.fasterxml.jackson.core.type.TypeReference; @@ -56,6 +57,15 @@ public Closeable onAggTradeEvent(String symbols, BinanceApiCallback(callback, AggTradeEvent.class)); } + + @Override + public Closeable onTradeEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")) + .map(String::trim) + .map(s -> String.format("%s@trade", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, TradeEvent.class)); + } public Closeable onUserDataUpdateEvent(String listenKey, BinanceApiCallback callback) { return createNewWebSocket(listenKey, new BinanceApiWebSocketListener<>(callback, UserDataUpdateEvent.class)); From 8884b7d5ff2ec0f2d5d66946e37c823eb5ae7d5e Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Wed, 22 Apr 2020 08:39:06 +0200 Subject: [PATCH 002/101] master added user data updates for OCO orders, created factory methods for oco buy and sell orders, added an example for OCO trades in the readme --- README.md | 16 ++ .../api/client/BinanceApiWebSocketClient.java | 9 +- .../api/client/domain/account/NewOrder.java | 67 ++++++-- .../api/client/domain/account/OcoLeg.java | 43 ++++++ .../domain/account/OcoOrderResponse.java | 50 +----- .../domain/event/OcoTradeUpdateEvent.java | 143 ++++++++++++++++++ .../domain/event/UserDataUpdateEvent.java | 23 ++- .../UserDataUpdateEventDeserializer.java | 29 ++-- .../impl/BinanceApiWebSocketClientImpl.java | 17 ++- 9 files changed, 318 insertions(+), 79 deletions(-) create mode 100644 src/main/java/com/binance/api/client/domain/account/OcoLeg.java create mode 100644 src/main/java/com/binance/api/client/domain/event/OcoTradeUpdateEvent.java diff --git a/README.md b/README.md index bf74d5d02..806d4ea80 100644 --- a/README.md +++ b/README.md @@ -519,5 +519,21 @@ Invalid symbol ``` +### OCO trades + +This API wrapper also supports placing OCO (one cancels the other) trade orders. Look for ``oco`` in the API interfaces for creating, canceling and watching for updates to the OCO orders. + +#### Example of an OCO buy order + +```java +NewOrder buy = NewOrder.ocoBuy("XRPBTC", "5", "0.00002675", "0.00002800", "0.00002840"); +/* + * Not required but easier to use than auto-generated binance ids. You can use + * this id to both cancel the order and get its status. + */ +buy.newClientOrderId(UUID.randomUUID().toString()); +OcoOrderResponse order = client.newOcoOrder(buy); +``` + ### More examples An extensive set of examples, covering most aspects of the API, can be found at https://github.com/joaopsilva/binance-java-api/tree/master/src/test/java/com/binance/api/examples. diff --git a/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java b/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java index bcb161d27..020ad5c57 100644 --- a/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java +++ b/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java @@ -1,5 +1,8 @@ package com.binance.api.client; +import java.io.Closeable; +import java.util.List; + import com.binance.api.client.domain.event.AggTradeEvent; import com.binance.api.client.domain.event.AllMarketTickersEvent; import com.binance.api.client.domain.event.CandlestickEvent; @@ -8,9 +11,6 @@ import com.binance.api.client.domain.event.UserDataUpdateEvent; import com.binance.api.client.domain.market.CandlestickInterval; -import java.io.Closeable; -import java.util.List; - /** * Binance API data streaming facade, supporting streaming of events through web sockets. */ @@ -45,7 +45,7 @@ public interface BinanceApiWebSocketClient extends Closeable { Closeable onAggTradeEvent(String symbols, BinanceApiCallback callback); /** - * Open a new web socket to receive {@link AggTradeEvent aggTradeEvents} on a callback. + * Open a new web socket to receive {@link TradeEvent tradeEvents} on a callback. * * @param symbols market (one or coma-separated) symbol(s) to subscribe to * @param callback the callback to call on new events @@ -73,6 +73,7 @@ public interface BinanceApiWebSocketClient extends Closeable { /** * @deprecated This method is no longer functional. Please use the returned {@link Closeable} from any of the other methods to close the web socket. */ + @Override @Deprecated void close(); } diff --git a/src/main/java/com/binance/api/client/domain/account/NewOrder.java b/src/main/java/com/binance/api/client/domain/account/NewOrder.java index 9ac6cbe21..a081f8b1f 100644 --- a/src/main/java/com/binance/api/client/domain/account/NewOrder.java +++ b/src/main/java/com/binance/api/client/domain/account/NewOrder.java @@ -1,11 +1,12 @@ package com.binance.api.client.domain.account; +import org.apache.commons.lang3.builder.ToStringBuilder; + import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.domain.OrderSide; import com.binance.api.client.domain.OrderType; import com.binance.api.client.domain.TimeInForce; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import org.apache.commons.lang3.builder.ToStringBuilder; /** * A trade order to enter or exit a position. @@ -57,17 +58,17 @@ public class NewOrder { * Used with stop orders. */ private String stopPrice; - + /** * Used with OCO orders. */ private String stopLimitPrice; - + /** * Optional client id for limit leg in OCO order */ private String limitClientOrderId; - + /** * Optional client id for stop leg in OCO order */ @@ -141,12 +142,12 @@ public NewOrder type(OrderType type) { this.type = type; return this; } - + public NewOrder stopLimitPrice(String price) { this.stopLimitPrice = price; return this; } - + public String getStopLimitPrice() { return this.stopLimitPrice; } @@ -195,20 +196,20 @@ public NewOrder newClientOrderId(String newClientOrderId) { this.newClientOrderId = newClientOrderId; return this; } - + public String getLimitClientOrderId() { return limitClientOrderId; } - + public NewOrder limitClientOrderId(String limitClientOrderId) { this.limitClientOrderId = limitClientOrderId; return this; } - + public String getStopClientOrderId() { return stopClientOrderId; } - + public NewOrder stopClientOrderId(String stopClientOrderId) { this.stopClientOrderId = stopClientOrderId; return this; @@ -286,6 +287,52 @@ public static NewOrder limitBuy(String symbol, TimeInForce timeInForce, String q return new NewOrder(symbol, OrderSide.BUY, OrderType.LIMIT, timeInForce, quantity, price); } + /** + * Creates a One-Cancels-Other buy order. Note that when the stopPrice is + * reached the exchange will execute a market buy up to the stopLimitPrice but + * you will still be considered the market maker. + * + * @param symbol the coin you want to buy + * @param quantity amount to buy + * @param price the limit order price (should be lower than the + * stopPrice and current price) + * @param stopPrice higher price that triggers buying order at the + * stopLimitPrice + * @param stopLimitPrice the limit order price that will be put to the order + * book when the stopPrice is reached. + * @return the new order + */ + public static NewOrder ocoBuy(String symbol, String quantity, String price, String stopPrice, String stopLimitPrice) { + NewOrder buy = limitBuy(symbol, TimeInForce.GTC, quantity, price); + buy.type(OrderType.OCO); + buy.stopPrice(stopPrice); + buy.stopLimitPrice(stopLimitPrice); + return buy; + } + + /** + * Creates a One-Cancels-Other sell order. Note that when the stopPrice is + * reached the exchange will execute a market sell at the stopLimitPrice but + * you will still be considered the market maker. + * + * @param symbol the coin you want to sell + * @param quantity amount to sell + * @param price the limit order price (should be higher than the + * stopPrice and current price) + * @param stopPrice lower price that triggers buying order at the + * stopLimitPrice + * @param stopLimitPrice the limit order price that will be put to the order + * book when the stopPrice is reached. + * @return the new order + */ + public static NewOrder ocoSell(String symbol, String quantity, String price, String stopPrice, String stopLimitPrice) { + NewOrder sell = limitSell(symbol, TimeInForce.GTC, quantity, price); + sell.type(OrderType.OCO); + sell.stopPrice(stopPrice); + sell.stopLimitPrice(stopLimitPrice); + return sell; + } + /** * Places a LIMIT sell order for the given quantity and price. * diff --git a/src/main/java/com/binance/api/client/domain/account/OcoLeg.java b/src/main/java/com/binance/api/client/domain/account/OcoLeg.java new file mode 100644 index 000000000..54709d42a --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/OcoLeg.java @@ -0,0 +1,43 @@ +package com.binance.api.client.domain.account; + +import com.fasterxml.jackson.annotation.JsonAlias; + +public class OcoLeg { + + @JsonAlias({"s", "symbol"}) + String symbol; + @JsonAlias({"i", "orderId"}) + long orderId; + @JsonAlias({"c", "clientOrderId"}) + String clientOrderId; + + public String getSymbol() { + return symbol; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + + public long getOrderId() { + return orderId; + } + + public void setOrderId(long orderId) { + this.orderId = orderId; + } + + public String getClientOrderId() { + return clientOrderId; + } + + public void setClientOrderId(String clientOrderId) { + this.clientOrderId = clientOrderId; + } + + @Override + public String toString() { + return "OcoLeg [symbol=" + symbol + ", orderId=" + orderId + ", clientOrderId=" + clientOrderId + "]"; + } + +} \ No newline at end of file diff --git a/src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java b/src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java index 82bc30092..30f62314f 100644 --- a/src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java +++ b/src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java @@ -1,12 +1,10 @@ package com.binance.api.client.domain.account; -import java.util.List; - import com.fasterxml.jackson.annotation.JsonIgnoreProperties; /** * Response returned when a new OCO order is placed. - * + * * @author sergey * */ @@ -20,40 +18,8 @@ public class OcoOrderResponse { String listClientOrderId; long transactionTime; String symbol; - List orders; - List orderReports; - - public static class OcoLeg { - - String symbol; - long orderId; - String clientOrderId; - - public String getSymbol() { - return symbol; - } - - public void setSymbol(String symbol) { - this.symbol = symbol; - } - - public long getOrderId() { - return orderId; - } - - public void setOrderId(long orderId) { - this.orderId = orderId; - } - - public String getClientOrderId() { - return clientOrderId; - } - - public void setClientOrderId(String clientOrderId) { - this.clientOrderId = clientOrderId; - } - - } + OcoLeg[] orders; + Order[] orderReports; public long getOrderListId() { return orderListId; @@ -111,20 +77,20 @@ public void setSymbol(String symbol) { this.symbol = symbol; } - public List getOrders() { + public OcoLeg[] getOrders() { return orders; } - public void setOrders(List orders) { + public void setOrders(OcoLeg[] orders) { this.orders = orders; } - public List getOrderReports() { + public Order[] getOrderReports() { return orderReports; } - public void setOrderReports(List orderReports) { + public void setOrderReports(Order[] orderReports) { this.orderReports = orderReports; } - + } diff --git a/src/main/java/com/binance/api/client/domain/event/OcoTradeUpdateEvent.java b/src/main/java/com/binance/api/client/domain/event/OcoTradeUpdateEvent.java new file mode 100644 index 000000000..30ee73bcb --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/event/OcoTradeUpdateEvent.java @@ -0,0 +1,143 @@ +package com.binance.api.client.domain.event; + +import java.util.Arrays; + +import com.binance.api.client.domain.OrderType; +import com.binance.api.client.domain.account.OcoLeg; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class OcoTradeUpdateEvent { + + @JsonProperty("e")// "listStatus" + private String eventType; + + @JsonProperty("E") + private long eventTime; + + @JsonProperty("s") + private String symbol; + + @JsonProperty("g") + private int orderListId; + + @JsonProperty("c") + private OrderType contingencyType; + + @JsonProperty("l") + private String listStatusType; + + @JsonProperty("L") + private String listOrderStatus; + + @JsonProperty("r") + private String listRejectReason; + + @JsonProperty("C") + private String listClientOrderId; + + @JsonProperty("T") + private long transactionTime; + + @JsonProperty("O") + private OcoLeg[] legs; + + public String getEventType() { + return eventType; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + public long getEventTime() { + return eventTime; + } + + public void setEventTime(long eventTime) { + this.eventTime = eventTime; + } + + public String getSymbol() { + return symbol; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + + public int getOrderListId() { + return orderListId; + } + + public void setOrderListId(int orderListId) { + this.orderListId = orderListId; + } + + public OrderType getContingencyType() { + return contingencyType; + } + + public void setContingencyType(OrderType contingencyType) { + this.contingencyType = contingencyType; + } + + public String getListStatusType() { + return listStatusType; + } + + public void setListStatusType(String listStatusType) { + this.listStatusType = listStatusType; + } + + public String getListOrderStatus() { + return listOrderStatus; + } + + public void setListOrderStatus(String listOrderStatus) { + this.listOrderStatus = listOrderStatus; + } + + public String getListRejectReason() { + return listRejectReason; + } + + public void setListRejectReason(String listRejectReason) { + this.listRejectReason = listRejectReason; + } + + public String getListClientOrderId() { + return listClientOrderId; + } + + public void setListClientOrderId(String listClientOrderId) { + this.listClientOrderId = listClientOrderId; + } + + public long getTransactionTime() { + return transactionTime; + } + + public void setTransactionTime(long transactionTime) { + this.transactionTime = transactionTime; + } + + public OcoLeg[] getLegs() { + return legs; + } + + public void setLegs(OcoLeg[] legs) { + this.legs = legs; + } + + @Override + public String toString() { + return "OcoTradeUpdateEvent [eventType=" + eventType + ", eventTime=" + eventTime + ", symbol=" + symbol + ", orderListId=" + + orderListId + ", contingencyType=" + contingencyType + ", listStatusType=" + listStatusType + ", listOrderStatus=" + + listOrderStatus + ", listRejectReason=" + listRejectReason + ", listClientOrderId=" + listClientOrderId + ", transactionTime=" + + transactionTime + ", legs=" + Arrays.toString(legs) + "]"; + } + + +} diff --git a/src/main/java/com/binance/api/client/domain/event/UserDataUpdateEvent.java b/src/main/java/com/binance/api/client/domain/event/UserDataUpdateEvent.java index d95662df1..81ef0d75a 100644 --- a/src/main/java/com/binance/api/client/domain/event/UserDataUpdateEvent.java +++ b/src/main/java/com/binance/api/client/domain/event/UserDataUpdateEvent.java @@ -1,18 +1,20 @@ package com.binance.api.client.domain.event; +import org.apache.commons.lang3.builder.ToStringBuilder; + import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.exception.UnsupportedEventException; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import org.apache.commons.lang3.builder.ToStringBuilder; /** - * User data update event which can be of four types: + * User data update event which can be of five types: *

* 1) outboundAccountInfo, whenever there is a change in the account (e.g. balance of an asset) * 2) outboundAccountPosition, the change in account balances caused by an event. * 3) executionReport, whenever there is a trade or an order * 4) balanceUpdate, the change in account balance (delta). + * 5) ocoTradeUpdate, the change in OCO trade order *

* Deserialization could fail with UnsupportedEventException in case of unsupported eventType. */ @@ -30,6 +32,8 @@ public class UserDataUpdateEvent { private OrderTradeUpdateEvent orderTradeUpdateEvent; + private OcoTradeUpdateEvent ocoTradeUpdateEvent; + public UserDataUpdateEventType getEventType() { return eventType; } @@ -70,6 +74,14 @@ public void setOrderTradeUpdateEvent(OrderTradeUpdateEvent orderTradeUpdateEvent this.orderTradeUpdateEvent = orderTradeUpdateEvent; } + public OcoTradeUpdateEvent getOcoTradeUpdateEvent() { + return ocoTradeUpdateEvent; + } + + public void setOcoTradeUpdateEvent(OcoTradeUpdateEvent ocoTradeUpdateEvent) { + this.ocoTradeUpdateEvent = ocoTradeUpdateEvent; + } + @Override public String toString() { ToStringBuilder sb = new ToStringBuilder(this, BinanceApiConstants.TO_STRING_BUILDER_STYLE) @@ -81,8 +93,10 @@ public String toString() { sb.append("accountPositionUpdateEvent", accountUpdateEvent); } else if (eventType == UserDataUpdateEventType.BALANCE_UPDATE) { sb.append("balanceUpdateEvent", balanceUpdateEvent); - } else { + } else if (eventType == UserDataUpdateEventType.ORDER_TRADE_UPDATE) { sb.append("orderTradeUpdateEvent", orderTradeUpdateEvent); + } else { + sb.append("ocoTradeUpdateEvent", ocoTradeUpdateEvent); } return sb.toString(); } @@ -92,6 +106,7 @@ public enum UserDataUpdateEventType { ACCOUNT_POSITION_UPDATE("outboundAccountPosition"), BALANCE_UPDATE("balanceUpdate"), ORDER_TRADE_UPDATE("executionReport"), + OCO_TRADE_UPDATE("listStatus") ; private final String eventTypeId; @@ -113,6 +128,8 @@ public static UserDataUpdateEventType fromEventTypeId(String eventTypeId) { return ACCOUNT_POSITION_UPDATE; } else if (BALANCE_UPDATE.eventTypeId.equals(eventTypeId)) { return BALANCE_UPDATE; + } else if (OCO_TRADE_UPDATE.eventTypeId.equals(eventTypeId)) { + return OCO_TRADE_UPDATE; } throw new UnsupportedEventException("Unrecognized user data update event type id: " + eventTypeId); } diff --git a/src/main/java/com/binance/api/client/domain/event/UserDataUpdateEventDeserializer.java b/src/main/java/com/binance/api/client/domain/event/UserDataUpdateEventDeserializer.java index 8888bb624..3251fc2e1 100644 --- a/src/main/java/com/binance/api/client/domain/event/UserDataUpdateEventDeserializer.java +++ b/src/main/java/com/binance/api/client/domain/event/UserDataUpdateEventDeserializer.java @@ -1,5 +1,7 @@ package com.binance.api.client.domain.event; +import java.io.IOException; + import com.binance.api.client.domain.event.UserDataUpdateEvent.UserDataUpdateEventType; import com.binance.api.client.exception.BinanceApiException; import com.fasterxml.jackson.core.JsonParser; @@ -9,8 +11,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; - /** * Custom deserializer for a User Data stream event, since the API can return four different responses in this stream. * @@ -38,17 +38,20 @@ public UserDataUpdateEvent deserialize(JsonParser jp, DeserializationContext ctx UserDataUpdateEvent userDataUpdateEvent = new UserDataUpdateEvent(); userDataUpdateEvent.setEventType(userDataUpdateEventType); userDataUpdateEvent.setEventTime(eventTime); - - if (userDataUpdateEventType == UserDataUpdateEventType.ACCOUNT_UPDATE || - userDataUpdateEventType == UserDataUpdateEventType.ACCOUNT_POSITION_UPDATE) { - AccountUpdateEvent accountUpdateEvent = getUserDataUpdateEventDetail(json, AccountUpdateEvent.class, mapper); - userDataUpdateEvent.setAccountUpdateEvent(accountUpdateEvent); - } else if (userDataUpdateEventType == UserDataUpdateEventType.BALANCE_UPDATE) { - BalanceUpdateEvent balanceUpdateEvent = getUserDataUpdateEventDetail(json, BalanceUpdateEvent.class, mapper); - userDataUpdateEvent.setBalanceUpdateEvent(balanceUpdateEvent); - } else { // userDataUpdateEventType == UserDataUpdateEventType.ORDER_TRADE_UPDATE - OrderTradeUpdateEvent orderTradeUpdateEvent = getUserDataUpdateEventDetail(json, OrderTradeUpdateEvent.class, mapper); - userDataUpdateEvent.setOrderTradeUpdateEvent(orderTradeUpdateEvent); + switch (userDataUpdateEventType) { + case ACCOUNT_UPDATE: + case ACCOUNT_POSITION_UPDATE: + userDataUpdateEvent.setAccountUpdateEvent(getUserDataUpdateEventDetail(json, AccountUpdateEvent.class, mapper)); + break; + case BALANCE_UPDATE: + userDataUpdateEvent.setBalanceUpdateEvent(getUserDataUpdateEventDetail(json, BalanceUpdateEvent.class, mapper)); + break; + case ORDER_TRADE_UPDATE: + userDataUpdateEvent.setOrderTradeUpdateEvent(getUserDataUpdateEventDetail(json, OrderTradeUpdateEvent.class, mapper)); + break; + case OCO_TRADE_UPDATE: + userDataUpdateEvent.setOcoTradeUpdateEvent(getUserDataUpdateEventDetail(json, OcoTradeUpdateEvent.class, mapper)); + break; } return userDataUpdateEvent; diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java index dff6d6478..f4e39afb9 100644 --- a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java @@ -1,9 +1,13 @@ package com.binance.api.client.impl; +import java.io.Closeable; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + import com.binance.api.client.BinanceApiCallback; import com.binance.api.client.BinanceApiWebSocketClient; import com.binance.api.client.config.BinanceApiConfig; -import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.domain.event.AggTradeEvent; import com.binance.api.client.domain.event.AllMarketTickersEvent; import com.binance.api.client.domain.event.CandlestickEvent; @@ -17,11 +21,6 @@ import okhttp3.Request; import okhttp3.WebSocket; -import java.io.Closeable; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - /** * Binance API WebSocket client implementation using OkHttp. */ @@ -51,6 +50,7 @@ public Closeable onCandlestickEvent(String symbols, CandlestickInterval interval return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, CandlestickEvent.class)); } + @Override public Closeable onAggTradeEvent(String symbols, BinanceApiCallback callback) { final String channel = Arrays.stream(symbols.split(",")) .map(String::trim) @@ -58,7 +58,7 @@ public Closeable onAggTradeEvent(String symbols, BinanceApiCallback(callback, AggTradeEvent.class)); } - + @Override public Closeable onTradeEvent(String symbols, BinanceApiCallback callback) { final String channel = Arrays.stream(symbols.split(",")) @@ -68,10 +68,12 @@ public Closeable onTradeEvent(String symbols, BinanceApiCallback cal return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, TradeEvent.class)); } + @Override public Closeable onUserDataUpdateEvent(String listenKey, BinanceApiCallback callback) { return createNewWebSocket(listenKey, new BinanceApiWebSocketListener<>(callback, UserDataUpdateEvent.class)); } + @Override public Closeable onAllMarketTickersEvent(BinanceApiCallback> callback) { final String channel = "!ticker@arr"; return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, new TypeReference>() {})); @@ -80,6 +82,7 @@ public Closeable onAllMarketTickersEvent(BinanceApiCallback Date: Wed, 22 Apr 2020 09:07:53 +0200 Subject: [PATCH 003/101] master added get OCO status --- .../api/client/BinanceApiRestClient.java | 20 +- .../domain/account/OcoOrderResponse.java | 171 +++++++++--------- .../request/OcoOrderStatusRequest.java | 68 +++++++ .../client/impl/BinanceApiRestClientImpl.java | 7 + .../api/client/impl/BinanceApiService.java | 49 ++--- 5 files changed, 209 insertions(+), 106 deletions(-) create mode 100644 src/main/java/com/binance/api/client/domain/account/request/OcoOrderStatusRequest.java diff --git a/src/main/java/com/binance/api/client/BinanceApiRestClient.java b/src/main/java/com/binance/api/client/BinanceApiRestClient.java index e855c1f17..8cd601d17 100644 --- a/src/main/java/com/binance/api/client/BinanceApiRestClient.java +++ b/src/main/java/com/binance/api/client/BinanceApiRestClient.java @@ -1,11 +1,13 @@ package com.binance.api.client; +import java.util.List; + import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; -import com.binance.api.client.domain.account.OcoOrderResponse; import com.binance.api.client.domain.account.NewOrder; import com.binance.api.client.domain.account.NewOrderResponse; +import com.binance.api.client.domain.account.OcoOrderResponse; import com.binance.api.client.domain.account.Order; import com.binance.api.client.domain.account.Trade; import com.binance.api.client.domain.account.TradeHistoryItem; @@ -14,10 +16,11 @@ import com.binance.api.client.domain.account.request.AllOrdersRequest; import com.binance.api.client.domain.account.request.CancelOrderRequest; import com.binance.api.client.domain.account.request.CancelOrderResponse; +import com.binance.api.client.domain.account.request.OcoOrderStatusRequest; import com.binance.api.client.domain.account.request.OrderRequest; import com.binance.api.client.domain.account.request.OrderStatusRequest; -import com.binance.api.client.domain.general.ExchangeInfo; import com.binance.api.client.domain.general.Asset; +import com.binance.api.client.domain.general.ExchangeInfo; import com.binance.api.client.domain.market.AggTrade; import com.binance.api.client.domain.market.BookTicker; import com.binance.api.client.domain.market.Candlestick; @@ -26,8 +29,6 @@ import com.binance.api.client.domain.market.TickerPrice; import com.binance.api.client.domain.market.TickerStatistics; -import java.util.List; - /** * Binance API facade, supporting synchronous/blocking access Binance's REST API. */ @@ -188,6 +189,17 @@ public interface BinanceApiRestClient { */ Order getOrderStatus(OrderStatusRequest orderStatusRequest); + /** + * Check an OCO order's status. Note that binance will not return order leg + * details in this call (orderReports will be null). You will need to use + * individual leg ids and {@link #getOrderStatus(OrderStatusRequest)} to get + * details about individual legs. + * + * @param statusRequest + * @return an order + */ + OcoOrderResponse getOcoOrderStatus(OcoOrderStatusRequest statusRequest); + /** * Cancel an active order. * diff --git a/src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java b/src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java index 30f62314f..d4013ed05 100644 --- a/src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java +++ b/src/main/java/com/binance/api/client/domain/account/OcoOrderResponse.java @@ -1,5 +1,7 @@ package com.binance.api.client.domain.account; +import java.util.Arrays; + import com.fasterxml.jackson.annotation.JsonIgnoreProperties; /** @@ -11,86 +13,93 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class OcoOrderResponse { - long orderListId; - String contingencyType; - String listStatusType; - String listOrderStatus; - String listClientOrderId; - long transactionTime; - String symbol; - OcoLeg[] orders; - Order[] orderReports; - - public long getOrderListId() { - return orderListId; - } - - public void setOrderListId(long orderListId) { - this.orderListId = orderListId; - } - - public String getContingencyType() { - return contingencyType; - } - - public void setContingencyType(String contingencyType) { - this.contingencyType = contingencyType; - } - - public String getListStatusType() { - return listStatusType; - } - - public void setListStatusType(String listStatusType) { - this.listStatusType = listStatusType; - } - - public String getListOrderStatus() { - return listOrderStatus; - } - - public void setListOrderStatus(String listOrderStatus) { - this.listOrderStatus = listOrderStatus; - } - - public String getListClientOrderId() { - return listClientOrderId; - } - - public void setListClientOrderId(String listClientOrderId) { - this.listClientOrderId = listClientOrderId; - } - - public long getTransactionTime() { - return transactionTime; - } - - public void setTransactionTime(long transactionTime) { - this.transactionTime = transactionTime; - } - - public String getSymbol() { - return symbol; - } - - public void setSymbol(String symbol) { - this.symbol = symbol; - } - - public OcoLeg[] getOrders() { - return orders; - } - - public void setOrders(OcoLeg[] orders) { - this.orders = orders; - } - - public Order[] getOrderReports() { - return orderReports; - } - - public void setOrderReports(Order[] orderReports) { - this.orderReports = orderReports; - } + long orderListId; + String contingencyType; + String listStatusType; + String listOrderStatus; + String listClientOrderId; + long transactionTime; + String symbol; + OcoLeg[] orders; + Order[] orderReports; + + public long getOrderListId() { + return orderListId; + } + + public void setOrderListId(long orderListId) { + this.orderListId = orderListId; + } + + public String getContingencyType() { + return contingencyType; + } + + public void setContingencyType(String contingencyType) { + this.contingencyType = contingencyType; + } + + public String getListStatusType() { + return listStatusType; + } + + public void setListStatusType(String listStatusType) { + this.listStatusType = listStatusType; + } + + public String getListOrderStatus() { + return listOrderStatus; + } + + public void setListOrderStatus(String listOrderStatus) { + this.listOrderStatus = listOrderStatus; + } + + public String getListClientOrderId() { + return listClientOrderId; + } + + public void setListClientOrderId(String listClientOrderId) { + this.listClientOrderId = listClientOrderId; + } + + public long getTransactionTime() { + return transactionTime; + } + + public void setTransactionTime(long transactionTime) { + this.transactionTime = transactionTime; + } + + public String getSymbol() { + return symbol; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + + public OcoLeg[] getOrders() { + return orders; + } + + public void setOrders(OcoLeg[] orders) { + this.orders = orders; + } + + public Order[] getOrderReports() { + return orderReports; + } + + public void setOrderReports(Order[] orderReports) { + this.orderReports = orderReports; + } + + @Override + public String toString() { + return "OcoOrderResponse [orderListId=" + orderListId + ", contingencyType=" + contingencyType + ", listStatusType=" + listStatusType + + ", listOrderStatus=" + listOrderStatus + ", listClientOrderId=" + listClientOrderId + ", transactionTime=" + transactionTime + + ", symbol=" + symbol + ", orders=" + Arrays.toString(orders) + ", orderReports=" + Arrays.toString(orderReports) + "]"; + } } diff --git a/src/main/java/com/binance/api/client/domain/account/request/OcoOrderStatusRequest.java b/src/main/java/com/binance/api/client/domain/account/request/OcoOrderStatusRequest.java new file mode 100644 index 000000000..34ef78402 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/request/OcoOrderStatusRequest.java @@ -0,0 +1,68 @@ +package com.binance.api.client.domain.account.request; + +import com.binance.api.client.constant.BinanceApiConstants; + +public class OcoOrderStatusRequest { + + Long orderListId; + + String origClientOrderId; + + Long recvWindow; + + Long timestamp; + + public OcoOrderStatusRequest(String origClientOrderId) { + this(); + this.origClientOrderId = origClientOrderId; + } + + public OcoOrderStatusRequest(Long orderId) { + this(); + orderListId = orderId; + } + + private OcoOrderStatusRequest() { + this.timestamp = System.currentTimeMillis(); + this.recvWindow = BinanceApiConstants.DEFAULT_RECEIVING_WINDOW; + } + + public Long getOrderListId() { + return orderListId; + } + + public void setOrderListId(Long orderListId) { + this.orderListId = orderListId; + } + + public String getOrigClientOrderId() { + return origClientOrderId; + } + + public void setOrigClientOrderId(String origClientOrderId) { + this.origClientOrderId = origClientOrderId; + } + + public Long getRecvWindow() { + return recvWindow; + } + + public void setRecvWindow(Long recvWindow) { + this.recvWindow = recvWindow; + } + + public Long getTimestamp() { + return timestamp; + } + + public void setTimestamp(Long timestamp) { + this.timestamp = timestamp; + } + + @Override + public String toString() { + return "OcoOrderStatusRequest [orderListId=" + orderListId + ", origClientOrderId=" + origClientOrderId + ", recvWindow=" + recvWindow + + ", timestamp=" + timestamp + "]"; + } + +} diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java index ded5d6b8c..8f7d311a3 100644 --- a/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java @@ -24,6 +24,7 @@ import com.binance.api.client.domain.account.request.AllOrdersRequest; import com.binance.api.client.domain.account.request.CancelOrderRequest; import com.binance.api.client.domain.account.request.CancelOrderResponse; +import com.binance.api.client.domain.account.request.OcoOrderStatusRequest; import com.binance.api.client.domain.account.request.OrderRequest; import com.binance.api.client.domain.account.request.OrderStatusRequest; import com.binance.api.client.domain.general.Asset; @@ -182,6 +183,12 @@ public Order getOrderStatus(OrderStatusRequest orderStatusRequest) { orderStatusRequest.getRecvWindow(), orderStatusRequest.getTimestamp())); } + @Override + public OcoOrderResponse getOcoOrderStatus(OcoOrderStatusRequest statusRequest) { + return executeSync(binanceApiService.getOcoOrderStatus(statusRequest.getOrderListId(), statusRequest.getOrigClientOrderId(), + statusRequest.getRecvWindow(), statusRequest.getTimestamp())); + } + @Override public CancelOrderResponse cancelOrder(CancelOrderRequest cancelOrderRequest) { return executeSync(binanceApiService.cancelOrder(cancelOrderRequest.getSymbol(), diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiService.java b/src/main/java/com/binance/api/client/impl/BinanceApiService.java index 523b80ec2..3f1b085bf 100644 --- a/src/main/java/com/binance/api/client/impl/BinanceApiService.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiService.java @@ -1,5 +1,7 @@ package com.binance.api.client.impl; +import java.util.List; + import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.domain.OrderSide; import com.binance.api.client.domain.OrderType; @@ -7,9 +9,9 @@ import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; -import com.binance.api.client.domain.account.OcoOrderResponse; import com.binance.api.client.domain.account.NewOrderResponse; import com.binance.api.client.domain.account.NewOrderResponseType; +import com.binance.api.client.domain.account.OcoOrderResponse; import com.binance.api.client.domain.account.Order; import com.binance.api.client.domain.account.Trade; import com.binance.api.client.domain.account.TradeHistoryItem; @@ -26,6 +28,7 @@ import com.binance.api.client.domain.market.OrderBook; import com.binance.api.client.domain.market.TickerPrice; import com.binance.api.client.domain.market.TickerStatistics; + import retrofit2.Call; import retrofit2.http.DELETE; import retrofit2.http.GET; @@ -35,8 +38,6 @@ import retrofit2.http.Query; import retrofit2.http.Url; -import java.util.List; - /** * Binance's REST API URL mappings and endpoint security configuration. */ @@ -120,36 +121,36 @@ Call newOrderTest(@Query("symbol") String symbol, @Query("side") OrderSide @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) @POST("/api/v3/order/oco") - Call newOcoOrder(@Query("symbol") String symbol, - @Query("listClientOrderId") String listClientOrderId, - @Query("side") OrderSide side, - @Query("quantity") String quantity, - @Query("limitClientOrderId") String limitClientOrderId, + Call newOcoOrder(@Query("symbol") String symbol, + @Query("listClientOrderId") String listClientOrderId, + @Query("side") OrderSide side, + @Query("quantity") String quantity, + @Query("limitClientOrderId") String limitClientOrderId, @Query("price") String price, @Query("limitIcebergQty") String icebergQty, - @Query("stopClientOrderId") String stopClientOrderId, - @Query("stopPrice") String stopPrice, - @Query("stopLimitPrice") String stopLimitPrice, + @Query("stopClientOrderId") String stopClientOrderId, + @Query("stopPrice") String stopPrice, + @Query("stopLimitPrice") String stopLimitPrice, @Query("stopLimitTimeInForce") TimeInForce stopLimitTimeInForce, @Query("newOrderRespType") NewOrderResponseType newOrderRespType, - @Query("recvWindow") Long recvWindow, + @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) @POST("/api/v3/order/oco/test") - Call newOcoOrderTest(@Query("symbol") String symbol, - @Query("listClientOrderId") String listClientOrderId, - @Query("side") OrderSide side, - @Query("quantity") String quantity, - @Query("limitClientOrderId") String limitClientOrderId, + Call newOcoOrderTest(@Query("symbol") String symbol, + @Query("listClientOrderId") String listClientOrderId, + @Query("side") OrderSide side, + @Query("quantity") String quantity, + @Query("limitClientOrderId") String limitClientOrderId, @Query("price") String price, @Query("limitIcebergQty") String icebergQty, - @Query("stopClientOrderId") String stopClientOrderId, - @Query("stopPrice") String stopPrice, - @Query("stopLimitPrice") String stopLimitPrice, + @Query("stopClientOrderId") String stopClientOrderId, + @Query("stopPrice") String stopPrice, + @Query("stopLimitPrice") String stopLimitPrice, @Query("stopLimitTimeInForce") TimeInForce stopLimitTimeInForce, @Query("newOrderRespType") NewOrderResponseType newOrderRespType, - @Query("recvWindow") Long recvWindow, + @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) @@ -158,6 +159,11 @@ Call getOrderStatus(@Query("symbol") String symbol, @Query("orderId") Lon @Query("origClientOrderId") String origClientOrderId, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) + @GET("/api/v3/orderList") + Call getOcoOrderStatus(@Query("orderListId") Long orderListId, @Query("origClientOrderId") String origClientOrderId, + @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) @DELETE("/api/v3/order") Call cancelOrder(@Query("symbol") String symbol, @Query("orderId") Long orderId, @@ -219,4 +225,5 @@ Call withdraw(@Query("asset") String asset, @Query("address") St @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER) @DELETE("/api/v1/userDataStream") Call closeAliveUserDataStream(@Query("listenKey") String listenKey); + } From 71e7df068759ad811b9d3847fd6ccd72dd77ff77 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 9 May 2020 17:17:05 +0200 Subject: [PATCH 004/101] master added setters for executedQty and status --- .../domain/account/request/CancelOrderResponse.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/binance/api/client/domain/account/request/CancelOrderResponse.java b/src/main/java/com/binance/api/client/domain/account/request/CancelOrderResponse.java index 7b96a01eb..e2b160044 100644 --- a/src/main/java/com/binance/api/client/domain/account/request/CancelOrderResponse.java +++ b/src/main/java/com/binance/api/client/domain/account/request/CancelOrderResponse.java @@ -1,8 +1,9 @@ package com.binance.api.client.domain.account.request; +import org.apache.commons.lang3.builder.ToStringBuilder; + import com.binance.api.client.constant.BinanceApiConstants; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import org.apache.commons.lang3.builder.ToStringBuilder; /** * Response object returned when an order is canceled. @@ -48,6 +49,14 @@ public String getExecutedQty() { return executedQty; } + public void setExecutedQty(String executedQty) { + this.executedQty = executedQty; + } + + public void setStatus(String status) { + this.status = status; + } + public String getOrderId() { return orderId; } From 53b88bac3bdbebe591523ff6b729f3d13d9493f7 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Mon, 1 Jun 2020 09:39:37 +0200 Subject: [PATCH 005/101] master reordered tostring fields for better readability --- .../com/binance/api/client/domain/account/NewOrder.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/binance/api/client/domain/account/NewOrder.java b/src/main/java/com/binance/api/client/domain/account/NewOrder.java index a081f8b1f..b987923f4 100644 --- a/src/main/java/com/binance/api/client/domain/account/NewOrder.java +++ b/src/main/java/com/binance/api/client/domain/account/NewOrder.java @@ -348,16 +348,17 @@ public String toString() { .append("symbol", symbol) .append("side", side) .append("type", type) - .append("timeInForce", timeInForce) + .append("price", price) + .append("stopLimitPrice", stopLimitPrice) + .append("stopPrice", stopPrice) .append("quantity", quantity) .append("quoteOrderQty", quoteOrderQty) - .append("price", price) .append("newClientOrderId", newClientOrderId) - .append("stopPrice", stopPrice) .append("icebergQty", icebergQty) .append("newOrderRespType", newOrderRespType) .append("recvWindow", recvWindow) .append("timestamp", timestamp) + .append("timeInForce", timeInForce) .toString(); } } From 5aa76e01e73d429fa801509636817da791cc558a Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Wed, 29 Jul 2020 09:22:15 +0200 Subject: [PATCH 006/101] master added orderListId to toString() --- .../java/com/binance/api/client/domain/account/Order.java | 6 ++++-- .../api/client/domain/market/CandlestickInterval.java | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/binance/api/client/domain/account/Order.java b/src/main/java/com/binance/api/client/domain/account/Order.java index 9517ae47b..bfefb014f 100644 --- a/src/main/java/com/binance/api/client/domain/account/Order.java +++ b/src/main/java/com/binance/api/client/domain/account/Order.java @@ -1,5 +1,7 @@ package com.binance.api.client.domain.account; +import org.apache.commons.lang3.builder.ToStringBuilder; + import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.domain.OrderSide; import com.binance.api.client.domain.OrderStatus; @@ -7,7 +9,6 @@ import com.binance.api.client.domain.TimeInForce; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import org.apache.commons.lang3.builder.ToStringBuilder; /** * Trade order information. @@ -29,7 +30,7 @@ public class Order { * Client order id. */ private String clientOrderId; - + /** * Collective order id to which this order belongs (for OCO order legs) */ @@ -256,6 +257,7 @@ public String toString() { .append("symbol", symbol) .append("orderId", orderId) .append("clientOrderId", clientOrderId) + .append("orderListId", orderListId) .append("price", price) .append("origQty", origQty) .append("executedQty", executedQty) diff --git a/src/main/java/com/binance/api/client/domain/market/CandlestickInterval.java b/src/main/java/com/binance/api/client/domain/market/CandlestickInterval.java index b34334977..219165f26 100644 --- a/src/main/java/com/binance/api/client/domain/market/CandlestickInterval.java +++ b/src/main/java/com/binance/api/client/domain/market/CandlestickInterval.java @@ -33,4 +33,5 @@ public enum CandlestickInterval { public String getIntervalId() { return intervalId; } + } From 56d6a95808fe1d5838c678c0ff18953b22cf575c Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Fri, 4 Sep 2020 20:19:14 +0200 Subject: [PATCH 007/101] master added MAX_POSITION enum --- .../java/com/binance/api/client/domain/general/FilterType.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/binance/api/client/domain/general/FilterType.java b/src/main/java/com/binance/api/client/domain/general/FilterType.java index 1959f8ec5..699383995 100644 --- a/src/main/java/com/binance/api/client/domain/general/FilterType.java +++ b/src/main/java/com/binance/api/client/domain/general/FilterType.java @@ -13,6 +13,7 @@ public enum FilterType { MIN_NOTIONAL, MAX_NUM_ORDERS, MAX_ALGO_ORDERS, + MAX_POSITION, MAX_NUM_ALGO_ORDERS, ICEBERG_PARTS, PERCENT_PRICE, From 2ccdf968b70a6a8a804ea5e3e397794faefc443a Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Fri, 4 Sep 2020 20:21:16 +0200 Subject: [PATCH 008/101] master bumping version up --- pom.xml | 2 +- pom.xml.versionsBackup | 63 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 pom.xml.versionsBackup diff --git a/pom.xml b/pom.xml index 863f5c234..89ebaf66d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.binance.api binance-api-client - 1.0.1 + 1.0.2 The MIT License diff --git a/pom.xml.versionsBackup b/pom.xml.versionsBackup new file mode 100644 index 000000000..863f5c234 --- /dev/null +++ b/pom.xml.versionsBackup @@ -0,0 +1,63 @@ + + + 4.0.0 + + com.binance.api + binance-api-client + 1.0.1 + + + The MIT License + https://opensource.org/licenses/MIT + + + + + 2.4.0 + + + + + com.squareup.retrofit2 + retrofit + ${com.squareup.retrofit2.version} + + + com.squareup.retrofit2 + converter-jackson + ${com.squareup.retrofit2.version} + + + commons-codec + commons-codec + 1.10 + + + org.apache.commons + commons-lang3 + 3.6 + + + junit + junit + 4.12 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + From ef4c8d848a06d090e02e486023fce25f736975cd Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Mon, 19 Oct 2020 20:36:19 +0200 Subject: [PATCH 009/101] master added async versions of create/cancel oco orders, added logging of http payloads --- pom.xml | 13 ++++++- .../api/client/BinanceApiAsyncRestClient.java | 29 +++++++++++--- .../api/client/config/BinanceApiConfig.java | 5 +++ .../impl/BinanceApiAsyncRestClientImpl.java | 26 +++++++++++-- .../impl/BinanceApiServiceGenerator.java | 38 ++++++++++++++----- 5 files changed, 90 insertions(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index 89ebaf66d..f89f6935a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.binance.api binance-api-client - 1.0.2 + 1.0.4 The MIT License @@ -29,6 +29,17 @@ converter-jackson ${com.squareup.retrofit2.version} + + com.squareup.okhttp3 + logging-interceptor + 3.10.0 + + + org.slf4j + slf4j-api + 1.7.25 + compile + commons-codec commons-codec diff --git a/src/main/java/com/binance/api/client/BinanceApiAsyncRestClient.java b/src/main/java/com/binance/api/client/BinanceApiAsyncRestClient.java index d0e3b1c01..4ba138b79 100644 --- a/src/main/java/com/binance/api/client/BinanceApiAsyncRestClient.java +++ b/src/main/java/com/binance/api/client/BinanceApiAsyncRestClient.java @@ -1,10 +1,13 @@ package com.binance.api.client; +import java.util.List; + import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; import com.binance.api.client.domain.account.NewOrder; import com.binance.api.client.domain.account.NewOrderResponse; +import com.binance.api.client.domain.account.OcoOrderResponse; import com.binance.api.client.domain.account.Order; import com.binance.api.client.domain.account.Trade; import com.binance.api.client.domain.account.TradeHistoryItem; @@ -27,8 +30,6 @@ import com.binance.api.client.domain.market.TickerPrice; import com.binance.api.client.domain.market.TickerStatistics; -import java.util.List; - /** * Binance API facade, supporting asynchronous/non-blocking access Binance's REST API. */ @@ -136,10 +137,10 @@ public interface BinanceApiAsyncRestClient { * @param callback the callback that handles the response */ void get24HrPriceStatistics(String symbol, BinanceApiCallback callback); - + /** * Get 24 hour price change statistics for all symbols (asynchronous). - * + * * @param callback the callback that handles the response */ void getAll24HrPriceStatistics(BinanceApiCallback> callback); @@ -150,10 +151,10 @@ public interface BinanceApiAsyncRestClient { * @param callback the callback that handles the response */ void getAllPrices(BinanceApiCallback> callback); - + /** * Get latest price for symbol (asynchronous). - * + * * @param symbol ticker symbol (e.g. ETHBTC) * @param callback the callback that handles the response */ @@ -176,6 +177,14 @@ public interface BinanceApiAsyncRestClient { */ void newOrder(NewOrder order, BinanceApiCallback callback); + /** + * Send in a new OCO order. + * + * @param order the new order to submit. + * @param callback the callback that handles the response. + */ + void newOcoOrder(NewOrder order, BinanceApiCallback callback); + /** * Test new order creation and signature/recvWindow long. Creates and validates a new order but does not send it into the matching engine. * @@ -200,6 +209,14 @@ public interface BinanceApiAsyncRestClient { */ void cancelOrder(CancelOrderRequest cancelOrderRequest, BinanceApiCallback callback); + /** + * Cancel an active order (asynchronous). + * + * @param cancelOrderRequest order status request parameters + * @param callback the callback that handles the response + */ + void cancelOcoOrder(CancelOrderRequest cancelOrderRequest, BinanceApiCallback callback); + /** * Get all open orders on a symbol (asynchronous). * diff --git a/src/main/java/com/binance/api/client/config/BinanceApiConfig.java b/src/main/java/com/binance/api/client/config/BinanceApiConfig.java index d05538efb..d72af292e 100644 --- a/src/main/java/com/binance/api/client/config/BinanceApiConfig.java +++ b/src/main/java/com/binance/api/client/config/BinanceApiConfig.java @@ -49,4 +49,9 @@ public static String getAssetInfoApiBaseUrl() { return String.format("https://%s/", getBaseDomain()); } + /** + * Set this system property to true to enable HTTP payloads logging + */ + public static final String BINANCE_API_LOGGING_ENABLED = "binance.api.logging.enabled"; + } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java index e6ea387b9..8655d7e6b 100644 --- a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java @@ -1,14 +1,20 @@ package com.binance.api.client.impl; +import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; + +import java.util.List; + import com.binance.api.client.BinanceApiAsyncRestClient; import com.binance.api.client.BinanceApiCallback; import com.binance.api.client.config.BinanceApiConfig; import com.binance.api.client.constant.BinanceApiConstants; +import com.binance.api.client.domain.TimeInForce; import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; import com.binance.api.client.domain.account.NewOrder; import com.binance.api.client.domain.account.NewOrderResponse; +import com.binance.api.client.domain.account.OcoOrderResponse; import com.binance.api.client.domain.account.Order; import com.binance.api.client.domain.account.Trade; import com.binance.api.client.domain.account.TradeHistoryItem; @@ -31,10 +37,6 @@ import com.binance.api.client.domain.market.TickerPrice; import com.binance.api.client.domain.market.TickerStatistics; -import java.util.List; - -import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; - /** * Implementation of Binance's REST API using Retrofit with asynchronous/non-blocking method calls. */ @@ -146,6 +148,15 @@ public void newOrder(NewOrder order, BinanceApiCallback callba } } + @Override + public void newOcoOrder(NewOrder order, BinanceApiCallback callback) { + binanceApiService + .newOcoOrder(order.getSymbol(), order.getNewClientOrderId(), order.getSide(), order.getQuantity(), order.getLimitClientOrderId(), + order.getPrice(), order.getIcebergQty(), order.getStopClientOrderId(), order.getStopPrice(), order.getStopLimitPrice(), + TimeInForce.GTC, order.getNewOrderRespType(), order.getRecvWindow(), order.getTimestamp()) + .enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + @Override public void newOrderTest(NewOrder order, BinanceApiCallback callback) { binanceApiService.newOrderTest(order.getSymbol(), order.getSide(), order.getType(), @@ -169,6 +180,13 @@ public void cancelOrder(CancelOrderRequest cancelOrderRequest, BinanceApiCallbac cancelOrderRequest.getRecvWindow(), cancelOrderRequest.getTimestamp()).enqueue(new BinanceApiCallbackAdapter<>(callback)); } + @Override + public void cancelOcoOrder(CancelOrderRequest cancelOrderRequest, BinanceApiCallback callback) { + binanceApiService.cancelOcoOrder(cancelOrderRequest.getSymbol(), + cancelOrderRequest.getOrderId(), cancelOrderRequest.getOrigClientOrderId(), cancelOrderRequest.getNewClientOrderId(), + cancelOrderRequest.getRecvWindow(), cancelOrderRequest.getTimestamp()).enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + @Override public void getOpenOrders(OrderRequest orderRequest, BinanceApiCallback> callback) { binanceApiService.getOpenOrders(orderRequest.getSymbol(), diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java index 7bab3ad1e..c2d3a450d 100644 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -1,25 +1,30 @@ package com.binance.api.client.impl; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.binance.api.client.BinanceApiError; import com.binance.api.client.config.BinanceApiConfig; -import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.exception.BinanceApiException; import com.binance.api.client.security.AuthenticationInterceptor; + import okhttp3.Dispatcher; import okhttp3.OkHttpClient; -import okhttp3.RequestBody; +import okhttp3.OkHttpClient.Builder; import okhttp3.ResponseBody; -import org.apache.commons.lang3.StringUtils; +import okhttp3.logging.HttpLoggingInterceptor; +import okhttp3.logging.HttpLoggingInterceptor.Level; import retrofit2.Call; import retrofit2.Converter; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.util.concurrent.TimeUnit; - /** * Generates a Binance API implementation based on @see {@link BinanceApiService}. */ @@ -32,10 +37,23 @@ public class BinanceApiServiceGenerator { Dispatcher dispatcher = new Dispatcher(); dispatcher.setMaxRequestsPerHost(500); dispatcher.setMaxRequests(500); - sharedClient = new OkHttpClient.Builder() + Builder builder = new OkHttpClient.Builder() .dispatcher(dispatcher) - .pingInterval(20, TimeUnit.SECONDS) - .build(); + .pingInterval(20, TimeUnit.SECONDS); + if (Boolean.getBoolean(BinanceApiConfig.BINANCE_API_LOGGING_ENABLED)) { + Logger lg = LoggerFactory.getLogger(BinanceApiService.class); + HttpLoggingInterceptor logger = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { + + + @Override + public void log(String message) { + lg.info(message); + } + }); + logger.setLevel(Level.BODY); + builder.addInterceptor(logger); + } + sharedClient = builder.build(); } @SuppressWarnings("unchecked") From da482ade708e5215270529d1b6b83ee6bdaada43 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Wed, 27 Jan 2021 10:01:56 +0100 Subject: [PATCH 010/101] master switched to v3 endpoint for symbols --- pom.xml | 2 +- pom.xml.versionsBackup | 63 ------------------- .../api/client/domain/general/SymbolInfo.java | 43 ++++++++++++- .../api/client/impl/BinanceApiService.java | 2 +- 4 files changed, 42 insertions(+), 68 deletions(-) delete mode 100644 pom.xml.versionsBackup diff --git a/pom.xml b/pom.xml index f89f6935a..e870abdf9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.binance.api binance-api-client - 1.0.4 + 1.1.0-SNAPSHOT The MIT License diff --git a/pom.xml.versionsBackup b/pom.xml.versionsBackup deleted file mode 100644 index 863f5c234..000000000 --- a/pom.xml.versionsBackup +++ /dev/null @@ -1,63 +0,0 @@ - - - 4.0.0 - - com.binance.api - binance-api-client - 1.0.1 - - - The MIT License - https://opensource.org/licenses/MIT - - - - - 2.4.0 - - - - - com.squareup.retrofit2 - retrofit - ${com.squareup.retrofit2.version} - - - com.squareup.retrofit2 - converter-jackson - ${com.squareup.retrofit2.version} - - - commons-codec - commons-codec - 1.10 - - - org.apache.commons - commons-lang3 - 3.6 - - - junit - junit - 4.12 - test - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - - - - - - diff --git a/src/main/java/com/binance/api/client/domain/general/SymbolInfo.java b/src/main/java/com/binance/api/client/domain/general/SymbolInfo.java index 7eed2e97c..1cfe77940 100644 --- a/src/main/java/com/binance/api/client/domain/general/SymbolInfo.java +++ b/src/main/java/com/binance/api/client/domain/general/SymbolInfo.java @@ -1,15 +1,20 @@ package com.binance.api.client.domain.general; +import java.util.List; + +import org.apache.commons.lang3.builder.ToStringBuilder; + import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.domain.OrderType; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import org.apache.commons.lang3.builder.ToStringBuilder; - -import java.util.List; /** * Symbol information (base/quote). */ +/** + * @author sergey + * + */ @JsonIgnoreProperties(ignoreUnknown = true) public class SymbolInfo { @@ -29,6 +34,12 @@ public class SymbolInfo { private boolean icebergAllowed; + private boolean ocoAllowed; + + private boolean isSpotTradingAllowed; + + private boolean isMarginTradingAllowed; + private List filters; public String getSymbol() { @@ -128,4 +139,30 @@ public String toString() { .append("filters", filters) .toString(); } + + public boolean isOcoAllowed() { + return ocoAllowed; + } + + public void setOcoAllowed(boolean ocoAllowed) { + this.ocoAllowed = ocoAllowed; + } + + public boolean isSpotTradingAllowed() { + return isSpotTradingAllowed; + } + + public void setSpotTradingAllowed(boolean isSpotTradingAllowed) { + this.isSpotTradingAllowed = isSpotTradingAllowed; + } + + public boolean isMarginTradingAllowed() { + return isMarginTradingAllowed; + } + + public void setMarginTradingAllowed(boolean isMarginTradingAllowed) { + this.isMarginTradingAllowed = isMarginTradingAllowed; + } + + } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiService.java b/src/main/java/com/binance/api/client/impl/BinanceApiService.java index 3f1b085bf..f92c9370d 100644 --- a/src/main/java/com/binance/api/client/impl/BinanceApiService.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiService.java @@ -51,7 +51,7 @@ public interface BinanceApiService { @GET("/api/v1/time") Call getServerTime(); - @GET("/api/v1/exchangeInfo") + @GET("/api/v3/exchangeInfo") Call getExchangeInfo(); @GET From ca6a8145761a8228ec6caa2a8f6290c1c9ccb41f Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Wed, 27 Jan 2021 15:42:12 +0100 Subject: [PATCH 011/101] master added caution about client ids for cancel events --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 806d4ea80..f9d29dc9d 100644 --- a/README.md +++ b/README.md @@ -535,5 +535,7 @@ buy.newClientOrderId(UUID.randomUUID().toString()); OcoOrderResponse order = client.newOcoOrder(buy); ``` +*WATCH OUT*. newClientOrderId is your identifier of the two-leg trade. In order to distinguish between limit_maker and stop_loss part you need to set individual client ids for these legs at creation time (limitClientOrderId, stopClientOrderId). Beware however that when an oco stop happens the limit_maker leg order update will come without your client id, it will only contain binance generated numeric orderId (which you can re-query and get your clientOrderId anyway). This is the case with all cancel events - they will not have your own id, but binance orderId instead. + ### More examples An extensive set of examples, covering most aspects of the API, can be found at https://github.com/joaopsilva/binance-java-api/tree/master/src/test/java/com/binance/api/examples. From 6b5c45e49c12f1b606841279759bf68947569eef Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Wed, 27 Jan 2021 16:23:51 +0100 Subject: [PATCH 012/101] development added jenkinsfile --- Jenkinsfile | 47 +++++++++++++++++++++++++++++++++++++++++++++++ pom.xml | 16 ++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 000000000..4ed5e793b --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,47 @@ +pipeline { + agent any + tools { + maven 'mvn3' + jdk 'jdk14' + } + + options { + buildDiscarder(logRotator(numToKeepStr: '5')) + disableConcurrentBuilds() + } + + triggers { + pollSCM('') + } + + parameters { + booleanParam( + name: "RELEASE", + description: "Build a release from current commit.", + defaultValue: false) + } + stages { + stage('Build') { + steps { + sh 'mvn clean install' + } + post { + success { + junit 'target/surefire-reports/**/*.xml' + } + } + } + + stage('Release') { + when { + branch 'master' + } + steps { + script { + version = release() + echo "Released binance-java-api version ${version}" + } + } + } + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index fe870fda1..e27d58fa8 100644 --- a/pom.xml +++ b/pom.xml @@ -71,4 +71,20 @@ + + scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git + HEAD + + + + + kube-releases + http://kube1:9090/releases + + + kube-snapshots + http://kube1:9090/snapshots + + + From 493e226cbb1e9400385dde5e9ae8d708393c3929 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Wed, 27 Jan 2021 18:02:50 +0100 Subject: [PATCH 013/101] development added deploy --- Jenkinsfile | 4 ++-- pom.xml | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4ed5e793b..074682aba 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -21,9 +21,9 @@ pipeline { defaultValue: false) } stages { - stage('Build') { + stage('Build & Deploy SNAPSHOT') { steps { - sh 'mvn clean install' + deploy } post { success { diff --git a/pom.xml b/pom.xml index e27d58fa8..5576bbbe4 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ 2.4.0 + UTF-8 @@ -66,6 +67,7 @@ 1.8 1.8 + UTF-8 From fe552eb14d631a059c323d7ceb27ca4e7d2d10b2 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Thu, 28 Jan 2021 00:00:15 +0100 Subject: [PATCH 014/101] master oops --- Jenkinsfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 074682aba..7797ccddc 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -23,9 +23,11 @@ pipeline { stages { stage('Build & Deploy SNAPSHOT') { steps { + script { deploy - } - post { + } + } + post { success { junit 'target/surefire-reports/**/*.xml' } From 1f53edf2d46e3a185dcf3922fc60929d08212783 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Thu, 28 Jan 2021 00:02:26 +0100 Subject: [PATCH 015/101] master adding surefire for test reports --- Jenkinsfile | 4 +--- pom.xml | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7797ccddc..879baf753 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -23,9 +23,7 @@ pipeline { stages { stage('Build & Deploy SNAPSHOT') { steps { - script { - deploy - } + deploy() } post { success { diff --git a/pom.xml b/pom.xml index 5576bbbe4..855da7056 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,6 @@ - + 4.0.0 com.binance.api @@ -70,6 +69,9 @@ UTF-8 + + maven-surefire-plugin + From df40bd24b0a97e75a2d2ab2ee0def9f7966fcc92 Mon Sep 17 00:00:00 2001 From: jenkins Date: Wed, 27 Jan 2021 23:04:18 +0000 Subject: [PATCH 016/101] [ci-skip]prepare release binance-api-client-2.0.0 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 855da7056..3da8a464e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,11 +1,10 @@ - + 4.0.0 com.binance.api binance-api-client - 2.0.0-SNAPSHOT + 2.0.0 The MIT License @@ -77,7 +76,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.0.0 From 4b3ac0354eaf75ee23689b0334f87d18e4a0c0b1 Mon Sep 17 00:00:00 2001 From: jenkins Date: Wed, 27 Jan 2021 23:04:33 +0000 Subject: [PATCH 017/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3da8a464e..2f4b04aa9 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.0.0 + 2.0.1-SNAPSHOT The MIT License @@ -76,7 +76,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.0.0 + HEAD From 3aa17624ba386ae65b1900f51f1729c1801eb2d8 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 30 Jan 2021 18:18:28 +0100 Subject: [PATCH 018/101] development updated jenkinsfile --- Jenkinsfile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 879baf753..67af14c55 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -10,10 +10,6 @@ pipeline { disableConcurrentBuilds() } - triggers { - pollSCM('') - } - parameters { booleanParam( name: "RELEASE", From b36e8003e8a552808c26ad1700341f8176c15e42 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 30 Jan 2021 23:22:18 +0100 Subject: [PATCH 019/101] master added types to symbol filters and implemented validations described on binance api page --- pom.xml | 11 +- .../client/domain/general/ExchangeFilter.java | 44 ----- .../api/client/domain/general/FilterType.java | 14 +- .../domain/general/IcebergPartsFilter.java | 16 ++ .../client/domain/general/LotSizeFilter.java | 18 ++ .../domain/general/MarketLotSizeFilter.java | 19 ++ .../general/MaxNumAlgoOrdersFilter.java | 17 ++ .../domain/general/MaxNumIcebergOrders.java | 16 ++ .../domain/general/MaxNumOrdersFilter.java | 16 ++ .../domain/general/MaxPositionFilter.java | 17 ++ .../domain/general/MinNotionalFilter.java | 18 ++ .../domain/general/PercentPriceFilter.java | 18 ++ .../client/domain/general/PriceFilter.java | 28 +++ .../client/domain/general/SymbolFilter.java | 178 ++++-------------- .../api/client/domain/general/SymbolInfo.java | 8 + .../general/ExchangeInfoDeserializerTest.java | 29 +-- .../api/examples/GeneralEndpointsExample.java | 8 +- .../GeneralEndpointsExampleAsync.java | 7 +- 18 files changed, 260 insertions(+), 222 deletions(-) delete mode 100755 src/main/java/com/binance/api/client/domain/general/ExchangeFilter.java create mode 100644 src/main/java/com/binance/api/client/domain/general/IcebergPartsFilter.java create mode 100644 src/main/java/com/binance/api/client/domain/general/LotSizeFilter.java create mode 100644 src/main/java/com/binance/api/client/domain/general/MarketLotSizeFilter.java create mode 100644 src/main/java/com/binance/api/client/domain/general/MaxNumAlgoOrdersFilter.java create mode 100644 src/main/java/com/binance/api/client/domain/general/MaxNumIcebergOrders.java create mode 100644 src/main/java/com/binance/api/client/domain/general/MaxNumOrdersFilter.java create mode 100644 src/main/java/com/binance/api/client/domain/general/MaxPositionFilter.java create mode 100644 src/main/java/com/binance/api/client/domain/general/MinNotionalFilter.java create mode 100644 src/main/java/com/binance/api/client/domain/general/PercentPriceFilter.java create mode 100644 src/main/java/com/binance/api/client/domain/general/PriceFilter.java diff --git a/pom.xml b/pom.xml index 2f4b04aa9..5a305fdf2 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,11 @@ - + 4.0.0 com.binance.api binance-api-client - 2.0.1-SNAPSHOT + 2.1.0-SNAPSHOT The MIT License @@ -49,6 +50,12 @@ commons-lang3 3.6 + + org.projectlombok + lombok + 1.18.10 + provided + junit junit diff --git a/src/main/java/com/binance/api/client/domain/general/ExchangeFilter.java b/src/main/java/com/binance/api/client/domain/general/ExchangeFilter.java deleted file mode 100755 index e367deaef..000000000 --- a/src/main/java/com/binance/api/client/domain/general/ExchangeFilter.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.binance.api.client.domain.general; - -import com.binance.api.client.constant.BinanceApiConstants; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import org.apache.commons.lang3.builder.ToStringBuilder; - -/** - * Exchange Filters define trading rules an exchange. - * - * The MAX_NUM_ORDERS filter defines the maximum number of orders an account is allowed to have open on the exchange. Note that both "algo" orders and normal orders are counted for this filter. - * - * The MAX_ALGO_ORDERS filter defines the maximum number of "algo" orders an account is allowed to have open on the exchange. "Algo" orders are STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ExchangeFilter { - - private FilterType filterType; - - private Integer limit; - - public FilterType getFilterType() { - return filterType; - } - - public void setFilterType(FilterType filterType) { - this.filterType = filterType; - } - - public Integer getLimit() { - return limit; - } - - public void setLimit(Integer limit) { - this.limit = limit; - } - - @Override - public String toString() { - return new ToStringBuilder(this, BinanceApiConstants.TO_STRING_BUILDER_STYLE) - .append("filterType", filterType) - .append("limit", limit) - .toString(); - } -} diff --git a/src/main/java/com/binance/api/client/domain/general/FilterType.java b/src/main/java/com/binance/api/client/domain/general/FilterType.java index d6bd95471..5d734b376 100755 --- a/src/main/java/com/binance/api/client/domain/general/FilterType.java +++ b/src/main/java/com/binance/api/client/domain/general/FilterType.java @@ -7,20 +7,16 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) public enum FilterType { - // Symbol + PRICE_FILTER, + PERCENT_PRICE, LOT_SIZE, MIN_NOTIONAL, - MAX_NUM_ORDERS, - MAX_ALGO_ORDERS, - MAX_NUM_ALGO_ORDERS, ICEBERG_PARTS, - PERCENT_PRICE, MARKET_LOT_SIZE, + MAX_NUM_ORDERS, + MAX_NUM_ALGO_ORDERS, MAX_NUM_ICEBERG_ORDERS, MAX_POSITION, - - // Exchange - EXCHANGE_MAX_NUM_ORDERS, - EXCHANGE_MAX_ALGO_ORDERS + } diff --git a/src/main/java/com/binance/api/client/domain/general/IcebergPartsFilter.java b/src/main/java/com/binance/api/client/domain/general/IcebergPartsFilter.java new file mode 100644 index 000000000..df45de78b --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/general/IcebergPartsFilter.java @@ -0,0 +1,16 @@ +package com.binance.api.client.domain.general; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class IcebergPartsFilter extends SymbolFilter { + + int limit; + + public boolean validate(int icebergParts) { + return icebergParts <= limit; + } + +} diff --git a/src/main/java/com/binance/api/client/domain/general/LotSizeFilter.java b/src/main/java/com/binance/api/client/domain/general/LotSizeFilter.java new file mode 100644 index 000000000..7b2d0159d --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/general/LotSizeFilter.java @@ -0,0 +1,18 @@ +package com.binance.api.client.domain.general; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class LotSizeFilter extends SymbolFilter { + + String minQty; + String maxQty; + String stepSize; + + public boolean validate(double quantity) { + return quantity >= dbl(minQty) && quantity <= dbl(maxQty) && (quantity - dbl(minQty)) % dbl(stepSize) == 0; + } + +} diff --git a/src/main/java/com/binance/api/client/domain/general/MarketLotSizeFilter.java b/src/main/java/com/binance/api/client/domain/general/MarketLotSizeFilter.java new file mode 100644 index 000000000..092266cd0 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/general/MarketLotSizeFilter.java @@ -0,0 +1,19 @@ +package com.binance.api.client.domain.general; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class MarketLotSizeFilter extends SymbolFilter { + + String minQty; + String maxQty; + String stepSize; + + public boolean validate(double quantity) { + return quantity >= dbl(minQty) && quantity <= dbl(maxQty) && (quantity - dbl(minQty)) % dbl(stepSize) == 0; + + } + +} diff --git a/src/main/java/com/binance/api/client/domain/general/MaxNumAlgoOrdersFilter.java b/src/main/java/com/binance/api/client/domain/general/MaxNumAlgoOrdersFilter.java new file mode 100644 index 000000000..fb4fff434 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/general/MaxNumAlgoOrdersFilter.java @@ -0,0 +1,17 @@ +package com.binance.api.client.domain.general; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class MaxNumAlgoOrdersFilter extends SymbolFilter { + + int maxNumAlgoOrders; + + public boolean validate(int algoOrders) { + return algoOrders <= maxNumAlgoOrders; + } + + +} diff --git a/src/main/java/com/binance/api/client/domain/general/MaxNumIcebergOrders.java b/src/main/java/com/binance/api/client/domain/general/MaxNumIcebergOrders.java new file mode 100644 index 000000000..dca241468 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/general/MaxNumIcebergOrders.java @@ -0,0 +1,16 @@ +package com.binance.api.client.domain.general; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class MaxNumIcebergOrders extends SymbolFilter { + + int maxNumIcebergOrders; + + public boolean validate(int icebergOrders) { + return icebergOrders <= maxNumIcebergOrders; + } + +} diff --git a/src/main/java/com/binance/api/client/domain/general/MaxNumOrdersFilter.java b/src/main/java/com/binance/api/client/domain/general/MaxNumOrdersFilter.java new file mode 100644 index 000000000..8f450dddf --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/general/MaxNumOrdersFilter.java @@ -0,0 +1,16 @@ +package com.binance.api.client.domain.general; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class MaxNumOrdersFilter extends SymbolFilter { + + int maxNumOrders; + + public boolean validate(int numOrders) { + return numOrders <= maxNumOrders; + } + +} diff --git a/src/main/java/com/binance/api/client/domain/general/MaxPositionFilter.java b/src/main/java/com/binance/api/client/domain/general/MaxPositionFilter.java new file mode 100644 index 000000000..bbb6fbf38 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/general/MaxPositionFilter.java @@ -0,0 +1,17 @@ +package com.binance.api.client.domain.general; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class MaxPositionFilter extends SymbolFilter { + + String maxPosition; + + public boolean validate(double position) { + return position <= dbl(maxPosition); + } + + +} diff --git a/src/main/java/com/binance/api/client/domain/general/MinNotionalFilter.java b/src/main/java/com/binance/api/client/domain/general/MinNotionalFilter.java new file mode 100644 index 000000000..ef157c6fa --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/general/MinNotionalFilter.java @@ -0,0 +1,18 @@ +package com.binance.api.client.domain.general; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class MinNotionalFilter extends SymbolFilter { + + String minNotional; + boolean applyToMarket; + int avgPriceMins; + + public boolean validate(double price, double quantity) { + return price * quantity >= dbl(minNotional); + } + +} diff --git a/src/main/java/com/binance/api/client/domain/general/PercentPriceFilter.java b/src/main/java/com/binance/api/client/domain/general/PercentPriceFilter.java new file mode 100644 index 000000000..1ea0b1d84 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/general/PercentPriceFilter.java @@ -0,0 +1,18 @@ +package com.binance.api.client.domain.general; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PercentPriceFilter extends SymbolFilter { + + String multiplierUp; + String multiplierDown; + int avgPriceMins; + + public boolean validate(double price, double currentPrice) { + return price <= currentPrice * dbl(multiplierUp) && price >= currentPrice * dbl(multiplierDown); + } + +} diff --git a/src/main/java/com/binance/api/client/domain/general/PriceFilter.java b/src/main/java/com/binance/api/client/domain/general/PriceFilter.java new file mode 100644 index 000000000..21da96674 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/general/PriceFilter.java @@ -0,0 +1,28 @@ +package com.binance.api.client.domain.general; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PriceFilter extends SymbolFilter { + + String minPrice; + String maxPrice; + String tickSize; + + public boolean validate(double price) { + boolean valid = true; + if (dbl(minPrice) != 0) { + valid &= price >= dbl(minPrice); + } + if (dbl(maxPrice) != 0) { + valid &= price <= dbl(maxPrice); + } + if (dbl(tickSize) != 0) { + valid &= (price - dbl(minPrice)) % dbl(tickSize) == 0; + } + return valid; + } + +} diff --git a/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java b/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java index 7f4c6bb99..1e440f156 100755 --- a/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java +++ b/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java @@ -1,6 +1,13 @@ package com.binance.api.client.domain.general; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + +import lombok.Data; /** * Filters define trading rules on a symbol or an exchange. Filters come in two forms: symbol filters and exchange filters. @@ -16,152 +23,31 @@ * The MAX_ALGO_ORDERS filter defines the maximum number of "algo" orders an account is allowed to have open on a symbol. "Algo" orders are STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class SymbolFilter { - - // PRICE_FILTER - - private FilterType filterType; - - /** - * Defines the minimum price/stopPrice allowed. - */ - private String minPrice; - - /** - * Defines the maximum price/stopPrice allowed. - */ - private String maxPrice; - - /** - * Defines the intervals that a price/stopPrice can be increased/decreased by. - */ - private String tickSize; - - - // LOT_SIZE - - /** - * Defines the minimum quantity/icebergQty allowed. - */ - private String minQty; - - /** - * Defines the maximum quantity/icebergQty allowed. - */ - private String maxQty; - - /** - * Defines the intervals that a quantity/icebergQty can be increased/decreased by. - */ - private String stepSize; - - // MIN_NOTIONAL - - /** - * Defines the minimum notional value allowed for an order on a symbol. An order's notional value is the price * quantity. - */ - private String minNotional; - - - // MAX_NUM_ALGO_ORDERS - - /** - * Defines the maximum number of "algo" orders an account is allowed to have open on a symbol. "Algo" orders are STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders. - */ - private String maxNumAlgoOrders; - - /** - * MAX_NUM_ORDERS filter defines the maximum number of orders an account is allowed to have open on a symbol. Note that both "algo" orders and normal orders are counted for this filter. - * MAX_ALGO_ORDERS filter defines the maximum number of "algo" orders an account is allowed to have open on a symbol. "Algo" orders are STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, and TAKE_PROFIT_LIMIT orders. - * ICEBERG_PARTS filter defines the maximum parts an iceberg order can have. The number of ICEBERG_PARTS is defined as CEIL(qty / icebergQty). - */ - private String limit; - - public FilterType getFilterType() { - return filterType; - } - - public void setFilterType(FilterType filterType) { - this.filterType = filterType; - } - - public String getMinPrice() { - return minPrice; +@JsonTypeInfo( + use = Id.NAME, + property = "filterType", + include = As.EXISTING_PROPERTY, + visible = true +) +@JsonSubTypes({ + @Type(value = PriceFilter.class, name = "PRICE_FILTER"), + @Type(value = PercentPriceFilter.class, name = "PERCENT_PRICE"), + @Type(value = LotSizeFilter.class, name = "LOT_SIZE"), + @Type(value = MinNotionalFilter.class, name = "MIN_NOTIONAL"), + @Type(value = IcebergPartsFilter.class, name = "ICEBERG_PARTS"), + @Type(value = MarketLotSizeFilter.class, name = "MARKET_LOT_SIZE"), + @Type(value = MaxNumOrdersFilter.class, name = "MAX_NUM_ORDERS"), + @Type(value = MaxNumAlgoOrdersFilter.class, name = "MAX_NUM_ALGO_ORDERS"), + @Type(value = MaxNumIcebergOrders.class, name = "MAX_NUM_ICEBERG_ORDERS"), + @Type(value = MaxPositionFilter.class, name = "MAX_POSITION"), +}) +@Data +public abstract class SymbolFilter { + + FilterType filterType; + + double dbl(String s) { + return Double.parseDouble(s); } - public void setMinPrice(String minPrice) { - this.minPrice = minPrice; - } - - public String getMaxPrice() { - return maxPrice; - } - - public void setMaxPrice(String maxPrice) { - this.maxPrice = maxPrice; - } - - public String getTickSize() { - return tickSize; - } - - public void setTickSize(String tickSize) { - this.tickSize = tickSize; - } - - public String getMinQty() { - return minQty; - } - - public void setMinQty(String minQty) { - this.minQty = minQty; - } - - public String getMaxQty() { - return maxQty; - } - - public void setMaxQty(String maxQty) { - this.maxQty = maxQty; - } - - public String getStepSize() { - return stepSize; - } - - public void setStepSize(String stepSize) { - this.stepSize = stepSize; - } - - public String getMinNotional() { - return minNotional; - } - - public void setMinNotional(String minNotional) { - this.minNotional = minNotional; - } - - public String getMaxNumAlgoOrders() { - return maxNumAlgoOrders; - } - - public SymbolFilter setMaxNumAlgoOrders(String maxNumAlgoOrders) { - this.maxNumAlgoOrders = maxNumAlgoOrders; - return this; - } - - public String getLimit() { - return limit; - } - - public void setLimit(String limit) { - this.limit = limit; - } - - @Override - public String toString() { - return "SymbolFilter [filterType=" + filterType + ", minPrice=" + minPrice + ", maxPrice=" + maxPrice + ", tickSize=" + tickSize - + ", minQty=" + minQty + ", maxQty=" + maxQty + ", stepSize=" + stepSize + ", minNotional=" + minNotional - + ", maxNumAlgoOrders=" + maxNumAlgoOrders + ", limit=" + limit + "]"; - } } \ No newline at end of file diff --git a/src/main/java/com/binance/api/client/domain/general/SymbolInfo.java b/src/main/java/com/binance/api/client/domain/general/SymbolInfo.java index 6dc694f3b..3c46405f1 100755 --- a/src/main/java/com/binance/api/client/domain/general/SymbolInfo.java +++ b/src/main/java/com/binance/api/client/domain/general/SymbolInfo.java @@ -121,6 +121,14 @@ public SymbolFilter getSymbolFilter(FilterType filterType) { .get(); } + public T getSymbolFilter(Class filterType) { + return filters.stream() + .filter(symbolFilter -> symbolFilter.getClass() == filterType) + .map(filterType::cast) + .findFirst() + .get(); + } + @Override public String toString() { return new ToStringBuilder(this, BinanceApiConstants.TO_STRING_BUILDER_STYLE) diff --git a/src/test/java/com/binance/api/domain/general/ExchangeInfoDeserializerTest.java b/src/test/java/com/binance/api/domain/general/ExchangeInfoDeserializerTest.java index 94467915b..f6bb97002 100755 --- a/src/test/java/com/binance/api/domain/general/ExchangeInfoDeserializerTest.java +++ b/src/test/java/com/binance/api/domain/general/ExchangeInfoDeserializerTest.java @@ -1,8 +1,21 @@ package com.binance.api.domain.general; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + import com.binance.api.client.domain.OrderType; import com.binance.api.client.domain.general.ExchangeInfo; import com.binance.api.client.domain.general.FilterType; +import com.binance.api.client.domain.general.LotSizeFilter; +import com.binance.api.client.domain.general.MinNotionalFilter; +import com.binance.api.client.domain.general.PriceFilter; import com.binance.api.client.domain.general.RateLimit; import com.binance.api.client.domain.general.RateLimitInterval; import com.binance.api.client.domain.general.RateLimitType; @@ -10,15 +23,6 @@ import com.binance.api.client.domain.general.SymbolInfo; import com.binance.api.client.domain.general.SymbolStatus; import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.Test; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; /** * Test deserialization of exchange information. @@ -100,19 +104,20 @@ public void testExchangeInfoDeserialization() { List symbolFilters = symbolInfo.getFilters(); assertEquals(symbolFilters.size(), 3); - SymbolFilter priceFilter = symbolFilters.get(0); + PriceFilter priceFilter = (PriceFilter) symbolFilters.get(0); assertEquals(priceFilter.getFilterType(), FilterType.PRICE_FILTER); + assertEquals(priceFilter.getMinPrice(), "0.00000100"); assertEquals(priceFilter.getMaxPrice(), "100000.00000000"); assertEquals(priceFilter.getTickSize(), "0.00000100"); - SymbolFilter lotSizeFilter = symbolFilters.get(1); + LotSizeFilter lotSizeFilter = (LotSizeFilter) symbolFilters.get(1); assertEquals(lotSizeFilter.getFilterType(), FilterType.LOT_SIZE); assertEquals(lotSizeFilter.getMinQty(), "0.00100000"); assertEquals(lotSizeFilter.getMaxQty(), "100000.00000000"); assertEquals(lotSizeFilter.getStepSize(), "0.00100000"); - SymbolFilter minNotionalFilter = symbolFilters.get(2); + MinNotionalFilter minNotionalFilter = (MinNotionalFilter) symbolFilters.get(2); assertEquals(minNotionalFilter.getFilterType(), FilterType.MIN_NOTIONAL); assertEquals(minNotionalFilter.getMinNotional(), "0.00100000"); } catch (IOException e) { diff --git a/src/test/java/com/binance/api/examples/GeneralEndpointsExample.java b/src/test/java/com/binance/api/examples/GeneralEndpointsExample.java index 760d6f26a..74b4c845e 100755 --- a/src/test/java/com/binance/api/examples/GeneralEndpointsExample.java +++ b/src/test/java/com/binance/api/examples/GeneralEndpointsExample.java @@ -1,15 +1,15 @@ package com.binance.api.examples; +import java.util.List; + import com.binance.api.client.BinanceApiClientFactory; import com.binance.api.client.BinanceApiRestClient; import com.binance.api.client.domain.general.Asset; import com.binance.api.client.domain.general.ExchangeInfo; import com.binance.api.client.domain.general.FilterType; -import com.binance.api.client.domain.general.SymbolFilter; +import com.binance.api.client.domain.general.PriceFilter; import com.binance.api.client.domain.general.SymbolInfo; -import java.util.List; - /** * Examples on how to use the general endpoints. */ @@ -35,7 +35,7 @@ public static void main(String[] args) { SymbolInfo symbolInfo = exchangeInfo.getSymbolInfo("ETHBTC"); System.out.println(symbolInfo.getStatus()); - SymbolFilter priceFilter = symbolInfo.getSymbolFilter(FilterType.PRICE_FILTER); + PriceFilter priceFilter = (PriceFilter) symbolInfo.getSymbolFilter(FilterType.PRICE_FILTER); System.out.println(priceFilter.getMinPrice()); System.out.println(priceFilter.getTickSize()); diff --git a/src/test/java/com/binance/api/examples/GeneralEndpointsExampleAsync.java b/src/test/java/com/binance/api/examples/GeneralEndpointsExampleAsync.java index 96739a5c2..5669c4a7a 100755 --- a/src/test/java/com/binance/api/examples/GeneralEndpointsExampleAsync.java +++ b/src/test/java/com/binance/api/examples/GeneralEndpointsExampleAsync.java @@ -2,13 +2,10 @@ import com.binance.api.client.BinanceApiAsyncRestClient; import com.binance.api.client.BinanceApiClientFactory; -import com.binance.api.client.domain.general.Asset; import com.binance.api.client.domain.general.FilterType; -import com.binance.api.client.domain.general.SymbolFilter; +import com.binance.api.client.domain.general.PriceFilter; import com.binance.api.client.domain.general.SymbolInfo; -import java.util.List; - /** * Examples on how to use the general endpoints. */ @@ -33,7 +30,7 @@ public static void main(String[] args) throws InterruptedException { SymbolInfo symbolInfo = exchangeInfo.getSymbolInfo("ETHBTC"); System.out.println(symbolInfo.getStatus()); - SymbolFilter priceFilter = symbolInfo.getSymbolFilter(FilterType.PRICE_FILTER); + PriceFilter priceFilter = (PriceFilter) symbolInfo.getSymbolFilter(FilterType.PRICE_FILTER); System.out.println(priceFilter.getMinPrice()); System.out.println(priceFilter.getTickSize()); }); From d0a57f3a5e6b14440c575e7b3136d73b11591d73 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 30 Jan 2021 22:24:26 +0000 Subject: [PATCH 020/101] [ci-skip]prepare release binance-api-client-2.1.0 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 5a305fdf2..85f9b2d43 100644 --- a/pom.xml +++ b/pom.xml @@ -1,11 +1,10 @@ - + 4.0.0 com.binance.api binance-api-client - 2.1.0-SNAPSHOT + 2.1.0 The MIT License @@ -83,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.1.0 From 482b8e9523ece0b4cd3dc3ec0ff2dba3de1d7fc8 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 30 Jan 2021 22:24:53 +0000 Subject: [PATCH 021/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 85f9b2d43..65819d41d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.1.0 + 2.1.1-SNAPSHOT The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.1.0 + HEAD From 0b632ef736a2fe5af1660b6cf53ba93c2586014e Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 13 Feb 2021 11:33:19 +0100 Subject: [PATCH 022/101] master implemented rate limiter to prevent us from getting a ban --- pom.xml | 2 +- .../com/binance/api/client/constant/Util.java | 34 +++++---- .../impl/BinanceApiServiceGenerator.java | 1 + .../impl/RequestRateLimitingInterceptor.java | 75 +++++++++++++++++++ 4 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java diff --git a/pom.xml b/pom.xml index 65819d41d..2d63dd4e6 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.1.1-SNAPSHOT + 2.2.0-SNAPSHOT The MIT License diff --git a/src/main/java/com/binance/api/client/constant/Util.java b/src/main/java/com/binance/api/client/constant/Util.java index 6e459c037..c210601f2 100644 --- a/src/main/java/com/binance/api/client/constant/Util.java +++ b/src/main/java/com/binance/api/client/constant/Util.java @@ -1,6 +1,5 @@ package com.binance.api.client.constant; - import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -10,24 +9,27 @@ */ public final class Util { - /** - * List of fiat currencies. - */ - public static final List FIAT_CURRENCY = Collections.unmodifiableList(Arrays.asList("USDT", "BUSD", "PAX", "TUSD", "USDC", "NGN", "RUB", "USDS, TRY")); + /** + * List of fiat currencies. + */ + public static final List FIAT_CURRENCY = Collections.unmodifiableList(Arrays.asList("USDT", "USD", "EUR", "BUSD", "PAX", "BKRW", + "TUSD", "USDC", "TRY", "AUD", "BIDR", "BRL", "DAI", "GBP", "IDRT", "NGN", "RUB", "UAH", "ZAR", "BVND")); - public static final String BTC_TICKER = "BTC"; + public static final String BTC_TICKER = "BTC"; - private Util() { + private Util() { - } + } - /** - * Check if the ticker is fiat currency. - */ - public static boolean isFiatCurrency(String symbol) { - for (String fiat : FIAT_CURRENCY) { - if (symbol.equals(fiat)) return true; - } - return false; + /** + * Check if the ticker is fiat currency. + */ + public static boolean isFiatCurrency(String symbol) { + for (String fiat : FIAT_CURRENCY) { + if (symbol.equals(fiat)) { + return true; + } } + return false; + } } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java index c2d3a450d..a5270aa17 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -53,6 +53,7 @@ public void log(String message) { logger.setLevel(Level.BODY); builder.addInterceptor(logger); } + builder.addInterceptor(new RequestRateLimitingInterceptor()); sharedClient = builder.build(); } diff --git a/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java b/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java new file mode 100644 index 000000000..2c3c4eb07 --- /dev/null +++ b/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java @@ -0,0 +1,75 @@ +package com.binance.api.client.impl; + +import static java.lang.Integer.parseInt; +import static java.lang.Thread.sleep; + +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import com.binance.api.client.domain.general.ExchangeInfo; +import com.binance.api.client.domain.general.RateLimit; +import com.binance.api.client.domain.general.RateLimitType; +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +@Slf4j +public class RequestRateLimitingInterceptor implements Interceptor { + + private final int limit; + + public RequestRateLimitingInterceptor() { + this(getLimitsDefault()); + } + + static RateLimit getLimitsDefault() { + ObjectMapper mapper = new ObjectMapper(); + try { + ExchangeInfo info = mapper.readerFor(ExchangeInfo.class).readValue(new URL("https://api.binance.com/api/v3/exchangeInfo")); + List rateLimits = info.getRateLimits(); + return rateLimits.stream().filter(l -> l.getRateLimitType() == RateLimitType.REQUEST_WEIGHT).findFirst().get(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public RequestRateLimitingInterceptor(RateLimit requestWeightLimit) { + this.limit = (int) (requestWeightLimit.getLimit() * 0.9); + } + + AtomicInteger weightUsed = new AtomicInteger(); + + @Override + public Response intercept(Chain chain) throws IOException { + if (weightUsed.get() >= limit) { + try { + log.warn("approaching request weight limit, slowing down a bit"); + sleep(1000); + } catch (InterruptedException e) { + throw new IOException(e); + } + } + Request request = chain.request(); + Response response = chain.proceed(request); + if (response.code() == 418 || response.code() == 429) { + try { + log.warn("reached request weight limit, retrying after {}", response.header("retry-after")); + sleep(parseInt(response.header("retry-after")) * 1000); + response = chain.proceed(request); + } catch (Exception e) { + throw new IOException(e); + } + } + String header = response.header("x-mbx-used-weight"); + if (header != null) { + weightUsed.set(parseInt(header)); + } + return response; + } + +} From aec04c4b4c3e5a19ec2fa27d9793a4347d09c701 Mon Sep 17 00:00:00 2001 From: Sergey Zolotaryov Date: Sat, 13 Feb 2021 11:35:33 +0100 Subject: [PATCH 023/101] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f9d29dc9d..b87b90d48 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ binance-java-api is a lightweight Java library for interacting with the [Binance ## Features * Support for synchronous and asynchronous REST requests to all [General](https://www.binance.com/restapipub.html#user-content-general-endpoints), [Market Data](https://www.binance.com/restapipub.html#user-content-market-data-endpoints), [Account](https://www.binance.com/restapipub.html#user-content-account-endpoints) endpoints, and [User](https://www.binance.com/restapipub.html#user-content-user-data-stream-endpoints) stream endpoints. * Support for User Data, Trade, Kline, and Depth event streaming using [Binance WebSocket API](https://www.binance.com/restapipub.html#wss-endpoint). +* Implements a simple request rate limiter to prevent your application from getting an IP ban in case you send too many requests too fast. ## Installation 1. Install library into your Maven's local repository by running `mvn install` From 34776867d4d5f8137633d87abb53b81b21ee53ba Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 13 Feb 2021 10:37:52 +0000 Subject: [PATCH 024/101] [ci-skip]prepare release binance-api-client-2.2.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2d63dd4e6..129169a63 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.2.0-SNAPSHOT + 2.2.0 The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.2.0 From 9cc15d322357a4d352643143ef13f460c11ec4d7 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 13 Feb 2021 10:39:50 +0000 Subject: [PATCH 025/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 129169a63..618370abc 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.2.0 + 2.2.1-SNAPSHOT The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.2.0 + HEAD From bb67e9e5d81269dedfb64935178963c5747c2fdb Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 13 Feb 2021 14:19:31 +0100 Subject: [PATCH 026/101] master forgot to close response after retry --- .../binance/api/client/impl/RequestRateLimitingInterceptor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java b/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java index 2c3c4eb07..772eef1e1 100644 --- a/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java +++ b/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java @@ -58,6 +58,7 @@ public Response intercept(Chain chain) throws IOException { Response response = chain.proceed(request); if (response.code() == 418 || response.code() == 429) { try { + response.close(); log.warn("reached request weight limit, retrying after {}", response.header("retry-after")); sleep(parseInt(response.header("retry-after")) * 1000); response = chain.proceed(request); From c0ba11cc3c870dc0bbd2f0b0762c58d9034db80b Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 13 Feb 2021 13:23:29 +0000 Subject: [PATCH 027/101] [ci-skip]prepare release binance-api-client-2.2.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 618370abc..663080056 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.2.1-SNAPSHOT + 2.2.1 The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.2.1 From 192f3fbdcd6ee460ad2681643ada31a26d3b6d0c Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 13 Feb 2021 13:24:31 +0000 Subject: [PATCH 028/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 663080056..1b696eb74 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.2.1 + 2.2.2-SNAPSHOT The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.2.1 + HEAD From 64e6ec60ffd2bb4840bb21bd61fdcf40fa6a0e23 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Fri, 19 Feb 2021 19:17:23 +0100 Subject: [PATCH 029/101] master added proper toString methods to OrderStatusRequest --- .../client/domain/account/request/OrderRequest.java | 12 +++--------- .../domain/account/request/OrderStatusRequest.java | 11 ++--------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/binance/api/client/domain/account/request/OrderRequest.java b/src/main/java/com/binance/api/client/domain/account/request/OrderRequest.java index cad6033e3..17e4c2922 100755 --- a/src/main/java/com/binance/api/client/domain/account/request/OrderRequest.java +++ b/src/main/java/com/binance/api/client/domain/account/request/OrderRequest.java @@ -2,12 +2,14 @@ import com.binance.api.client.constant.BinanceApiConstants; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import org.apache.commons.lang3.builder.ToStringBuilder; + +import lombok.ToString; /** * Base request parameters for order-related methods. */ @JsonIgnoreProperties(ignoreUnknown = true) +@ToString public class OrderRequest { private final String symbol; @@ -44,12 +46,4 @@ public OrderRequest timestamp(Long timestamp) { return this; } - @Override - public String toString() { - return new ToStringBuilder(this, BinanceApiConstants.TO_STRING_BUILDER_STYLE) - .append("symbol", symbol) - .append("recvWindow", recvWindow) - .append("timestamp", timestamp) - .toString(); - } } diff --git a/src/main/java/com/binance/api/client/domain/account/request/OrderStatusRequest.java b/src/main/java/com/binance/api/client/domain/account/request/OrderStatusRequest.java index bde5326d2..d763e45c3 100755 --- a/src/main/java/com/binance/api/client/domain/account/request/OrderStatusRequest.java +++ b/src/main/java/com/binance/api/client/domain/account/request/OrderStatusRequest.java @@ -1,11 +1,11 @@ package com.binance.api.client.domain.account.request; -import com.binance.api.client.constant.BinanceApiConstants; -import org.apache.commons.lang3.builder.ToStringBuilder; +import lombok.ToString; /** * A specialized order request with additional filters. */ +@ToString public class OrderStatusRequest extends OrderRequest { private Long orderId; @@ -40,11 +40,4 @@ public OrderStatusRequest origClientOrderId(String origClientOrderId) { return this; } - @Override - public String toString() { - return new ToStringBuilder(this, BinanceApiConstants.TO_STRING_BUILDER_STYLE) - .append("orderId", orderId) - .append("origClientOrderId", origClientOrderId) - .toString(); - } } From f7c04bcc596da7bea74fb4b1664b53b1be220738 Mon Sep 17 00:00:00 2001 From: jenkins Date: Fri, 19 Feb 2021 18:25:10 +0000 Subject: [PATCH 030/101] [ci-skip]prepare release binance-api-client-2.2.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1b696eb74..51fc373bc 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.2.2-SNAPSHOT + 2.2.2 The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.2.2 From da15c5c79b7999b2e55dcc984ec9e843e0c2b708 Mon Sep 17 00:00:00 2001 From: jenkins Date: Fri, 19 Feb 2021 18:27:50 +0000 Subject: [PATCH 031/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 51fc373bc..bba08e40d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.2.2 + 2.2.3-SNAPSHOT The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.2.2 + HEAD From 33802aa1783c74579f76a7a7f4b7403128d8d724 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Wed, 24 Feb 2021 23:38:07 +0100 Subject: [PATCH 032/101] master added convert dust to BNB method call --- README.md | 11 ++++++++++ pom.xml | 2 +- .../api/client/BinanceApiAsyncRestClient.java | 9 ++++++++ .../api/client/BinanceApiRestClient.java | 11 +++++++++- .../domain/account/DustTransferResponse.java | 22 +++++++++++++++++++ .../domain/account/DustTransferResult.java | 22 +++++++++++++++++++ .../impl/BinanceApiAsyncRestClientImpl.java | 7 ++++++ .../client/impl/BinanceApiRestClientImpl.java | 7 ++++++ .../api/client/impl/BinanceApiService.java | 8 +++++++ 9 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/binance/api/client/domain/account/DustTransferResponse.java create mode 100644 src/main/java/com/binance/api/client/domain/account/DustTransferResult.java diff --git a/README.md b/README.md index f9d29dc9d..71b1dc46e 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,17 @@ DepositAddress[address=0x99...,success=true,addressTag=,asset=ETH] ``` +#### Convert tiny amounts to BNB + +There are cases when amount of coin on balance is too small to trade it. It is possible however to exchange these "dust" amounts for BNB, which can be used to pay reduced commission fees. In order to do so choose your assets and call the + +```java +DustTransferResponse response = client.convertDustToBnb(Arrays.asList("ONE", "XRP")); + +``` + + + ### User stream endpoints #### Start user data stream, keepalive, and close data stream diff --git a/pom.xml b/pom.xml index bba08e40d..45072ec2d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.2.3-SNAPSHOT + 2.3.0-SNAPSHOT The MIT License diff --git a/src/main/java/com/binance/api/client/BinanceApiAsyncRestClient.java b/src/main/java/com/binance/api/client/BinanceApiAsyncRestClient.java index 4ba138b79..49a7bec45 100755 --- a/src/main/java/com/binance/api/client/BinanceApiAsyncRestClient.java +++ b/src/main/java/com/binance/api/client/BinanceApiAsyncRestClient.java @@ -5,6 +5,7 @@ import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; +import com.binance.api.client.domain.account.DustTransferResponse; import com.binance.api.client.domain.account.NewOrder; import com.binance.api.client.domain.account.NewOrderResponse; import com.binance.api.client.domain.account.OcoOrderResponse; @@ -328,4 +329,12 @@ public interface BinanceApiAsyncRestClient { * @param callback the callback that handles the response which contains a listenKey */ void closeUserDataStream(String listenKey, BinanceApiCallback callback); + + /** + * Converts tiny amounts of coins to BNB. + * + * @param assets the assets you wish to exchange for BNBs + * @param callback the callback that handles the response + */ + void convertDustToBnb(List assets, BinanceApiCallback callback); } \ No newline at end of file diff --git a/src/main/java/com/binance/api/client/BinanceApiRestClient.java b/src/main/java/com/binance/api/client/BinanceApiRestClient.java index 8cd601d17..6c754ff2a 100755 --- a/src/main/java/com/binance/api/client/BinanceApiRestClient.java +++ b/src/main/java/com/binance/api/client/BinanceApiRestClient.java @@ -5,6 +5,7 @@ import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; +import com.binance.api.client.domain.account.DustTransferResponse; import com.binance.api.client.domain.account.NewOrder; import com.binance.api.client.domain.account.NewOrderResponse; import com.binance.api.client.domain.account.OcoOrderResponse; @@ -324,4 +325,12 @@ public interface BinanceApiRestClient { */ void closeUserDataStream(String listenKey); -} + /** + * Converts tiny amounts of coins to BNB. + * + * @param assets the assets you wish to exchange for BNBs + * @return transfer response + */ + DustTransferResponse convertDustToBnb(List assets); + +} \ No newline at end of file diff --git a/src/main/java/com/binance/api/client/domain/account/DustTransferResponse.java b/src/main/java/com/binance/api/client/domain/account/DustTransferResponse.java new file mode 100644 index 000000000..1355bb1af --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/DustTransferResponse.java @@ -0,0 +1,22 @@ +package com.binance.api.client.domain.account; + +import java.util.List; + +import lombok.Data; + +/** + * Represents a response from the call to dust transfer (swapping tiny untradeable amounts of coins for BNB). + * + * @author sergey + * + */ +@Data +public class DustTransferResponse { + + double totalServiceCharge; + + double totalTransfered; + + List transferResult; + +} diff --git a/src/main/java/com/binance/api/client/domain/account/DustTransferResult.java b/src/main/java/com/binance/api/client/domain/account/DustTransferResult.java new file mode 100644 index 000000000..ef84d0c45 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/DustTransferResult.java @@ -0,0 +1,22 @@ +package com.binance.api.client.domain.account; + +import lombok.Data; + +/** + * Individual small amount exchange for BNB. + * + * @author sergey + * + * @see DustTransferResponse + */ +@Data +public class DustTransferResult { + + double amount; + String fromAsset; + long operateTime; + double serviceChargeAmount; + long tranId; + double transferedAmount; + +} diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java index 8655d7e6b..2becf4697 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java @@ -1,6 +1,7 @@ package com.binance.api.client.impl; import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; +import static java.lang.System.currentTimeMillis; import java.util.List; @@ -12,6 +13,7 @@ import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; +import com.binance.api.client.domain.account.DustTransferResponse; import com.binance.api.client.domain.account.NewOrder; import com.binance.api.client.domain.account.NewOrderResponse; import com.binance.api.client.domain.account.OcoOrderResponse; @@ -250,6 +252,11 @@ public void getDepositAddress(String asset, BinanceApiCallback c .enqueue(new BinanceApiCallbackAdapter<>(callback)); } + @Override + public void convertDustToBnb(List assets, BinanceApiCallback callback) { + binanceApiService.dustTransfer(assets, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, currentTimeMillis()); + } + // User stream endpoints @Override diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java index 8f7d311a3..3e303cb7e 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java @@ -2,6 +2,7 @@ import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; import static com.binance.api.client.impl.BinanceApiServiceGenerator.executeSync; +import static java.lang.System.currentTimeMillis; import java.util.List; @@ -13,6 +14,7 @@ import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; +import com.binance.api.client.domain.account.DustTransferResponse; import com.binance.api.client.domain.account.NewOrder; import com.binance.api.client.domain.account.NewOrderResponse; import com.binance.api.client.domain.account.OcoOrderResponse; @@ -260,6 +262,11 @@ public DepositAddress getDepositAddress(String asset) { return executeSync(binanceApiService.getDepositAddress(asset, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, System.currentTimeMillis())); } + @Override + public DustTransferResponse convertDustToBnb(List assets) { + return executeSync(binanceApiService.dustTransfer(assets, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, currentTimeMillis())); + } + // User stream endpoints @Override diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiService.java b/src/main/java/com/binance/api/client/impl/BinanceApiService.java index 6dd66468e..fec258238 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiService.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiService.java @@ -10,6 +10,7 @@ import com.binance.api.client.domain.account.Account; import com.binance.api.client.domain.account.DepositAddress; import com.binance.api.client.domain.account.DepositHistory; +import com.binance.api.client.domain.account.DustTransferResponse; import com.binance.api.client.domain.account.Liquidity; import com.binance.api.client.domain.account.LiquidityOperationRecord; import com.binance.api.client.domain.account.LoanQueryResult; @@ -379,4 +380,11 @@ Call> getSwapHistory( @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) + @POST("/sapi/v1/asset/dust") + Call dustTransfer( + @Query("asset") List assets, + @Query("recvWindow") Long recvWindow, + @Query("timestamp") Long timestamp); + } From bf2f8aec91ba0e63cce620798197ff6a4054aac5 Mon Sep 17 00:00:00 2001 From: jenkins Date: Wed, 24 Feb 2021 22:43:56 +0000 Subject: [PATCH 033/101] [ci-skip]prepare release binance-api-client-2.3.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 45072ec2d..197f262a6 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.3.0-SNAPSHOT + 2.3.0 The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.3.0 From 5ceb46029dbdbb52ae2c781c1968da6102667e48 Mon Sep 17 00:00:00 2001 From: jenkins Date: Wed, 24 Feb 2021 22:47:56 +0000 Subject: [PATCH 034/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 197f262a6..da04285b8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.3.0 + 2.3.1-SNAPSHOT The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.3.0 + HEAD From 365a238108c44b3331690e039b2a20af75aca27f Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Thu, 25 Feb 2021 21:25:41 +0100 Subject: [PATCH 035/101] master forgot to enqueue the call for async client --- .../binance/api/client/impl/BinanceApiAsyncRestClientImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java index 2becf4697..9c8f2fce3 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java @@ -254,7 +254,8 @@ public void getDepositAddress(String asset, BinanceApiCallback c @Override public void convertDustToBnb(List assets, BinanceApiCallback callback) { - binanceApiService.dustTransfer(assets, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, currentTimeMillis()); + binanceApiService.dustTransfer(assets, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, currentTimeMillis()) + .enqueue(new BinanceApiCallbackAdapter<>(callback)); } // User stream endpoints From d9f6d306b237cbd18b96ef0ef244566b0778fc01 Mon Sep 17 00:00:00 2001 From: jenkins Date: Thu, 25 Feb 2021 20:30:34 +0000 Subject: [PATCH 036/101] [ci-skip]prepare release binance-api-client-2.3.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index da04285b8..bbb563993 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.3.1-SNAPSHOT + 2.3.1 The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.3.1 From d2ad930b72335c0cabeb3880e046106c45a4c3a8 Mon Sep 17 00:00:00 2001 From: jenkins Date: Thu, 25 Feb 2021 20:32:47 +0000 Subject: [PATCH 037/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bbb563993..96de5461a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.3.1 + 2.3.2-SNAPSHOT The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.3.1 + HEAD From 535ce9ac2973adb975c56e607407be8e74a5a7dc Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 27 Feb 2021 23:59:51 +0100 Subject: [PATCH 038/101] asynchttpclient started working on it --- pom.xml | 29 ++++++-- .../api/client/config/BinanceApiConfig.java | 5 -- .../impl/BinanceApiServiceGenerator.java | 72 +++++++------------ 3 files changed, 50 insertions(+), 56 deletions(-) diff --git a/pom.xml b/pom.xml index 96de5461a..536190efc 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,11 @@ - + 4.0.0 com.binance.api binance-api-client - 2.3.2-SNAPSHOT + 2.4.0-SNAPSHOT The MIT License @@ -29,9 +30,12 @@ ${com.squareup.retrofit2.version} - com.squareup.okhttp3 - logging-interceptor - 3.10.0 + org.asynchttpclient + async-http-client + + + org.asynchttpclient + async-http-client-extras-retrofit2 org.slf4j @@ -63,11 +67,23 @@ + + + + org.asynchttpclient + async-http-client-bom + 2.12.2 + pom + import + + + + - org.apache.maven.plugins maven-compiler-plugin + 3.8.1 1.8 1.8 @@ -76,6 +92,7 @@ maven-surefire-plugin + 3.0.0-M5 diff --git a/src/main/java/com/binance/api/client/config/BinanceApiConfig.java b/src/main/java/com/binance/api/client/config/BinanceApiConfig.java index d72af292e..d05538efb 100644 --- a/src/main/java/com/binance/api/client/config/BinanceApiConfig.java +++ b/src/main/java/com/binance/api/client/config/BinanceApiConfig.java @@ -49,9 +49,4 @@ public static String getAssetInfoApiBaseUrl() { return String.format("https://%s/", getBaseDomain()); } - /** - * Set this system property to true to enable HTTP payloads logging - */ - public static final String BINANCE_API_LOGGING_ENABLED = "binance.api.logging.enabled"; - } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java index a5270aa17..f7785a58a 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -4,9 +4,11 @@ import java.lang.annotation.Annotation; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.asynchttpclient.DefaultAsyncHttpClientConfig; +import org.asynchttpclient.Dsl; +import org.asynchttpclient.filter.FilterContext; +import org.asynchttpclient.filter.FilterException; +import org.asynchttpclient.filter.RequestFilter; import com.binance.api.client.BinanceApiError; import com.binance.api.client.config.BinanceApiConfig; @@ -17,12 +19,10 @@ import okhttp3.OkHttpClient; import okhttp3.OkHttpClient.Builder; import okhttp3.ResponseBody; -import okhttp3.logging.HttpLoggingInterceptor; -import okhttp3.logging.HttpLoggingInterceptor.Level; +import ratpack.retrofit.RatpackRetrofit; import retrofit2.Call; import retrofit2.Converter; import retrofit2.Response; -import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; /** @@ -30,29 +30,24 @@ */ public class BinanceApiServiceGenerator { - private static final OkHttpClient sharedClient; private static final Converter.Factory converterFactory = JacksonConverterFactory.create(); + private static OkHttpClient sharedClient; static { + Dsl.asyncHttpClient(new DefaultAsyncHttpClientConfig.Builder().addRequestFilter(new RequestFilter() { + + @Override + public FilterContext filter(FilterContext ctx) throws FilterException { + return ctx; + } + })); + Dispatcher dispatcher = new Dispatcher(); dispatcher.setMaxRequestsPerHost(500); dispatcher.setMaxRequests(500); Builder builder = new OkHttpClient.Builder() .dispatcher(dispatcher) .pingInterval(20, TimeUnit.SECONDS); - if (Boolean.getBoolean(BinanceApiConfig.BINANCE_API_LOGGING_ENABLED)) { - Logger lg = LoggerFactory.getLogger(BinanceApiService.class); - HttpLoggingInterceptor logger = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { - - - @Override - public void log(String message) { - lg.info(message); - } - }); - logger.setLevel(Level.BODY); - builder.addInterceptor(logger); - } builder.addInterceptor(new RequestRateLimitingInterceptor()); sharedClient = builder.build(); } @@ -62,26 +57,19 @@ public void log(String message) { (Converter)converterFactory.responseBodyConverter( BinanceApiError.class, new Annotation[0], null); - public static S createService(Class serviceClass) { - return createService(serviceClass, null, null); - } - public static S createService(Class serviceClass, String apiKey, String secret) { - Retrofit.Builder retrofitBuilder = new Retrofit.Builder() - .baseUrl(BinanceApiConfig.getApiBaseUrl()) - .addConverterFactory(converterFactory); - - if (StringUtils.isEmpty(apiKey) || StringUtils.isEmpty(secret)) { - retrofitBuilder.client(sharedClient); - } else { - // `adaptedClient` will use its own interceptor, but share thread pool etc with the 'parent' client - AuthenticationInterceptor interceptor = new AuthenticationInterceptor(apiKey, secret); - OkHttpClient adaptedClient = sharedClient.newBuilder().addInterceptor(interceptor).build(); - retrofitBuilder.client(adaptedClient); - } - - Retrofit retrofit = retrofitBuilder.build(); - return retrofit.create(serviceClass); + + return RatpackRetrofit.client(BinanceApiConfig.getApiBaseUrl()).configure(builder -> { + builder.addConverterFactory(converterFactory); + }).build(serviceClass); +// // `adaptedClient` will use its own interceptor, but share thread pool etc with +// // the 'parent' client + AuthenticationInterceptor interceptor = new AuthenticationInterceptor(apiKey, secret); +// OkHttpClient adaptedClient = sharedClient.newBuilder().addInterceptor(interceptor).build(); +// retrofitBuilder.client(adaptedClient); +// +// Retrofit retrofit = retrofitBuilder.build(); +// return retrofit.create(serviceClass); } /** @@ -108,10 +96,4 @@ public static BinanceApiError getBinanceApiError(Response response) throws IO return errorBodyConverter.convert(response.errorBody()); } - /** - * Returns the shared OkHttpClient instance. - */ - public static OkHttpClient getSharedClient() { - return sharedClient; - } } From ea0c9df8bdc746109b925612a772f358da0f8b7a Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sun, 28 Feb 2021 11:32:14 +0100 Subject: [PATCH 039/101] asynchttpclient implemented async http client based on netty instead of okhttp --- pom.xml | 6 + .../api/client/BinanceApiClientFactory.java | 12 +- .../api/client/BinanceApiWebSocketClient.java | 8 +- .../impl/BinanceApiServiceGenerator.java | 145 +++++++------- .../impl/BinanceApiWebSocketClientImpl.java | 185 ++++++++---------- .../impl/BinanceApiWebSocketListener.java | 45 +++-- .../impl/RequestRateLimitingInterceptor.java | 51 ++--- .../security/AuthenticationInterceptor.java | 105 +++------- .../api/client/security/HmacSHA256Signer.java | 4 +- .../java/com/binance/api/examples/Netty.java | 17 ++ src/test/resources/logback-test.xml | 11 ++ 11 files changed, 281 insertions(+), 308 deletions(-) create mode 100644 src/test/java/com/binance/api/examples/Netty.java create mode 100644 src/test/resources/logback-test.xml diff --git a/pom.xml b/pom.xml index 536190efc..509f40512 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,12 @@ 4.12 test + + ch.qos.logback + logback-classic + 1.2.3 + test + diff --git a/src/main/java/com/binance/api/client/BinanceApiClientFactory.java b/src/main/java/com/binance/api/client/BinanceApiClientFactory.java index 842ad20d5..f5a6b2db1 100755 --- a/src/main/java/com/binance/api/client/BinanceApiClientFactory.java +++ b/src/main/java/com/binance/api/client/BinanceApiClientFactory.java @@ -1,8 +1,12 @@ package com.binance.api.client; -import com.binance.api.client.impl.*; - -import static com.binance.api.client.impl.BinanceApiServiceGenerator.getSharedClient; +import com.binance.api.client.impl.BinanceApiAsyncMarginRestClientImpl; +import com.binance.api.client.impl.BinanceApiAsyncRestClientImpl; +import com.binance.api.client.impl.BinanceApiMarginRestClientImpl; +import com.binance.api.client.impl.BinanceApiRestClientImpl; +import com.binance.api.client.impl.BinanceApiServiceGenerator; +import com.binance.api.client.impl.BinanceApiSwapRestClientImpl; +import com.binance.api.client.impl.BinanceApiWebSocketClientImpl; /** * A factory for creating BinanceApi client objects. @@ -83,7 +87,7 @@ public BinanceApiMarginRestClient newMarginRestClient() { * Creates a new web socket client used for handling data streams. */ public BinanceApiWebSocketClient newWebSocketClient() { - return new BinanceApiWebSocketClientImpl(getSharedClient()); + return new BinanceApiWebSocketClientImpl(BinanceApiServiceGenerator.getSharedClient()); } /** diff --git a/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java b/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java index f690b7d46..4cccdc72a 100755 --- a/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java +++ b/src/main/java/com/binance/api/client/BinanceApiWebSocketClient.java @@ -15,7 +15,7 @@ /** * Binance API data streaming facade, supporting streaming of events through web sockets. */ -public interface BinanceApiWebSocketClient extends Closeable { +public interface BinanceApiWebSocketClient { /** * Open a new web socket to receive {@link DepthEvent depthEvents} on a callback. @@ -97,10 +97,4 @@ public interface BinanceApiWebSocketClient extends Closeable { */ Closeable onAllBookTickersEvent(BinanceApiCallback callback); - /** - * @deprecated This method is no longer functional. Please use the returned {@link Closeable} from any of the other methods to close the web socket. - */ - @Override - @Deprecated - void close(); } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java index f7785a58a..13a513f73 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -2,98 +2,101 @@ import java.io.IOException; import java.lang.annotation.Annotation; -import java.util.concurrent.TimeUnit; -import org.asynchttpclient.DefaultAsyncHttpClientConfig; +import org.asynchttpclient.AsyncHttpClient; +import org.asynchttpclient.DefaultAsyncHttpClientConfig.Builder; import org.asynchttpclient.Dsl; -import org.asynchttpclient.filter.FilterContext; -import org.asynchttpclient.filter.FilterException; -import org.asynchttpclient.filter.RequestFilter; +import org.asynchttpclient.extras.retrofit.AsyncHttpClientCallFactory; import com.binance.api.client.BinanceApiError; import com.binance.api.client.config.BinanceApiConfig; import com.binance.api.client.exception.BinanceApiException; import com.binance.api.client.security.AuthenticationInterceptor; -import okhttp3.Dispatcher; -import okhttp3.OkHttpClient; -import okhttp3.OkHttpClient.Builder; +import io.netty.channel.ChannelOption; import okhttp3.ResponseBody; -import ratpack.retrofit.RatpackRetrofit; import retrofit2.Call; import retrofit2.Converter; import retrofit2.Response; +import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; /** - * Generates a Binance API implementation based on @see {@link BinanceApiService}. + * Generates a Binance API implementation based on @see + * {@link BinanceApiService}. */ public class BinanceApiServiceGenerator { - private static final Converter.Factory converterFactory = JacksonConverterFactory.create(); - private static OkHttpClient sharedClient; - - static { - Dsl.asyncHttpClient(new DefaultAsyncHttpClientConfig.Builder().addRequestFilter(new RequestFilter() { - - @Override - public FilterContext filter(FilterContext ctx) throws FilterException { - return ctx; - } - })); - - Dispatcher dispatcher = new Dispatcher(); - dispatcher.setMaxRequestsPerHost(500); - dispatcher.setMaxRequests(500); - Builder builder = new OkHttpClient.Builder() - .dispatcher(dispatcher) - .pingInterval(20, TimeUnit.SECONDS); - builder.addInterceptor(new RequestRateLimitingInterceptor()); - sharedClient = builder.build(); - } + private static final Converter.Factory converterFactory = JacksonConverterFactory.create(); + private static AsyncHttpClient sharedClient; - @SuppressWarnings("unchecked") - private static final Converter errorBodyConverter = - (Converter)converterFactory.responseBodyConverter( - BinanceApiError.class, new Annotation[0], null); - - public static S createService(Class serviceClass, String apiKey, String secret) { - - return RatpackRetrofit.client(BinanceApiConfig.getApiBaseUrl()).configure(builder -> { - builder.addConverterFactory(converterFactory); - }).build(serviceClass); -// // `adaptedClient` will use its own interceptor, but share thread pool etc with -// // the 'parent' client - AuthenticationInterceptor interceptor = new AuthenticationInterceptor(apiKey, secret); -// OkHttpClient adaptedClient = sharedClient.newBuilder().addInterceptor(interceptor).build(); -// retrofitBuilder.client(adaptedClient); -// -// Retrofit retrofit = retrofitBuilder.build(); -// return retrofit.create(serviceClass); - } + static { + RequestRateLimitingInterceptor rateLimiter = new RequestRateLimitingInterceptor(); + sharedClient = Dsl.asyncHttpClient(new Builder().setKeepAlive(true) - /** - * Execute a REST call and block until the response is received. - */ - public static T executeSync(Call call) { - try { - Response response = call.execute(); - if (response.isSuccessful()) { - return response.body(); - } else { - BinanceApiError apiError = getBinanceApiError(response); - throw new BinanceApiException(apiError); - } - } catch (IOException e) { - throw new BinanceApiException(e); - } - } + .addChannelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, Integer.getInteger("binance.api.connectio.timeout.millis", 20000)) + + .setMaxConnectionsPerHost(Integer.getInteger("binance.api.max.connections.per.host", 500)) + + .setWebSocketMaxBufferSize(Integer.getInteger("binance.api.ws.buffer.size", 65536)) + + .setWebSocketMaxFrameSize(Integer.getInteger("binance.api.ws.frame.size", 65536)) + + .addRequestFilter(rateLimiter::request).addResponseFilter(rateLimiter::response) + + .build()); + } + + @SuppressWarnings("unchecked") + private static final Converter errorBodyConverter = (Converter) converterFactory + .responseBodyConverter(BinanceApiError.class, new Annotation[0], null); + + public static S createService(Class serviceClass, String apiKey, String secret) { + AuthenticationInterceptor authenticationInterceptor = new AuthenticationInterceptor(apiKey, secret); + AsyncHttpClientCallFactory ahccf = AsyncHttpClientCallFactory.builder() - /** - * Extracts and converts the response error body into an object. - */ - public static BinanceApiError getBinanceApiError(Response response) throws IOException, BinanceApiException { - return errorBodyConverter.convert(response.errorBody()); + .httpClient(sharedClient) + + .callCustomizer(callBuilder -> callBuilder.requestCustomizer(authenticationInterceptor)) + + .build(); + + Retrofit retrofit = new Retrofit.Builder() + + .baseUrl(BinanceApiConfig.getApiBaseUrl()) + + .callFactory(ahccf) + + .addConverterFactory(converterFactory).build(); + return retrofit.create(serviceClass); + } + + /** + * Execute a REST call and block until the response is received. + */ + public static T executeSync(Call call) { + try { + Response response = call.execute(); + if (response.isSuccessful()) { + return response.body(); + } else { + BinanceApiError apiError = getBinanceApiError(response); + throw new BinanceApiException(apiError); + } + } catch (IOException e) { + throw new BinanceApiException(e); } + } + + /** + * Extracts and converts the response error body into an object. + */ + public static BinanceApiError getBinanceApiError(Response response) throws IOException, BinanceApiException { + return errorBodyConverter.convert(response.errorBody()); + } + + public static AsyncHttpClient getSharedClient() { + return sharedClient; + } } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java index 4e10d48f1..cd7631522 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java @@ -5,6 +5,10 @@ import java.util.List; import java.util.stream.Collectors; +import org.asynchttpclient.AsyncHttpClient; +import org.asynchttpclient.netty.ws.NettyWebSocket; +import org.asynchttpclient.ws.WebSocketUpgradeHandler; + import com.binance.api.client.BinanceApiCallback; import com.binance.api.client.BinanceApiWebSocketClient; import com.binance.api.client.config.BinanceApiConfig; @@ -18,110 +22,87 @@ import com.binance.api.client.domain.market.CandlestickInterval; import com.fasterxml.jackson.core.type.TypeReference; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.WebSocket; +import lombok.SneakyThrows; /** * Binance API WebSocket client implementation using OkHttp. */ -public class BinanceApiWebSocketClientImpl implements BinanceApiWebSocketClient, Closeable { - - private final OkHttpClient client; - - public BinanceApiWebSocketClientImpl(OkHttpClient client) { - this.client = client; - } - - @Override - public Closeable onDepthEvent(String symbols, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")) - .map(String::trim) - .map(s -> String.format("%s@depth", s)) - .collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, DepthEvent.class)); - } - - @Override - public Closeable onCandlestickEvent(String symbols, CandlestickInterval interval, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")) - .map(String::trim) - .map(s -> String.format("%s@kline_%s", s, interval.getIntervalId())) - .collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, CandlestickEvent.class)); - } - - @Override - public Closeable onAggTradeEvent(String symbols, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")) - .map(String::trim) - .map(s -> String.format("%s@aggTrade", s)) - .collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, AggTradeEvent.class)); - } - - @Override - public Closeable onTradeEvent(String symbols, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")) - .map(String::trim) - .map(s -> String.format("%s@trade", s)) - .collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, TradeEvent.class)); - } - - @Override - public Closeable onUserDataUpdateEvent(String listenKey, BinanceApiCallback callback) { - return createNewWebSocket(listenKey, new BinanceApiWebSocketListener<>(callback, UserDataUpdateEvent.class)); - } - - @Override - public Closeable onTickerEvent(String symbols, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")) - .map(String::trim) - .map(s -> String.format("%s@ticker", s)) - .collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, TickerEvent.class)); - } - - @Override - public Closeable onAllMarketTickersEvent(BinanceApiCallback> callback) { - final String channel = "!ticker@arr"; - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, new TypeReference>() { - })); - } - - @Override - public Closeable onBookTickerEvent(String symbols, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")) - .map(String::trim) - .map(s -> String.format("%s@bookTicker", s)) - .collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, BookTickerEvent.class)); - } - - @Override - public Closeable onAllBookTickersEvent(BinanceApiCallback callback) { - final String channel = "!bookTicker"; - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, BookTickerEvent.class)); - } - - /** - * @deprecated This method is no longer functional. Please use the returned {@link Closeable} from any of the other methods to close the web socket. - */ - @Deprecated - @Override - public void close() { - } - - private Closeable createNewWebSocket(String channel, BinanceApiWebSocketListener listener) { - String streamingUrl = String.format("%s/%s", BinanceApiConfig.getStreamApiBaseUrl(), channel); - Request request = new Request.Builder().url(streamingUrl).build(); - final WebSocket webSocket = client.newWebSocket(request, listener); - return () -> { - final int code = 1000; - listener.onClosing(webSocket, code, null); - webSocket.close(code, null); - listener.onClosed(webSocket, code, null); - }; - } +public class BinanceApiWebSocketClientImpl implements BinanceApiWebSocketClient { + + private final AsyncHttpClient client; + + public BinanceApiWebSocketClientImpl(AsyncHttpClient client) { + this.client = client; + } + + @Override + public Closeable onDepthEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@depth", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, DepthEvent.class)); + } + + @Override + public Closeable onCandlestickEvent(String symbols, CandlestickInterval interval, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim) + .map(s -> String.format("%s@kline_%s", s, interval.getIntervalId())).collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, CandlestickEvent.class)); + } + + @Override + public Closeable onAggTradeEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@aggTrade", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, AggTradeEvent.class)); + } + + @Override + public Closeable onTradeEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@trade", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, TradeEvent.class)); + } + + @Override + public Closeable onUserDataUpdateEvent(String listenKey, BinanceApiCallback callback) { + return createNewWebSocket(listenKey, new BinanceApiWebSocketListener<>(callback, UserDataUpdateEvent.class)); + } + + @Override + public Closeable onTickerEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@ticker", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, TickerEvent.class)); + } + + @Override + public Closeable onAllMarketTickersEvent(BinanceApiCallback> callback) { + final String channel = "!ticker@arr"; + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, new TypeReference>() { + })); + } + + @Override + public Closeable onBookTickerEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@bookTicker", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, BookTickerEvent.class)); + } + + @Override + public Closeable onAllBookTickersEvent(BinanceApiCallback callback) { + final String channel = "!bookTicker"; + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, BookTickerEvent.class)); + } + + @SneakyThrows + private Closeable createNewWebSocket(String channel, BinanceApiWebSocketListener listener) { + String streamingUrl = String.format("%s/%s", BinanceApiConfig.getStreamApiBaseUrl(), channel); + NettyWebSocket socket = this.client.prepareGet(streamingUrl).execute(new WebSocketUpgradeHandler.Builder() + + .addWebSocketListener(listener) + + .build()).get(); + return socket::sendCloseFrame; + } } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketListener.java b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketListener.java index e2004090e..df8f7fecf 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketListener.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketListener.java @@ -1,20 +1,18 @@ package com.binance.api.client.impl; +import java.io.IOException; + +import org.asynchttpclient.ws.WebSocket; + import com.binance.api.client.BinanceApiCallback; -import com.binance.api.client.exception.BinanceApiException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; -import okhttp3.Response; -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; - -import java.io.IOException; /** * Binance API WebSocket listener. */ -public class BinanceApiWebSocketListener extends WebSocketListener { +public class BinanceApiWebSocketListener implements org.asynchttpclient.ws.WebSocketListener { private BinanceApiCallback callback; @@ -22,7 +20,7 @@ public class BinanceApiWebSocketListener extends WebSocketListener { private final ObjectReader objectReader; - private boolean closing = false; + private WebSocket websocket; public BinanceApiWebSocketListener(BinanceApiCallback callback, Class eventClass) { this.callback = callback; @@ -35,24 +33,31 @@ public BinanceApiWebSocketListener(BinanceApiCallback callback, TypeReference } @Override - public void onMessage(WebSocket webSocket, String text) { - try { - T event = objectReader.readValue(text); - callback.onResponse(event); - } catch (IOException e) { - throw new BinanceApiException(e); - } + public void onClose(WebSocket websocket, int code, String reason) { } @Override - public void onClosing(final WebSocket webSocket, final int code, final String reason) { - closing = true; + public void onError(Throwable t) { + this.callback.onFailure(t); } @Override - public void onFailure(WebSocket webSocket, Throwable t, Response response) { - if (!closing) { - callback.onFailure(t); + public void onOpen(WebSocket websocket) { + this.websocket = websocket; + } + + @Override + public void onPingFrame(byte[] payload) { + this.websocket.sendPongFrame(payload); + } + + @Override + public void onTextFrame(String payload, boolean finalFragment, int rsv) { + try { + callback.onResponse(objectReader.readValue(payload)); + } catch (IOException e) { + callback.onFailure(e); } } + } \ No newline at end of file diff --git a/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java b/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java index 772eef1e1..a3600a712 100644 --- a/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java +++ b/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java @@ -8,18 +8,21 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import org.asynchttpclient.HttpResponseStatus; +import org.asynchttpclient.filter.FilterContext; +import org.asynchttpclient.filter.FilterContext.FilterContextBuilder; + import com.binance.api.client.domain.general.ExchangeInfo; import com.binance.api.client.domain.general.RateLimit; import com.binance.api.client.domain.general.RateLimitType; import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.handler.codec.http.HttpHeaders; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import okhttp3.Interceptor; -import okhttp3.Request; -import okhttp3.Response; @Slf4j -public class RequestRateLimitingInterceptor implements Interceptor { +public class RequestRateLimitingInterceptor { private final int limit; @@ -44,33 +47,31 @@ public RequestRateLimitingInterceptor(RateLimit requestWeightLimit) { AtomicInteger weightUsed = new AtomicInteger(); - @Override - public Response intercept(Chain chain) throws IOException { + @SneakyThrows + public FilterContext request(FilterContext ctx) { if (weightUsed.get() >= limit) { - try { - log.warn("approaching request weight limit, slowing down a bit"); - sleep(1000); - } catch (InterruptedException e) { - throw new IOException(e); - } + log.warn("approaching request weight limit, slowing down a bit"); + sleep(1000); } - Request request = chain.request(); - Response response = chain.proceed(request); - if (response.code() == 418 || response.code() == 429) { - try { - response.close(); - log.warn("reached request weight limit, retrying after {}", response.header("retry-after")); - sleep(parseInt(response.header("retry-after")) * 1000); - response = chain.proceed(request); - } catch (Exception e) { - throw new IOException(e); - } + return ctx; + } + + @SneakyThrows + public FilterContext response(FilterContext ctx) { + HttpResponseStatus status = ctx.getResponseStatus(); + HttpHeaders headers = ctx.getResponseHeaders(); + if (status.getStatusCode() == 418 || status.getStatusCode() == 429) { + log.warn("reached request weight limit, retrying after {}", headers.get("retry-after")); + sleep(parseInt(headers.get("retry-after")) * 1000); + return new FilterContextBuilder().request(ctx.getRequest()).replayRequest(true).build(); } - String header = response.header("x-mbx-used-weight"); + String header = headers.get("x-mbx-used-weight"); if (header != null) { + log.info(header); weightUsed.set(parseInt(header)); } - return response; + + return ctx; } } diff --git a/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java b/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java index 197aaabcb..df9f81f3b 100755 --- a/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java +++ b/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java @@ -1,92 +1,43 @@ package com.binance.api.client.security; -import com.binance.api.client.constant.BinanceApiConstants; -import okhttp3.HttpUrl; -import okhttp3.Interceptor; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okio.Buffer; +import java.util.function.Consumer; + import org.apache.commons.lang3.StringUtils; +import org.asynchttpclient.Request; +import org.asynchttpclient.RequestBuilder; +import org.asynchttpclient.RequestBuilderBase; +import org.asynchttpclient.SignatureCalculator; -import java.io.IOException; -import java.util.Objects; +import com.binance.api.client.constant.BinanceApiConstants; /** - * A request interceptor that injects the API Key Header into requests, and signs messages, whenever required. + * A request interceptor that injects the API Key Header into requests, and + * signs messages, whenever required. */ -public class AuthenticationInterceptor implements Interceptor { - - private final String apiKey; - - private final String secret; - - public AuthenticationInterceptor(String apiKey, String secret) { - this.apiKey = apiKey; - this.secret = secret; - } - - @Override - public Response intercept(Chain chain) throws IOException { - Request original = chain.request(); - Request.Builder newRequestBuilder = original.newBuilder(); - - boolean isApiKeyRequired = original.header(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY) != null; - boolean isSignatureRequired = original.header(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED) != null; - newRequestBuilder.removeHeader(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY) - .removeHeader(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED); +public class AuthenticationInterceptor implements Consumer, SignatureCalculator { - // Endpoint requires sending a valid API-KEY - if (isApiKeyRequired || isSignatureRequired) { - newRequestBuilder.addHeader(BinanceApiConstants.API_KEY_HEADER, apiKey); - } + private final String apiKey; - // Endpoint requires signing the payload - if (isSignatureRequired) { - String payload = original.url().query(); - if (!StringUtils.isEmpty(payload)) { - String signature = HmacSHA256Signer.sign(payload, secret); - HttpUrl signedUrl = original.url().newBuilder().addQueryParameter("signature", signature).build(); - newRequestBuilder.url(signedUrl); - } - } + private final String secret; - // Build new request after adding the necessary authentication information - Request newRequest = newRequestBuilder.build(); - return chain.proceed(newRequest); - } + public AuthenticationInterceptor(String apiKey, String secret) { + this.apiKey = apiKey; + this.secret = secret; + } - /** - * Extracts the request body into a String. - * - * @return request body as a string - */ - @SuppressWarnings("unused") - private static String bodyToString(RequestBody request) { - try (final Buffer buffer = new Buffer()) { - final RequestBody copy = request; - if (copy != null) { - copy.writeTo(buffer); - } else { - return ""; - } - return buffer.readUtf8(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } + @Override + public void accept(RequestBuilder request) { + request.addHeader(BinanceApiConstants.API_KEY_HEADER, apiKey); + request.setSignatureCalculator(this); + } - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final AuthenticationInterceptor that = (AuthenticationInterceptor) o; - return Objects.equals(apiKey, that.apiKey) && - Objects.equals(secret, that.secret); + @Override + public void calculateAndAddSignature(Request request, RequestBuilderBase requestBuilder) { + String payload = request.getUri().getQuery(); + if (!StringUtils.isEmpty(payload)) { + String signature = HmacSHA256Signer.sign(payload, secret); + requestBuilder.addQueryParam("signature", signature); } + } - @Override - public int hashCode() { - return Objects.hash(apiKey, secret); - } } \ No newline at end of file diff --git a/src/main/java/com/binance/api/client/security/HmacSHA256Signer.java b/src/main/java/com/binance/api/client/security/HmacSHA256Signer.java index 17309e273..89fdc888f 100755 --- a/src/main/java/com/binance/api/client/security/HmacSHA256Signer.java +++ b/src/main/java/com/binance/api/client/security/HmacSHA256Signer.java @@ -1,10 +1,10 @@ package com.binance.api.client.security; -import org.apache.commons.codec.binary.Hex; - import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; +import org.apache.commons.codec.binary.Hex; + /** * Utility class to sign messages using HMAC-SHA256. */ diff --git a/src/test/java/com/binance/api/examples/Netty.java b/src/test/java/com/binance/api/examples/Netty.java new file mode 100644 index 000000000..14e330410 --- /dev/null +++ b/src/test/java/com/binance/api/examples/Netty.java @@ -0,0 +1,17 @@ +package com.binance.api.examples; + +import java.io.IOException; + +import org.junit.Test; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; + +public class Netty { + + @Test + public void test() throws JsonParseException, JsonMappingException, IOException { + + } + +} diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 000000000..aa90870a0 --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,11 @@ + + + true + + %-4relative [%thread] %-5level %logger{35} - %msg%n + + + + + + \ No newline at end of file From 6becfeb8958b09c83d44a7df23d8c1646d7892e2 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sun, 28 Feb 2021 11:42:35 +0100 Subject: [PATCH 040/101] asynchttpclient added smoke tests for connectivity --- .../impl/RequestRateLimitingInterceptor.java | 1 - .../java/com/binance/api/examples/Netty.java | 17 ----- .../binance/api/examples/NettyTransport.java | 64 +++++++++++++++++++ 3 files changed, 64 insertions(+), 18 deletions(-) delete mode 100644 src/test/java/com/binance/api/examples/Netty.java create mode 100644 src/test/java/com/binance/api/examples/NettyTransport.java diff --git a/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java b/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java index a3600a712..4d766895f 100644 --- a/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java +++ b/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java @@ -67,7 +67,6 @@ public FilterContext response(FilterContext ctx) { } String header = headers.get("x-mbx-used-weight"); if (header != null) { - log.info(header); weightUsed.set(parseInt(header)); } diff --git a/src/test/java/com/binance/api/examples/Netty.java b/src/test/java/com/binance/api/examples/Netty.java deleted file mode 100644 index 14e330410..000000000 --- a/src/test/java/com/binance/api/examples/Netty.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.binance.api.examples; - -import java.io.IOException; - -import org.junit.Test; - -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonMappingException; - -public class Netty { - - @Test - public void test() throws JsonParseException, JsonMappingException, IOException { - - } - -} diff --git a/src/test/java/com/binance/api/examples/NettyTransport.java b/src/test/java/com/binance/api/examples/NettyTransport.java new file mode 100644 index 000000000..cf93fa7e6 --- /dev/null +++ b/src/test/java/com/binance/api/examples/NettyTransport.java @@ -0,0 +1,64 @@ +package com.binance.api.examples; + +import static org.junit.Assert.fail; + +import java.io.Closeable; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; + +import org.junit.Before; +import org.junit.Test; + +import com.binance.api.client.BinanceApiCallback; +import com.binance.api.client.BinanceApiClientFactory; +import com.binance.api.client.domain.event.TradeEvent; + +public class NettyTransport { + + private BinanceApiClientFactory factory; + + @Before + public void pre() { + factory = BinanceApiClientFactory.newInstance("", ""); + } + + @Test + public void ping() { + factory.newRestClient().ping(); + } + + @Test + public void ping_async() throws InterruptedException { + BinanceApiClientFactory factory = BinanceApiClientFactory.newInstance("", ""); + CountDownLatch latch = new CountDownLatch(1); + factory.newAsyncRestClient().ping(new BinanceApiCallback() { + + @Override + public void onResponse(Void response) { + latch.countDown(); + } + }); + latch.await(); + } + + @Test + public void socket() throws IOException, InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + Closeable closeable = factory.newWebSocketClient().onTradeEvent("btcusdt", new BinanceApiCallback() { + + @Override + public void onResponse(TradeEvent response) { + latch.countDown(); + } + + @Override + public void onFailure(Throwable cause) { + latch.countDown(); + fail("test failed"); + } + }); + latch.await(); + closeable.close(); + } + +} From 4f1f2fd2c9b1b3753c49136d562a998426cec530 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sun, 28 Feb 2021 11:46:52 +0100 Subject: [PATCH 041/101] asynchttpclient hmm, test does not get executed --- .../impl/NettyTransportTest.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/test/java/com/binance/api/{examples/NettyTransport.java => client/impl/NettyTransportTest.java} (95%) diff --git a/src/test/java/com/binance/api/examples/NettyTransport.java b/src/test/java/com/binance/api/client/impl/NettyTransportTest.java similarity index 95% rename from src/test/java/com/binance/api/examples/NettyTransport.java rename to src/test/java/com/binance/api/client/impl/NettyTransportTest.java index cf93fa7e6..f5b2b488d 100644 --- a/src/test/java/com/binance/api/examples/NettyTransport.java +++ b/src/test/java/com/binance/api/client/impl/NettyTransportTest.java @@ -1,4 +1,4 @@ -package com.binance.api.examples; +package com.binance.api.client.impl; import static org.junit.Assert.fail; @@ -13,7 +13,7 @@ import com.binance.api.client.BinanceApiClientFactory; import com.binance.api.client.domain.event.TradeEvent; -public class NettyTransport { +public class NettyTransportTest { private BinanceApiClientFactory factory; From 80a1d47f51a5f058b513896cfcbbbddc3a157151 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sun, 28 Feb 2021 11:55:33 +0100 Subject: [PATCH 042/101] asynchttpclient trying with epoll --- .../binance/api/client/impl/BinanceApiServiceGenerator.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java index 13a513f73..968d86b94 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -14,6 +14,7 @@ import com.binance.api.client.security.AuthenticationInterceptor; import io.netty.channel.ChannelOption; +import io.netty.channel.epoll.EpollEventLoopGroup; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Converter; @@ -34,6 +35,10 @@ public class BinanceApiServiceGenerator { RequestRateLimitingInterceptor rateLimiter = new RequestRateLimitingInterceptor(); sharedClient = Dsl.asyncHttpClient(new Builder().setKeepAlive(true) + .setUseNativeTransport(true) + + .setEventLoopGroup(new EpollEventLoopGroup(Runtime.getRuntime().availableProcessors())) + .addChannelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, Integer.getInteger("binance.api.connectio.timeout.millis", 20000)) .setMaxConnectionsPerHost(Integer.getInteger("binance.api.max.connections.per.host", 500)) From 50216823561bd795e77f21e1bf6e04a86e0134e2 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sun, 28 Feb 2021 11:59:59 +0100 Subject: [PATCH 043/101] asynchttpclient epoll works only on amd64 --- .../binance/api/client/impl/BinanceApiServiceGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java index 968d86b94..2333f40fe 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -14,7 +14,7 @@ import com.binance.api.client.security.AuthenticationInterceptor; import io.netty.channel.ChannelOption; -import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Converter; @@ -37,7 +37,7 @@ public class BinanceApiServiceGenerator { .setUseNativeTransport(true) - .setEventLoopGroup(new EpollEventLoopGroup(Runtime.getRuntime().availableProcessors())) + .setEventLoopGroup(new NioEventLoopGroup(Runtime.getRuntime().availableProcessors())) .addChannelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, Integer.getInteger("binance.api.connectio.timeout.millis", 20000)) From ebe71a0eac0893c0d365b32163584b7626cfddcd Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 28 Feb 2021 11:09:15 +0000 Subject: [PATCH 044/101] [ci-skip]prepare release binance-api-client-2.4.0 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 509f40512..405c4272e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,11 +1,10 @@ - + 4.0.0 com.binance.api binance-api-client - 2.4.0-SNAPSHOT + 2.4.0 The MIT License @@ -105,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.4.0 From 3eba140ee75b3cc96151bc5038139e811ad5d17d Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 28 Feb 2021 11:14:56 +0000 Subject: [PATCH 045/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 405c4272e..0d30c434d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.4.0 + 2.4.1-SNAPSHOT The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.4.0 + HEAD From 003e1a921bda596945cd9505fb9f5d88561ee59c Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sun, 28 Feb 2021 17:44:07 +0100 Subject: [PATCH 046/101] master added optional epoll resolution --- .../impl/BinanceApiServiceGenerator.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java index 2333f40fe..420adedae 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -14,7 +14,10 @@ import com.binance.api.client.security.AuthenticationInterceptor; import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; +import lombok.extern.slf4j.Slf4j; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Converter; @@ -26,6 +29,7 @@ * Generates a Binance API implementation based on @see * {@link BinanceApiService}. */ +@Slf4j public class BinanceApiServiceGenerator { private static final Converter.Factory converterFactory = JacksonConverterFactory.create(); @@ -33,13 +37,23 @@ public class BinanceApiServiceGenerator { static { RequestRateLimitingInterceptor rateLimiter = new RequestRateLimitingInterceptor(); + + EventLoopGroup eventLoopGroup; + try { + eventLoopGroup = new EpollEventLoopGroup(Runtime.getRuntime().availableProcessors()); + log.info("using epoll netty http event loop"); + } catch (UnsatisfiedLinkError e) { + eventLoopGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors()); + log.info("using nio netty http event loop"); + } + sharedClient = Dsl.asyncHttpClient(new Builder().setKeepAlive(true) .setUseNativeTransport(true) - .setEventLoopGroup(new NioEventLoopGroup(Runtime.getRuntime().availableProcessors())) + .setEventLoopGroup(eventLoopGroup) - .addChannelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, Integer.getInteger("binance.api.connectio.timeout.millis", 20000)) + .addChannelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, Integer.getInteger("binance.api.connection.timeout.millis", 20000)) .setMaxConnectionsPerHost(Integer.getInteger("binance.api.max.connections.per.host", 500)) From d0c5e21bfffee474d9acf0e27ae1f467ea8fa585 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 28 Feb 2021 16:48:50 +0000 Subject: [PATCH 047/101] [ci-skip]prepare release binance-api-client-2.4.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0d30c434d..ce778cbdd 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.4.1-SNAPSHOT + 2.4.1 The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.4.1 From 89414079cbda5860e69f31f05de69894a9364440 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 28 Feb 2021 16:49:25 +0000 Subject: [PATCH 048/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ce778cbdd..638e8cbdc 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.4.1 + 2.4.2-SNAPSHOT The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.4.1 + HEAD From 19e0b539c7fef40ad12dd8fb60724518878b292c Mon Sep 17 00:00:00 2001 From: Sergey Zolotaryov Date: Sun, 28 Feb 2021 17:55:10 +0100 Subject: [PATCH 049/101] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b545b4054..c86727b3a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ binance-java-api is a lightweight Java library for interacting with the [Binance * Support for synchronous and asynchronous REST requests to all [General](https://www.binance.com/restapipub.html#user-content-general-endpoints), [Market Data](https://www.binance.com/restapipub.html#user-content-market-data-endpoints), [Account](https://www.binance.com/restapipub.html#user-content-account-endpoints) endpoints, and [User](https://www.binance.com/restapipub.html#user-content-user-data-stream-endpoints) stream endpoints. * Support for User Data, Trade, Kline, and Depth event streaming using [Binance WebSocket API](https://www.binance.com/restapipub.html#wss-endpoint). * Implements a simple request rate limiter to prevent your application from getting an IP ban in case you send too many requests too fast. +* Uses Netty as HTTP client and will automatically switch to Epoll event loop if available on the classpath for your platform. ## Installation 1. Install library into your Maven's local repository by running `mvn install` From bf27be4243b85efcdb017e3119725c8dc2709403 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sun, 28 Feb 2021 18:13:04 +0100 Subject: [PATCH 050/101] master bumping version to get a release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 638e8cbdc..f452575a7 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.4.2-SNAPSHOT + 2.4.3-SNAPSHOT The MIT License From 640a7351a87531f7034d0aa5f784bb74e05fdcdd Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 28 Feb 2021 17:17:11 +0000 Subject: [PATCH 051/101] [ci-skip]prepare release binance-api-client-2.4.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f452575a7..55001650e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.4.3-SNAPSHOT + 2.4.3 The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.4.3 From b1b3c384ba55032e966f80e5d9534741fab7f342 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 28 Feb 2021 17:18:08 +0000 Subject: [PATCH 052/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 55001650e..c7f555b38 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.4.3 + 2.4.4-SNAPSHOT The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.4.3 + HEAD From 9d69b928092e11eb1f35def8b7cd5022774dd611 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sun, 28 Feb 2021 20:50:41 +0100 Subject: [PATCH 053/101] master auth and signature are not always required. binance fails if i include them --- Jenkinsfile | 3 ++- .../security/AuthenticationInterceptor.java | 15 +++++++++++++-- src/main/java/org/asynchttpclient/Header.java | 13 +++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/asynchttpclient/Header.java diff --git a/Jenkinsfile b/Jenkinsfile index 67af14c55..f13eacda5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -33,9 +33,10 @@ pipeline { branch 'master' } steps { - script { + script { version = release() echo "Released binance-java-api version ${version}" + currentBuild.displayName = version } } } diff --git a/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java b/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java index df9f81f3b..8fdca1ef7 100755 --- a/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java +++ b/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java @@ -3,6 +3,7 @@ import java.util.function.Consumer; import org.apache.commons.lang3.StringUtils; +import org.asynchttpclient.Header; import org.asynchttpclient.Request; import org.asynchttpclient.RequestBuilder; import org.asynchttpclient.RequestBuilderBase; @@ -27,8 +28,18 @@ public AuthenticationInterceptor(String apiKey, String secret) { @Override public void accept(RequestBuilder request) { - request.addHeader(BinanceApiConstants.API_KEY_HEADER, apiKey); - request.setSignatureCalculator(this); + boolean isApiKeyRequired = Header.get(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY) != null; + boolean isSignatureRequired = Header.get(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED) != null; + Header.remove(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY); + Header.remove(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED); + + // Endpoint requires sending a valid API-KEY + if (isApiKeyRequired || isSignatureRequired) { + request.addHeader(BinanceApiConstants.API_KEY_HEADER, apiKey); + } + if (isSignatureRequired) { + request.setSignatureCalculator(this); + } } @Override diff --git a/src/main/java/org/asynchttpclient/Header.java b/src/main/java/org/asynchttpclient/Header.java new file mode 100644 index 000000000..8f9e9494a --- /dev/null +++ b/src/main/java/org/asynchttpclient/Header.java @@ -0,0 +1,13 @@ +package org.asynchttpclient; + +public class Header { + + public static String get(RequestBuilder builder, String header) { + return builder.headers.get(header); + } + + public static void remove(RequestBuilder request, String header) { + request.headers.remove(header); + } + +} From b898010014e564fb36e5789a8e6ce6762e786dc2 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 28 Feb 2021 19:55:30 +0000 Subject: [PATCH 054/101] [ci-skip]prepare release binance-api-client-2.4.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c7f555b38..fdf6142c5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.4.4-SNAPSHOT + 2.4.4 The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.4.4 From 813b4ec477427ada7846347a34269a67b0f8a887 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 28 Feb 2021 19:57:14 +0000 Subject: [PATCH 055/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fdf6142c5..52d10fb87 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.4.4 + 2.4.5-SNAPSHOT The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.4.4 + HEAD From b7eca0ef3109713844cf3189fb2a7cfeec7e7d84 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 6 Mar 2021 12:56:32 +0100 Subject: [PATCH 056/101] master releasing version without asynchttpclient --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 45072ec2d..765e234c6 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.3.0-SNAPSHOT + 2.4.0-SNAPSHOT The MIT License From 294ff53a6e4c859cc5a0e9aa4ea51bf0292a9a83 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 6 Mar 2021 12:59:10 +0100 Subject: [PATCH 057/101] master dumping version for a clean release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 765e234c6..7f8865456 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.4.0-SNAPSHOT + 2.6.0-SNAPSHOT The MIT License From 79a110df2e2d34d81790e6bbc242198d7b617a46 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 6 Mar 2021 12:08:28 +0000 Subject: [PATCH 058/101] [ci-skip]prepare release binance-api-client-2.6.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7f8865456..c26a01f78 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.6.0-SNAPSHOT + 2.6.0 The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.6.0 From bd2f8fcee2362b8d167d73ba7829c7cfa940624a Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 6 Mar 2021 12:09:01 +0000 Subject: [PATCH 059/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c26a01f78..69ca6467e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.6.0 + 2.6.1-SNAPSHOT The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.6.0 + HEAD From 1b035cc3b7ae917f4f0e5f3cca11c1b102202830 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 6 Mar 2021 21:39:16 +0100 Subject: [PATCH 060/101] netty-client marking parameters which contain request timestamp as potentially retriable, for a retry the client has to reset timestamp to current --- pom.xml | 2 +- .../com/binance/api/client/domain/Retriable.java | 9 +++++++++ .../api/client/domain/account/MarginNewOrder.java | 7 +++++-- .../binance/api/client/domain/account/NewOrder.java | 5 ++++- .../account/request/OcoOrderStatusRequest.java | 12 ++++++++---- .../client/domain/account/request/OrderRequest.java | 11 +++++++---- .../client/exception/BinanceTimeoutException.java | 7 +++++++ .../api/client/impl/BinanceApiServiceGenerator.java | 2 +- 8 files changed, 42 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/binance/api/client/domain/Retriable.java create mode 100644 src/main/java/com/binance/api/client/exception/BinanceTimeoutException.java diff --git a/pom.xml b/pom.xml index 52d10fb87..f7784c532 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.4.5-SNAPSHOT + 3.0.0-SNAPSHOT The MIT License diff --git a/src/main/java/com/binance/api/client/domain/Retriable.java b/src/main/java/com/binance/api/client/domain/Retriable.java new file mode 100644 index 000000000..4870346d0 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/Retriable.java @@ -0,0 +1,9 @@ +package com.binance.api.client.domain; + +public interface Retriable { + + long getTimestamp(); + + T timestamp(long newTimestamp); + +} diff --git a/src/main/java/com/binance/api/client/domain/account/MarginNewOrder.java b/src/main/java/com/binance/api/client/domain/account/MarginNewOrder.java index 74dd71d0b..89f3677a7 100644 --- a/src/main/java/com/binance/api/client/domain/account/MarginNewOrder.java +++ b/src/main/java/com/binance/api/client/domain/account/MarginNewOrder.java @@ -1,17 +1,19 @@ package com.binance.api.client.domain.account; +import org.apache.commons.lang3.builder.ToStringBuilder; + import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.domain.OrderSide; import com.binance.api.client.domain.OrderType; +import com.binance.api.client.domain.Retriable; import com.binance.api.client.domain.TimeInForce; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import org.apache.commons.lang3.builder.ToStringBuilder; /** * A trade order to enter or exit a position. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class MarginNewOrder { +public class MarginNewOrder implements Retriable { /** * Symbol to place the order on. @@ -222,6 +224,7 @@ public MarginNewOrder recvWindow(Long recvWindow) { return this; } + @Override public long getTimestamp() { return timestamp; } diff --git a/src/main/java/com/binance/api/client/domain/account/NewOrder.java b/src/main/java/com/binance/api/client/domain/account/NewOrder.java index b987923f4..1fb551c6c 100755 --- a/src/main/java/com/binance/api/client/domain/account/NewOrder.java +++ b/src/main/java/com/binance/api/client/domain/account/NewOrder.java @@ -5,6 +5,7 @@ import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.domain.OrderSide; import com.binance.api.client.domain.OrderType; +import com.binance.api.client.domain.Retriable; import com.binance.api.client.domain.TimeInForce; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -12,7 +13,7 @@ * A trade order to enter or exit a position. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class NewOrder { +public class NewOrder implements Retriable { /** * Symbol to place the order on. @@ -251,10 +252,12 @@ public NewOrder recvWindow(Long recvWindow) { return this; } + @Override public long getTimestamp() { return timestamp; } + @Override public NewOrder timestamp(long timestamp) { this.timestamp = timestamp; return this; diff --git a/src/main/java/com/binance/api/client/domain/account/request/OcoOrderStatusRequest.java b/src/main/java/com/binance/api/client/domain/account/request/OcoOrderStatusRequest.java index 34ef78402..369bf5c35 100644 --- a/src/main/java/com/binance/api/client/domain/account/request/OcoOrderStatusRequest.java +++ b/src/main/java/com/binance/api/client/domain/account/request/OcoOrderStatusRequest.java @@ -1,8 +1,9 @@ package com.binance.api.client.domain.account.request; import com.binance.api.client.constant.BinanceApiConstants; +import com.binance.api.client.domain.Retriable; -public class OcoOrderStatusRequest { +public class OcoOrderStatusRequest implements Retriable { Long orderListId; @@ -10,7 +11,7 @@ public class OcoOrderStatusRequest { Long recvWindow; - Long timestamp; + long timestamp; public OcoOrderStatusRequest(String origClientOrderId) { this(); @@ -51,12 +52,15 @@ public void setRecvWindow(Long recvWindow) { this.recvWindow = recvWindow; } - public Long getTimestamp() { + @Override + public long getTimestamp() { return timestamp; } - public void setTimestamp(Long timestamp) { + @Override + public OcoOrderStatusRequest timestamp(long timestamp) { this.timestamp = timestamp; + return this; } @Override diff --git a/src/main/java/com/binance/api/client/domain/account/request/OrderRequest.java b/src/main/java/com/binance/api/client/domain/account/request/OrderRequest.java index 17e4c2922..6eb8e799d 100755 --- a/src/main/java/com/binance/api/client/domain/account/request/OrderRequest.java +++ b/src/main/java/com/binance/api/client/domain/account/request/OrderRequest.java @@ -1,6 +1,7 @@ package com.binance.api.client.domain.account.request; import com.binance.api.client.constant.BinanceApiConstants; +import com.binance.api.client.domain.Retriable; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.ToString; @@ -10,13 +11,13 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) @ToString -public class OrderRequest { +public class OrderRequest implements Retriable { private final String symbol; private Long recvWindow; - private Long timestamp; + private long timestamp; public OrderRequest(String symbol) { this.symbol = symbol; @@ -37,11 +38,13 @@ public OrderRequest recvWindow(Long recvWindow) { return this; } - public Long getTimestamp() { + @Override + public long getTimestamp() { return timestamp; } - public OrderRequest timestamp(Long timestamp) { + @Override + public OrderRequest timestamp(long timestamp) { this.timestamp = timestamp; return this; } diff --git a/src/main/java/com/binance/api/client/exception/BinanceTimeoutException.java b/src/main/java/com/binance/api/client/exception/BinanceTimeoutException.java new file mode 100644 index 000000000..a0ca9aa9d --- /dev/null +++ b/src/main/java/com/binance/api/client/exception/BinanceTimeoutException.java @@ -0,0 +1,7 @@ +package com.binance.api.client.exception; + +public class BinanceTimeoutException extends BinanceApiException { + + private static final long serialVersionUID = -8224605820697804199L; + +} diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java index 420adedae..00092cf9c 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -53,7 +53,7 @@ public class BinanceApiServiceGenerator { .setEventLoopGroup(eventLoopGroup) - .addChannelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, Integer.getInteger("binance.api.connection.timeout.millis", 20000)) + .addChannelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, Integer.getInteger("binance.api.connection.timeout.millis", 40000)) .setMaxConnectionsPerHost(Integer.getInteger("binance.api.max.connections.per.host", 500)) From febd16f02620ec927ce6a248e579c715455b92b4 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sun, 7 Mar 2021 10:35:29 +0100 Subject: [PATCH 061/101] netty-client releasing netty version once again --- Jenkinsfile | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index f13eacda5..5d436f072 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,7 +2,7 @@ pipeline { agent any tools { maven 'mvn3' - jdk 'jdk14' + jdk 'jdk15' } options { @@ -10,12 +10,6 @@ pipeline { disableConcurrentBuilds() } - parameters { - booleanParam( - name: "RELEASE", - description: "Build a release from current commit.", - defaultValue: false) - } stages { stage('Build & Deploy SNAPSHOT') { steps { @@ -30,7 +24,10 @@ pipeline { stage('Release') { when { - branch 'master' + anyOf { + branch 'master'; + branch 'netty-client' + } } steps { script { From 585dae3e13bf1b8e2970e2914c1c12d358e73941 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 7 Mar 2021 09:42:39 +0000 Subject: [PATCH 062/101] [ci-skip]prepare release binance-api-client-3.0.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f7784c532..b35d1989f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.0.0-SNAPSHOT + 3.0.0 The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-3.0.0 From c842407f258b182f3aa76c21fd3acc0c3afb132a Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 7 Mar 2021 09:42:57 +0000 Subject: [PATCH 063/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b35d1989f..b50900ab6 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.0.0 + 3.0.1-SNAPSHOT The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.0.0 + HEAD From e8f10eda0263718d21625119ce84dd3a59ab50cd Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Tue, 9 Mar 2021 13:43:29 +0100 Subject: [PATCH 064/101] netty-client rewriting request body before the retry in case we're out of receiving window --- .../impl/RequestRateLimitingInterceptor.java | 28 +++++++++++++++---- .../RequestRateLimitingInterceptorTest.java | 20 +++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 src/test/java/com/binance/api/client/impl/RequestRateLimitingInterceptorTest.java diff --git a/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java b/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java index 4d766895f..9aa96338e 100644 --- a/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java +++ b/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java @@ -9,6 +9,8 @@ import java.util.concurrent.atomic.AtomicInteger; import org.asynchttpclient.HttpResponseStatus; +import org.asynchttpclient.Request; +import org.asynchttpclient.RequestBuilder; import org.asynchttpclient.filter.FilterContext; import org.asynchttpclient.filter.FilterContext.FilterContextBuilder; @@ -25,15 +27,13 @@ public class RequestRateLimitingInterceptor { private final int limit; - public RequestRateLimitingInterceptor() { this(getLimitsDefault()); } static RateLimit getLimitsDefault() { - ObjectMapper mapper = new ObjectMapper(); try { - ExchangeInfo info = mapper.readerFor(ExchangeInfo.class).readValue(new URL("https://api.binance.com/api/v3/exchangeInfo")); + ExchangeInfo info = new ObjectMapper().readerFor(ExchangeInfo.class).readValue(new URL("https://api.binance.com/api/v3/exchangeInfo")); List rateLimits = info.getRateLimits(); return rateLimits.stream().filter(l -> l.getRateLimitType() == RateLimitType.REQUEST_WEIGHT).findFirst().get(); } catch (IOException e) { @@ -61,9 +61,21 @@ public FilterContext response(FilterContext ctx) { HttpResponseStatus status = ctx.getResponseStatus(); HttpHeaders headers = ctx.getResponseHeaders(); if (status.getStatusCode() == 418 || status.getStatusCode() == 429) { - log.warn("reached request weight limit, retrying after {}", headers.get("retry-after")); - sleep(parseInt(headers.get("retry-after")) * 1000); - return new FilterContextBuilder().request(ctx.getRequest()).replayRequest(true).build(); + log.warn("reached request weight limit, retrying after {} seconds", headers.get("retry-after")); + sleep((parseInt(headers.get("retry-after")) + 1) * 1000); + Request request = ctx.getRequest(); + String stringData = request.getStringData(); + if (stringData != null && stringData.contains("\"timestamp\"")) { + // using json would be an overkill here + stringData = replaceTimestamp(stringData, System.currentTimeMillis()); + RequestBuilder copy = new RequestBuilder(); + copy.setUri(request.getUri()); + copy.setHeaders(request.getHeaders()); + copy.setBody(stringData); + request = copy.build(); + } + + return new FilterContextBuilder().request(request).replayRequest(true).build(); } String header = headers.get("x-mbx-used-weight"); if (header != null) { @@ -73,4 +85,8 @@ public FilterContext response(FilterContext ctx) { return ctx; } + static String replaceTimestamp(String stringData, long now) { + return stringData.replaceFirst("\"timestamp\"\\s*\\:\\s*([^\\D]+)","\"timestamp\":" +now); + } + } diff --git a/src/test/java/com/binance/api/client/impl/RequestRateLimitingInterceptorTest.java b/src/test/java/com/binance/api/client/impl/RequestRateLimitingInterceptorTest.java new file mode 100644 index 000000000..599977e6d --- /dev/null +++ b/src/test/java/com/binance/api/client/impl/RequestRateLimitingInterceptorTest.java @@ -0,0 +1,20 @@ +package com.binance.api.client.impl; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.binance.api.client.domain.account.request.OrderStatusRequest; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class RequestRateLimitingInterceptorTest { + + @Test + public void replaceTimestamp() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + String oldRequest = mapper.writeValueAsString(new OrderStatusRequest("BTCEUR", 1234l)); + String string = RequestRateLimitingInterceptor.replaceTimestamp(oldRequest, 12345); + assertEquals("{\"symbol\":\"BTCEUR\",\"recvWindow\":60000,\"timestamp\":12345,\"orderId\":1234,\"origClientOrderId\":null}", string); + } + +} From 676940e399ca3f6671ef64050fac0024be4a8ba2 Mon Sep 17 00:00:00 2001 From: jenkins Date: Tue, 9 Mar 2021 12:45:31 +0000 Subject: [PATCH 065/101] [ci-skip]prepare release binance-api-client-3.0.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b50900ab6..b1fe58b63 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.0.1-SNAPSHOT + 3.0.1 The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-3.0.1 From c13c13c8a4a2192118c15863cbf046ccdacd3727 Mon Sep 17 00:00:00 2001 From: jenkins Date: Tue, 9 Mar 2021 12:46:00 +0000 Subject: [PATCH 066/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b1fe58b63..89d67d612 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.0.1 + 3.0.2-SNAPSHOT The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.0.1 + HEAD From d4f295efe90806b6432da51849ef519624b35c9e Mon Sep 17 00:00:00 2001 From: jenkins Date: Tue, 9 Mar 2021 19:06:25 +0000 Subject: [PATCH 067/101] [ci-skip]prepare release binance-api-client-2.6.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 69ca6467e..7792d5064 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.6.1-SNAPSHOT + 2.6.1 The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-2.6.1 From f3b9fbfc012243f333cd0f2860f35fff0140261e Mon Sep 17 00:00:00 2001 From: jenkins Date: Tue, 9 Mar 2021 19:06:38 +0000 Subject: [PATCH 068/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7792d5064..65de92bd4 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 2.6.1 + 2.6.2-SNAPSHOT The MIT License @@ -82,7 +82,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-2.6.1 + HEAD From 50009ecb09aae5901a7f4ad1bc6537d17ecf0d86 Mon Sep 17 00:00:00 2001 From: jenkins Date: Tue, 9 Mar 2021 19:08:22 +0000 Subject: [PATCH 069/101] [ci-skip]prepare release binance-api-client-3.0.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 89d67d612..2c3ea738a 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.0.2-SNAPSHOT + 3.0.2 The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-3.0.2 From ea6513926afb91476ffc974451e7b0c8bcb17c47 Mon Sep 17 00:00:00 2001 From: jenkins Date: Tue, 9 Mar 2021 19:08:37 +0000 Subject: [PATCH 070/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2c3ea738a..713a86dc4 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.0.2 + 3.0.3-SNAPSHOT The MIT License @@ -104,7 +104,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.0.2 + HEAD From 71618744c3ee49ef203811ce1094fe0bd1412372 Mon Sep 17 00:00:00 2001 From: Sergey Zolotaryov Date: Wed, 10 Mar 2021 22:53:46 +0100 Subject: [PATCH 071/101] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c86727b3a..76292b233 100755 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ binance-java-api is a lightweight Java library for interacting with the [Binance ## Features * Support for synchronous and asynchronous REST requests to all [General](https://www.binance.com/restapipub.html#user-content-general-endpoints), [Market Data](https://www.binance.com/restapipub.html#user-content-market-data-endpoints), [Account](https://www.binance.com/restapipub.html#user-content-account-endpoints) endpoints, and [User](https://www.binance.com/restapipub.html#user-content-user-data-stream-endpoints) stream endpoints. * Support for User Data, Trade, Kline, and Depth event streaming using [Binance WebSocket API](https://www.binance.com/restapipub.html#wss-endpoint). -* Implements a simple request rate limiter to prevent your application from getting an IP ban in case you send too many requests too fast. +* Implements a simple request rate limiter to prevent your application from getting an IP ban in case you send too many requests too fast. Automatically retries request if your IP was banned. * Uses Netty as HTTP client and will automatically switch to Epoll event loop if available on the classpath for your platform. ## Installation From 6409d010b6b7c0e227b1c592ff714215124a9a02 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sun, 14 Mar 2021 11:46:04 +0100 Subject: [PATCH 072/101] netty-client added guava rate limiter --- pom.xml | 4 +++ .../impl/BinanceApiServiceGenerator.java | 25 +++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 713a86dc4..84077a577 100755 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,10 @@ org.asynchttpclient async-http-client-extras-retrofit2 + + org.asynchttpclient + async-http-client-extras-guava + org.slf4j slf4j-api diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java index 00092cf9c..c7c085317 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -1,11 +1,14 @@ package com.binance.api.client.impl; +import static java.lang.Integer.getInteger; + import java.io.IOException; import java.lang.annotation.Annotation; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.DefaultAsyncHttpClientConfig.Builder; import org.asynchttpclient.Dsl; +import org.asynchttpclient.extras.guava.RateLimitedThrottleRequestFilter; import org.asynchttpclient.extras.retrofit.AsyncHttpClientCallFactory; import com.binance.api.client.BinanceApiError; @@ -36,7 +39,6 @@ public class BinanceApiServiceGenerator { private static AsyncHttpClient sharedClient; static { - RequestRateLimitingInterceptor rateLimiter = new RequestRateLimitingInterceptor(); EventLoopGroup eventLoopGroup; try { @@ -47,23 +49,30 @@ public class BinanceApiServiceGenerator { log.info("using nio netty http event loop"); } - sharedClient = Dsl.asyncHttpClient(new Builder().setKeepAlive(true) + Builder builder = new Builder().setKeepAlive(true) .setUseNativeTransport(true) .setEventLoopGroup(eventLoopGroup) - .addChannelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, Integer.getInteger("binance.api.connection.timeout.millis", 40000)) + .addChannelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, getInteger("binance.api.connection.timeout.millis", 40000)) - .setMaxConnectionsPerHost(Integer.getInteger("binance.api.max.connections.per.host", 500)) + .setMaxConnectionsPerHost(getInteger("binance.api.max.connections.per.host", 500)) - .setWebSocketMaxBufferSize(Integer.getInteger("binance.api.ws.buffer.size", 65536)) + .setWebSocketMaxBufferSize(getInteger("binance.api.ws.buffer.size", 65536)) - .setWebSocketMaxFrameSize(Integer.getInteger("binance.api.ws.frame.size", 65536)) + .setWebSocketMaxFrameSize(getInteger("binance.api.ws.frame.size", 65536)); - .addRequestFilter(rateLimiter::request).addResponseFilter(rateLimiter::response) + if (System.getProperty("binance.api.rate.max-connections") != null + && System.getProperty("binance.api.rate.requests-per-second") != null) { + builder.addRequestFilter(new RateLimitedThrottleRequestFilter(getInteger("binance.api.rate.max-connections"), + getInteger("binance.api.rate.requests-per-second"), 60000)); + } else { + RequestRateLimitingInterceptor rateLimiter = new RequestRateLimitingInterceptor(); + builder.addRequestFilter(rateLimiter::request).addResponseFilter(rateLimiter::response); + } - .build()); + sharedClient = Dsl.asyncHttpClient(builder.build()); } @SuppressWarnings("unchecked") From e55d7773f7071e30fc29bb6d59b8026209438050 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 14 Mar 2021 11:07:36 +0000 Subject: [PATCH 073/101] [ci-skip]prepare release binance-api-client-3.0.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 84077a577..547fa872a 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.0.3-SNAPSHOT + 3.0.3 The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-3.0.3 From 2b3cde715dd45ac86cdaf6400c239f0f4703beec Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 14 Mar 2021 11:07:56 +0000 Subject: [PATCH 074/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 547fa872a..ecac1f1e5 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.0.3 + 3.0.4-SNAPSHOT The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.0.3 + HEAD From 90707394adfd62d5de1c6fd1def453e08f72f290 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sun, 14 Mar 2021 13:09:35 +0100 Subject: [PATCH 075/101] netty-client separating ws and rest clients --- .../com/binance/api/client/BinanceApiClientFactory.java | 2 +- .../api/client/impl/BinanceApiServiceGenerator.java | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/binance/api/client/BinanceApiClientFactory.java b/src/main/java/com/binance/api/client/BinanceApiClientFactory.java index f5a6b2db1..f48a06de2 100755 --- a/src/main/java/com/binance/api/client/BinanceApiClientFactory.java +++ b/src/main/java/com/binance/api/client/BinanceApiClientFactory.java @@ -87,7 +87,7 @@ public BinanceApiMarginRestClient newMarginRestClient() { * Creates a new web socket client used for handling data streams. */ public BinanceApiWebSocketClient newWebSocketClient() { - return new BinanceApiWebSocketClientImpl(BinanceApiServiceGenerator.getSharedClient()); + return new BinanceApiWebSocketClientImpl(BinanceApiServiceGenerator.getSharedWsClient()); } /** diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java index c7c085317..e8c0396a0 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -37,6 +37,7 @@ public class BinanceApiServiceGenerator { private static final Converter.Factory converterFactory = JacksonConverterFactory.create(); private static AsyncHttpClient sharedClient; + private static AsyncHttpClient sharedWsClient; static { @@ -63,6 +64,8 @@ public class BinanceApiServiceGenerator { .setWebSocketMaxFrameSize(getInteger("binance.api.ws.frame.size", 65536)); + sharedWsClient = Dsl.asyncHttpClient(builder.build()); + if (System.getProperty("binance.api.rate.max-connections") != null && System.getProperty("binance.api.rate.requests-per-second") != null) { builder.addRequestFilter(new RateLimitedThrottleRequestFilter(getInteger("binance.api.rate.max-connections"), @@ -123,8 +126,8 @@ public static BinanceApiError getBinanceApiError(Response response) throws IO return errorBodyConverter.convert(response.errorBody()); } - public static AsyncHttpClient getSharedClient() { - return sharedClient; + public static AsyncHttpClient getSharedWsClient() { + return sharedWsClient; } } From e691ce2839945552160b03b10802742554e33734 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 14 Mar 2021 12:11:38 +0000 Subject: [PATCH 076/101] [ci-skip]prepare release binance-api-client-3.0.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ecac1f1e5..3bf0005ce 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.0.4-SNAPSHOT + 3.0.4 The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-3.0.4 From fdc7a1147b908a281c1aed8d026c4778870a25a7 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 14 Mar 2021 12:11:56 +0000 Subject: [PATCH 077/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3bf0005ce..8b08a3b3b 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.0.4 + 3.0.5-SNAPSHOT The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.0.4 + HEAD From ed8988edd3a2afbf84f54f03f832fa3a6c0be5ad Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 27 Mar 2021 10:53:55 +0100 Subject: [PATCH 078/101] netty-client added support for isolated margin trades / accounts / transfers / borrow / repay --- pom.xml | 2 +- .../BinanceApiAsyncIsolatedMarginClient.java | 126 ++++++++++++++++ .../api/client/BinanceApiClientFactory.java | 11 ++ .../domain/account/isolated/AccountType.java | 7 + .../isolated/IsolatedMarginAccount.java | 20 +++ .../isolated/IsolatedMarginAccountInfo.java | 16 ++ .../isolated/IsolatedMarginAssetBalance.java | 19 +++ .../isolated/IsolatedMarginSymbol.java | 21 +++ .../isolated/IsolatedMarginTransfer.java | 37 +++++ .../IsolatedMarginTransferResult.java | 12 ++ .../account/isolated/MarginLevelStatus.java | 7 + .../isolated/NewIsolatedAccountResponse.java | 11 ++ .../api/client/domain/event/ListenKey.java | 17 +-- ...nanceApiAsyncIsolatedMarginClientImpl.java | 137 ++++++++++++++++++ .../BinanceApiAsyncMarginRestClientImpl.java | 29 ++-- .../impl/BinanceApiMarginRestClientImpl.java | 40 +++-- .../api/client/impl/BinanceApiService.java | 63 ++++++-- 17 files changed, 522 insertions(+), 53 deletions(-) create mode 100644 src/main/java/com/binance/api/client/BinanceApiAsyncIsolatedMarginClient.java create mode 100644 src/main/java/com/binance/api/client/domain/account/isolated/AccountType.java create mode 100644 src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAccount.java create mode 100644 src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAccountInfo.java create mode 100644 src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAssetBalance.java create mode 100644 src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginSymbol.java create mode 100644 src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginTransfer.java create mode 100644 src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginTransferResult.java create mode 100644 src/main/java/com/binance/api/client/domain/account/isolated/MarginLevelStatus.java create mode 100644 src/main/java/com/binance/api/client/domain/account/isolated/NewIsolatedAccountResponse.java create mode 100644 src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java diff --git a/pom.xml b/pom.xml index 8b08a3b3b..a3422a0ca 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.0.5-SNAPSHOT + 3.1.0-SNAPSHOT The MIT License diff --git a/src/main/java/com/binance/api/client/BinanceApiAsyncIsolatedMarginClient.java b/src/main/java/com/binance/api/client/BinanceApiAsyncIsolatedMarginClient.java new file mode 100644 index 000000000..4e5eccecf --- /dev/null +++ b/src/main/java/com/binance/api/client/BinanceApiAsyncIsolatedMarginClient.java @@ -0,0 +1,126 @@ +package com.binance.api.client; + +import java.util.List; +import java.util.Optional; + +import com.binance.api.client.domain.account.MarginNewOrder; +import com.binance.api.client.domain.account.MarginNewOrderResponse; +import com.binance.api.client.domain.account.MarginTransaction; +import com.binance.api.client.domain.account.Order; +import com.binance.api.client.domain.account.Trade; +import com.binance.api.client.domain.account.isolated.IsolatedMarginAccountInfo; +import com.binance.api.client.domain.account.isolated.IsolatedMarginSymbol; +import com.binance.api.client.domain.account.isolated.IsolatedMarginTransfer; +import com.binance.api.client.domain.account.isolated.IsolatedMarginTransferResult; +import com.binance.api.client.domain.account.isolated.NewIsolatedAccountResponse; +import com.binance.api.client.domain.account.request.CancelOrderRequest; +import com.binance.api.client.domain.account.request.CancelOrderResponse; +import com.binance.api.client.domain.account.request.OrderRequest; +import com.binance.api.client.domain.account.request.OrderStatusRequest; +import com.binance.api.client.domain.event.ListenKey; + +public interface BinanceApiAsyncIsolatedMarginClient { + + void createAccount(String base, String quote, BinanceApiCallback cb); + + void queryAccount(Optional> symbols, BinanceApiCallback cb); + + /** + * Get all open orders on margin account for a symbol (async). + * + * @param orderRequest order request parameters + * @param callback the callback that handles the response + */ + void getOpenOrders(OrderRequest orderRequest, BinanceApiCallback> callback); + + /** + * Send in a new margin order (async). + * + * @param order the new order to submit. + * @return a response containing details about the newly placed order. + */ + void newOrder(MarginNewOrder order, BinanceApiCallback callback); + + /** + * Cancel an active margin order (async). + * + * @param cancelOrderRequest order status request parameters + */ + void cancelOrder(CancelOrderRequest cancelOrderRequest, BinanceApiCallback callback); + + /** + * Check margin order's status (async). + * + * @param orderStatusRequest order status request options/filters + * @return an order + */ + void getOrderStatus(OrderStatusRequest orderStatusRequest, BinanceApiCallback callback); + + /** + * Get margin trades for a specific symbol (async). + * + * @param symbol symbol to get trades from + * @return a list of trades + */ + void getMyTrades(String symbol, BinanceApiCallback> callback); + + /** + * Transfers funds between spot and isolated margin account (account must be + * first created). + * + * @param transfer + * @param cb + */ + void transfer(IsolatedMarginTransfer transfer, BinanceApiCallback cb); + + /** + * Apply for a loan + * @param asset asset to repay + * @param amount amount to repay + * @return transaction id + */ + void borrow(String asset, String symbol, String amount, BinanceApiCallback callback); + + /** + * Repay loan for margin account + * @param asset asset to repay + * @param amount amount to repay + * @return transaction id + */ + void repay(String asset, String symbol, String amount, BinanceApiCallback callback); + + void getSymbol(String symbol, BinanceApiCallback cb); + + void getSymbols(BinanceApiCallback> cb); + + /** + * Start a new user data stream. + * + * @param symbol the isolated account symbol you want to receive events for + * @param callback the callback that handles the response which contains a + * listenKey + */ + void startUserDataStream(String symbol, BinanceApiCallback callback); + + /** + * PING a user data stream to prevent a time out. + * + * @param symbol the isolated account symbol you want to receive events for + * @param listenKey listen key that identifies a data stream + * @param callback the callback that handles the response which contains a + * listenKey + */ + void keepAliveUserDataStream(String symbol, String listenKey, BinanceApiCallback callback); + + /** + * Close out a new user data stream. + * + * @param symbol the isolated account symbol you want to stop receiving + * events for + * @param listenKey listen key that identifies a data stream + * @param callback the callback that handles the response which contains a + * listenKey + */ + void closeUserDataStream(String symbol, String listenKey, BinanceApiCallback callback); + +} diff --git a/src/main/java/com/binance/api/client/BinanceApiClientFactory.java b/src/main/java/com/binance/api/client/BinanceApiClientFactory.java index f48a06de2..64f610c4c 100755 --- a/src/main/java/com/binance/api/client/BinanceApiClientFactory.java +++ b/src/main/java/com/binance/api/client/BinanceApiClientFactory.java @@ -1,5 +1,6 @@ package com.binance.api.client; +import com.binance.api.client.impl.BinanceApiAsyncIsolatedMarginClientImpl; import com.binance.api.client.impl.BinanceApiAsyncMarginRestClientImpl; import com.binance.api.client.impl.BinanceApiAsyncRestClientImpl; import com.binance.api.client.impl.BinanceApiMarginRestClientImpl; @@ -96,4 +97,14 @@ public BinanceApiWebSocketClient newWebSocketClient() { public BinanceApiSwapRestClient newSwapRestClient() { return new BinanceApiSwapRestClientImpl(apiKey, secret); } + + /** + * Creates a new asynchronous isolated margin rest client. + * + * @return the client + */ + public BinanceApiAsyncIsolatedMarginClient newAsyncIsolatedMarginRestClient() { + return new BinanceApiAsyncIsolatedMarginClientImpl(apiKey, secret); + } + } diff --git a/src/main/java/com/binance/api/client/domain/account/isolated/AccountType.java b/src/main/java/com/binance/api/client/domain/account/isolated/AccountType.java new file mode 100644 index 000000000..0ca85fe6d --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/isolated/AccountType.java @@ -0,0 +1,7 @@ +package com.binance.api.client.domain.account.isolated; + +public enum AccountType { + + SPOT, ISOLATED_MARGIN + +} diff --git a/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAccount.java b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAccount.java new file mode 100644 index 000000000..16ddbe174 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAccount.java @@ -0,0 +1,20 @@ +package com.binance.api.client.domain.account.isolated; + +import lombok.Data; + +@Data +public class IsolatedMarginAccount { + + IsolatedMarginAssetBalance baseAsset; + IsolatedMarginAssetBalance quoteAsset; + String symbol; + boolean isolatedCreated; + String marginLevel; + MarginLevelStatus marginLevelStatus; + String marginRatio; + String indexPrice; + String liquidatePrice; + String liquidateRate; + boolean tradeEnabled; + +} diff --git a/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAccountInfo.java b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAccountInfo.java new file mode 100644 index 000000000..e1c6cefdb --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAccountInfo.java @@ -0,0 +1,16 @@ +package com.binance.api.client.domain.account.isolated; + +import java.util.List; + +import lombok.Data; + +@Data +public class IsolatedMarginAccountInfo { + + List assets; + + String totalAssetOfBtc; + String totalLiabilityOfBtc; + String totalNetAssetOfBtc; + +} diff --git a/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAssetBalance.java b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAssetBalance.java new file mode 100644 index 000000000..c8435b1c8 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginAssetBalance.java @@ -0,0 +1,19 @@ +package com.binance.api.client.domain.account.isolated; + +import lombok.Data; + +@Data +public class IsolatedMarginAssetBalance { + + String asset; + boolean borrowEnabled; + String borrowed; + String free; + String interest; + String locked; + String netAsset; + String netAssetOfBtc; + boolean repayEnabled; + String totalAsset; + +} diff --git a/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginSymbol.java b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginSymbol.java new file mode 100644 index 000000000..4dc964f7e --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginSymbol.java @@ -0,0 +1,21 @@ +package com.binance.api.client.domain.account.isolated; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; + +@Data +public class IsolatedMarginSymbol { + + String symbol; + String base; + String quote; + + @JsonProperty("isMarginTrade") + boolean marginTrade; + @JsonProperty("isBuyAllowed") + boolean buyAllowed; + @JsonProperty("isSellAllowed") + boolean sellAllowed; + +} diff --git a/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginTransfer.java b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginTransfer.java new file mode 100644 index 000000000..78e42985b --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginTransfer.java @@ -0,0 +1,37 @@ +package com.binance.api.client.domain.account.isolated; + +import java.math.BigDecimal; + +import lombok.Getter; + +@Getter +public class IsolatedMarginTransfer { + + String asset; + + String symbol; + + BigDecimal amount; + + AccountType from; + + AccountType to; + + public static IsolatedMarginTransfer toSpot(String asset, String symbol, BigDecimal amount) { + IsolatedMarginTransfer transfer = new IsolatedMarginTransfer(); + transfer.asset = asset; + transfer.symbol = symbol; + transfer.amount = amount; + transfer.from = AccountType.ISOLATED_MARGIN; + transfer.to = AccountType.SPOT; + return transfer; + } + + public static IsolatedMarginTransfer toMargin(String asset, String symbol, BigDecimal amount) { + IsolatedMarginTransfer transfer = toSpot(asset, symbol, amount); + transfer.from = AccountType.SPOT; + transfer.to = AccountType.ISOLATED_MARGIN; + return transfer; + } + +} diff --git a/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginTransferResult.java b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginTransferResult.java new file mode 100644 index 000000000..d9f16b161 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/isolated/IsolatedMarginTransferResult.java @@ -0,0 +1,12 @@ +package com.binance.api.client.domain.account.isolated; + +import lombok.Data; + +@Data +public class IsolatedMarginTransferResult { + + String tranId; + + String clientTag; + +} diff --git a/src/main/java/com/binance/api/client/domain/account/isolated/MarginLevelStatus.java b/src/main/java/com/binance/api/client/domain/account/isolated/MarginLevelStatus.java new file mode 100644 index 000000000..2051b2129 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/isolated/MarginLevelStatus.java @@ -0,0 +1,7 @@ +package com.binance.api.client.domain.account.isolated; + +public enum MarginLevelStatus { + + EXCESSIVE, NORMAL, MARGIN_CALL, PRE_LIQUIDATION, FORCE_LIQUIDATION + +} diff --git a/src/main/java/com/binance/api/client/domain/account/isolated/NewIsolatedAccountResponse.java b/src/main/java/com/binance/api/client/domain/account/isolated/NewIsolatedAccountResponse.java new file mode 100644 index 000000000..ff0b9dc37 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/account/isolated/NewIsolatedAccountResponse.java @@ -0,0 +1,11 @@ +package com.binance.api.client.domain.account.isolated; + +import lombok.Data; + +@Data +public class NewIsolatedAccountResponse { + + boolean success; + String symbol; + +} diff --git a/src/main/java/com/binance/api/client/domain/event/ListenKey.java b/src/main/java/com/binance/api/client/domain/event/ListenKey.java index a5ab73977..8767c98ed 100755 --- a/src/main/java/com/binance/api/client/domain/event/ListenKey.java +++ b/src/main/java/com/binance/api/client/domain/event/ListenKey.java @@ -2,24 +2,15 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + /** * Dummy type to wrap a listen key from a server response. */ @JsonIgnoreProperties(ignoreUnknown = true) +@Data public class ListenKey { - private String listenKey; - - public String getListenKey() { - return listenKey; - } - - public void setListenKey(String listenKey) { - this.listenKey = listenKey; - } + String listenKey; - @Override - public String toString() { - return listenKey; - } } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java new file mode 100644 index 000000000..f41b4b237 --- /dev/null +++ b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java @@ -0,0 +1,137 @@ +package com.binance.api.client.impl; + +import static com.binance.api.client.constant.BinanceApiConstants.DEFAULT_RECEIVING_WINDOW; +import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; +import static java.lang.System.currentTimeMillis; +import static java.util.stream.Collectors.joining; + +import java.util.List; +import java.util.Optional; + +import com.binance.api.client.BinanceApiAsyncIsolatedMarginClient; +import com.binance.api.client.BinanceApiCallback; +import com.binance.api.client.constant.BinanceApiConstants; +import com.binance.api.client.domain.account.MarginNewOrder; +import com.binance.api.client.domain.account.MarginNewOrderResponse; +import com.binance.api.client.domain.account.MarginTransaction; +import com.binance.api.client.domain.account.Order; +import com.binance.api.client.domain.account.Trade; +import com.binance.api.client.domain.account.isolated.IsolatedMarginAccountInfo; +import com.binance.api.client.domain.account.isolated.IsolatedMarginSymbol; +import com.binance.api.client.domain.account.isolated.IsolatedMarginTransfer; +import com.binance.api.client.domain.account.isolated.IsolatedMarginTransferResult; +import com.binance.api.client.domain.account.isolated.NewIsolatedAccountResponse; +import com.binance.api.client.domain.account.request.CancelOrderRequest; +import com.binance.api.client.domain.account.request.CancelOrderResponse; +import com.binance.api.client.domain.account.request.OrderRequest; +import com.binance.api.client.domain.account.request.OrderStatusRequest; +import com.binance.api.client.domain.event.ListenKey; + +public class BinanceApiAsyncIsolatedMarginClientImpl implements BinanceApiAsyncIsolatedMarginClient { + + private static final String IS_ISOLATED = "TRUE"; + private BinanceApiService binanceApiService; + + public BinanceApiAsyncIsolatedMarginClientImpl(String apiKey, String secret) { + binanceApiService = createService(BinanceApiService.class, apiKey, secret); + } + + @Override + public void createAccount(String base, String quote, BinanceApiCallback cb) { + binanceApiService.createIsolatedMarginAccount(base, quote, DEFAULT_RECEIVING_WINDOW, currentTimeMillis()) + .enqueue(new BinanceApiCallbackAdapter<>(cb)); + } + + @Override + public void queryAccount(Optional> symbols, BinanceApiCallback cb) { + if (symbols.isPresent()) { + String ss = symbols.get().stream().collect(joining(",")); + binanceApiService.queryIsolatedMarginAccount(ss, DEFAULT_RECEIVING_WINDOW, currentTimeMillis()) + .enqueue(new BinanceApiCallbackAdapter<>(cb)); + } else { + binanceApiService.queryIsolatedMarginAccount(DEFAULT_RECEIVING_WINDOW, currentTimeMillis()) + .enqueue(new BinanceApiCallbackAdapter<>(cb)); + } + } + + @Override + public void getOpenOrders(OrderRequest orderRequest, BinanceApiCallback> callback) { + binanceApiService.getOpenMarginOrders(orderRequest.getSymbol(), IS_ISOLATED, orderRequest.getRecvWindow(), orderRequest.getTimestamp()) + .enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + + @Override + public void newOrder(MarginNewOrder order, BinanceApiCallback callback) { + binanceApiService.newMarginOrder(order.getSymbol(), IS_ISOLATED, order.getSide(), order.getType(), order.getTimeInForce(), + order.getQuantity(), order.getPrice(), order.getNewClientOrderId(), order.getStopPrice(), order.getIcebergQty(), + order.getNewOrderRespType(), order.getSideEffectType(), order.getRecvWindow(), order.getTimestamp()) + .enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + + @Override + public void cancelOrder(CancelOrderRequest cancelOrderRequest, BinanceApiCallback callback) { + binanceApiService.cancelMarginOrder(cancelOrderRequest.getSymbol(), IS_ISOLATED, cancelOrderRequest.getOrderId(), + cancelOrderRequest.getOrigClientOrderId(), cancelOrderRequest.getNewClientOrderId(), cancelOrderRequest.getRecvWindow(), + cancelOrderRequest.getTimestamp()).enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + + @Override + public void getOrderStatus(OrderStatusRequest orderStatusRequest, BinanceApiCallback callback) { + binanceApiService + .getMarginOrderStatus(orderStatusRequest.getSymbol(), IS_ISOLATED, orderStatusRequest.getOrderId(), + orderStatusRequest.getOrigClientOrderId(), orderStatusRequest.getRecvWindow(), orderStatusRequest.getTimestamp()) + .enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + + @Override + public void getMyTrades(String symbol, BinanceApiCallback> callback) { + binanceApiService.getMyMarginTrades(symbol, IS_ISOLATED, null, null, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, currentTimeMillis()) + .enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + + @Override + public void borrow(String asset, String symbol, String amount, BinanceApiCallback callback) { + binanceApiService.borrow(asset, IS_ISOLATED, symbol, amount, BinanceApiConstants.DEFAULT_MARGIN_RECEIVING_WINDOW, currentTimeMillis()) + .enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + + @Override + public void repay(String asset, String symbol, String amount, BinanceApiCallback callback) { + binanceApiService.repay(asset, IS_ISOLATED, symbol, amount, BinanceApiConstants.DEFAULT_MARGIN_RECEIVING_WINDOW, currentTimeMillis()) + .enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + + @Override + public void transfer(IsolatedMarginTransfer transfer, BinanceApiCallback cb) { + binanceApiService + .transfer(transfer.getAsset(), transfer.getSymbol(), transfer.getFrom().name(), transfer.getTo().name(), + transfer.getAmount().toPlainString(), DEFAULT_RECEIVING_WINDOW, currentTimeMillis()) + .enqueue(new BinanceApiCallbackAdapter<>(cb)); + } + + @Override + public void getSymbol(String symbol, BinanceApiCallback cb) { + binanceApiService.querySymbol(symbol, DEFAULT_RECEIVING_WINDOW, currentTimeMillis()).enqueue(new BinanceApiCallbackAdapter<>(cb)); + } + + @Override + public void getSymbols(BinanceApiCallback> cb) { + binanceApiService.querySymbols(DEFAULT_RECEIVING_WINDOW, currentTimeMillis()).enqueue(new BinanceApiCallbackAdapter<>(cb)); + } + + @Override + public void startUserDataStream(String symbol, BinanceApiCallback callback) { + binanceApiService.startIsolatedMarginUserDataStream(symbol).enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + + @Override + public void keepAliveUserDataStream(String symbol, String listenKey, BinanceApiCallback callback) { + binanceApiService.keepAliveIsolatedMarginUserDataStream(symbol, listenKey).enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + + @Override + public void closeUserDataStream(String symbol, String listenKey, BinanceApiCallback callback) { + binanceApiService.closeIsolatedMarginAliveUserDataStream(symbol, listenKey).enqueue(new BinanceApiCallbackAdapter<>(callback)); + } + +} diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncMarginRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncMarginRestClientImpl.java index d3e5c22f6..9097239b4 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncMarginRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncMarginRestClientImpl.java @@ -1,20 +1,25 @@ package com.binance.api.client.impl; +import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; + +import java.util.List; + import com.binance.api.client.BinanceApiAsyncMarginRestClient; import com.binance.api.client.BinanceApiCallback; import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.domain.TransferType; -import com.binance.api.client.domain.account.*; +import com.binance.api.client.domain.account.MarginAccount; +import com.binance.api.client.domain.account.MarginNewOrder; +import com.binance.api.client.domain.account.MarginNewOrderResponse; +import com.binance.api.client.domain.account.MarginTransaction; +import com.binance.api.client.domain.account.Order; +import com.binance.api.client.domain.account.Trade; import com.binance.api.client.domain.account.request.CancelOrderRequest; import com.binance.api.client.domain.account.request.CancelOrderResponse; import com.binance.api.client.domain.account.request.OrderRequest; import com.binance.api.client.domain.account.request.OrderStatusRequest; import com.binance.api.client.domain.event.ListenKey; -import java.util.List; - -import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; - /** * Implementation of Binance's Margin REST API using Retrofit with asynchronous/non-blocking method calls. */ @@ -41,34 +46,34 @@ public void getAccount(BinanceApiCallback callback) { @Override public void getOpenOrders(OrderRequest orderRequest, BinanceApiCallback> callback) { - binanceApiService.getOpenMarginOrders(orderRequest.getSymbol(), orderRequest.getRecvWindow(), + binanceApiService.getOpenMarginOrders(orderRequest.getSymbol(), null, orderRequest.getRecvWindow(), orderRequest.getTimestamp()).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void newOrder(MarginNewOrder order, BinanceApiCallback callback) { - binanceApiService.newMarginOrder(order.getSymbol(), order.getSide(), order.getType(), order.getTimeInForce(), + binanceApiService.newMarginOrder(order.getSymbol(), null, order.getSide(), order.getType(), order.getTimeInForce(), order.getQuantity(), order.getPrice(), order.getNewClientOrderId(), order.getStopPrice(), order.getIcebergQty(), order.getNewOrderRespType(), order.getSideEffectType(), order.getRecvWindow(), order.getTimestamp()).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void cancelOrder(CancelOrderRequest cancelOrderRequest, BinanceApiCallback callback) { - binanceApiService.cancelMarginOrder(cancelOrderRequest.getSymbol(), + binanceApiService.cancelMarginOrder(cancelOrderRequest.getSymbol(), null, cancelOrderRequest.getOrderId(), cancelOrderRequest.getOrigClientOrderId(), cancelOrderRequest.getNewClientOrderId(), cancelOrderRequest.getRecvWindow(), cancelOrderRequest.getTimestamp()).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void getOrderStatus(OrderStatusRequest orderStatusRequest, BinanceApiCallback callback) { - binanceApiService.getMarginOrderStatus(orderStatusRequest.getSymbol(), + binanceApiService.getMarginOrderStatus(orderStatusRequest.getSymbol(), null, orderStatusRequest.getOrderId(), orderStatusRequest.getOrigClientOrderId(), orderStatusRequest.getRecvWindow(), orderStatusRequest.getTimestamp()).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void getMyTrades(String symbol, BinanceApiCallback> callback) { - binanceApiService.getMyTrades(symbol, null, null, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, System.currentTimeMillis()).enqueue(new BinanceApiCallbackAdapter<>(callback)); + binanceApiService.getMyMarginTrades(symbol, null, null, null, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, System.currentTimeMillis()).enqueue(new BinanceApiCallbackAdapter<>(callback)); } // user stream endpoints @@ -92,12 +97,12 @@ public void transfer(String asset, String amount, TransferType type, BinanceApiC @Override public void borrow(String asset, String amount, BinanceApiCallback callback) { long timestamp = System.currentTimeMillis(); - binanceApiService.borrow(asset, amount, BinanceApiConstants.DEFAULT_MARGIN_RECEIVING_WINDOW, timestamp).enqueue(new BinanceApiCallbackAdapter<>(callback)); + binanceApiService.borrow(asset, null, null, amount, BinanceApiConstants.DEFAULT_MARGIN_RECEIVING_WINDOW, timestamp).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void repay(String asset, String amount, BinanceApiCallback callback) { long timestamp = System.currentTimeMillis(); - binanceApiService.repay(asset, amount, BinanceApiConstants.DEFAULT_MARGIN_RECEIVING_WINDOW, timestamp).enqueue(new BinanceApiCallbackAdapter<>(callback)); + binanceApiService.repay(asset, null, null, amount, BinanceApiConstants.DEFAULT_MARGIN_RECEIVING_WINDOW, timestamp).enqueue(new BinanceApiCallbackAdapter<>(callback)); } } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiMarginRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiMarginRestClientImpl.java index 959fa2a5f..db3bcd840 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiMarginRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiMarginRestClientImpl.java @@ -1,19 +1,27 @@ package com.binance.api.client.impl; +import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; +import static com.binance.api.client.impl.BinanceApiServiceGenerator.executeSync; + +import java.util.List; + import com.binance.api.client.BinanceApiMarginRestClient; import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.domain.TransferType; -import com.binance.api.client.domain.account.*; +import com.binance.api.client.domain.account.LoanQueryResult; +import com.binance.api.client.domain.account.MarginAccount; +import com.binance.api.client.domain.account.MarginNewOrder; +import com.binance.api.client.domain.account.MarginNewOrderResponse; +import com.binance.api.client.domain.account.MarginTransaction; +import com.binance.api.client.domain.account.MaxBorrowableQueryResult; +import com.binance.api.client.domain.account.Order; +import com.binance.api.client.domain.account.RepayQueryResult; +import com.binance.api.client.domain.account.Trade; import com.binance.api.client.domain.account.request.CancelOrderRequest; import com.binance.api.client.domain.account.request.CancelOrderResponse; import com.binance.api.client.domain.account.request.OrderRequest; import com.binance.api.client.domain.account.request.OrderStatusRequest; -import java.util.List; - -import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; -import static com.binance.api.client.impl.BinanceApiServiceGenerator.executeSync; - /** * Implementation of Binance's Margin REST API using Retrofit with asynchronous/non-blocking method calls. */ @@ -33,27 +41,27 @@ public MarginAccount getAccount() { @Override public List getOpenOrders(OrderRequest orderRequest) { - return executeSync(binanceApiService.getOpenMarginOrders(orderRequest.getSymbol(), orderRequest.getRecvWindow(), + return executeSync(binanceApiService.getOpenMarginOrders(orderRequest.getSymbol(), null, orderRequest.getRecvWindow(), orderRequest.getTimestamp())); } @Override public MarginNewOrderResponse newOrder(MarginNewOrder order) { - return executeSync(binanceApiService.newMarginOrder(order.getSymbol(), order.getSide(), order.getType(), + return executeSync(binanceApiService.newMarginOrder(order.getSymbol(), null, order.getSide(), order.getType(), order.getTimeInForce(), order.getQuantity(), order.getPrice(), order.getNewClientOrderId(), order.getStopPrice(), order.getIcebergQty(), order.getNewOrderRespType(), order.getSideEffectType(), order.getRecvWindow(), order.getTimestamp())); } @Override public CancelOrderResponse cancelOrder(CancelOrderRequest cancelOrderRequest) { - return executeSync(binanceApiService.cancelMarginOrder(cancelOrderRequest.getSymbol(), + return executeSync(binanceApiService.cancelMarginOrder(cancelOrderRequest.getSymbol(), null, cancelOrderRequest.getOrderId(), cancelOrderRequest.getOrigClientOrderId(), cancelOrderRequest.getNewClientOrderId(), cancelOrderRequest.getRecvWindow(), cancelOrderRequest.getTimestamp())); } @Override public Order getOrderStatus(OrderStatusRequest orderStatusRequest) { - return executeSync(binanceApiService.getMarginOrderStatus(orderStatusRequest.getSymbol(), + return executeSync(binanceApiService.getMarginOrderStatus(orderStatusRequest.getSymbol(), null, orderStatusRequest.getOrderId(), orderStatusRequest.getOrigClientOrderId(), orderStatusRequest.getRecvWindow(), orderStatusRequest.getTimestamp())); } @@ -84,36 +92,36 @@ public MarginTransaction transfer(String asset, String amount, TransferType type @Override public MarginTransaction borrow(String asset, String amount) { long timestamp = System.currentTimeMillis(); - return executeSync(binanceApiService.borrow(asset, amount, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, timestamp)); + return executeSync(binanceApiService.borrow(asset, null, null, amount, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, timestamp)); } @Override public LoanQueryResult queryLoan(String asset, String txId) { long timestamp = System.currentTimeMillis(); - return executeSync(binanceApiService.queryLoan(asset, txId, timestamp)); + return executeSync(binanceApiService.queryLoan(asset, null, txId, timestamp)); } @Override public RepayQueryResult queryRepay(String asset, String txId) { long timestamp = System.currentTimeMillis(); - return executeSync(binanceApiService.queryRepay(asset, txId, timestamp)); + return executeSync(binanceApiService.queryRepay(asset, null, txId, timestamp)); } @Override public RepayQueryResult queryRepay(String asset, long startTime) { long timestamp = System.currentTimeMillis(); - return executeSync(binanceApiService.queryRepay(asset, startTime, timestamp)); + return executeSync(binanceApiService.queryRepay(asset, null, startTime, timestamp)); } @Override public MaxBorrowableQueryResult queryMaxBorrowable(String asset) { long timestamp = System.currentTimeMillis(); - return executeSync(binanceApiService.queryMaxBorrowable(asset, timestamp)); + return executeSync(binanceApiService.queryMaxBorrowable(asset, null, timestamp)); } @Override public MarginTransaction repay(String asset, String amount) { long timestamp = System.currentTimeMillis(); - return executeSync(binanceApiService.repay(asset, amount, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, timestamp)); + return executeSync(binanceApiService.repay(asset, null, null, amount, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, timestamp)); } } \ No newline at end of file diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiService.java b/src/main/java/com/binance/api/client/impl/BinanceApiService.java index f2182a9dd..5143216d4 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiService.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiService.java @@ -32,6 +32,10 @@ import com.binance.api.client.domain.account.TradeHistoryItem; import com.binance.api.client.domain.account.WithdrawHistory; import com.binance.api.client.domain.account.WithdrawResult; +import com.binance.api.client.domain.account.isolated.IsolatedMarginAccountInfo; +import com.binance.api.client.domain.account.isolated.IsolatedMarginSymbol; +import com.binance.api.client.domain.account.isolated.IsolatedMarginTransferResult; +import com.binance.api.client.domain.account.isolated.NewIsolatedAccountResponse; import com.binance.api.client.domain.account.request.CancelOrderResponse; import com.binance.api.client.domain.account.request.SubAccountTransfer; import com.binance.api.client.domain.event.ListenKey; @@ -246,6 +250,43 @@ Call withdraw(@Query("asset") String asset, @Query("address") St @DELETE("/api/v1/userDataStream") Call closeAliveUserDataStream(@Query("listenKey") String listenKey); + // Isolated Margin Account endpoints + @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) + @POST("/sapi/v1/margin/isolated/create") + Call createIsolatedMarginAccount(@Query("base") String base, @Query("quote") String quote, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + + @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) + @GET("/sapi/v1/margin/isolated/account") + Call queryIsolatedMarginAccount(@Query("symbols") String symbols, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + + @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) + @GET("/sapi/v1/margin/isolated/account") + Call queryIsolatedMarginAccount(@Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + + @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) + @POST("/sapi/v1/margin/isolated/transfer") + Call transfer(@Query("asset") String asset, @Query("symbol") String symbol, @Query("transFrom") String from, @Query("transTo") String to, @Query("amount") String amount, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + + @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) + @GET("/sapi/v1/margin/isolated/pair") + Call querySymbol(@Query("symbol") String symbol, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + + @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) + @GET("/sapi/v1/margin/isolated/allPairs") + Call> querySymbols(@Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + + @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER) + @POST("/sapi/v1/userDataStream/isolated") + Call startIsolatedMarginUserDataStream(@Query("symbol") String symbol); + + @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER) + @PUT("/sapi/v1/userDataStream/isolated") + Call keepAliveIsolatedMarginUserDataStream(@Query("symbol") String symbol, @Query("listenKey") String listenKey); + + @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER) + @DELETE("/sapi/v1/userDataStream/isolated") + Call closeIsolatedMarginAliveUserDataStream(@Query("symbol") String symbol, @Query("listenKey") String listenKey); + // Margin Account endpoints @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) @POST("/sapi/v1/margin/transfer") @@ -253,27 +294,27 @@ Call withdraw(@Query("asset") String asset, @Query("address") St @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) @POST("/sapi/v1/margin/loan") - Call borrow(@Query("asset") String asset, @Query("amount") String amount, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + Call borrow(@Query("asset") String asset, @Query("isIsolated") String isIsolated, @Query("symbol") String symbol, @Query("amount") String amount, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) @GET("/sapi/v1/margin/loan") - Call queryLoan(@Query("asset") String asset, @Query("txId") String txId, @Query("timestamp") Long timestamp); + Call queryLoan(@Query("asset") String asset, @Query("isIsolated") String isIsolated, @Query("txId") String txId, @Query("timestamp") Long timestamp); @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) @GET("/sapi/v1/margin/repay") - Call queryRepay(@Query("asset") String asset, @Query("txId") String txId, @Query("timestamp") Long timestamp); + Call queryRepay(@Query("asset") String asset, @Query("isIsolated") String isIsolated, @Query("txId") String txId, @Query("timestamp") Long timestamp); @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) @GET("/sapi/v1/margin/maxBorrowable") - Call queryMaxBorrowable(@Query("asset") String asset, @Query("timestamp") Long timestamp); + Call queryMaxBorrowable(@Query("asset") String asset, @Query("isIsolated") String isIsolated, @Query("timestamp") Long timestamp); @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) @GET("/sapi/v1/margin/repay") - Call queryRepay(@Query("asset") String asset, @Query("startTime") Long starttime, @Query("timestamp") Long timestamp); + Call queryRepay(@Query("asset") String asset, @Query("isIsolated") String isIsolated, @Query("startTime") Long starttime, @Query("timestamp") Long timestamp); @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) @POST("/sapi/v1/margin/repay") - Call repay(@Query("asset") String asset, @Query("amount") String amount, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + Call repay(@Query("asset") String asset, @Query("isIsolated") String isIsolated, @Query("symbol") String symbol, @Query("amount") String amount, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) @GET("/sapi/v1/margin/account") @@ -281,11 +322,11 @@ Call withdraw(@Query("asset") String asset, @Query("address") St @Headers({BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER}) @GET("/sapi/v1/margin/openOrders") - Call> getOpenMarginOrders(@Query("symbol") String symbol, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); + Call> getOpenMarginOrders(@Query("symbol") String symbol, @Query("isIsolated") String isIsolated, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) @POST("/sapi/v1/margin/order") - Call newMarginOrder(@Query("symbol") String symbol, @Query("side") OrderSide side, @Query("type") OrderType type, + Call newMarginOrder(@Query("symbol") String symbol, @Query("isIsolated") String isIsolated, @Query("side") OrderSide side, @Query("type") OrderType type, @Query("timeInForce") TimeInForce timeInForce, @Query("quantity") String quantity, @Query("price") String price, @Query("newClientOrderId") String newClientOrderId, @Query("stopPrice") String stopPrice, @Query("icebergQty") String icebergQty, @Query("newOrderRespType") NewOrderResponseType newOrderRespType, @@ -293,19 +334,19 @@ Call newMarginOrder(@Query("symbol") String symbol, @Que @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) @DELETE("/sapi/v1/margin/order") - Call cancelMarginOrder(@Query("symbol") String symbol, @Query("orderId") Long orderId, + Call cancelMarginOrder(@Query("symbol") String symbol, @Query("isIsolated") String isIsolated, @Query("orderId") Long orderId, @Query("origClientOrderId") String origClientOrderId, @Query("newClientOrderId") String newClientOrderId, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) @GET("/sapi/v1/margin/order") - Call getMarginOrderStatus(@Query("symbol") String symbol, @Query("orderId") Long orderId, + Call getMarginOrderStatus(@Query("symbol") String symbol, @Query("isIsolated") String isIsolated, @Query("orderId") Long orderId, @Query("origClientOrderId") String origClientOrderId, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED_HEADER) @GET("/sapi/v1/margin/myTrades") - Call> getMyMarginTrades(@Query("symbol") String symbol, @Query("limit") Integer limit, @Query("fromId") Long fromId, + Call> getMyMarginTrades(@Query("symbol") String symbol, @Query("isIsolated") String isIsolated, @Query("limit") Integer limit, @Query("fromId") Long fromId, @Query("recvWindow") Long recvWindow, @Query("timestamp") Long timestamp); @Headers(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY_HEADER) From 67697c093bff1ec0d38a04aba27bb298ed875753 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 27 Mar 2021 09:55:54 +0000 Subject: [PATCH 079/101] [ci-skip]prepare release binance-api-client-3.1.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a3422a0ca..edcac60b6 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.1.0-SNAPSHOT + 3.1.0 The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-3.1.0 From 3b21d1dc238a8fefc391b225b167d8d4a45cc621 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 27 Mar 2021 09:56:12 +0000 Subject: [PATCH 080/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index edcac60b6..376d0049b 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.1.0 + 3.1.1-SNAPSHOT The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.1.0 + HEAD From 0c4131770f9d0e7f644a4d95eae56397501498ae Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 27 Mar 2021 11:01:13 +0100 Subject: [PATCH 081/101] netty-client added readme on isolated trading --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 76292b233..8d9898bcd 100755 --- a/README.md +++ b/README.md @@ -550,5 +550,9 @@ OcoOrderResponse order = client.newOcoOrder(buy); *WATCH OUT*. newClientOrderId is your identifier of the two-leg trade. In order to distinguish between limit_maker and stop_loss part you need to set individual client ids for these legs at creation time (limitClientOrderId, stopClientOrderId). Beware however that when an oco stop happens the limit_maker leg order update will come without your client id, it will only contain binance generated numeric orderId (which you can re-query and get your clientOrderId anyway). This is the case with all cancel events - they will not have your own id, but binance orderId instead. +### Isolated margin trading + +If you want to play short or just want to trade long with leverage you might want to create an isolated margin account for a selected pair / borrow some asset and sell it. Later you will re-buy it at a lower price and repay the debt. You can use `com.binance.api.client.BinanceApiClientFactory.newAsyncIsolatedMarginRestClient()` for this purpose. It contains all methods you may need to create isolated margin accounts and create isolated margin trades and borrow / repay debts. If you want to listen to order or account state changes use the `com.binance.api.client.BinanceApiAsyncIsolatedMarginClient.startUserDataStream(String, BinanceApiCallback)` to start a data stream for a specific isolated margin account. Then use the obtained key with user data websocket (`com.binance.api.client.BinanceApiWebSocketClient.onUserDataUpdateEvent(String, BinanceApiCallback)`) to subscribe to notifications. + ### More examples An extensive set of examples, covering most aspects of the API, can be found at https://github.com/joaopsilva/binance-java-api/tree/master/src/test/java/com/binance/api/examples. From 6290b5fc4964b40ffdf70de5307de6769dea8d51 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 27 Mar 2021 10:07:29 +0000 Subject: [PATCH 082/101] [ci-skip]prepare release binance-api-client-3.1.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 376d0049b..11583ac0f 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.1.1-SNAPSHOT + 3.1.1 The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - HEAD + binance-api-client-3.1.1 From 418d6557f119a2d94cf1db33230b216cce1784fa Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 27 Mar 2021 20:09:29 +0100 Subject: [PATCH 083/101] master releasing --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 11583ac0f..5faf99450 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.1.1 + 3.1.2-SNAPSHOT The MIT License From 3551b86835edee4c9b51bf5a006940110e60b7be Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 27 Mar 2021 19:11:28 +0000 Subject: [PATCH 084/101] [ci-skip]prepare release binance-api-client-3.1.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5faf99450..1e96bfc1f 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.1.2-SNAPSHOT + 3.1.2 The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.1.1 + binance-api-client-3.1.2 From 10ef6db57541e6724f2cc36c219e4aef7aad85c2 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 27 Mar 2021 19:11:46 +0000 Subject: [PATCH 085/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1e96bfc1f..86c21f8a1 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.1.2 + 3.1.3-SNAPSHOT The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.1.2 + binance-api-client-3.1.1 From 0e01d881394a51c31f0ab7e52aea23f22fbcabdf Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Sat, 3 Apr 2021 22:27:48 +0200 Subject: [PATCH 086/101] master fixed default ws buffer size to accomodate for huge payloads the allTickers stream is pushing --- .../binance/api/client/impl/BinanceApiServiceGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java index e8c0396a0..985887f4c 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -60,9 +60,9 @@ public class BinanceApiServiceGenerator { .setMaxConnectionsPerHost(getInteger("binance.api.max.connections.per.host", 500)) - .setWebSocketMaxBufferSize(getInteger("binance.api.ws.buffer.size", 65536)) + .setWebSocketMaxBufferSize(getInteger("binance.api.ws.buffer.size", 1<<19)) - .setWebSocketMaxFrameSize(getInteger("binance.api.ws.frame.size", 65536)); + .setWebSocketMaxFrameSize(getInteger("binance.api.ws.frame.size", 1<<19)); sharedWsClient = Dsl.asyncHttpClient(builder.build()); From 608286639eebcaa5da3597e91f9594b555241d59 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 3 Apr 2021 20:29:51 +0000 Subject: [PATCH 087/101] [ci-skip]prepare release binance-api-client-3.1.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 86c21f8a1..0de98dd7b 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.1.3-SNAPSHOT + 3.1.3 The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.1.1 + binance-api-client-3.1.3 From 9a7e6d3cf1a43b6000e6ce45fcd8c4cb21c1c0d3 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sat, 3 Apr 2021 20:30:04 +0000 Subject: [PATCH 088/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0de98dd7b..1ab31fb0a 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.1.3 + 3.1.4-SNAPSHOT The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.1.3 + binance-api-client-3.1.1 From f6da3746267d7d7fa1565904f26781ca80a61406 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Thu, 20 May 2021 21:17:42 +0200 Subject: [PATCH 089/101] master added original okhttp client back, it works better on arm processors --- Jenkinsfile | 1 - README.md | 2 +- pom.xml | 2 +- .../api/client/BinanceApiClientFactory.java | 5 +- .../binance/api/client/impl/ApiGenerator.java | 11 ++ .../api/client/impl/ApiServiceGenerator.java | 67 +++++++ ...nanceApiAsyncIsolatedMarginClientImpl.java | 3 +- .../BinanceApiAsyncMarginRestClientImpl.java | 4 +- .../impl/BinanceApiAsyncRestClientImpl.java | 3 +- .../impl/BinanceApiCallbackAdapter.java | 10 +- .../impl/BinanceApiMarginRestClientImpl.java | 8 +- .../client/impl/BinanceApiRestClientImpl.java | 5 +- .../impl/BinanceApiServiceGenerator.java | 133 +++---------- .../impl/BinanceApiSwapRestClientImpl.java | 34 ++-- .../impl/BinanceApiWebSocketClientImpl.java | 184 ++++++++++-------- .../impl/BinanceApiWebSocketListener.java | 120 ++++++------ .../impl/NettyBinanceApiServiceGenerator.java | 99 ++++++++++ .../NettyBinanceApiWebSocketClientImpl.java | 108 ++++++++++ .../NettyBinanceApiWebSocketListener.java | 63 ++++++ ... NettyRequestRateLimitingInterceptor.java} | 6 +- .../security/AuthenticationInterceptor.java | 150 +++++++++----- .../NettyAuthenticationInterceptor.java | 54 +++++ ...tyRequestRateLimitingInterceptorTest.java} | 4 +- 23 files changed, 728 insertions(+), 348 deletions(-) create mode 100644 src/main/java/com/binance/api/client/impl/ApiGenerator.java create mode 100644 src/main/java/com/binance/api/client/impl/ApiServiceGenerator.java mode change 100755 => 100644 src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java mode change 100755 => 100644 src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java mode change 100755 => 100644 src/main/java/com/binance/api/client/impl/BinanceApiWebSocketListener.java create mode 100755 src/main/java/com/binance/api/client/impl/NettyBinanceApiServiceGenerator.java create mode 100755 src/main/java/com/binance/api/client/impl/NettyBinanceApiWebSocketClientImpl.java create mode 100755 src/main/java/com/binance/api/client/impl/NettyBinanceApiWebSocketListener.java rename src/main/java/com/binance/api/client/impl/{RequestRateLimitingInterceptor.java => NettyRequestRateLimitingInterceptor.java} (94%) mode change 100755 => 100644 src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java create mode 100755 src/main/java/com/binance/api/client/security/NettyAuthenticationInterceptor.java rename src/test/java/com/binance/api/client/impl/{RequestRateLimitingInterceptorTest.java => NettyRequestRateLimitingInterceptorTest.java} (79%) diff --git a/Jenkinsfile b/Jenkinsfile index 5d436f072..a2d084297 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -26,7 +26,6 @@ pipeline { when { anyOf { branch 'master'; - branch 'netty-client' } } steps { diff --git a/README.md b/README.md index 8d9898bcd..1aa187ccf 100755 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ binance-java-api is a lightweight Java library for interacting with the [Binance * Support for synchronous and asynchronous REST requests to all [General](https://www.binance.com/restapipub.html#user-content-general-endpoints), [Market Data](https://www.binance.com/restapipub.html#user-content-market-data-endpoints), [Account](https://www.binance.com/restapipub.html#user-content-account-endpoints) endpoints, and [User](https://www.binance.com/restapipub.html#user-content-user-data-stream-endpoints) stream endpoints. * Support for User Data, Trade, Kline, and Depth event streaming using [Binance WebSocket API](https://www.binance.com/restapipub.html#wss-endpoint). * Implements a simple request rate limiter to prevent your application from getting an IP ban in case you send too many requests too fast. Automatically retries request if your IP was banned. -* Uses Netty as HTTP client and will automatically switch to Epoll event loop if available on the classpath for your platform. +* Uses Netty as HTTP client and will automatically switch to Epoll event loop if available on the classpath for your platform. In order to enable Netty set `binance.api.use.netty=true` system property. ## Installation 1. Install library into your Maven's local repository by running `mvn install` diff --git a/pom.xml b/pom.xml index 1ab31fb0a..b1c079e0f 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.1.4-SNAPSHOT + 3.2.0-SNAPSHOT The MIT License diff --git a/src/main/java/com/binance/api/client/BinanceApiClientFactory.java b/src/main/java/com/binance/api/client/BinanceApiClientFactory.java index 64f610c4c..9d563bc42 100755 --- a/src/main/java/com/binance/api/client/BinanceApiClientFactory.java +++ b/src/main/java/com/binance/api/client/BinanceApiClientFactory.java @@ -1,13 +1,12 @@ package com.binance.api.client; +import com.binance.api.client.impl.ApiServiceGenerator; import com.binance.api.client.impl.BinanceApiAsyncIsolatedMarginClientImpl; import com.binance.api.client.impl.BinanceApiAsyncMarginRestClientImpl; import com.binance.api.client.impl.BinanceApiAsyncRestClientImpl; import com.binance.api.client.impl.BinanceApiMarginRestClientImpl; import com.binance.api.client.impl.BinanceApiRestClientImpl; -import com.binance.api.client.impl.BinanceApiServiceGenerator; import com.binance.api.client.impl.BinanceApiSwapRestClientImpl; -import com.binance.api.client.impl.BinanceApiWebSocketClientImpl; /** * A factory for creating BinanceApi client objects. @@ -88,7 +87,7 @@ public BinanceApiMarginRestClient newMarginRestClient() { * Creates a new web socket client used for handling data streams. */ public BinanceApiWebSocketClient newWebSocketClient() { - return new BinanceApiWebSocketClientImpl(BinanceApiServiceGenerator.getSharedWsClient()); + return ApiServiceGenerator.createSocket(); } /** diff --git a/src/main/java/com/binance/api/client/impl/ApiGenerator.java b/src/main/java/com/binance/api/client/impl/ApiGenerator.java new file mode 100644 index 000000000..a852d1ad8 --- /dev/null +++ b/src/main/java/com/binance/api/client/impl/ApiGenerator.java @@ -0,0 +1,11 @@ +package com.binance.api.client.impl; + +import com.binance.api.client.BinanceApiWebSocketClient; + +public interface ApiGenerator { + + T createService(Class serviceClass, String apikey, String secret); + + BinanceApiWebSocketClient createSocket(); + +} diff --git a/src/main/java/com/binance/api/client/impl/ApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/ApiServiceGenerator.java new file mode 100644 index 000000000..3c6636452 --- /dev/null +++ b/src/main/java/com/binance/api/client/impl/ApiServiceGenerator.java @@ -0,0 +1,67 @@ +package com.binance.api.client.impl; + +import java.io.IOException; +import java.lang.annotation.Annotation; + +import com.binance.api.client.BinanceApiError; +import com.binance.api.client.BinanceApiWebSocketClient; +import com.binance.api.client.exception.BinanceApiException; + +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.Converter; +import retrofit2.Response; + +public class ApiServiceGenerator { + + private static ApiGenerator generator; + + static { + try { + if (Boolean.getBoolean("binance.api.use.netty")) { + generator = (ApiGenerator) Class.forName("com.binance.api.client.impl.NettyBinanceApiServiceGenerator").newInstance(); + } else { + generator = (ApiGenerator) Class.forName("com.binance.api.client.impl.BinanceApiServiceGenerator").newInstance(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static T createService(Class serviceClass, String apiKey, String secret) { + return generator.createService(serviceClass, apiKey, secret); + } + + public static BinanceApiWebSocketClient createSocket() { + return generator.createSocket(); + } + + /** + * Execute a REST call and block until the response is received. + */ + public static T executeSync(Call call) { + try { + Response response = call.execute(); + if (response.isSuccessful()) { + return response.body(); + } else { + BinanceApiError apiError = ApiServiceGenerator.getBinanceApiError(response); + throw new BinanceApiException(apiError); + } + } catch (IOException e) { + throw new BinanceApiException(e); + } + } + + /** + * Extracts and converts the response error body into an object. + */ + public static BinanceApiError getBinanceApiError(Response response) throws IOException, BinanceApiException { + return errorBodyConverter.convert(response.errorBody()); + } + + @SuppressWarnings("unchecked") + static final Converter errorBodyConverter = (Converter) NettyBinanceApiServiceGenerator.converterFactory + .responseBodyConverter(BinanceApiError.class, new Annotation[0], null); + +} diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java index f41b4b237..b2754305e 100644 --- a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java @@ -1,7 +1,6 @@ package com.binance.api.client.impl; import static com.binance.api.client.constant.BinanceApiConstants.DEFAULT_RECEIVING_WINDOW; -import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; import static java.lang.System.currentTimeMillis; import static java.util.stream.Collectors.joining; @@ -33,7 +32,7 @@ public class BinanceApiAsyncIsolatedMarginClientImpl implements BinanceApiAsyncI private BinanceApiService binanceApiService; public BinanceApiAsyncIsolatedMarginClientImpl(String apiKey, String secret) { - binanceApiService = createService(BinanceApiService.class, apiKey, secret); + binanceApiService = ApiServiceGenerator.createService(BinanceApiService.class, apiKey, secret); } @Override diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncMarginRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncMarginRestClientImpl.java index 9097239b4..31b86a591 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncMarginRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncMarginRestClientImpl.java @@ -1,7 +1,5 @@ package com.binance.api.client.impl; -import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; - import java.util.List; import com.binance.api.client.BinanceApiAsyncMarginRestClient; @@ -28,7 +26,7 @@ public class BinanceApiAsyncMarginRestClientImpl implements BinanceApiAsyncMargi private final BinanceApiService binanceApiService; public BinanceApiAsyncMarginRestClientImpl(String apiKey, String secret) { - binanceApiService = createService(BinanceApiService.class, apiKey, secret); + binanceApiService = ApiServiceGenerator.createService(BinanceApiService.class, apiKey, secret); } // Margin Account endpoints diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java index 9c8f2fce3..7509413bf 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncRestClientImpl.java @@ -1,6 +1,5 @@ package com.binance.api.client.impl; -import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; import static java.lang.System.currentTimeMillis; import java.util.List; @@ -47,7 +46,7 @@ public class BinanceApiAsyncRestClientImpl implements BinanceApiAsyncRestClient private final BinanceApiService binanceApiService; public BinanceApiAsyncRestClientImpl(String apiKey, String secret) { - binanceApiService = createService(BinanceApiService.class, apiKey, secret); + binanceApiService = ApiServiceGenerator.createService(BinanceApiService.class, apiKey, secret); } // General endpoints diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiCallbackAdapter.java b/src/main/java/com/binance/api/client/impl/BinanceApiCallbackAdapter.java index 355fa1348..2a3adf7bd 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiCallbackAdapter.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiCallbackAdapter.java @@ -1,16 +1,15 @@ package com.binance.api.client.impl; +import java.io.IOException; + import com.binance.api.client.BinanceApiCallback; import com.binance.api.client.BinanceApiError; import com.binance.api.client.exception.BinanceApiException; + import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; -import java.io.IOException; - -import static com.binance.api.client.impl.BinanceApiServiceGenerator.getBinanceApiError; - /** * An adapter/wrapper which transforms a Callback from Retrofit into a BinanceApiCallback which is exposed to the client. */ @@ -22,6 +21,7 @@ public BinanceApiCallbackAdapter(BinanceApiCallback callback) { this.callback = callback; } + @Override public void onResponse(Call call, Response response) { if (response.isSuccessful()) { callback.onResponse(response.body()); @@ -32,7 +32,7 @@ public void onResponse(Call call, Response response) { return; } try { - BinanceApiError apiError = getBinanceApiError(response); + BinanceApiError apiError = ApiServiceGenerator.getBinanceApiError(response); onFailure(call, new BinanceApiException(apiError)); } catch (IOException e) { onFailure(call, new BinanceApiException(e)); diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiMarginRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiMarginRestClientImpl.java index db3bcd840..7c23e4c2c 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiMarginRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiMarginRestClientImpl.java @@ -1,7 +1,7 @@ package com.binance.api.client.impl; -import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; -import static com.binance.api.client.impl.BinanceApiServiceGenerator.executeSync; +import static com.binance.api.client.impl.ApiServiceGenerator.executeSync; +import static java.lang.System.currentTimeMillis; import java.util.List; @@ -30,12 +30,12 @@ public class BinanceApiMarginRestClientImpl implements BinanceApiMarginRestClien private final BinanceApiService binanceApiService; public BinanceApiMarginRestClientImpl(String apiKey, String secret) { - binanceApiService = createService(BinanceApiService.class, apiKey, secret); + binanceApiService = ApiServiceGenerator.createService(BinanceApiService.class, apiKey, secret); } @Override public MarginAccount getAccount() { - long timestamp = System.currentTimeMillis(); + long timestamp = currentTimeMillis(); return executeSync(binanceApiService.getMarginAccount(BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, timestamp)); } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java index 7e6ac96db..cefff850f 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiRestClientImpl.java @@ -1,7 +1,6 @@ package com.binance.api.client.impl; -import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; -import static com.binance.api.client.impl.BinanceApiServiceGenerator.executeSync; +import static com.binance.api.client.impl.ApiServiceGenerator.executeSync; import static java.lang.System.currentTimeMillis; import java.util.List; @@ -50,7 +49,7 @@ public class BinanceApiRestClientImpl implements BinanceApiRestClient { private final BinanceApiService binanceApiService; public BinanceApiRestClientImpl(String apiKey, String secret) { - binanceApiService = createService(BinanceApiService.class, apiKey, secret); + binanceApiService = ApiServiceGenerator.createService(BinanceApiService.class, apiKey, secret); } // General endpoints diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java old mode 100755 new mode 100644 index 985887f4c..c31fe0e22 --- a/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiServiceGenerator.java @@ -1,30 +1,16 @@ package com.binance.api.client.impl; -import static java.lang.Integer.getInteger; +import java.util.concurrent.TimeUnit; -import java.io.IOException; -import java.lang.annotation.Annotation; +import org.apache.commons.lang3.StringUtils; -import org.asynchttpclient.AsyncHttpClient; -import org.asynchttpclient.DefaultAsyncHttpClientConfig.Builder; -import org.asynchttpclient.Dsl; -import org.asynchttpclient.extras.guava.RateLimitedThrottleRequestFilter; -import org.asynchttpclient.extras.retrofit.AsyncHttpClientCallFactory; - -import com.binance.api.client.BinanceApiError; +import com.binance.api.client.BinanceApiWebSocketClient; import com.binance.api.client.config.BinanceApiConfig; -import com.binance.api.client.exception.BinanceApiException; import com.binance.api.client.security.AuthenticationInterceptor; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.epoll.EpollEventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import lombok.extern.slf4j.Slf4j; -import okhttp3.ResponseBody; -import retrofit2.Call; +import okhttp3.Dispatcher; +import okhttp3.OkHttpClient; import retrofit2.Converter; -import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; @@ -32,102 +18,41 @@ * Generates a Binance API implementation based on @see * {@link BinanceApiService}. */ -@Slf4j -public class BinanceApiServiceGenerator { - - private static final Converter.Factory converterFactory = JacksonConverterFactory.create(); - private static AsyncHttpClient sharedClient; - private static AsyncHttpClient sharedWsClient; - - static { - - EventLoopGroup eventLoopGroup; - try { - eventLoopGroup = new EpollEventLoopGroup(Runtime.getRuntime().availableProcessors()); - log.info("using epoll netty http event loop"); - } catch (UnsatisfiedLinkError e) { - eventLoopGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors()); - log.info("using nio netty http event loop"); - } - - Builder builder = new Builder().setKeepAlive(true) - - .setUseNativeTransport(true) +public class BinanceApiServiceGenerator implements ApiGenerator { - .setEventLoopGroup(eventLoopGroup) + private final OkHttpClient sharedClient; + private final Converter.Factory converterFactory = JacksonConverterFactory.create(); - .addChannelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, getInteger("binance.api.connection.timeout.millis", 40000)) - - .setMaxConnectionsPerHost(getInteger("binance.api.max.connections.per.host", 500)) - - .setWebSocketMaxBufferSize(getInteger("binance.api.ws.buffer.size", 1<<19)) - - .setWebSocketMaxFrameSize(getInteger("binance.api.ws.frame.size", 1<<19)); + { + Dispatcher dispatcher = new Dispatcher(); + dispatcher.setMaxRequestsPerHost(500); + dispatcher.setMaxRequests(500); + sharedClient = new OkHttpClient.Builder().dispatcher(dispatcher).pingInterval(20, TimeUnit.SECONDS).build(); + } - sharedWsClient = Dsl.asyncHttpClient(builder.build()); + @Override + public S createService(Class serviceClass, String apiKey, String secret) { + Retrofit.Builder retrofitBuilder = new Retrofit.Builder().baseUrl(BinanceApiConfig.getApiBaseUrl()) + .addConverterFactory(converterFactory); - if (System.getProperty("binance.api.rate.max-connections") != null - && System.getProperty("binance.api.rate.requests-per-second") != null) { - builder.addRequestFilter(new RateLimitedThrottleRequestFilter(getInteger("binance.api.rate.max-connections"), - getInteger("binance.api.rate.requests-per-second"), 60000)); + if (StringUtils.isEmpty(apiKey) || StringUtils.isEmpty(secret)) { + retrofitBuilder.client(sharedClient); } else { - RequestRateLimitingInterceptor rateLimiter = new RequestRateLimitingInterceptor(); - builder.addRequestFilter(rateLimiter::request).addResponseFilter(rateLimiter::response); + // `adaptedClient` will use its own interceptor, but share thread pool etc with + // the 'parent' client + AuthenticationInterceptor interceptor = new AuthenticationInterceptor(apiKey, secret); + OkHttpClient adaptedClient = sharedClient.newBuilder().addInterceptor(interceptor).build(); + retrofitBuilder.client(adaptedClient); } - sharedClient = Dsl.asyncHttpClient(builder.build()); - } - - @SuppressWarnings("unchecked") - private static final Converter errorBodyConverter = (Converter) converterFactory - .responseBodyConverter(BinanceApiError.class, new Annotation[0], null); - - public static S createService(Class serviceClass, String apiKey, String secret) { - AuthenticationInterceptor authenticationInterceptor = new AuthenticationInterceptor(apiKey, secret); - AsyncHttpClientCallFactory ahccf = AsyncHttpClientCallFactory.builder() - - .httpClient(sharedClient) - - .callCustomizer(callBuilder -> callBuilder.requestCustomizer(authenticationInterceptor)) - - .build(); - - Retrofit retrofit = new Retrofit.Builder() - - .baseUrl(BinanceApiConfig.getApiBaseUrl()) - - .callFactory(ahccf) - - .addConverterFactory(converterFactory).build(); + Retrofit retrofit = retrofitBuilder.build(); return retrofit.create(serviceClass); } - /** - * Execute a REST call and block until the response is received. - */ - public static T executeSync(Call call) { - try { - Response response = call.execute(); - if (response.isSuccessful()) { - return response.body(); - } else { - BinanceApiError apiError = getBinanceApiError(response); - throw new BinanceApiException(apiError); - } - } catch (IOException e) { - throw new BinanceApiException(e); - } - } - - /** - * Extracts and converts the response error body into an object. - */ - public static BinanceApiError getBinanceApiError(Response response) throws IOException, BinanceApiException { - return errorBodyConverter.convert(response.errorBody()); - } + @Override + public BinanceApiWebSocketClient createSocket() { + return new BinanceApiWebSocketClientImpl(sharedClient); - public static AsyncHttpClient getSharedWsClient() { - return sharedWsClient; } } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiSwapRestClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiSwapRestClientImpl.java index 4f1a18bc4..7b13f02ae 100755 --- a/src/main/java/com/binance/api/client/impl/BinanceApiSwapRestClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiSwapRestClientImpl.java @@ -1,14 +1,16 @@ package com.binance.api.client.impl; +import java.util.List; + import com.binance.api.client.BinanceApiSwapRestClient; import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.domain.SwapRemoveType; -import com.binance.api.client.domain.account.*; - -import java.util.List; - -import static com.binance.api.client.impl.BinanceApiServiceGenerator.createService; -import static com.binance.api.client.impl.BinanceApiServiceGenerator.executeSync; +import com.binance.api.client.domain.account.Liquidity; +import com.binance.api.client.domain.account.LiquidityOperationRecord; +import com.binance.api.client.domain.account.Pool; +import com.binance.api.client.domain.account.SwapHistory; +import com.binance.api.client.domain.account.SwapQuote; +import com.binance.api.client.domain.account.SwapRecord; /** * Implementation of Binance's SWAP REST API using Retrofit method calls. @@ -18,18 +20,18 @@ public class BinanceApiSwapRestClientImpl implements BinanceApiSwapRestClient { private final BinanceApiService binanceApiService; public BinanceApiSwapRestClientImpl(String apiKey, String secret) { - binanceApiService = createService(BinanceApiService.class, apiKey, secret); + binanceApiService = ApiServiceGenerator.createService(BinanceApiService.class, apiKey, secret); } @Override public List listAllSwapPools() { - return executeSync(binanceApiService.listAllSwapPools()); + return ApiServiceGenerator.executeSync(binanceApiService.listAllSwapPools()); } @Override public Liquidity getPoolLiquidityInfo(String poolId) { long timestamp = System.currentTimeMillis(); - List liquidities = executeSync(binanceApiService.getPoolLiquidityInfo(poolId, + List liquidities = ApiServiceGenerator.executeSync(binanceApiService.getPoolLiquidityInfo(poolId, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, timestamp)); if (liquidities != null && !liquidities.isEmpty()) { @@ -41,7 +43,7 @@ public Liquidity getPoolLiquidityInfo(String poolId) { @Override public LiquidityOperationRecord addLiquidity(String poolId, String asset, String quantity) { long timestamp = System.currentTimeMillis(); - return executeSync(binanceApiService.addLiquidity(poolId, + return ApiServiceGenerator.executeSync(binanceApiService.addLiquidity(poolId, asset, quantity, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, @@ -51,7 +53,7 @@ public LiquidityOperationRecord addLiquidity(String poolId, String asset, String @Override public LiquidityOperationRecord removeLiquidity(String poolId, SwapRemoveType type, List asset, String shareAmount) { long timestamp = System.currentTimeMillis(); - return executeSync(binanceApiService.removeLiquidity(poolId, + return ApiServiceGenerator.executeSync(binanceApiService.removeLiquidity(poolId, type, asset, shareAmount, @@ -62,7 +64,7 @@ public LiquidityOperationRecord removeLiquidity(String poolId, SwapRemoveType ty @Override public List getPoolLiquidityOperationRecords(String poolId, Integer limit) { long timestamp = System.currentTimeMillis(); - return executeSync(binanceApiService.getPoolLiquidityOperationRecords( + return ApiServiceGenerator.executeSync(binanceApiService.getPoolLiquidityOperationRecords( poolId, limit, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, @@ -73,7 +75,7 @@ public List getPoolLiquidityOperationRecords(String po @Override public LiquidityOperationRecord getLiquidityOperationRecord(String operationId) { long timestamp = System.currentTimeMillis(); - List records = executeSync(binanceApiService.getLiquidityOperationRecord( + List records = ApiServiceGenerator.executeSync(binanceApiService.getLiquidityOperationRecord( operationId, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, timestamp)); @@ -88,7 +90,7 @@ public SwapQuote requestQuote(String quoteAsset, String baseAsset, String quoteQty) { long timestamp = System.currentTimeMillis(); - return executeSync(binanceApiService.requestQuote(quoteAsset, baseAsset, quoteQty, + return ApiServiceGenerator.executeSync(binanceApiService.requestQuote(quoteAsset, baseAsset, quoteQty, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, timestamp)); } @@ -96,7 +98,7 @@ public SwapQuote requestQuote(String quoteAsset, @Override public SwapRecord swap(String quoteAsset, String baseAsset, String quoteQty) { long timestamp = System.currentTimeMillis(); - return executeSync(binanceApiService.swap(quoteAsset, baseAsset, quoteQty, + return ApiServiceGenerator.executeSync(binanceApiService.swap(quoteAsset, baseAsset, quoteQty, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, timestamp)); } @@ -104,7 +106,7 @@ public SwapRecord swap(String quoteAsset, String baseAsset, String quoteQty) { @Override public SwapHistory getSwapHistory(String swapId) { long timestamp = System.currentTimeMillis(); - List history = executeSync(binanceApiService.getSwapHistory(swapId, + List history = ApiServiceGenerator.executeSync(binanceApiService.getSwapHistory(swapId, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, timestamp)); if (history != null && !history.isEmpty()) { diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java old mode 100755 new mode 100644 index cd7631522..0b3c3c24b --- a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketClientImpl.java @@ -5,10 +5,6 @@ import java.util.List; import java.util.stream.Collectors; -import org.asynchttpclient.AsyncHttpClient; -import org.asynchttpclient.netty.ws.NettyWebSocket; -import org.asynchttpclient.ws.WebSocketUpgradeHandler; - import com.binance.api.client.BinanceApiCallback; import com.binance.api.client.BinanceApiWebSocketClient; import com.binance.api.client.config.BinanceApiConfig; @@ -22,87 +18,109 @@ import com.binance.api.client.domain.market.CandlestickInterval; import com.fasterxml.jackson.core.type.TypeReference; -import lombok.SneakyThrows; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.WebSocket; /** * Binance API WebSocket client implementation using OkHttp. */ -public class BinanceApiWebSocketClientImpl implements BinanceApiWebSocketClient { - - private final AsyncHttpClient client; - - public BinanceApiWebSocketClientImpl(AsyncHttpClient client) { - this.client = client; - } - - @Override - public Closeable onDepthEvent(String symbols, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@depth", s)) - .collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, DepthEvent.class)); - } - - @Override - public Closeable onCandlestickEvent(String symbols, CandlestickInterval interval, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")).map(String::trim) - .map(s -> String.format("%s@kline_%s", s, interval.getIntervalId())).collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, CandlestickEvent.class)); - } - - @Override - public Closeable onAggTradeEvent(String symbols, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@aggTrade", s)) - .collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, AggTradeEvent.class)); - } - - @Override - public Closeable onTradeEvent(String symbols, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@trade", s)) - .collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, TradeEvent.class)); - } - - @Override - public Closeable onUserDataUpdateEvent(String listenKey, BinanceApiCallback callback) { - return createNewWebSocket(listenKey, new BinanceApiWebSocketListener<>(callback, UserDataUpdateEvent.class)); - } - - @Override - public Closeable onTickerEvent(String symbols, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@ticker", s)) - .collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, TickerEvent.class)); - } - - @Override - public Closeable onAllMarketTickersEvent(BinanceApiCallback> callback) { - final String channel = "!ticker@arr"; - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, new TypeReference>() { - })); - } - - @Override - public Closeable onBookTickerEvent(String symbols, BinanceApiCallback callback) { - final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@bookTicker", s)) - .collect(Collectors.joining("/")); - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, BookTickerEvent.class)); - } - - @Override - public Closeable onAllBookTickersEvent(BinanceApiCallback callback) { - final String channel = "!bookTicker"; - return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, BookTickerEvent.class)); - } - - @SneakyThrows - private Closeable createNewWebSocket(String channel, BinanceApiWebSocketListener listener) { - String streamingUrl = String.format("%s/%s", BinanceApiConfig.getStreamApiBaseUrl(), channel); - NettyWebSocket socket = this.client.prepareGet(streamingUrl).execute(new WebSocketUpgradeHandler.Builder() - - .addWebSocketListener(listener) - - .build()).get(); - return socket::sendCloseFrame; - } +public class BinanceApiWebSocketClientImpl implements BinanceApiWebSocketClient, Closeable { + + private final OkHttpClient client; + + public BinanceApiWebSocketClientImpl(OkHttpClient client) { + this.client = client; + } + + @Override + public Closeable onDepthEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")) + .map(String::trim) + .map(s -> String.format("%s@depth", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, DepthEvent.class)); + } + + @Override + public Closeable onCandlestickEvent(String symbols, CandlestickInterval interval, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")) + .map(String::trim) + .map(s -> String.format("%s@kline_%s", s, interval.getIntervalId())) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, CandlestickEvent.class)); + } + + @Override + public Closeable onAggTradeEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")) + .map(String::trim) + .map(s -> String.format("%s@aggTrade", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, AggTradeEvent.class)); + } + + @Override + public Closeable onUserDataUpdateEvent(String listenKey, BinanceApiCallback callback) { + return createNewWebSocket(listenKey, new BinanceApiWebSocketListener<>(callback, UserDataUpdateEvent.class)); + } + + @Override + public Closeable onTickerEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")) + .map(String::trim) + .map(s -> String.format("%s@ticker", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, TickerEvent.class)); + } + + @Override + public Closeable onAllMarketTickersEvent(BinanceApiCallback> callback) { + final String channel = "!ticker@arr"; + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, new TypeReference>() { + })); + } + + @Override + public Closeable onBookTickerEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")) + .map(String::trim) + .map(s -> String.format("%s@bookTicker", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, BookTickerEvent.class)); + } + + @Override + public Closeable onAllBookTickersEvent(BinanceApiCallback callback) { + final String channel = "!bookTicker"; + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, BookTickerEvent.class)); + } + + @Override + public Closeable onTradeEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@trade", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new BinanceApiWebSocketListener<>(callback, TradeEvent.class)); + } + + /** + * @deprecated This method is no longer functional. Please use the returned {@link Closeable} from any of the other methods to close the web socket. + */ + @Deprecated + @Override + public void close() { + } + + private Closeable createNewWebSocket(String channel, BinanceApiWebSocketListener listener) { + String streamingUrl = String.format("%s/%s", BinanceApiConfig.getStreamApiBaseUrl(), channel); + Request request = new Request.Builder().url(streamingUrl).build(); + final WebSocket webSocket = client.newWebSocket(request, listener); + return () -> { + final int code = 1000; + listener.onClosing(webSocket, code, null); + webSocket.close(code, null); + listener.onClosed(webSocket, code, null); + }; + } + } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketListener.java b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketListener.java old mode 100755 new mode 100644 index df8f7fecf..6eda2d183 --- a/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketListener.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiWebSocketListener.java @@ -1,63 +1,59 @@ -package com.binance.api.client.impl; - -import java.io.IOException; - -import org.asynchttpclient.ws.WebSocket; - -import com.binance.api.client.BinanceApiCallback; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectReader; - -/** - * Binance API WebSocket listener. - */ -public class BinanceApiWebSocketListener implements org.asynchttpclient.ws.WebSocketListener { - - private BinanceApiCallback callback; - - private static final ObjectMapper mapper = new ObjectMapper(); - - private final ObjectReader objectReader; - - private WebSocket websocket; - - public BinanceApiWebSocketListener(BinanceApiCallback callback, Class eventClass) { - this.callback = callback; - this.objectReader = mapper.readerFor(eventClass); - } - - public BinanceApiWebSocketListener(BinanceApiCallback callback, TypeReference eventTypeReference) { - this.callback = callback; - this.objectReader = mapper.readerFor(eventTypeReference); - } - - @Override - public void onClose(WebSocket websocket, int code, String reason) { - } - - @Override - public void onError(Throwable t) { - this.callback.onFailure(t); - } - - @Override - public void onOpen(WebSocket websocket) { - this.websocket = websocket; - } - - @Override - public void onPingFrame(byte[] payload) { - this.websocket.sendPongFrame(payload); - } - - @Override - public void onTextFrame(String payload, boolean finalFragment, int rsv) { - try { - callback.onResponse(objectReader.readValue(payload)); - } catch (IOException e) { - callback.onFailure(e); - } - } - +package com.binance.api.client.impl; + +import java.io.IOException; + +import com.binance.api.client.BinanceApiCallback; +import com.binance.api.client.exception.BinanceApiException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; + +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; + +/** + * Binance API WebSocket listener. + */ +public class BinanceApiWebSocketListener extends WebSocketListener { + + private BinanceApiCallback callback; + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final ObjectReader objectReader; + + private boolean closing = false; + + public BinanceApiWebSocketListener(BinanceApiCallback callback, Class eventClass) { + this.callback = callback; + this.objectReader = mapper.readerFor(eventClass); + } + + public BinanceApiWebSocketListener(BinanceApiCallback callback, TypeReference eventTypeReference) { + this.callback = callback; + this.objectReader = mapper.readerFor(eventTypeReference); + } + + @Override + public void onMessage(WebSocket webSocket, String text) { + try { + T event = objectReader.readValue(text); + callback.onResponse(event); + } catch (IOException e) { + throw new BinanceApiException(e); + } + } + + @Override + public void onClosing(final WebSocket webSocket, final int code, final String reason) { + closing = true; + } + + @Override + public void onFailure(WebSocket webSocket, Throwable t, Response response) { + if (!closing) { + callback.onFailure(t); + } + } } \ No newline at end of file diff --git a/src/main/java/com/binance/api/client/impl/NettyBinanceApiServiceGenerator.java b/src/main/java/com/binance/api/client/impl/NettyBinanceApiServiceGenerator.java new file mode 100755 index 000000000..9104958a8 --- /dev/null +++ b/src/main/java/com/binance/api/client/impl/NettyBinanceApiServiceGenerator.java @@ -0,0 +1,99 @@ +package com.binance.api.client.impl; + +import static java.lang.Integer.getInteger; + +import org.asynchttpclient.AsyncHttpClient; +import org.asynchttpclient.DefaultAsyncHttpClientConfig.Builder; +import org.asynchttpclient.Dsl; +import org.asynchttpclient.extras.guava.RateLimitedThrottleRequestFilter; +import org.asynchttpclient.extras.retrofit.AsyncHttpClientCallFactory; + +import com.binance.api.client.BinanceApiWebSocketClient; +import com.binance.api.client.config.BinanceApiConfig; +import com.binance.api.client.security.NettyAuthenticationInterceptor; + +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import lombok.extern.slf4j.Slf4j; +import retrofit2.Converter; +import retrofit2.Retrofit; +import retrofit2.converter.jackson.JacksonConverterFactory; + +/** + * Generates a Binance API implementation based on @see + * {@link BinanceApiService}. + */ +@Slf4j +public class NettyBinanceApiServiceGenerator implements ApiGenerator { + + static final Converter.Factory converterFactory = JacksonConverterFactory.create(); + private AsyncHttpClient sharedClient; + private AsyncHttpClient sharedWsClient; + + { + EventLoopGroup eventLoopGroup; + try { + eventLoopGroup = new EpollEventLoopGroup(Runtime.getRuntime().availableProcessors()); + log.info("using epoll netty http event loop"); + } catch (UnsatisfiedLinkError e) { + eventLoopGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors()); + log.info("using nio netty http event loop"); + } + + Builder builder = new Builder().setKeepAlive(true) + + .setUseNativeTransport(true) + + .setEventLoopGroup(eventLoopGroup) + + .addChannelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, getInteger("binance.api.connection.timeout.millis", 40000)) + + .setMaxConnectionsPerHost(getInteger("binance.api.max.connections.per.host", 500)) + + .setWebSocketMaxBufferSize(getInteger("binance.api.ws.buffer.size", 1 << 19)) + + .setWebSocketMaxFrameSize(getInteger("binance.api.ws.frame.size", 1 << 19)); + + sharedWsClient = Dsl.asyncHttpClient(builder.build()); + + if (System.getProperty("binance.api.rate.max-connections") != null + && System.getProperty("binance.api.rate.requests-per-second") != null) { + builder.addRequestFilter(new RateLimitedThrottleRequestFilter(getInteger("binance.api.rate.max-connections"), + getInteger("binance.api.rate.requests-per-second"), 60000)); + } else { + NettyRequestRateLimitingInterceptor rateLimiter = new NettyRequestRateLimitingInterceptor(); + builder.addRequestFilter(rateLimiter::request).addResponseFilter(rateLimiter::response); + } + + sharedClient = Dsl.asyncHttpClient(builder.build()); + } + + @Override + public S createService(Class serviceClass, String apiKey, String secret) { + NettyAuthenticationInterceptor authenticationInterceptor = new NettyAuthenticationInterceptor(apiKey, secret); + AsyncHttpClientCallFactory ahccf = AsyncHttpClientCallFactory.builder() + + .httpClient(sharedClient) + + .callCustomizer(callBuilder -> callBuilder.requestCustomizer(authenticationInterceptor)) + + .build(); + + Retrofit retrofit = new Retrofit.Builder() + + .baseUrl(BinanceApiConfig.getApiBaseUrl()) + + .callFactory(ahccf) + + .addConverterFactory(converterFactory).build(); + return retrofit.create(serviceClass); + } + + @Override + public BinanceApiWebSocketClient createSocket() { + return new NettyBinanceApiWebSocketClientImpl(sharedWsClient); + } + +} diff --git a/src/main/java/com/binance/api/client/impl/NettyBinanceApiWebSocketClientImpl.java b/src/main/java/com/binance/api/client/impl/NettyBinanceApiWebSocketClientImpl.java new file mode 100755 index 000000000..6d2acd4d7 --- /dev/null +++ b/src/main/java/com/binance/api/client/impl/NettyBinanceApiWebSocketClientImpl.java @@ -0,0 +1,108 @@ +package com.binance.api.client.impl; + +import java.io.Closeable; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.asynchttpclient.AsyncHttpClient; +import org.asynchttpclient.netty.ws.NettyWebSocket; +import org.asynchttpclient.ws.WebSocketUpgradeHandler; + +import com.binance.api.client.BinanceApiCallback; +import com.binance.api.client.BinanceApiWebSocketClient; +import com.binance.api.client.config.BinanceApiConfig; +import com.binance.api.client.domain.event.AggTradeEvent; +import com.binance.api.client.domain.event.BookTickerEvent; +import com.binance.api.client.domain.event.CandlestickEvent; +import com.binance.api.client.domain.event.DepthEvent; +import com.binance.api.client.domain.event.TickerEvent; +import com.binance.api.client.domain.event.TradeEvent; +import com.binance.api.client.domain.event.UserDataUpdateEvent; +import com.binance.api.client.domain.market.CandlestickInterval; +import com.fasterxml.jackson.core.type.TypeReference; + +import lombok.SneakyThrows; + +/** + * Binance API WebSocket client implementation using OkHttp. + */ +public class NettyBinanceApiWebSocketClientImpl implements BinanceApiWebSocketClient { + + private final AsyncHttpClient client; + + public NettyBinanceApiWebSocketClientImpl(AsyncHttpClient client) { + this.client = client; + } + + @Override + public Closeable onDepthEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@depth", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new NettyBinanceApiWebSocketListener<>(callback, DepthEvent.class)); + } + + @Override + public Closeable onCandlestickEvent(String symbols, CandlestickInterval interval, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim) + .map(s -> String.format("%s@kline_%s", s, interval.getIntervalId())).collect(Collectors.joining("/")); + return createNewWebSocket(channel, new NettyBinanceApiWebSocketListener<>(callback, CandlestickEvent.class)); + } + + @Override + public Closeable onAggTradeEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@aggTrade", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new NettyBinanceApiWebSocketListener<>(callback, AggTradeEvent.class)); + } + + @Override + public Closeable onTradeEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@trade", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new NettyBinanceApiWebSocketListener<>(callback, TradeEvent.class)); + } + + @Override + public Closeable onUserDataUpdateEvent(String listenKey, BinanceApiCallback callback) { + return createNewWebSocket(listenKey, new NettyBinanceApiWebSocketListener<>(callback, UserDataUpdateEvent.class)); + } + + @Override + public Closeable onTickerEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@ticker", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new NettyBinanceApiWebSocketListener<>(callback, TickerEvent.class)); + } + + @Override + public Closeable onAllMarketTickersEvent(BinanceApiCallback> callback) { + final String channel = "!ticker@arr"; + return createNewWebSocket(channel, new NettyBinanceApiWebSocketListener<>(callback, new TypeReference>() { + })); + } + + @Override + public Closeable onBookTickerEvent(String symbols, BinanceApiCallback callback) { + final String channel = Arrays.stream(symbols.split(",")).map(String::trim).map(s -> String.format("%s@bookTicker", s)) + .collect(Collectors.joining("/")); + return createNewWebSocket(channel, new NettyBinanceApiWebSocketListener<>(callback, BookTickerEvent.class)); + } + + @Override + public Closeable onAllBookTickersEvent(BinanceApiCallback callback) { + final String channel = "!bookTicker"; + return createNewWebSocket(channel, new NettyBinanceApiWebSocketListener<>(callback, BookTickerEvent.class)); + } + + @SneakyThrows + private Closeable createNewWebSocket(String channel, NettyBinanceApiWebSocketListener listener) { + String streamingUrl = String.format("%s/%s", BinanceApiConfig.getStreamApiBaseUrl(), channel); + NettyWebSocket socket = this.client.prepareGet(streamingUrl).execute(new WebSocketUpgradeHandler.Builder() + + .addWebSocketListener(listener) + + .build()).get(); + return socket::sendCloseFrame; + } +} diff --git a/src/main/java/com/binance/api/client/impl/NettyBinanceApiWebSocketListener.java b/src/main/java/com/binance/api/client/impl/NettyBinanceApiWebSocketListener.java new file mode 100755 index 000000000..c393a1e10 --- /dev/null +++ b/src/main/java/com/binance/api/client/impl/NettyBinanceApiWebSocketListener.java @@ -0,0 +1,63 @@ +package com.binance.api.client.impl; + +import java.io.IOException; + +import org.asynchttpclient.ws.WebSocket; + +import com.binance.api.client.BinanceApiCallback; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; + +/** + * Binance API WebSocket listener. + */ +public class NettyBinanceApiWebSocketListener implements org.asynchttpclient.ws.WebSocketListener { + + private BinanceApiCallback callback; + + private static final ObjectMapper mapper = new ObjectMapper(); + + private final ObjectReader objectReader; + + private WebSocket websocket; + + public NettyBinanceApiWebSocketListener(BinanceApiCallback callback, Class eventClass) { + this.callback = callback; + this.objectReader = mapper.readerFor(eventClass); + } + + public NettyBinanceApiWebSocketListener(BinanceApiCallback callback, TypeReference eventTypeReference) { + this.callback = callback; + this.objectReader = mapper.readerFor(eventTypeReference); + } + + @Override + public void onClose(WebSocket websocket, int code, String reason) { + } + + @Override + public void onError(Throwable t) { + this.callback.onFailure(t); + } + + @Override + public void onOpen(WebSocket websocket) { + this.websocket = websocket; + } + + @Override + public void onPingFrame(byte[] payload) { + this.websocket.sendPongFrame(payload); + } + + @Override + public void onTextFrame(String payload, boolean finalFragment, int rsv) { + try { + callback.onResponse(objectReader.readValue(payload)); + } catch (IOException e) { + callback.onFailure(e); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java b/src/main/java/com/binance/api/client/impl/NettyRequestRateLimitingInterceptor.java similarity index 94% rename from src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java rename to src/main/java/com/binance/api/client/impl/NettyRequestRateLimitingInterceptor.java index 9aa96338e..4c5a3c967 100644 --- a/src/main/java/com/binance/api/client/impl/RequestRateLimitingInterceptor.java +++ b/src/main/java/com/binance/api/client/impl/NettyRequestRateLimitingInterceptor.java @@ -24,10 +24,10 @@ import lombok.extern.slf4j.Slf4j; @Slf4j -public class RequestRateLimitingInterceptor { +public class NettyRequestRateLimitingInterceptor { private final int limit; - public RequestRateLimitingInterceptor() { + public NettyRequestRateLimitingInterceptor() { this(getLimitsDefault()); } @@ -41,7 +41,7 @@ static RateLimit getLimitsDefault() { } } - public RequestRateLimitingInterceptor(RateLimit requestWeightLimit) { + public NettyRequestRateLimitingInterceptor(RateLimit requestWeightLimit) { this.limit = (int) (requestWeightLimit.getLimit() * 0.9); } diff --git a/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java b/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java old mode 100755 new mode 100644 index 8fdca1ef7..9c045c936 --- a/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java +++ b/src/main/java/com/binance/api/client/security/AuthenticationInterceptor.java @@ -1,54 +1,98 @@ -package com.binance.api.client.security; - -import java.util.function.Consumer; - -import org.apache.commons.lang3.StringUtils; -import org.asynchttpclient.Header; -import org.asynchttpclient.Request; -import org.asynchttpclient.RequestBuilder; -import org.asynchttpclient.RequestBuilderBase; -import org.asynchttpclient.SignatureCalculator; - -import com.binance.api.client.constant.BinanceApiConstants; - -/** - * A request interceptor that injects the API Key Header into requests, and - * signs messages, whenever required. - */ -public class AuthenticationInterceptor implements Consumer, SignatureCalculator { - - private final String apiKey; - - private final String secret; - - public AuthenticationInterceptor(String apiKey, String secret) { - this.apiKey = apiKey; - this.secret = secret; - } - - @Override - public void accept(RequestBuilder request) { - boolean isApiKeyRequired = Header.get(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY) != null; - boolean isSignatureRequired = Header.get(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED) != null; - Header.remove(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY); - Header.remove(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED); - - // Endpoint requires sending a valid API-KEY - if (isApiKeyRequired || isSignatureRequired) { - request.addHeader(BinanceApiConstants.API_KEY_HEADER, apiKey); - } - if (isSignatureRequired) { - request.setSignatureCalculator(this); - } - } - - @Override - public void calculateAndAddSignature(Request request, RequestBuilderBase requestBuilder) { - String payload = request.getUri().getQuery(); - if (!StringUtils.isEmpty(payload)) { - String signature = HmacSHA256Signer.sign(payload, secret); - requestBuilder.addQueryParam("signature", signature); - } - } - +package com.binance.api.client.security; + +import java.io.IOException; +import java.util.Objects; + +import org.apache.commons.lang3.StringUtils; + +import com.binance.api.client.constant.BinanceApiConstants; + +import okhttp3.HttpUrl; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okio.Buffer; + +/** + * A request interceptor that injects the API Key Header into requests, and signs messages, whenever required. + */ +public class AuthenticationInterceptor implements Interceptor { + + private final String apiKey; + + private final String secret; + + public AuthenticationInterceptor(String apiKey, String secret) { + this.apiKey = apiKey; + this.secret = secret; + } + + @Override + public Response intercept(Chain chain) throws IOException { + Request original = chain.request(); + Request.Builder newRequestBuilder = original.newBuilder(); + + boolean isApiKeyRequired = original.header(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY) != null; + boolean isSignatureRequired = original.header(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED) != null; + newRequestBuilder.removeHeader(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY) + .removeHeader(BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED); + + // Endpoint requires sending a valid API-KEY + if (isApiKeyRequired || isSignatureRequired) { + newRequestBuilder.addHeader(BinanceApiConstants.API_KEY_HEADER, apiKey); + } + + // Endpoint requires signing the payload + if (isSignatureRequired) { + String payload = original.url().query(); + if (!StringUtils.isEmpty(payload)) { + String signature = HmacSHA256Signer.sign(payload, secret); + HttpUrl signedUrl = original.url().newBuilder().addQueryParameter("signature", signature).build(); + newRequestBuilder.url(signedUrl); + } + } + + // Build new request after adding the necessary authentication information + Request newRequest = newRequestBuilder.build(); + return chain.proceed(newRequest); + } + + /** + * Extracts the request body into a String. + * + * @return request body as a string + */ + @SuppressWarnings("unused") + private static String bodyToString(RequestBody request) { + try (final Buffer buffer = new Buffer()) { + final RequestBody copy = request; + if (copy != null) { + copy.writeTo(buffer); + } else { + return ""; + } + return buffer.readUtf8(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final AuthenticationInterceptor that = (AuthenticationInterceptor) o; + return Objects.equals(apiKey, that.apiKey) && + Objects.equals(secret, that.secret); + } + + @Override + public int hashCode() { + return Objects.hash(apiKey, secret); + } } \ No newline at end of file diff --git a/src/main/java/com/binance/api/client/security/NettyAuthenticationInterceptor.java b/src/main/java/com/binance/api/client/security/NettyAuthenticationInterceptor.java new file mode 100755 index 000000000..23b57d3fb --- /dev/null +++ b/src/main/java/com/binance/api/client/security/NettyAuthenticationInterceptor.java @@ -0,0 +1,54 @@ +package com.binance.api.client.security; + +import java.util.function.Consumer; + +import org.apache.commons.lang3.StringUtils; +import org.asynchttpclient.Header; +import org.asynchttpclient.Request; +import org.asynchttpclient.RequestBuilder; +import org.asynchttpclient.RequestBuilderBase; +import org.asynchttpclient.SignatureCalculator; + +import com.binance.api.client.constant.BinanceApiConstants; + +/** + * A request interceptor that injects the API Key Header into requests, and + * signs messages, whenever required. + */ +public class NettyAuthenticationInterceptor implements Consumer, SignatureCalculator { + + private final String apiKey; + + private final String secret; + + public NettyAuthenticationInterceptor(String apiKey, String secret) { + this.apiKey = apiKey; + this.secret = secret; + } + + @Override + public void accept(RequestBuilder request) { + boolean isApiKeyRequired = Header.get(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY) != null; + boolean isSignatureRequired = Header.get(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED) != null; + Header.remove(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_APIKEY); + Header.remove(request, BinanceApiConstants.ENDPOINT_SECURITY_TYPE_SIGNED); + + // Endpoint requires sending a valid API-KEY + if (isApiKeyRequired || isSignatureRequired) { + request.addHeader(BinanceApiConstants.API_KEY_HEADER, apiKey); + } + if (isSignatureRequired) { + request.setSignatureCalculator(this); + } + } + + @Override + public void calculateAndAddSignature(Request request, RequestBuilderBase requestBuilder) { + String payload = request.getUri().getQuery(); + if (!StringUtils.isEmpty(payload)) { + String signature = HmacSHA256Signer.sign(payload, secret); + requestBuilder.addQueryParam("signature", signature); + } + } + +} \ No newline at end of file diff --git a/src/test/java/com/binance/api/client/impl/RequestRateLimitingInterceptorTest.java b/src/test/java/com/binance/api/client/impl/NettyRequestRateLimitingInterceptorTest.java similarity index 79% rename from src/test/java/com/binance/api/client/impl/RequestRateLimitingInterceptorTest.java rename to src/test/java/com/binance/api/client/impl/NettyRequestRateLimitingInterceptorTest.java index 599977e6d..e70477725 100644 --- a/src/test/java/com/binance/api/client/impl/RequestRateLimitingInterceptorTest.java +++ b/src/test/java/com/binance/api/client/impl/NettyRequestRateLimitingInterceptorTest.java @@ -7,13 +7,13 @@ import com.binance.api.client.domain.account.request.OrderStatusRequest; import com.fasterxml.jackson.databind.ObjectMapper; -public class RequestRateLimitingInterceptorTest { +public class NettyRequestRateLimitingInterceptorTest { @Test public void replaceTimestamp() throws Exception { ObjectMapper mapper = new ObjectMapper(); String oldRequest = mapper.writeValueAsString(new OrderStatusRequest("BTCEUR", 1234l)); - String string = RequestRateLimitingInterceptor.replaceTimestamp(oldRequest, 12345); + String string = NettyRequestRateLimitingInterceptor.replaceTimestamp(oldRequest, 12345); assertEquals("{\"symbol\":\"BTCEUR\",\"recvWindow\":60000,\"timestamp\":12345,\"orderId\":1234,\"origClientOrderId\":null}", string); } From 2c25d2e5e1122130628ff07e1f9ade1750daa4e9 Mon Sep 17 00:00:00 2001 From: jenkins Date: Thu, 20 May 2021 19:19:38 +0000 Subject: [PATCH 090/101] [ci-skip]prepare release binance-api-client-3.2.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b1c079e0f..710ee93b1 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.2.0-SNAPSHOT + 3.2.0 The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.1.1 + binance-api-client-3.2.0 From a90ac40a3eed0920866f93dde89f6132a0781f7f Mon Sep 17 00:00:00 2001 From: jenkins Date: Thu, 20 May 2021 19:20:24 +0000 Subject: [PATCH 091/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 710ee93b1..fffa43ea0 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.2.0 + 3.2.1-SNAPSHOT The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.2.0 + binance-api-client-3.1.1 From 7182fd379f888b6ab1c8212db20556234e97f113 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Mon, 14 Jun 2021 20:53:19 +0200 Subject: [PATCH 092/101] master reusing the same code to do sync and async isolated margin calls --- .../BinanceApiIsolatedMarginClient.java | 120 ++++++++++++++++++ ...nanceApiAsyncIsolatedMarginClientBase.java | 113 +++++++++++++++++ ...nanceApiAsyncIsolatedMarginClientImpl.java | 63 +++------ .../BinanceApiIsolatedMarginClientImpl.java | 108 ++++++++++++++++ 4 files changed, 358 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/binance/api/client/BinanceApiIsolatedMarginClient.java create mode 100644 src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientBase.java create mode 100644 src/main/java/com/binance/api/client/impl/BinanceApiIsolatedMarginClientImpl.java diff --git a/src/main/java/com/binance/api/client/BinanceApiIsolatedMarginClient.java b/src/main/java/com/binance/api/client/BinanceApiIsolatedMarginClient.java new file mode 100644 index 000000000..d2946f699 --- /dev/null +++ b/src/main/java/com/binance/api/client/BinanceApiIsolatedMarginClient.java @@ -0,0 +1,120 @@ +package com.binance.api.client; + +import java.util.List; +import java.util.Optional; + +import com.binance.api.client.domain.account.MarginNewOrder; +import com.binance.api.client.domain.account.MarginNewOrderResponse; +import com.binance.api.client.domain.account.MarginTransaction; +import com.binance.api.client.domain.account.Order; +import com.binance.api.client.domain.account.Trade; +import com.binance.api.client.domain.account.isolated.IsolatedMarginAccountInfo; +import com.binance.api.client.domain.account.isolated.IsolatedMarginSymbol; +import com.binance.api.client.domain.account.isolated.IsolatedMarginTransfer; +import com.binance.api.client.domain.account.isolated.IsolatedMarginTransferResult; +import com.binance.api.client.domain.account.isolated.NewIsolatedAccountResponse; +import com.binance.api.client.domain.account.request.CancelOrderRequest; +import com.binance.api.client.domain.account.request.CancelOrderResponse; +import com.binance.api.client.domain.account.request.OrderRequest; +import com.binance.api.client.domain.account.request.OrderStatusRequest; +import com.binance.api.client.domain.event.ListenKey; + +public interface BinanceApiIsolatedMarginClient { + + NewIsolatedAccountResponse createAccount(String base, String quote); + + IsolatedMarginAccountInfo queryAccount(Optional> symbols); + + /** + * Get all open orders on margin account for a symbol. + * + * @param orderRequest order request parameters + */ + List getOpenOrders(OrderRequest orderRequest); + + /** + * Send in a new margin order. + * + * @param order the new order to submit. + * @return a response containing details about the newly placed order. + */ + MarginNewOrderResponse newOrder(MarginNewOrder order); + + /** + * Cancel an active margin order. + * + * @param cancelOrderRequest order status request parameters + */ + CancelOrderResponse cancelOrder(CancelOrderRequest cancelOrderRequest); + + /** + * Check margin order's status. + * + * @param orderStatusRequest order status request options/filters + * @return an order + */ + Order getOrderStatus(OrderStatusRequest orderStatusRequest); + + /** + * Get margin trades for a specific symbol. + * + * @param symbol symbol to get trades from + * @return a list of trades + */ + List getMyTrades(String symbol); + + /** + * Transfers funds between spot and isolated margin account (account must be + * first created). + * + * @param transfer + */ + IsolatedMarginTransferResult transfer(IsolatedMarginTransfer transfer); + + /** + * Apply for a loan + * + * @param asset asset to repay + * @param amount amount to repay + * @return transaction id + */ + MarginTransaction borrow(String asset, String symbol, String amount); + + /** + * Repay loan for margin account + * + * @param asset asset to repay + * @param amount amount to repay + * @return transaction id + */ + MarginTransaction repay(String asset, String symbol, String amount); + + IsolatedMarginSymbol getSymbol(String symbol); + + List getSymbols(); + + /** + * Start a new user data stream. + * + * @param symbol the isolated account symbol you want to receive events for + */ + ListenKey startUserDataStream(String symbol); + + /** + * PING a user data stream to prevent a time out. + * + * @param symbol the isolated account symbol you want to receive events for + * @param listenKey listen key that identifies a data stream + */ + void keepAliveUserDataStream(String symbol, String listenKey); + + /** + * Close out a new user data stream. + * + * @param symbol the isolated account symbol you want to stop receiving + * events for + * @param listenKey listen key that identifies a data stream + */ + void closeUserDataStream(String symbol, String listenKey); + +} diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientBase.java b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientBase.java new file mode 100644 index 000000000..94c80b89d --- /dev/null +++ b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientBase.java @@ -0,0 +1,113 @@ +package com.binance.api.client.impl; + +import static com.binance.api.client.constant.BinanceApiConstants.DEFAULT_RECEIVING_WINDOW; +import static java.lang.System.currentTimeMillis; +import static java.util.stream.Collectors.joining; + +import java.util.List; +import java.util.Optional; + +import com.binance.api.client.constant.BinanceApiConstants; +import com.binance.api.client.domain.account.MarginNewOrder; +import com.binance.api.client.domain.account.MarginNewOrderResponse; +import com.binance.api.client.domain.account.MarginTransaction; +import com.binance.api.client.domain.account.Order; +import com.binance.api.client.domain.account.Trade; +import com.binance.api.client.domain.account.isolated.IsolatedMarginAccountInfo; +import com.binance.api.client.domain.account.isolated.IsolatedMarginSymbol; +import com.binance.api.client.domain.account.isolated.IsolatedMarginTransfer; +import com.binance.api.client.domain.account.isolated.IsolatedMarginTransferResult; +import com.binance.api.client.domain.account.isolated.NewIsolatedAccountResponse; +import com.binance.api.client.domain.account.request.CancelOrderRequest; +import com.binance.api.client.domain.account.request.CancelOrderResponse; +import com.binance.api.client.domain.account.request.OrderRequest; +import com.binance.api.client.domain.account.request.OrderStatusRequest; +import com.binance.api.client.domain.event.ListenKey; + +import retrofit2.Call; + +public class BinanceApiAsyncIsolatedMarginClientBase { + + private static final String IS_ISOLATED = "TRUE"; + private BinanceApiService binanceApiService; + + public BinanceApiAsyncIsolatedMarginClientBase(String apiKey, String secret) { + binanceApiService = ApiServiceGenerator.createService(BinanceApiService.class, apiKey, secret); + } + + public Call createAccount(String base, String quote) { + return binanceApiService.createIsolatedMarginAccount(base, quote, DEFAULT_RECEIVING_WINDOW, currentTimeMillis()); + } + + public Call queryAccount(Optional> symbols) { + if (symbols.isPresent()) { + String ss = symbols.get().stream().collect(joining(",")); + return binanceApiService.queryIsolatedMarginAccount(ss, DEFAULT_RECEIVING_WINDOW, currentTimeMillis()); + } else { + return binanceApiService.queryIsolatedMarginAccount(DEFAULT_RECEIVING_WINDOW, currentTimeMillis()); + } + } + + public Call> getOpenOrders(OrderRequest orderRequest) { + return binanceApiService.getOpenMarginOrders(orderRequest.getSymbol(), IS_ISOLATED, orderRequest.getRecvWindow(), + orderRequest.getTimestamp()); + } + + public Call newOrder(MarginNewOrder order) { + return binanceApiService.newMarginOrder(order.getSymbol(), IS_ISOLATED, order.getSide(), order.getType(), order.getTimeInForce(), + order.getQuantity(), order.getPrice(), order.getNewClientOrderId(), order.getStopPrice(), order.getIcebergQty(), + order.getNewOrderRespType(), order.getSideEffectType(), order.getRecvWindow(), order.getTimestamp()); + } + + public Call cancelOrder(CancelOrderRequest cancelOrderRequest) { + return binanceApiService.cancelMarginOrder(cancelOrderRequest.getSymbol(), IS_ISOLATED, cancelOrderRequest.getOrderId(), + cancelOrderRequest.getOrigClientOrderId(), cancelOrderRequest.getNewClientOrderId(), cancelOrderRequest.getRecvWindow(), + cancelOrderRequest.getTimestamp()); + } + + public Call getOrderStatus(OrderStatusRequest orderStatusRequest) { + return binanceApiService.getMarginOrderStatus(orderStatusRequest.getSymbol(), IS_ISOLATED, orderStatusRequest.getOrderId(), + orderStatusRequest.getOrigClientOrderId(), orderStatusRequest.getRecvWindow(), orderStatusRequest.getTimestamp()); + } + + public Call> getMyTrades(String symbol) { + return binanceApiService.getMyMarginTrades(symbol, IS_ISOLATED, null, null, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, + currentTimeMillis()); + } + + public Call borrow(String asset, String symbol, String amount) { + return binanceApiService.borrow(asset, IS_ISOLATED, symbol, amount, BinanceApiConstants.DEFAULT_MARGIN_RECEIVING_WINDOW, + currentTimeMillis()); + } + + public Call repay(String asset, String symbol, String amount) { + return binanceApiService.repay(asset, IS_ISOLATED, symbol, amount, BinanceApiConstants.DEFAULT_MARGIN_RECEIVING_WINDOW, + currentTimeMillis()); + } + + public Call transfer(IsolatedMarginTransfer transfer) { + return binanceApiService.transfer(transfer.getAsset(), transfer.getSymbol(), transfer.getFrom().name(), transfer.getTo().name(), + transfer.getAmount().toPlainString(), DEFAULT_RECEIVING_WINDOW, currentTimeMillis()); + } + + public Call getSymbol(String symbol) { + return binanceApiService.querySymbol(symbol, DEFAULT_RECEIVING_WINDOW, currentTimeMillis()); + } + + public Call> getSymbols() { + return binanceApiService.querySymbols(DEFAULT_RECEIVING_WINDOW, currentTimeMillis()); + } + + public Call startUserDataStream(String symbol) { + return binanceApiService.startIsolatedMarginUserDataStream(symbol); + } + + public Call keepAliveUserDataStream(String symbol, String listenKey) { + return binanceApiService.keepAliveIsolatedMarginUserDataStream(symbol, listenKey); + } + + public Call closeUserDataStream(String symbol, String listenKey) { + return binanceApiService.closeIsolatedMarginAliveUserDataStream(symbol, listenKey); + } + +} diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java index b2754305e..d31dbdff1 100644 --- a/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java +++ b/src/main/java/com/binance/api/client/impl/BinanceApiAsyncIsolatedMarginClientImpl.java @@ -1,15 +1,10 @@ package com.binance.api.client.impl; -import static com.binance.api.client.constant.BinanceApiConstants.DEFAULT_RECEIVING_WINDOW; -import static java.lang.System.currentTimeMillis; -import static java.util.stream.Collectors.joining; - import java.util.List; import java.util.Optional; import com.binance.api.client.BinanceApiAsyncIsolatedMarginClient; import com.binance.api.client.BinanceApiCallback; -import com.binance.api.client.constant.BinanceApiConstants; import com.binance.api.client.domain.account.MarginNewOrder; import com.binance.api.client.domain.account.MarginNewOrderResponse; import com.binance.api.client.domain.account.MarginTransaction; @@ -28,109 +23,85 @@ public class BinanceApiAsyncIsolatedMarginClientImpl implements BinanceApiAsyncIsolatedMarginClient { - private static final String IS_ISOLATED = "TRUE"; - private BinanceApiService binanceApiService; + private BinanceApiAsyncIsolatedMarginClientBase clientBase; public BinanceApiAsyncIsolatedMarginClientImpl(String apiKey, String secret) { - binanceApiService = ApiServiceGenerator.createService(BinanceApiService.class, apiKey, secret); + clientBase = new BinanceApiAsyncIsolatedMarginClientBase(apiKey, secret); } @Override public void createAccount(String base, String quote, BinanceApiCallback cb) { - binanceApiService.createIsolatedMarginAccount(base, quote, DEFAULT_RECEIVING_WINDOW, currentTimeMillis()) - .enqueue(new BinanceApiCallbackAdapter<>(cb)); + clientBase.createAccount(base, quote).enqueue(new BinanceApiCallbackAdapter<>(cb)); } @Override public void queryAccount(Optional> symbols, BinanceApiCallback cb) { - if (symbols.isPresent()) { - String ss = symbols.get().stream().collect(joining(",")); - binanceApiService.queryIsolatedMarginAccount(ss, DEFAULT_RECEIVING_WINDOW, currentTimeMillis()) - .enqueue(new BinanceApiCallbackAdapter<>(cb)); - } else { - binanceApiService.queryIsolatedMarginAccount(DEFAULT_RECEIVING_WINDOW, currentTimeMillis()) - .enqueue(new BinanceApiCallbackAdapter<>(cb)); - } + clientBase.queryAccount(symbols).enqueue(new BinanceApiCallbackAdapter<>(cb)); } @Override public void getOpenOrders(OrderRequest orderRequest, BinanceApiCallback> callback) { - binanceApiService.getOpenMarginOrders(orderRequest.getSymbol(), IS_ISOLATED, orderRequest.getRecvWindow(), orderRequest.getTimestamp()) - .enqueue(new BinanceApiCallbackAdapter<>(callback)); + clientBase.getOpenOrders(orderRequest).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void newOrder(MarginNewOrder order, BinanceApiCallback callback) { - binanceApiService.newMarginOrder(order.getSymbol(), IS_ISOLATED, order.getSide(), order.getType(), order.getTimeInForce(), - order.getQuantity(), order.getPrice(), order.getNewClientOrderId(), order.getStopPrice(), order.getIcebergQty(), - order.getNewOrderRespType(), order.getSideEffectType(), order.getRecvWindow(), order.getTimestamp()) - .enqueue(new BinanceApiCallbackAdapter<>(callback)); + clientBase.newOrder(order).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void cancelOrder(CancelOrderRequest cancelOrderRequest, BinanceApiCallback callback) { - binanceApiService.cancelMarginOrder(cancelOrderRequest.getSymbol(), IS_ISOLATED, cancelOrderRequest.getOrderId(), - cancelOrderRequest.getOrigClientOrderId(), cancelOrderRequest.getNewClientOrderId(), cancelOrderRequest.getRecvWindow(), - cancelOrderRequest.getTimestamp()).enqueue(new BinanceApiCallbackAdapter<>(callback)); + clientBase.cancelOrder(cancelOrderRequest).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void getOrderStatus(OrderStatusRequest orderStatusRequest, BinanceApiCallback callback) { - binanceApiService - .getMarginOrderStatus(orderStatusRequest.getSymbol(), IS_ISOLATED, orderStatusRequest.getOrderId(), - orderStatusRequest.getOrigClientOrderId(), orderStatusRequest.getRecvWindow(), orderStatusRequest.getTimestamp()) - .enqueue(new BinanceApiCallbackAdapter<>(callback)); + clientBase.getOrderStatus(orderStatusRequest).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void getMyTrades(String symbol, BinanceApiCallback> callback) { - binanceApiService.getMyMarginTrades(symbol, IS_ISOLATED, null, null, BinanceApiConstants.DEFAULT_RECEIVING_WINDOW, currentTimeMillis()) - .enqueue(new BinanceApiCallbackAdapter<>(callback)); + clientBase.getMyTrades(symbol).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void borrow(String asset, String symbol, String amount, BinanceApiCallback callback) { - binanceApiService.borrow(asset, IS_ISOLATED, symbol, amount, BinanceApiConstants.DEFAULT_MARGIN_RECEIVING_WINDOW, currentTimeMillis()) - .enqueue(new BinanceApiCallbackAdapter<>(callback)); + clientBase.borrow(asset, symbol, amount).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void repay(String asset, String symbol, String amount, BinanceApiCallback callback) { - binanceApiService.repay(asset, IS_ISOLATED, symbol, amount, BinanceApiConstants.DEFAULT_MARGIN_RECEIVING_WINDOW, currentTimeMillis()) - .enqueue(new BinanceApiCallbackAdapter<>(callback)); + clientBase.repay(asset, symbol, amount).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void transfer(IsolatedMarginTransfer transfer, BinanceApiCallback cb) { - binanceApiService - .transfer(transfer.getAsset(), transfer.getSymbol(), transfer.getFrom().name(), transfer.getTo().name(), - transfer.getAmount().toPlainString(), DEFAULT_RECEIVING_WINDOW, currentTimeMillis()) - .enqueue(new BinanceApiCallbackAdapter<>(cb)); + clientBase.transfer(transfer).enqueue(new BinanceApiCallbackAdapter<>(cb)); } @Override public void getSymbol(String symbol, BinanceApiCallback cb) { - binanceApiService.querySymbol(symbol, DEFAULT_RECEIVING_WINDOW, currentTimeMillis()).enqueue(new BinanceApiCallbackAdapter<>(cb)); + clientBase.getSymbol(symbol).enqueue(new BinanceApiCallbackAdapter<>(cb)); } @Override public void getSymbols(BinanceApiCallback> cb) { - binanceApiService.querySymbols(DEFAULT_RECEIVING_WINDOW, currentTimeMillis()).enqueue(new BinanceApiCallbackAdapter<>(cb)); + clientBase.getSymbols().enqueue(new BinanceApiCallbackAdapter<>(cb)); } @Override public void startUserDataStream(String symbol, BinanceApiCallback callback) { - binanceApiService.startIsolatedMarginUserDataStream(symbol).enqueue(new BinanceApiCallbackAdapter<>(callback)); + clientBase.startUserDataStream(symbol).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void keepAliveUserDataStream(String symbol, String listenKey, BinanceApiCallback callback) { - binanceApiService.keepAliveIsolatedMarginUserDataStream(symbol, listenKey).enqueue(new BinanceApiCallbackAdapter<>(callback)); + clientBase.keepAliveUserDataStream(symbol, listenKey).enqueue(new BinanceApiCallbackAdapter<>(callback)); } @Override public void closeUserDataStream(String symbol, String listenKey, BinanceApiCallback callback) { - binanceApiService.closeIsolatedMarginAliveUserDataStream(symbol, listenKey).enqueue(new BinanceApiCallbackAdapter<>(callback)); + clientBase.closeUserDataStream(symbol, listenKey).enqueue(new BinanceApiCallbackAdapter<>(callback)); } } diff --git a/src/main/java/com/binance/api/client/impl/BinanceApiIsolatedMarginClientImpl.java b/src/main/java/com/binance/api/client/impl/BinanceApiIsolatedMarginClientImpl.java new file mode 100644 index 000000000..0817ef794 --- /dev/null +++ b/src/main/java/com/binance/api/client/impl/BinanceApiIsolatedMarginClientImpl.java @@ -0,0 +1,108 @@ +package com.binance.api.client.impl; + +import static com.binance.api.client.impl.ApiServiceGenerator.executeSync; + +import java.util.List; +import java.util.Optional; + +import com.binance.api.client.BinanceApiIsolatedMarginClient; +import com.binance.api.client.domain.account.MarginNewOrder; +import com.binance.api.client.domain.account.MarginNewOrderResponse; +import com.binance.api.client.domain.account.MarginTransaction; +import com.binance.api.client.domain.account.Order; +import com.binance.api.client.domain.account.Trade; +import com.binance.api.client.domain.account.isolated.IsolatedMarginAccountInfo; +import com.binance.api.client.domain.account.isolated.IsolatedMarginSymbol; +import com.binance.api.client.domain.account.isolated.IsolatedMarginTransfer; +import com.binance.api.client.domain.account.isolated.IsolatedMarginTransferResult; +import com.binance.api.client.domain.account.isolated.NewIsolatedAccountResponse; +import com.binance.api.client.domain.account.request.CancelOrderRequest; +import com.binance.api.client.domain.account.request.CancelOrderResponse; +import com.binance.api.client.domain.account.request.OrderRequest; +import com.binance.api.client.domain.account.request.OrderStatusRequest; +import com.binance.api.client.domain.event.ListenKey; + +public class BinanceApiIsolatedMarginClientImpl implements BinanceApiIsolatedMarginClient { + + private BinanceApiAsyncIsolatedMarginClientBase clientBase; + + public BinanceApiIsolatedMarginClientImpl(String apiKey, String secret) { + clientBase = new BinanceApiAsyncIsolatedMarginClientBase(apiKey, secret); + } + + @Override + public NewIsolatedAccountResponse createAccount(String base, String quote) { + return executeSync(clientBase.createAccount(base, quote)); + } + + @Override + public IsolatedMarginAccountInfo queryAccount(Optional> symbols) { + return executeSync(clientBase.queryAccount(symbols)); + } + + @Override + public List getOpenOrders(OrderRequest orderRequest) { + return executeSync(clientBase.getOpenOrders(orderRequest)); + } + + @Override + public MarginNewOrderResponse newOrder(MarginNewOrder order) { + return executeSync(clientBase.newOrder(order)); + } + + @Override + public CancelOrderResponse cancelOrder(CancelOrderRequest cancelOrderRequest) { + return executeSync(clientBase.cancelOrder(cancelOrderRequest)); + } + + @Override + public Order getOrderStatus(OrderStatusRequest orderStatusRequest) { + return executeSync(clientBase.getOrderStatus(orderStatusRequest)); + } + + @Override + public List getMyTrades(String symbol) { + return executeSync(clientBase.getMyTrades(symbol)); + } + + @Override + public MarginTransaction borrow(String asset, String symbol, String amount) { + return executeSync(clientBase.borrow(asset, symbol, amount)); + } + + @Override + public MarginTransaction repay(String asset, String symbol, String amount) { + return executeSync(clientBase.repay(asset, symbol, amount)); + } + + @Override + public IsolatedMarginTransferResult transfer(IsolatedMarginTransfer transfer) { + return executeSync(clientBase.transfer(transfer)); + } + + @Override + public IsolatedMarginSymbol getSymbol(String symbol) { + return executeSync(clientBase.getSymbol(symbol)); + } + + @Override + public List getSymbols() { + return executeSync(clientBase.getSymbols()); + } + + @Override + public ListenKey startUserDataStream(String symbol) { + return executeSync(clientBase.startUserDataStream(symbol)); + } + + @Override + public void keepAliveUserDataStream(String symbol, String listenKey) { + executeSync(clientBase.keepAliveUserDataStream(symbol, listenKey)); + } + + @Override + public void closeUserDataStream(String symbol, String listenKey) { + executeSync(clientBase.closeUserDataStream(symbol, listenKey)); + } + +} From 40301e2403d366ff695e6f27f08f6d9cbb3a8e1e Mon Sep 17 00:00:00 2001 From: jenkins Date: Mon, 14 Jun 2021 18:55:17 +0000 Subject: [PATCH 093/101] [ci-skip]prepare release binance-api-client-3.2.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fffa43ea0..58b947c68 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.2.1-SNAPSHOT + 3.2.1 The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.1.1 + binance-api-client-3.2.1 From c51c67d41c820902f263a94e59d07007d664f31e Mon Sep 17 00:00:00 2001 From: jenkins Date: Mon, 14 Jun 2021 18:55:32 +0000 Subject: [PATCH 094/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 58b947c68..49ec1101f 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.2.1 + 3.2.2-SNAPSHOT The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.2.1 + binance-api-client-3.1.1 From 12bd6b883d0c787a77c26af964cff183fafe2a08 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Wed, 16 Jun 2021 20:45:28 +0200 Subject: [PATCH 095/101] master added factory for isolated sync rest client --- .../binance/api/client/BinanceApiClientFactory.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/com/binance/api/client/BinanceApiClientFactory.java b/src/main/java/com/binance/api/client/BinanceApiClientFactory.java index 9d563bc42..bc503b8b1 100755 --- a/src/main/java/com/binance/api/client/BinanceApiClientFactory.java +++ b/src/main/java/com/binance/api/client/BinanceApiClientFactory.java @@ -4,6 +4,7 @@ import com.binance.api.client.impl.BinanceApiAsyncIsolatedMarginClientImpl; import com.binance.api.client.impl.BinanceApiAsyncMarginRestClientImpl; import com.binance.api.client.impl.BinanceApiAsyncRestClientImpl; +import com.binance.api.client.impl.BinanceApiIsolatedMarginClientImpl; import com.binance.api.client.impl.BinanceApiMarginRestClientImpl; import com.binance.api.client.impl.BinanceApiRestClientImpl; import com.binance.api.client.impl.BinanceApiSwapRestClientImpl; @@ -106,4 +107,13 @@ public BinanceApiAsyncIsolatedMarginClient newAsyncIsolatedMarginRestClient() { return new BinanceApiAsyncIsolatedMarginClientImpl(apiKey, secret); } + /** + * Creates a new isolated margin rest client. + * + * @return the client + */ + public BinanceApiIsolatedMarginClient newIsolatedMarginRestClient() { + return new BinanceApiIsolatedMarginClientImpl(apiKey, secret); + } + } From 014c43c09f41b6da3640044d7957c495d3c0b6c3 Mon Sep 17 00:00:00 2001 From: jenkins Date: Wed, 16 Jun 2021 18:47:29 +0000 Subject: [PATCH 096/101] [ci-skip]prepare release binance-api-client-3.2.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 49ec1101f..912f76608 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.2.2-SNAPSHOT + 3.2.2 The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.1.1 + binance-api-client-3.2.2 From b2396fe440df7569414359561c65b86def005dfd Mon Sep 17 00:00:00 2001 From: jenkins Date: Wed, 16 Jun 2021 18:48:11 +0000 Subject: [PATCH 097/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 912f76608..0e7aad334 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.2.2 + 3.2.3-SNAPSHOT The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.2.2 + binance-api-client-3.1.1 From 0f6856748cce6e011046c10732f5dccaac0352a1 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Tue, 29 Jun 2021 08:46:58 +0200 Subject: [PATCH 098/101] master added RAW_REQUESTS to RequestLimitType --- .../com/binance/api/client/domain/general/RateLimitType.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/binance/api/client/domain/general/RateLimitType.java b/src/main/java/com/binance/api/client/domain/general/RateLimitType.java index 25775dab5..fb5ee0e89 100755 --- a/src/main/java/com/binance/api/client/domain/general/RateLimitType.java +++ b/src/main/java/com/binance/api/client/domain/general/RateLimitType.java @@ -8,5 +8,6 @@ @JsonIgnoreProperties(ignoreUnknown = true) public enum RateLimitType { REQUEST_WEIGHT, - ORDERS + ORDERS, + RAW_REQUESTS } From e1ea192cfaa9640872561713b04253877bfa9727 Mon Sep 17 00:00:00 2001 From: jenkins Date: Tue, 29 Jun 2021 06:49:16 +0000 Subject: [PATCH 099/101] [ci-skip]prepare release binance-api-client-3.2.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0e7aad334..d1c3bbd07 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.2.3-SNAPSHOT + 3.2.3 The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.1.1 + binance-api-client-3.2.3 From 11a192115957c40c1cb3c4d58efac69f226334b5 Mon Sep 17 00:00:00 2001 From: jenkins Date: Tue, 29 Jun 2021 06:49:34 +0000 Subject: [PATCH 100/101] [ci-skip]prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d1c3bbd07..1c4d2d2cf 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.binance.api binance-api-client - 3.2.3 + 3.2.4-SNAPSHOT The MIT License @@ -108,7 +108,7 @@ scm:git:http://${env.GIT_USERNAME}:${env.GIT_PASSWORD}@kube1:3000/anydoby/binance-java-api.git - binance-api-client-3.2.3 + binance-api-client-3.1.1 From e278acddb9c9cbbd5b7db9c49cb2497de7e35a48 Mon Sep 17 00:00:00 2001 From: "Sergey (S.) Zolotarov" Date: Fri, 22 Apr 2022 21:15:50 +0200 Subject: [PATCH 101/101] master added TRAILING_DELTA exchangeinfo filter --- .../api/client/domain/general/FilterType.java | 1 + .../api/client/domain/general/SymbolFilter.java | 1 + .../domain/general/TrailingDeltaFilter.java | 15 +++++++++++++++ 3 files changed, 17 insertions(+) create mode 100644 src/main/java/com/binance/api/client/domain/general/TrailingDeltaFilter.java diff --git a/src/main/java/com/binance/api/client/domain/general/FilterType.java b/src/main/java/com/binance/api/client/domain/general/FilterType.java index e6569dcb9..da71f6f2b 100755 --- a/src/main/java/com/binance/api/client/domain/general/FilterType.java +++ b/src/main/java/com/binance/api/client/domain/general/FilterType.java @@ -19,5 +19,6 @@ public enum FilterType { MARKET_LOT_SIZE, MAX_NUM_ICEBERG_ORDERS, MAX_POSITION, + TRAILING_DELTA } diff --git a/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java b/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java index 1e440f156..ef5695b27 100755 --- a/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java +++ b/src/main/java/com/binance/api/client/domain/general/SymbolFilter.java @@ -40,6 +40,7 @@ @Type(value = MaxNumAlgoOrdersFilter.class, name = "MAX_NUM_ALGO_ORDERS"), @Type(value = MaxNumIcebergOrders.class, name = "MAX_NUM_ICEBERG_ORDERS"), @Type(value = MaxPositionFilter.class, name = "MAX_POSITION"), + @Type(value = TrailingDeltaFilter.class, name = "TRAILING_DELTA"), }) @Data public abstract class SymbolFilter { diff --git a/src/main/java/com/binance/api/client/domain/general/TrailingDeltaFilter.java b/src/main/java/com/binance/api/client/domain/general/TrailingDeltaFilter.java new file mode 100644 index 000000000..954230db3 --- /dev/null +++ b/src/main/java/com/binance/api/client/domain/general/TrailingDeltaFilter.java @@ -0,0 +1,15 @@ +package com.binance.api.client.domain.general; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class TrailingDeltaFilter extends SymbolFilter { + + String minTrailingAboveDelta; + String maxTrailingAboveDelta; + String minTrailingBelowDelta; + String maxTrailingBelowDelta; + +}