diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 648d4107a6d..e49f2297495 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,18 +6,3 @@ updates: interval: daily time: "12:00" open-pull-requests-limit: 10 - ignore: - - dependency-name: com.fasterxml.jackson.core:jackson-databind - versions: - - 2.12.1 - - 2.12.2 - - dependency-name: com.fasterxml.jackson.dataformat:jackson-dataformat-csv - versions: - - 2.12.1 - - 2.12.2 - - dependency-name: io.netty:netty-all - versions: - - 4.1.61.Final - - dependency-name: com.pubnub:pubnub-gson - versions: - - 4.35.0 diff --git a/.gitignore b/.gitignore index 9d82f82f739..5aebb76c393 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,12 @@ bin/ log/ classes/ +# maven wrapper +.mvn +mvnw +mvnw.cmd + + # Misc. .DS_Store *.swp diff --git a/LICENSE b/LICENSE index 547b7d2e140..d7a74109833 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright 2015-2023 Knowm Inc. (http://knowm.org) and contributors. +Copyright 2015-2024 Knowm Inc. (http://knowm.org) and contributors. Copyright 2012-2015 Xeiam LLC (http://xeiam.com) and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/pom.xml b/pom.xml index 0250003299b..633f6ea6d69 100644 --- a/pom.xml +++ b/pom.xml @@ -31,17 +31,17 @@ UTF-8 1.8 - 3.24.2 + 3.25.3 3.14.0 2.14.3 3.0 - 1.7.0 - 5.9.3 - 1.18.26 + 1.7.1 + 5.10.2 + 1.18.32 3.8.6 1.4.12 0.10.2 - 2.0.6 + 2.0.13 5.0.0 4.4.0 3.2.0 @@ -91,7 +91,6 @@ - xchange-bankera xchange-bibox xchange-binance xchange-bitbay @@ -142,6 +141,7 @@ xchange-examples xchange-ftx xchange-gateio + xchange-gateio-v4 xchange-globitex xchange-gemini xchange-hitbtc @@ -181,13 +181,13 @@ xchange-enigma - xchange-stream-bankera xchange-stream-binance xchange-stream-bitfinex xchange-stream-bitflyer xchange-stream-bitmex xchange-stream-bitstamp xchange-stream-btcmarkets + xchange-stream-bybit xchange-stream-cexio xchange-stream-coinbasepro xchange-stream-coinjar @@ -229,6 +229,11 @@ true + + maven_central + Maven Central + https://repo.maven.apache.org/maven2/ + @@ -327,6 +332,13 @@ test + + org.junit.jupiter + junit-jupiter-engine + ${version.junit} + test + + ch.qos.logback @@ -425,7 +437,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.0.1 + 3.2.3 sign-artifacts diff --git a/xchange-bankera/api-specification.txt b/xchange-bankera/api-specification.txt deleted file mode 100644 index e1450540ecc..00000000000 --- a/xchange-bankera/api-specification.txt +++ /dev/null @@ -1,8 +0,0 @@ -Bankera api spec - -url: https://exchange.bankera.com -doc: https://exchange.bankera.com/api-documentation.html - -You need an extra parameters: -exSpec.setExchangeSpecificParametersItem("clientId", "clientId"); -exSpec.setExchangeSpecificParametersItem("clientSecret", "clientSecret"); \ No newline at end of file diff --git a/xchange-bankera/pom.xml b/xchange-bankera/pom.xml deleted file mode 100644 index cc7911e43f6..00000000000 --- a/xchange-bankera/pom.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - 4.0.0 - - - org.knowm.xchange - xchange-parent - 5.1.2-SNAPSHOT - - - xchange-bankera - - XChange Bankera - XChange implementation for the Bankera Exchange - - http://knowm.org/open-source/xchange/ - 2012 - - - Knowm Inc. - http://knowm.org/open-source/xchange/ - - - - - - - org.knowm.xchange - xchange-core - ${project.version} - - - - - diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/Bankera.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/Bankera.java deleted file mode 100644 index ac8f2cf8ef5..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/Bankera.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.knowm.xchange.bankera; - -import jakarta.ws.rs.FormParam; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import java.io.IOException; -import org.knowm.xchange.bankera.dto.BankeraException; -import org.knowm.xchange.bankera.dto.BankeraToken; -import org.knowm.xchange.bankera.dto.marketdata.BankeraMarketInfo; -import org.knowm.xchange.bankera.dto.marketdata.BankeraOrderBook; -import org.knowm.xchange.bankera.dto.marketdata.BankeraTickerResponse; -import org.knowm.xchange.bankera.dto.marketdata.BankeraTradesResponse; - -@Path("/") -@Produces(MediaType.APPLICATION_JSON) -public interface Bankera { - - @POST - @Path("/oauth/token") - BankeraToken getToken( - @FormParam("grant_type") String grantType, - @FormParam("client_id") String clientId, - @FormParam("client_secret") String clientSecret) - throws BankeraException; - - @GET - @Path("/tickers/{market}") - BankeraTickerResponse getMarketTicker(@PathParam("market") String market) - throws BankeraException, IOException; - - @GET - @Path("/orderbooks/{market}") - BankeraOrderBook getOrderbook(@PathParam("market") String market) - throws BankeraException, IOException; - - @GET - @Path("/trades/{market}") - BankeraTradesResponse getRecentTrades(@PathParam("market") String market) - throws BankeraException, IOException; - - @GET - @Path("/general/info") - BankeraMarketInfo getMarketInfo() throws BankeraException, IOException; -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/BankeraAdapters.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/BankeraAdapters.java deleted file mode 100644 index c6de13e9581..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/BankeraAdapters.java +++ /dev/null @@ -1,241 +0,0 @@ -package org.knowm.xchange.bankera; - -import java.math.BigDecimal; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.stream.Collectors; -import org.knowm.xchange.bankera.dto.BankeraException; -import org.knowm.xchange.bankera.dto.account.BankeraUserInfo; -import org.knowm.xchange.bankera.dto.account.BankeraWallet; -import org.knowm.xchange.bankera.dto.marketdata.BankeraOrderBook; -import org.knowm.xchange.bankera.dto.marketdata.BankeraTickerResponse; -import org.knowm.xchange.bankera.dto.marketdata.BankeraTrade; -import org.knowm.xchange.bankera.dto.marketdata.BankeraTradesResponse; -import org.knowm.xchange.bankera.dto.trade.BankeraOpenOrders; -import org.knowm.xchange.bankera.dto.trade.BankeraOrder; -import org.knowm.xchange.bankera.dto.trade.BankeraUserTrades; -import org.knowm.xchange.currency.Currency; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.Order; -import org.knowm.xchange.dto.Order.OrderType; -import org.knowm.xchange.dto.account.AccountInfo; -import org.knowm.xchange.dto.account.Balance; -import org.knowm.xchange.dto.account.Wallet; -import org.knowm.xchange.dto.marketdata.OrderBook; -import org.knowm.xchange.dto.marketdata.Ticker; -import org.knowm.xchange.dto.marketdata.Trade; -import org.knowm.xchange.dto.marketdata.Trades; -import org.knowm.xchange.dto.trade.LimitOrder; -import org.knowm.xchange.dto.trade.UserTrade; -import org.knowm.xchange.exceptions.ExchangeException; -import org.knowm.xchange.exceptions.ExchangeSecurityException; - -public final class BankeraAdapters { - - private static final String ORDER_SIDE_BUY = "buy"; - - private BankeraAdapters() {} - - public static AccountInfo adaptAccountInfo(BankeraUserInfo userInfo) { - return new AccountInfo( - String.valueOf(userInfo.getUser().getId()), adaptWallet(userInfo.getUser().getWallets())); - } - - public static Wallet adaptWallet(List wallets) { - List balances = - wallets.stream() - .map( - w -> - new Balance.Builder() - .total(new BigDecimal(w.getTotal())) - .available(new BigDecimal(w.getBalance())) - .frozen(new BigDecimal(w.getReserved())) - .currency(new Currency(w.getCurrency())) - .build()) - .collect(Collectors.toList()); - return Wallet.Builder.from(balances).build(); - } - - public static ExchangeException adaptError(BankeraException exception) { - return exception.getHttpStatusCode() == 403 - ? new ExchangeSecurityException() - : new ExchangeException(exception.getError(), exception); - } - - /** - * Adapts Bankera BankeraTickerResponse to a Ticker - * - * @param ticker Specific ticker - * @param currencyPair BankeraCurrency pair (e.g. ETH/BTC) - * @return Ticker - */ - public static Ticker adaptTicker(BankeraTickerResponse ticker, CurrencyPair currencyPair) { - - BigDecimal high = new BigDecimal(ticker.getTicker().getHigh()); - BigDecimal low = new BigDecimal(ticker.getTicker().getLow()); - BigDecimal bid = new BigDecimal(ticker.getTicker().getBid()); - BigDecimal ask = new BigDecimal(ticker.getTicker().getAsk()); - BigDecimal last = new BigDecimal(ticker.getTicker().getLast()); - BigDecimal volume = new BigDecimal(ticker.getTicker().getVolume()); - Date timestamp = new Date(ticker.getTicker().getTimestamp()); - - return new Ticker.Builder() - .currencyPair(currencyPair) - .high(high) - .low(low) - .bid(bid) - .ask(ask) - .last(last) - .volume(volume) - .timestamp(timestamp) - .build(); - } - - public static OrderBook adaptOrderBook(BankeraOrderBook orderbook, CurrencyPair currencyPair) { - - List bids = createOrders(currencyPair, OrderType.BID, orderbook.getBids()); - List asks = createOrders(currencyPair, OrderType.ASK, orderbook.getAsks()); - - return new OrderBook(null, asks, bids); - } - - public static Trades adaptTrades( - BankeraTradesResponse tradesResponse, CurrencyPair currencyPair) { - - List bankeraTrades = tradesResponse.getTrades(); - List tradesList = new ArrayList<>(); - - bankeraTrades.forEach( - bankeraTrade -> { - BigDecimal amount = new BigDecimal(bankeraTrade.getAmount()); - BigDecimal price = new BigDecimal(bankeraTrade.getPrice()); - Date date = new Date(Long.parseLong(bankeraTrade.getTime())); - OrderType type = - bankeraTrade.getSide().equalsIgnoreCase(ORDER_SIDE_BUY) - ? OrderType.BID - : OrderType.ASK; - tradesList.add( - new Trade.Builder() - .type(type) - .originalAmount(amount) - .currencyPair(currencyPair) - .price(price) - .timestamp(date) - .build()); - }); - return new Trades(tradesList, 0L, Trades.TradeSortType.SortByTimestamp); - } - - private static List createOrders( - CurrencyPair currencyPair, - OrderType orderType, - List ordersList) { - - List limitOrders = new ArrayList<>(); - if (ordersList == null) return limitOrders; - - ordersList.forEach( - order -> { - limitOrders.add( - new LimitOrder( - orderType, - new BigDecimal(order.getAmount()), - currencyPair, - String.valueOf(order.getId()), - null, - new BigDecimal(order.getPrice()))); - }); - - return limitOrders; - } - - public static List adaptOpenOrders(BankeraOpenOrders openOrders) { - - List orderList = new ArrayList<>(); - - openOrders - .getOpenOrders() - .forEach( - bankeraOrder -> { - String[] currencies = bankeraOrder.getMarket().split("-"); - CurrencyPair pair = new CurrencyPair(currencies[0], currencies[1]); - orderList.add( - new LimitOrder( - bankeraOrder.getSide().equalsIgnoreCase("buy") - ? OrderType.BID - : OrderType.ASK, - new BigDecimal(bankeraOrder.getAmount()), - new BigDecimal(bankeraOrder.getRemainingAmount()), - pair, - String.valueOf(bankeraOrder.getId()), - new Date(Long.valueOf(bankeraOrder.getCreatedAt())), - new BigDecimal(bankeraOrder.getPrice()))); - }); - - return orderList; - } - - public static List adaptUserTrades(BankeraUserTrades userTrades) { - List tradeList = new ArrayList<>(); - - userTrades - .getTrades() - .forEach( - trade -> { - String[] currencies = trade.getMarket().split("-"); - CurrencyPair pair = new CurrencyPair(currencies[0], currencies[1]); - Currency feeCurrency = new Currency(currencies[1]); - tradeList.add( - UserTrade.builder() - .type(trade.getSide().equalsIgnoreCase("buy") ? OrderType.BID : OrderType.ASK) - .originalAmount(new BigDecimal(trade.getAmount())) - .currencyPair(pair) - .price(new BigDecimal(trade.getPrice())) - .timestamp(new Date(Long.parseLong(trade.getCompletedAt()))) - .id(String.valueOf(trade.getId())) - .orderId(String.valueOf(trade.getOrderId())) - .feeAmount(new BigDecimal(trade.getFeeAmount())) - .feeCurrency(feeCurrency) - .build()); - }); - - return tradeList; - } - - public static Order adaptOrder(BankeraOrder bankeraOrder) { - String[] currencies = bankeraOrder.getMarket().split("-"); - CurrencyPair pair = new CurrencyPair(currencies[0], currencies[1]); - DecimalFormat format = new DecimalFormat(); - format.setParseBigDecimal(true); - return new LimitOrder( - bankeraOrder.getSide().equalsIgnoreCase("buy") ? OrderType.BID : OrderType.ASK, - new BigDecimal(bankeraOrder.getAmount()), - pair, - String.valueOf(bankeraOrder.getId()), - new Date(Long.parseLong(bankeraOrder.getCreatedAt())), - new BigDecimal(bankeraOrder.getPrice()), - new BigDecimal(bankeraOrder.getPrice()), - new BigDecimal(bankeraOrder.getExecutedAmount()), - bankeraOrder.getTotalFee(), - adaptOrderStatus(bankeraOrder.getStatus())); - } - - private static Order.OrderStatus adaptOrderStatus(String status) { - switch (status.toLowerCase()) { - case "open": - return Order.OrderStatus.NEW; - case "completed": - return Order.OrderStatus.FILLED; - case "cancelled": - return Order.OrderStatus.CANCELED; - case "rejected": - return Order.OrderStatus.REJECTED; - case "pending cancel": - return Order.OrderStatus.PENDING_CANCEL; - default: - return Order.OrderStatus.UNKNOWN; - } - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/BankeraAuthenticated.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/BankeraAuthenticated.java deleted file mode 100644 index 670f76bb452..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/BankeraAuthenticated.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.knowm.xchange.bankera; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.HeaderParam; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.MediaType; -import java.io.IOException; -import java.util.List; -import org.knowm.xchange.bankera.dto.BankeraException; -import org.knowm.xchange.bankera.dto.BaseBankeraRequest; -import org.knowm.xchange.bankera.dto.CreateOrderRequest; -import org.knowm.xchange.bankera.dto.account.BankeraUserInfo; -import org.knowm.xchange.bankera.dto.trade.BankeraOpenOrders; -import org.knowm.xchange.bankera.dto.trade.BankeraOrder; -import org.knowm.xchange.bankera.dto.trade.BankeraUserTrades; - -@Path("/") -@Produces(MediaType.APPLICATION_JSON) -public interface BankeraAuthenticated extends Bankera { - @GET - @Path("users/info") - BankeraUserInfo getUserInfo(@HeaderParam("Authorization") String authorization) - throws BankeraException, IOException; - - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Path("orders/new") - BankeraOrder placeOrder( - @HeaderParam("Authorization") String authorization, CreateOrderRequest request) - throws BankeraException, IOException; - - @GET - @Path("orders/open") - BankeraOpenOrders getOpenOrders( - @HeaderParam("Authorization") String authorization, - @QueryParam("market") String market, - @QueryParam("limit") Integer limit, - @QueryParam("offset") Integer offset) - throws BankeraException, IOException; - - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Path("orders/cancel/{orderId}") - BankeraOrder cancelOrder( - @HeaderParam("Authorization") String authorization, - @PathParam("orderId") Long orderId, - BaseBankeraRequest request) - throws BankeraException, IOException; - - @POST - @Path("orders/cancel/all") - List cancelAllOrders(@HeaderParam("Authorization") String authorization) - throws BankeraException, IOException; - - @GET - @Path("trades/history") - BankeraUserTrades getUserTrades( - @HeaderParam("Authorization") String authorization, @QueryParam("market") String market) - throws BankeraException, IOException; - - @GET - @Path("orders/details") - BankeraOrder getUserOrder( - @HeaderParam("Authorization") String authorization, @QueryParam("orderId") String orderId) - throws BankeraException, IOException; -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/BankeraExchange.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/BankeraExchange.java deleted file mode 100644 index db68f176674..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/BankeraExchange.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.knowm.xchange.bankera; - -import org.knowm.xchange.BaseExchange; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.ExchangeSpecification; -import org.knowm.xchange.bankera.dto.BankeraException; -import org.knowm.xchange.bankera.dto.BankeraToken; -import org.knowm.xchange.bankera.service.BankeraAccountService; -import org.knowm.xchange.bankera.service.BankeraBaseService; -import org.knowm.xchange.bankera.service.BankeraMarketDataService; -import org.knowm.xchange.bankera.service.BankeraTradeService; -import org.knowm.xchange.utils.nonce.TimestampIncrementingNonceFactory; -import si.mazi.rescu.SynchronizedValueFactory; - -public class BankeraExchange extends BaseExchange implements Exchange { - - private SynchronizedValueFactory nonceFactory = new TimestampIncrementingNonceFactory(); - protected BankeraToken token; - - @Override - protected void initServices() { - this.accountService = new BankeraAccountService(this); - this.marketDataService = new BankeraMarketDataService(this); - this.tradeService = new BankeraTradeService(this); - } - - @Override - public ExchangeSpecification getDefaultExchangeSpecification() { - - ExchangeSpecification exchangeSpecification = new ExchangeSpecification(this.getClass()); - exchangeSpecification.setSslUri("https://api-exchange.bankera.com"); - exchangeSpecification.setHost("api-exchange.bankera.com"); - exchangeSpecification.setPort(443); - exchangeSpecification.setExchangeName("Bankera"); - exchangeSpecification.setExchangeDescription("The Bankera exchange."); - - return exchangeSpecification; - } - - @Override - public SynchronizedValueFactory getNonceFactory() { - return nonceFactory; - } - - public BankeraToken getToken() { - if (token == null || token.getExpirationTime() < System.currentTimeMillis() + 30000L) { - try { - token = ((BankeraBaseService) accountService).createToken(); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - return token; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/BankeraException.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/BankeraException.java deleted file mode 100644 index 574e637df0f..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/BankeraException.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.knowm.xchange.bankera.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import si.mazi.rescu.HttpStatusExceptionSupport; - -public class BankeraException extends HttpStatusExceptionSupport { - - private final String error; - - public BankeraException( - @JsonProperty("status") Integer status, @JsonProperty("error") String error) { - super(error); - this.error = error; - } - - public String getError() { - return error; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/BankeraToken.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/BankeraToken.java deleted file mode 100644 index c4636d0ab4c..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/BankeraToken.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.knowm.xchange.bankera.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class BankeraToken { - private String accessToken; - private String tokenType; - private int expiresIn; - private Long expirationTime; - private String scope; - private String userId; - private String userName; - private String environmentCode; - private String loginIp; - private String jti; - - public BankeraToken( - @JsonProperty("access_token") final String accessToken, - @JsonProperty("token_type") final String tokenType, - @JsonProperty("expires_in") final int expiresIn, - @JsonProperty("scope") final String scope, - @JsonProperty("user_id") final String userId, - @JsonProperty("user_name") final String userName, - @JsonProperty("environment_code") final String environmentCode, - @JsonProperty("login_ip") final String loginIp, - @JsonProperty("jti") final String jti) { - this.accessToken = accessToken; - this.tokenType = tokenType; - this.expiresIn = expiresIn; - this.scope = scope; - this.userId = userId; - this.userName = userName; - this.environmentCode = environmentCode; - this.loginIp = loginIp; - this.jti = jti; - this.expirationTime = System.currentTimeMillis() + (this.expiresIn * 1000L); - } - - public String getAccessToken() { - return accessToken; - } - - public String getTokenType() { - return tokenType; - } - - public int getExpiresIn() { - return expiresIn; - } - - public Long getExpirationTime() { - return expirationTime; - } - - public String getScope() { - return scope; - } - - public String getUserId() { - return userId; - } - - public String getUserName() { - return userName; - } - - public String getEnvironmentCode() { - return environmentCode; - } - - public String getLoginIp() { - return loginIp; - } - - public String getJti() { - return jti; - } - - @Override - public String toString() { - return "BankeraToken{" - + "accessToken='" - + accessToken - + '\'' - + ", tokenType='" - + tokenType - + '\'' - + ", expiresIn=" - + expiresIn - + ", scope='" - + scope - + '\'' - + ", userId='" - + userId - + '\'' - + ", userName='" - + userName - + '\'' - + ", environmentCode='" - + environmentCode - + '\'' - + ", loginIp='" - + loginIp - + '\'' - + ", jti='" - + jti - + '\'' - + '}'; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/BaseBankeraRequest.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/BaseBankeraRequest.java deleted file mode 100644 index b6b57769624..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/BaseBankeraRequest.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.knowm.xchange.bankera.dto; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class BaseBankeraRequest { - - @JsonProperty("nonce") - private final Long nonce; - - public BaseBankeraRequest(Long nonce) { - this.nonce = nonce; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/CreateOrderRequest.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/CreateOrderRequest.java deleted file mode 100644 index a8fcb35bd2e..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/CreateOrderRequest.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.knowm.xchange.bankera.dto; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; -import org.apache.commons.lang3.StringUtils; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class CreateOrderRequest { - - private static final String LIMIT_ORDER_TYPE = "limit"; - private static final String MARKET_ORDER_TYPE = "market"; - - @JsonProperty("market") - private final String market; - - @JsonProperty("side") - private final String side; - - @JsonProperty("type") - private final String type; - - @JsonProperty("price") - private final String price; - - @JsonProperty("amount") - private final String amount; - - @JsonProperty("client_order_id") - private final String clientOrderId; - - @JsonProperty("nonce") - private final Long nonce; - - public CreateOrderRequest( - String market, String side, BigDecimal amount, String clientOrderId, Long nonce) { - this.market = market; - this.side = side; - this.amount = amount.toPlainString(); - this.type = MARKET_ORDER_TYPE; - this.price = StringUtils.EMPTY; - this.clientOrderId = clientOrderId; - this.nonce = nonce; - } - - public CreateOrderRequest( - String market, - String side, - BigDecimal amount, - BigDecimal price, - String clientOrderId, - Long nonce) { - this.market = market; - this.side = side; - this.amount = amount.toPlainString(); - this.type = LIMIT_ORDER_TYPE; - this.price = price.toPlainString(); - this.clientOrderId = clientOrderId; - this.nonce = nonce; - } - - public enum Side { - BUY("buy"), - SELL("sell"); - - private String side; - - Side(String side) { - this.side = side; - } - - public String getSide() { - return this.side; - } - - public static Side getEnum(String side) { - for (Side currentEnum : Side.values()) { - if (currentEnum.getSide().equalsIgnoreCase(side)) { - return currentEnum; - } - } - throw new UnsupportedOperationException("Unknown order side: " + side); - } - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/account/BankeraUser.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/account/BankeraUser.java deleted file mode 100644 index c39d553dba0..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/account/BankeraUser.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.knowm.xchange.bankera.dto.account; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; - -@JsonIgnoreProperties(ignoreUnknown = true) -public class BankeraUser { - private int id; - private List wallets; - - public BankeraUser( - @JsonProperty("id") int id, @JsonProperty("wallets") List wallets) { - this.id = id; - this.wallets = wallets; - } - - public int getId() { - return id; - } - - public List getWallets() { - return wallets; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/account/BankeraUserInfo.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/account/BankeraUserInfo.java deleted file mode 100644 index b689c5b6fe1..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/account/BankeraUserInfo.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.knowm.xchange.bankera.dto.account; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class BankeraUserInfo { - private BankeraUser user; - - public BankeraUserInfo(@JsonProperty("user") BankeraUser user) { - this.user = user; - } - - public BankeraUser getUser() { - return user; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/account/BankeraWallet.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/account/BankeraWallet.java deleted file mode 100644 index 12f9113d35c..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/account/BankeraWallet.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.knowm.xchange.bankera.dto.account; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class BankeraWallet { - private int id; - private String balance; - private String reserved; - private String total; - private String currency; - - public BankeraWallet( - @JsonProperty("id") int id, - @JsonProperty("balance") String balance, - @JsonProperty("reserved") String reserved, - @JsonProperty("total") String total, - @JsonProperty("currency") String currency) { - this.id = id; - this.balance = balance; - this.reserved = reserved; - this.total = total; - this.currency = currency; - } - - public int getId() { - return id; - } - - public String getBalance() { - return balance; - } - - public String getReserved() { - return reserved; - } - - public String getTotal() { - return total; - } - - public String getCurrency() { - return currency; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraMarket.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraMarket.java deleted file mode 100644 index 8c1c8138fa5..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraMarket.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.knowm.xchange.bankera.dto.marketdata; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonIgnoreProperties(ignoreUnknown = true) -public class BankeraMarket { - private int id; - private String slug; - private String name; - - public BankeraMarket( - @JsonProperty("id") int id, - @JsonProperty("slug") String slug, - @JsonProperty("name") String name) { - this.id = id; - this.slug = slug; - this.name = name; - } - - public int getId() { - return id; - } - - public String getSlug() { - return slug; - } - - public String getName() { - return name; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraMarketInfo.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraMarketInfo.java deleted file mode 100644 index 1bf81f6dc6c..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraMarketInfo.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.knowm.xchange.bankera.dto.marketdata; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class BankeraMarketInfo { - - private List markets; - - public BankeraMarketInfo(@JsonProperty("markets") List markets) { - this.markets = markets; - } - - public List getMarkets() { - return markets; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraOrderBook.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraOrderBook.java deleted file mode 100644 index 5211d0527e8..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraOrderBook.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.knowm.xchange.bankera.dto.marketdata; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; - -public class BankeraOrderBook { - - private final List bids; - private final List asks; - - public BankeraOrderBook( - @JsonProperty("bids") List bids, - @JsonProperty("asks") List asks) { - this.asks = asks; - this.bids = bids; - } - - public List getBids() { - return bids; - } - - public List getAsks() { - return asks; - } - - public static final class OrderBookOrder { - private final Integer id; - private final String price; - private final String amount; - - public OrderBookOrder( - @JsonProperty("id") Integer id, - @JsonProperty("price") String price, - @JsonProperty("amount") String amount) { - - this.id = id; - this.price = price; - this.amount = amount; - } - - public Integer getId() { - return id; - } - - public String getPrice() { - return price; - } - - public String getAmount() { - return amount; - } - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTicker.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTicker.java deleted file mode 100644 index cfd6b1f5420..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTicker.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.knowm.xchange.bankera.dto.marketdata; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class BankeraTicker { - - private final Integer id; - private final String high; - private final String low; - private final String bid; - private final String ask; - private final String last; - private final String volume; - private final long timestamp; - - /** - * Constructor - * - * @param id - * @param high - * @param low - * @param bid - * @param ask - * @param last - * @param volume - * @param timestamp - */ - public BankeraTicker( - @JsonProperty("id") Integer id, - @JsonProperty("high") String high, - @JsonProperty("low") String low, - @JsonProperty("bid") String bid, - @JsonProperty("ask") String ask, - @JsonProperty("last") String last, - @JsonProperty("volume") String volume, - @JsonProperty("timestamp") long timestamp) { - - this.id = id; - this.high = high; - this.low = low; - this.bid = bid; - this.ask = ask; - this.last = last; - this.volume = volume; - this.timestamp = timestamp; - } - - public Integer getId() { - return id; - } - - public String getHigh() { - return high; - } - - public String getLow() { - return low; - } - - public String getBid() { - return bid; - } - - public String getAsk() { - return ask; - } - - public String getLast() { - return last; - } - - public String getVolume() { - return volume; - } - - public long getTimestamp() { - return timestamp; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTickerResponse.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTickerResponse.java deleted file mode 100644 index 2703b5b98df..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTickerResponse.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.knowm.xchange.bankera.dto.marketdata; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class BankeraTickerResponse { - - private final BankeraTicker ticker; - - public BankeraTickerResponse(@JsonProperty("ticker") BankeraTicker ticker) { - this.ticker = ticker; - } - - @JsonProperty("ticker") - public BankeraTicker getTicker() { - return ticker; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTrade.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTrade.java deleted file mode 100644 index 9e6ead11913..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTrade.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.knowm.xchange.bankera.dto.marketdata; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class BankeraTrade { - - private final String market; - private final String amount; - private final String price; - private final String side; - private final String time; - - public BankeraTrade( - @JsonProperty("market") String market, - @JsonProperty("amount") String amount, - @JsonProperty("price") String price, - @JsonProperty("side") String side, - @JsonProperty("time") String time) { - this.market = market; - this.amount = amount; - this.price = price; - this.side = side; - this.time = time; - } - - public String getMarket() { - return market; - } - - public String getAmount() { - return amount; - } - - public String getPrice() { - return price; - } - - public String getSide() { - return side; - } - - public String getTime() { - return time; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTradesResponse.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTradesResponse.java deleted file mode 100644 index a43c97ef0f3..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/marketdata/BankeraTradesResponse.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.knowm.xchange.bankera.dto.marketdata; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; - -public class BankeraTradesResponse { - - private final List trades; - - public BankeraTradesResponse(@JsonProperty("deals") List trades) { - this.trades = trades; - } - - public List getTrades() { - return trades; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraOpenOrders.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraOpenOrders.java deleted file mode 100644 index a82e09272a2..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraOpenOrders.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.knowm.xchange.bankera.dto.trade; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; - -public class BankeraOpenOrders { - - private final List openOrders; - - public BankeraOpenOrders(@JsonProperty("orders") List openOrders) { - - this.openOrders = openOrders; - } - - public List getOpenOrders() { - return openOrders; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraOrder.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraOrder.java deleted file mode 100644 index 00500d1453c..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraOrder.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.knowm.xchange.bankera.dto.trade; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; -import java.util.List; -import java.util.function.Function; - -public class BankeraOrder { - - private final long id; - private final String market; - private final String price; - private final String side; - private final String type; - private final String status; - private final String amount; - private final String remainingAmount; - private final String executedAmount; - private final String clientOrderId; - private final String createdAt; - private final String updatedAt; - private final String cancelledAt; - private final List transactions; - - /** - * @param id id of order - * @param market order market name - * @param price order price - * @param side order side (buy/sell) - * @param type order type (limit/market) - * @param status order status - * @param amount order amount - * @param clientOrderId custom or generated client order id - */ - public BankeraOrder( - @JsonProperty("id") long id, - @JsonProperty("market") String market, - @JsonProperty("price") String price, - @JsonProperty("side") String side, - @JsonProperty("type") String type, - @JsonProperty("status") String status, - @JsonProperty("amount") String amount, - @JsonProperty("executed_amount") String executedAmount, - @JsonProperty("remaining_amount") String remainingAmount, - @JsonProperty("client_order_id") String clientOrderId, - @JsonProperty("created_at") String createdAt, - @JsonProperty("updated_at") String updatedAt, - @JsonProperty("cancelled_at") String cancelledAt, - @JsonProperty("transactions") List transactions) { - - this.id = id; - this.market = market; - this.price = price; - this.side = side; - this.type = type; - this.status = status; - this.amount = amount; - this.clientOrderId = clientOrderId; - this.executedAmount = executedAmount; - this.remainingAmount = remainingAmount; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - this.cancelledAt = cancelledAt; - this.transactions = transactions; - } - - public long getId() { - return id; - } - - public String getMarket() { - return market; - } - - public String getPrice() { - return price; - } - - public String getSide() { - return side; - } - - public String getType() { - return type; - } - - public String getStatus() { - return status; - } - - public String getAmount() { - return amount; - } - - public String getClientOrderId() { - return clientOrderId; - } - - public String getRemainingAmount() { - return remainingAmount; - } - - public String getExecutedAmount() { - return executedAmount; - } - - public String getCreatedAt() { - return createdAt; - } - - public String getUpdatedAt() { - return updatedAt; - } - - public String getCancelledAt() { - return cancelledAt; - } - - public List getTransactions() { - return transactions; - } - - public BigDecimal getTotalFee() { - Function totalMapper = tx -> new BigDecimal(tx.getFee()); - return transactions.size() > 0 - ? transactions.stream().map(totalMapper).reduce(BigDecimal.ZERO, BigDecimal::add) - : BigDecimal.ZERO; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraTransaction.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraTransaction.java deleted file mode 100644 index fa8f74485cb..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraTransaction.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.knowm.xchange.bankera.dto.trade; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class BankeraTransaction { - - private final long id; - private final long orderId; - private final String market; - private final String amount; - private final String fee; - private final String price; - private final String total; - private final String status; - private final String type; - private final String platform; - private final String completedAt; - - public BankeraTransaction( - @JsonProperty("id") long id, - @JsonProperty("orderId") long orderId, - @JsonProperty("market") String market, - @JsonProperty("amount") String amount, - @JsonProperty("fee") String fee, - @JsonProperty("price") String price, - @JsonProperty("total") String total, - @JsonProperty("status") String status, - @JsonProperty("type") String type, - @JsonProperty("platform") String platform, - @JsonProperty("completed_at") String completedAt) { - this.id = id; - this.orderId = orderId; - this.market = market; - this.amount = amount; - this.fee = fee; - this.price = price; - this.total = total; - this.status = status; - this.type = type; - this.platform = platform; - this.completedAt = completedAt; - } - - public long getId() { - return id; - } - - public long getOrderId() { - return orderId; - } - - public String getMarket() { - return market; - } - - public String getAmount() { - return amount; - } - - public String getFee() { - return fee; - } - - public String getPrice() { - return price; - } - - public String getTotal() { - return total; - } - - public String getStatus() { - return status; - } - - public String getType() { - return type; - } - - public String getPlatform() { - return platform; - } - - public String getCompletedAt() { - return completedAt; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraUserTrade.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraUserTrade.java deleted file mode 100644 index 39a233ead23..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraUserTrade.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.knowm.xchange.bankera.dto.trade; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class BankeraUserTrade { - - private final long id; - private final long orderId; - private final String market; - private final String amount; - private final String price; - private final String feeAmount; - private final String total; - private final String status; - private final String side; - private final String completedAt; - - public BankeraUserTrade( - @JsonProperty("id") long id, - @JsonProperty("order_id") long orderId, - @JsonProperty("market") String market, - @JsonProperty("price") String price, - @JsonProperty("amount") String amount, - @JsonProperty("fee_amount") String feeAmount, - @JsonProperty("total") String total, - @JsonProperty("status") String status, - @JsonProperty("side") String side, - @JsonProperty("completed_at") String completedAt) { - this.id = id; - this.orderId = orderId; - this.market = market; - this.amount = amount; - this.feeAmount = feeAmount; - this.total = total; - this.status = status; - this.side = side; - this.completedAt = completedAt; - this.price = price; - } - - public long getId() { - return id; - } - - public long getOrderId() { - return orderId; - } - - public String getMarket() { - return market; - } - - public String getAmount() { - return amount; - } - - public String getFeeAmount() { - return feeAmount; - } - - public String getTotal() { - return total; - } - - public String getStatus() { - return status; - } - - public String getSide() { - return side; - } - - public String getCompletedAt() { - return completedAt; - } - - public String getPrice() { - return price; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraUserTrades.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraUserTrades.java deleted file mode 100644 index e7f37f4cbc5..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/dto/trade/BankeraUserTrades.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.knowm.xchange.bankera.dto.trade; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; - -public class BankeraUserTrades { - - private final List trades; - - public BankeraUserTrades(@JsonProperty("trades") List trades) { - - this.trades = trades; - } - - public List getTrades() { - return trades; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraAccountService.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraAccountService.java deleted file mode 100644 index 39e7b23546d..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraAccountService.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.knowm.xchange.bankera.service; - -import java.io.IOException; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bankera.BankeraAdapters; -import org.knowm.xchange.dto.account.AccountInfo; -import org.knowm.xchange.service.account.AccountService; - -public class BankeraAccountService extends BankeraAccountServiceRaw implements AccountService { - - public BankeraAccountService(Exchange exchange) { - super(exchange); - } - - @Override - public AccountInfo getAccountInfo() throws IOException { - return BankeraAdapters.adaptAccountInfo(getUserInfo()); - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraAccountServiceRaw.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraAccountServiceRaw.java deleted file mode 100644 index 785e9cd450a..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraAccountServiceRaw.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.knowm.xchange.bankera.service; - -import java.io.IOException; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bankera.BankeraAdapters; -import org.knowm.xchange.bankera.BankeraExchange; -import org.knowm.xchange.bankera.dto.BankeraException; -import org.knowm.xchange.bankera.dto.account.BankeraUserInfo; - -public class BankeraAccountServiceRaw extends BankeraBaseService { - - public BankeraAccountServiceRaw(Exchange exchange) { - super(exchange); - } - - public BankeraUserInfo getUserInfo() throws IOException { - try { - BankeraExchange bankeraExchange = (BankeraExchange) exchange; - String auth = "Bearer " + bankeraExchange.getToken().getAccessToken(); - return bankeraAuthenticated.getUserInfo(auth); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraBaseService.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraBaseService.java deleted file mode 100644 index 038361a4aa3..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraBaseService.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.knowm.xchange.bankera.service; - -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bankera.Bankera; -import org.knowm.xchange.bankera.BankeraAuthenticated; -import org.knowm.xchange.bankera.dto.BankeraException; -import org.knowm.xchange.bankera.dto.BankeraToken; -import org.knowm.xchange.client.ExchangeRestProxyBuilder; -import org.knowm.xchange.service.BaseExchangeService; -import org.knowm.xchange.service.BaseService; - -public class BankeraBaseService extends BaseExchangeService implements BaseService { - - protected final Bankera bankera; - protected final BankeraAuthenticated bankeraAuthenticated; - - public BankeraBaseService(Exchange exchange) { - super(exchange); - bankera = - ExchangeRestProxyBuilder.forInterface(Bankera.class, exchange.getExchangeSpecification()) - .build(); - bankeraAuthenticated = - ExchangeRestProxyBuilder.forInterface( - BankeraAuthenticated.class, exchange.getExchangeSpecification()) - .build(); - } - - public BankeraToken createToken() throws BankeraException { - String clientId = (String) exchange.getExchangeSpecification().getParameter("clientId"); - String clientSecret = (String) exchange.getExchangeSpecification().getParameter("clientSecret"); - return bankera.getToken("client_credentials", clientId, clientSecret); - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraMarketDataService.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraMarketDataService.java deleted file mode 100644 index 0b2bd0a3473..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraMarketDataService.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.knowm.xchange.bankera.service; - -import java.io.IOException; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bankera.BankeraAdapters; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.marketdata.OrderBook; -import org.knowm.xchange.dto.marketdata.Ticker; -import org.knowm.xchange.dto.marketdata.Trades; -import org.knowm.xchange.service.marketdata.MarketDataService; - -public class BankeraMarketDataService extends BankeraMarketDataServiceRaw - implements MarketDataService { - - public BankeraMarketDataService(Exchange exchange) { - - super(exchange); - } - - @Override - public Ticker getTicker(CurrencyPair currencyPair, Object... args) throws IOException { - - return BankeraAdapters.adaptTicker(getBankeraTicker(currencyPair), currencyPair); - } - - @Override - public OrderBook getOrderBook(CurrencyPair currencyPair, Object... args) throws IOException { - - return BankeraAdapters.adaptOrderBook(getOrderbook(currencyPair), currencyPair); - } - - @Override - public Trades getTrades(CurrencyPair currencyPair, Object... args) throws IOException { - - return BankeraAdapters.adaptTrades(getRecentTrades(currencyPair), currencyPair); - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraMarketDataServiceRaw.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraMarketDataServiceRaw.java deleted file mode 100644 index 1fe6c892719..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraMarketDataServiceRaw.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.knowm.xchange.bankera.service; - -import java.io.IOException; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bankera.BankeraAdapters; -import org.knowm.xchange.bankera.dto.BankeraException; -import org.knowm.xchange.bankera.dto.marketdata.BankeraMarketInfo; -import org.knowm.xchange.bankera.dto.marketdata.BankeraOrderBook; -import org.knowm.xchange.bankera.dto.marketdata.BankeraTickerResponse; -import org.knowm.xchange.bankera.dto.marketdata.BankeraTradesResponse; -import org.knowm.xchange.currency.CurrencyPair; - -public class BankeraMarketDataServiceRaw extends BankeraBaseService { - - public BankeraMarketDataServiceRaw(Exchange exchange) { - super(exchange); - } - - public BankeraTickerResponse getBankeraTicker(CurrencyPair currencyPair) throws IOException { - - try { - return bankera.getMarketTicker(getMarketNameFromPair(currencyPair)); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - - public BankeraOrderBook getOrderbook(CurrencyPair currencyPair) throws IOException { - - try { - return bankera.getOrderbook(getMarketNameFromPair(currencyPair)); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - - public BankeraMarketInfo getMarketInfo() throws IOException { - - try { - return bankera.getMarketInfo(); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - - public BankeraTradesResponse getRecentTrades(CurrencyPair currencyPair) throws IOException { - - try { - return bankera.getRecentTrades(getMarketNameFromPair(currencyPair)); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - - private static String getMarketNameFromPair(CurrencyPair pair) { - - return pair == null - ? null - : new StringBuilder() - .append(pair.base.getCurrencyCode()) - .append("-") - .append(pair.counter.getCurrencyCode()) - .toString(); - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraTradeService.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraTradeService.java deleted file mode 100644 index f0c325bd985..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraTradeService.java +++ /dev/null @@ -1,150 +0,0 @@ -package org.knowm.xchange.bankera.service; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bankera.BankeraAdapters; -import org.knowm.xchange.bankera.dto.trade.BankeraOrder; -import org.knowm.xchange.bankera.dto.trade.BankeraUserTrades; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.Order; -import org.knowm.xchange.dto.marketdata.Trades; -import org.knowm.xchange.dto.trade.LimitOrder; -import org.knowm.xchange.dto.trade.MarketOrder; -import org.knowm.xchange.dto.trade.OpenOrders; -import org.knowm.xchange.dto.trade.UserTrades; -import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException; -import org.knowm.xchange.service.trade.TradeService; -import org.knowm.xchange.service.trade.params.CancelOrderParams; -import org.knowm.xchange.service.trade.params.DefaultTradeHistoryParamCurrencyPair; -import org.knowm.xchange.service.trade.params.TradeHistoryParamCurrencyPair; -import org.knowm.xchange.service.trade.params.TradeHistoryParams; -import org.knowm.xchange.service.trade.params.orders.*; - -public class BankeraTradeService extends BankeraTradeServiceRaw implements TradeService { - - public BankeraTradeService(Exchange exchange) { - - super(exchange); - } - - @Override - public OpenOrders getOpenOrders() throws IOException { - return getOpenOrders(null); - } - - @Override - public OpenOrders getOpenOrders(OpenOrdersParams params) throws IOException { - return new OpenOrders(BankeraAdapters.adaptOpenOrders(getBankeraOpenOrders(params))); - } - - @Override - public String placeMarketOrder(MarketOrder marketOrder) throws IOException { - BankeraOrder order = placeBankeraMarketOrder(marketOrder); - return Long.toString(order.getId()); - } - - @Override - public String placeLimitOrder(LimitOrder limitOrder) throws IOException { - BankeraOrder order = placeBankeraLimitOrder(limitOrder); - return Long.toString(order.getId()); - } - - @Override - public boolean cancelOrder(String orderId) throws IOException { - BankeraOrder order = cancelBankeraOrder(orderId); - return order != null; - } - - @Override - public boolean cancelOrder(CancelOrderParams orderParams) throws IOException { - throw new NotYetImplementedForExchangeException(); - } - - @Override - public UserTrades getTradeHistory(TradeHistoryParams params) throws IOException { - - CurrencyPair currencyPair = null; - if (params instanceof TradeHistoryParamCurrencyPair) { - TradeHistoryParamCurrencyPair tradeHistoryParamCurrencyPair = - (TradeHistoryParamCurrencyPair) params; - currencyPair = tradeHistoryParamCurrencyPair.getCurrencyPair(); - } - - BankeraUserTrades trades = getUserTrades(currencyPair); - return new UserTrades( - BankeraAdapters.adaptUserTrades(trades), Trades.TradeSortType.SortByTimestamp); - } - - @Override - public TradeHistoryParams createTradeHistoryParams() { - return new DefaultTradeHistoryParamCurrencyPair(); - } - - @Override - public OpenOrdersParams createOpenOrdersParams() { - return new BankeraOpenOrderParams(); - } - - public static class BankeraOpenOrderParams - implements OpenOrdersParams, - OpenOrdersParamLimit, - OpenOrdersParamCurrencyPair, - OpenOrdersParamOffset { - - private Integer limit = 100; - private Integer offset = 0; - private CurrencyPair currencyPair; - - public BankeraOpenOrderParams() {} - - @Override - public boolean accept(LimitOrder order) { - return OpenOrdersParamCurrencyPair.super.accept(order); - } - - @Override - public Integer getLimit() { - return limit; - } - - @Override - public void setLimit(Integer limit) { - this.limit = limit; - } - - @Override - public Integer getOffset() { - return offset; - } - - @Override - public void setOffset(Integer offset) { - this.offset = offset; - } - - @Override - public CurrencyPair getCurrencyPair() { - return currencyPair; - } - - @Override - public void setCurrencyPair(CurrencyPair pair) { - this.currencyPair = pair; - } - } - - @Override - public Collection getOrder(OrderQueryParams... orderQueryParams) throws IOException { - List orders = new ArrayList<>(orderQueryParams.length); - - for (OrderQueryParams orderQueryParam : orderQueryParams) { - BankeraOrder order = getUserOrder(orderQueryParam.getOrderId()); - orders.add(BankeraAdapters.adaptOrder(order)); - } - - return orders; - } -} diff --git a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraTradeServiceRaw.java b/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraTradeServiceRaw.java deleted file mode 100644 index e3e027eb4e9..00000000000 --- a/xchange-bankera/src/main/java/org/knowm/xchange/bankera/service/BankeraTradeServiceRaw.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.knowm.xchange.bankera.service; - -import java.io.IOException; -import java.util.List; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bankera.BankeraAdapters; -import org.knowm.xchange.bankera.BankeraExchange; -import org.knowm.xchange.bankera.dto.BankeraException; -import org.knowm.xchange.bankera.dto.BaseBankeraRequest; -import org.knowm.xchange.bankera.dto.CreateOrderRequest; -import org.knowm.xchange.bankera.dto.trade.BankeraOpenOrders; -import org.knowm.xchange.bankera.dto.trade.BankeraOrder; -import org.knowm.xchange.bankera.dto.trade.BankeraUserTrades; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.Order; -import org.knowm.xchange.dto.trade.LimitOrder; -import org.knowm.xchange.dto.trade.MarketOrder; -import org.knowm.xchange.service.trade.params.orders.OpenOrdersParamCurrencyPair; -import org.knowm.xchange.service.trade.params.orders.OpenOrdersParamLimit; -import org.knowm.xchange.service.trade.params.orders.OpenOrdersParamOffset; -import org.knowm.xchange.service.trade.params.orders.OpenOrdersParams; - -public class BankeraTradeServiceRaw extends BankeraBaseService { - - public BankeraTradeServiceRaw(Exchange exchange) { - - super(exchange); - } - - public BankeraOpenOrders getBankeraOpenOrders(OpenOrdersParams params) throws IOException { - try { - BankeraExchange bankeraExchange = (BankeraExchange) exchange; - String auth = "Bearer " + bankeraExchange.getToken().getAccessToken(); - String market = null; - Integer limit = 100; - Integer offset = 0; - if (params instanceof OpenOrdersParamCurrencyPair) { - CurrencyPair currencyPair = ((OpenOrdersParamCurrencyPair) params).getCurrencyPair(); - market = getMarketNameFromPair(currencyPair); - } - if (params instanceof OpenOrdersParamLimit) { - limit = ((OpenOrdersParamLimit) params).getLimit(); - } - if (params instanceof OpenOrdersParamOffset) { - offset = ((OpenOrdersParamOffset) params).getOffset(); - } - return bankeraAuthenticated.getOpenOrders(auth, market, limit, offset); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - - public BankeraOrder placeBankeraLimitOrder(LimitOrder limitOrder) throws IOException { - try { - BankeraExchange bankeraExchange = (BankeraExchange) exchange; - String auth = "Bearer " + bankeraExchange.getToken().getAccessToken(); - String market = getMarketNameFromPair(limitOrder.getCurrencyPair()); - - return bankeraAuthenticated.placeOrder( - auth, - new CreateOrderRequest( - market, - (limitOrder.getType() == Order.OrderType.BID - ? CreateOrderRequest.Side.BUY.getSide() - : CreateOrderRequest.Side.SELL.getSide()), - limitOrder.getOriginalAmount(), - limitOrder.getLimitPrice(), - limitOrder.getId(), - exchange.getNonceFactory().createValue())); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - - public BankeraOrder placeBankeraMarketOrder(MarketOrder marketOrder) throws IOException { - - try { - BankeraExchange bankeraExchange = (BankeraExchange) exchange; - String auth = "Bearer " + bankeraExchange.getToken().getAccessToken(); - String market = getMarketNameFromPair(marketOrder.getCurrencyPair()); - - return bankeraAuthenticated.placeOrder( - auth, - new CreateOrderRequest( - market, - (marketOrder.getType() == Order.OrderType.BID - ? CreateOrderRequest.Side.BUY.getSide() - : CreateOrderRequest.Side.SELL.getSide()), - marketOrder.getOriginalAmount(), - marketOrder.getId(), - exchange.getNonceFactory().createValue())); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - - public BankeraOrder cancelBankeraOrder(String orderId) throws IOException { - - try { - BankeraExchange bankeraExchange = (BankeraExchange) exchange; - String auth = "Bearer " + bankeraExchange.getToken().getAccessToken(); - return bankeraAuthenticated.cancelOrder( - auth, - Long.valueOf(orderId), - new BaseBankeraRequest(exchange.getNonceFactory().createValue())); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - - public List cancelAllBankeraOrders() throws IOException { - - try { - BankeraExchange bankeraExchange = (BankeraExchange) exchange; - String auth = "Bearer " + bankeraExchange.getToken().getAccessToken(); - return bankeraAuthenticated.cancelAllOrders(auth); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - - public BankeraUserTrades getUserTrades(CurrencyPair currencyPair) throws IOException { - - try { - BankeraExchange bankeraExchange = (BankeraExchange) exchange; - String auth = "Bearer " + bankeraExchange.getToken().getAccessToken(); - String market = getMarketNameFromPair(currencyPair); - return bankeraAuthenticated.getUserTrades(auth, market); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - - public BankeraOrder getUserOrder(String orderId) throws IOException { - - try { - BankeraExchange bankeraExchange = (BankeraExchange) exchange; - String auth = "Bearer " + bankeraExchange.getToken().getAccessToken(); - return bankeraAuthenticated.getUserOrder(auth, orderId); - } catch (BankeraException e) { - throw BankeraAdapters.adaptError(e); - } - } - - private static String getMarketNameFromPair(CurrencyPair pair) { - - return pair == null - ? null - : new StringBuilder() - .append(pair.base.getCurrencyCode()) - .append("-") - .append(pair.counter.getCurrencyCode()) - .toString(); - } -} diff --git a/xchange-bankera/src/test/java/org/knowm/xchange/bankera/ExchangeUtils.java b/xchange-bankera/src/test/java/org/knowm/xchange/bankera/ExchangeUtils.java deleted file mode 100644 index 7d11f7a95ed..00000000000 --- a/xchange-bankera/src/test/java/org/knowm/xchange/bankera/ExchangeUtils.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.knowm.xchange.bankera; - -import java.io.InputStream; -import java.util.Properties; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.ExchangeFactory; -import org.knowm.xchange.ExchangeSpecification; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ExchangeUtils { - private static final Logger logger = LoggerFactory.getLogger(ExchangeUtils.class); - - public static Exchange createExchangeFromProperties() { - - ExchangeSpecification exSpec = new ExchangeSpecification(BankeraExchange.class); - - InputStream is = - ExchangeUtils.class.getResourceAsStream( - "/org/knowm/xchange/bankera/configuration.properties"); - Properties props = new Properties(); - - try { - props.load(is); - logger.debug(props.toString()); - - if (props.getProperty("client-id") != null) { - exSpec.setExchangeSpecificParametersItem("clientId", props.getProperty("client-id")); - } - if (props.getProperty("client-secret") != null) { - exSpec.setExchangeSpecificParametersItem( - "clientSecret", props.getProperty("client-secret")); - } - } catch (Exception e) { - logger.warn("Error while loading integration test properties", e); - return null; - } - - return ExchangeFactory.INSTANCE.createExchange(exSpec); - } -} diff --git a/xchange-bankera/src/test/java/org/knowm/xchange/bankera/dto/account/BankeraUserInfoTest.java b/xchange-bankera/src/test/java/org/knowm/xchange/bankera/dto/account/BankeraUserInfoTest.java deleted file mode 100644 index 1df347b7103..00000000000 --- a/xchange-bankera/src/test/java/org/knowm/xchange/bankera/dto/account/BankeraUserInfoTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.knowm.xchange.bankera.dto.account; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.List; -import org.junit.Test; - -public class BankeraUserInfoTest { - @Test - public void jsonMapperTest() throws IOException { - InputStream is = - getClass().getResourceAsStream("/org/knowm/xchange/bankera/dto/account/user_info.json"); - ObjectMapper mapper = new ObjectMapper(); - final BankeraUserInfo userInfo = mapper.readValue(is, BankeraUserInfo.class); - assertThat(userInfo).isNotNull(); - assertThat(userInfo.getUser()).isNotNull(); - BankeraUser user = userInfo.getUser(); - assertThat(user.getId()).isEqualTo(7); - assertThat(user.getWallets()).isNotEmpty().hasSize(5); - - List wallets = user.getWallets(); - - wallets.stream() - .forEach( - w -> { - assertThat(w.getBalance()).isEqualTo("10.000000000000000000000000000000"); - assertThat(w.getTotal()).isEqualTo("30.000000000000000000000000000000"); - assertThat(w.getReserved()).isEqualTo("20.000000000000000000000000000000"); - assertThat(w.getCurrency()).isNotNull(); - assertThat(Arrays.asList("BTC", "ETH", "BNK", "DASH", "XEM")) - .contains(w.getCurrency()); - }); - } -} diff --git a/xchange-bankera/src/test/java/org/knowm/xchange/bankera/service/AccountInfoFetchIntegration.java b/xchange-bankera/src/test/java/org/knowm/xchange/bankera/service/AccountInfoFetchIntegration.java deleted file mode 100644 index 690e604ac45..00000000000 --- a/xchange-bankera/src/test/java/org/knowm/xchange/bankera/service/AccountInfoFetchIntegration.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.knowm.xchange.bankera.service; - -import static org.junit.Assert.assertNotNull; - -import org.junit.Before; -import org.junit.Test; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bankera.ExchangeUtils; -import org.knowm.xchange.dto.account.AccountInfo; -import org.knowm.xchange.service.account.AccountService; - -public class AccountInfoFetchIntegration { - - private Exchange exchange; - - @Before - public void setUp() { - - exchange = ExchangeUtils.createExchangeFromProperties(); - } - - @Test - public void fetchAccountInfoTest() throws Exception { - AccountService service = exchange.getAccountService(); - assertNotNull(service); - // verify account info exists - AccountInfo info = service.getAccountInfo(); - assertNotNull(info); - } -} diff --git a/xchange-bankera/src/test/java/org/knowm/xchange/bankera/service/MarketDataServiceIntegration.java b/xchange-bankera/src/test/java/org/knowm/xchange/bankera/service/MarketDataServiceIntegration.java deleted file mode 100644 index 55be94b138d..00000000000 --- a/xchange-bankera/src/test/java/org/knowm/xchange/bankera/service/MarketDataServiceIntegration.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.knowm.xchange.bankera.service; - -import static org.junit.Assert.assertNotNull; - -import org.junit.BeforeClass; -import org.junit.Test; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bankera.ExchangeUtils; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.marketdata.OrderBook; -import org.knowm.xchange.dto.marketdata.Ticker; -import org.knowm.xchange.dto.marketdata.Trades; -import org.knowm.xchange.exceptions.ExchangeException; -import org.knowm.xchange.service.marketdata.MarketDataService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MarketDataServiceIntegration { - - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private static MarketDataService marketDataService; - - @BeforeClass - public static void init() { - - Exchange exchange = ExchangeUtils.createExchangeFromProperties(); - marketDataService = exchange.getMarketDataService(); - } - - @Test - public void getTickerTest() throws Exception { - - Ticker ticker = marketDataService.getTicker(CurrencyPair.ETH_BTC); - assertNotNull(ticker); - logger.info("Response: {}", ticker); - } - - @Test(expected = ExchangeException.class) - public void getTickerInvalidMarketTest() throws Exception { - marketDataService.getTicker(CurrencyPair.ADA_BNB); - } - - @Test - public void getOrderBookTest() throws Exception { - - OrderBook orderBook = marketDataService.getOrderBook(CurrencyPair.BNK_USDT); - assertNotNull(orderBook); - logger.info("Response: {}", orderBook); - } - - @Test - public void getTradesTest() throws Exception { - - Trades trades = marketDataService.getTrades(CurrencyPair.ETH_BTC); - assertNotNull(trades); - logger.info("Response: {}", trades); - } -} diff --git a/xchange-bankera/src/test/java/org/knowm/xchange/bankera/service/TradeServiceIntegration.java b/xchange-bankera/src/test/java/org/knowm/xchange/bankera/service/TradeServiceIntegration.java deleted file mode 100644 index 82d9e20d9a5..00000000000 --- a/xchange-bankera/src/test/java/org/knowm/xchange/bankera/service/TradeServiceIntegration.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.knowm.xchange.bankera.service; - -import java.math.BigDecimal; -import java.util.Collection; -import java.util.Date; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runners.MethodSorters; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.bankera.ExchangeUtils; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.Order; -import org.knowm.xchange.dto.trade.LimitOrder; -import org.knowm.xchange.dto.trade.MarketOrder; -import org.knowm.xchange.dto.trade.OpenOrders; -import org.knowm.xchange.dto.trade.UserTrades; -import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException; -import org.knowm.xchange.service.trade.TradeService; -import org.knowm.xchange.service.trade.params.DefaultCancelOrderParamId; -import org.knowm.xchange.service.trade.params.DefaultTradeHistoryParamCurrencyPair; -import org.knowm.xchange.service.trade.params.orders.DefaultOpenOrdersParamCurrencyPair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TradeServiceIntegration { - - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private static String orderId; - private static TradeService tradeService; - - @BeforeClass - public static void init() { - Exchange exchange = ExchangeUtils.createExchangeFromProperties(); - tradeService = exchange.getTradeService(); - } - - @Test - public void testAcreateLimitOrderTest() throws Exception { - - LimitOrder limitOrder = - new LimitOrder( - Order.OrderType.BID, - BigDecimal.valueOf(0.01), - CurrencyPair.ETH_BTC, - "", - new Date(), - BigDecimal.valueOf(0.000001)); - String createdOrderId = tradeService.placeLimitOrder(limitOrder); - Assert.assertNotNull(createdOrderId); - orderId = createdOrderId; - logger.info("Response: {}", orderId); - } - - @Test - public void testBcreateMarketOrderTest() throws Exception { - - MarketOrder marketOrder = - new MarketOrder( - Order.OrderType.ASK, BigDecimal.valueOf(0.01), CurrencyPair.ETH_BTC, "", new Date()); - - Assert.assertNotNull(tradeService.placeMarketOrder(marketOrder)); - } - - @Test - public void testCgetOpenOrdersByMarketTest() throws Exception { - - DefaultOpenOrdersParamCurrencyPair currencyPair = new DefaultOpenOrdersParamCurrencyPair(); - currencyPair.setCurrencyPair(CurrencyPair.ETH_BTC); - OpenOrders openOrders = tradeService.getOpenOrders(currencyPair); - logger.info("Response: {}", openOrders); - } - - @Test - public void testDgetAllOpenOrdersTest() throws Exception { - - OpenOrders openOrders = tradeService.getOpenOrders(); - logger.info("Response: {}", openOrders); - } - - @Test - public void testFcancelOrderTest() throws Exception { - - Assert.assertTrue(tradeService.cancelOrder(this.orderId)); - } - - @Test(expected = NotYetImplementedForExchangeException.class) - public void testGcancelAllOrdersTest() throws Exception { - - Assert.assertTrue(tradeService.cancelOrder(new DefaultCancelOrderParamId())); - } - - @Test - public void testEgetUserOrder() throws Exception { - - Collection orders = tradeService.getOrder(this.orderId); - logger.info("Response: {}", orders); - } - - @Test - public void testHgetUserTrades() throws Exception { - - DefaultTradeHistoryParamCurrencyPair currencyPair = new DefaultTradeHistoryParamCurrencyPair(); - currencyPair.setCurrencyPair(CurrencyPair.ETH_BTC); - UserTrades trades = tradeService.getTradeHistory(currencyPair); - logger.info("Response: {}", trades.getUserTrades()); - } -} diff --git a/xchange-bankera/src/test/resources/org/knowm/xchange/bankera/configuration.properties b/xchange-bankera/src/test/resources/org/knowm/xchange/bankera/configuration.properties deleted file mode 100644 index 4fe558a00c9..00000000000 --- a/xchange-bankera/src/test/resources/org/knowm/xchange/bankera/configuration.properties +++ /dev/null @@ -1,3 +0,0 @@ -#uncomment and fill those fields to test authenticated api in integration tests -#client-id= -#client-secret= \ No newline at end of file diff --git a/xchange-bankera/src/test/resources/org/knowm/xchange/bankera/dto/account/user_info.json b/xchange-bankera/src/test/resources/org/knowm/xchange/bankera/dto/account/user_info.json deleted file mode 100644 index e4c9689ab24..00000000000 --- a/xchange-bankera/src/test/resources/org/knowm/xchange/bankera/dto/account/user_info.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "user":{ - "id":7, - "verification":{"status":"Unsubmitted","last_declined_reason":null}, - "personal_profile":{ - "first_name":"John", - "last_name":"Doe", - "country":{ - "id":5, - "name":"AMERICAN SAMOA", - "iso2":"AS", - "iso3":"ASM" - }, - "phone":"12345647", - "birth_date":"2018-11-09 00:00:00", - "address":"Street 1", - "city":"Vilnius", - "zip_code":"11111", - "is_user_agree_for_news":false - }, - "business_profile":null, - "fee_set":{ - "fees":[ - {"buy_percent":"0.200000000000000000000000000000","sell_percent":"0.200000000000000000000000000000","market_id":1}, - {"buy_percent":"0.200000000000000000000000000000","sell_percent":"0.200000000000000000000000000000","market_id":2}, - {"buy_percent":"0.200000000000000000000000000000","sell_percent":"0.200000000000000000000000000000","market_id":3}, - {"buy_percent":"0.200000000000000000000000000000","sell_percent":"0.200000000000000000000000000000","market_id":4}, - {"buy_percent":"0.200000000000000000000000000000","sell_percent":"0.200000000000000000000000000000","market_id":5} - ] - }, - "wallets":[ - { - "id":16, - "currency":"DASH", - "balance":"10.000000000000000000000000000000", - "reserved":"20.000000000000000000000000000000", - "total":"30.000000000000000000000000000000" - }, - { - "id":18, - "currency":"XEM", - "balance":"10.000000000000000000000000000000", - "reserved":"20.000000000000000000000000000000", - "total":"30.000000000000000000000000000000" - }, - { - "id":20, - "currency":"BTC", - "balance":"10.000000000000000000000000000000", - "reserved":"20.000000000000000000000000000000", - "total":"30.000000000000000000000000000000" - }, - { - "id":22, - "currency":"ETH", - "balance":"10.000000000000000000000000000000", - "reserved":"20.000000000000000000000000000000", - "total":"30.000000000000000000000000000000" - }, - { - "id":24, - "currency":"BNK", - "balance":"10.000000000000000000000000000000", - "reserved":"20.000000000000000000000000000000", - "total":"30.000000000000000000000000000000" - } - ] - } -} \ No newline at end of file diff --git a/xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceKline.java b/xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceKline.java index ef1bcd451f2..bf503b809f6 100644 --- a/xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceKline.java +++ b/xchange-binance/src/main/java/org/knowm/xchange/binance/dto/marketdata/BinanceKline.java @@ -38,7 +38,7 @@ public BinanceKline(Instrument instrument, KlineInterval interval, Object[] obj) this.numberOfTrades = Long.parseLong(obj[8].toString()); this.takerBuyBaseAssetVolume = new BigDecimal(obj[9].toString()); this.takerBuyQuoteAssetVolume = new BigDecimal(obj[10].toString()); - this.closed = (Boolean) obj[11]; + this.closed = Boolean.parseBoolean(obj[11].toString()); } public BigDecimal getAveragePrice() { diff --git a/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/BitstampAuthenticatedV2.java b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/BitstampAuthenticatedV2.java index 3b016d34310..f94a2d393cc 100644 --- a/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/BitstampAuthenticatedV2.java +++ b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/BitstampAuthenticatedV2.java @@ -160,6 +160,27 @@ BitstampUserTransaction[] getUserTransactions( @FormParam("since_id") String sinceId) throws BitstampException, IOException; + @POST + @Path("{currency}_withdrawal/") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + BitstampWithdrawal withdrawCrypto( + @HeaderParam("X-Auth") String apiKey, + @HeaderParam("X-Auth-Signature") ParamsDigest signer, + @HeaderParam("X-Auth-Nonce") SynchronizedValueFactory nonce, + @HeaderParam("X-Auth-Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("X-Auth-Version") String version, + @PathParam("currency") String currency, + @FormParam("address") String address, + @FormParam("amount") BigDecimal amount, + @FormParam("contact_thirdparty") Boolean contactThirdparty, + @FormParam("contact_uuid") String contactUuid, + @FormParam("destination_tag") String destinationTag, + @FormParam("memo_id") String memoId, + @FormParam("network") String network, + @FormParam("transfer_id") Integer transferId, + @FormParam("vasp_uuid") String vaspUuid) + throws BitstampException, IOException; + /** * please keep in mind that the methods below are called through reflections with naming pattern * "withdraw" + Currency code @@ -745,6 +766,35 @@ DepositTransaction[] getUnconfirmedBTCDeposits( @HeaderParam("X-Auth-Version") String version) throws BitstampException, IOException; + @POST + @Path("{side}/instant/{pair}/") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + BitstampOrder placeInstantMarketOrder( + @HeaderParam("X-Auth") String apiKey, + @HeaderParam("X-Auth-Signature") ParamsDigest signer, + @HeaderParam("X-Auth-Nonce") SynchronizedValueFactory nonce, + @HeaderParam("X-Auth-Timestamp") SynchronizedValueFactory timeStamp, + @HeaderParam("X-Auth-Version") String version, + @PathParam("side") Side side, + @PathParam("pair") BitstampV2.Pair pair, + @FormParam("amount") BigDecimal amount) + throws BitstampException, IOException; + + @POST + @Path("{side}/instant/{pair}/") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + BitstampOrder placeInstantSellMarketOrder( + @HeaderParam("X-Auth") String apiKey, + @HeaderParam("X-Auth-Signature") ParamsDigest signer, + @HeaderParam("X-Auth-Nonce") SynchronizedValueFactory nonce, + @HeaderParam("X-Auth-Timestamp") SynchronizedValueFactory timeStamp, + @HeaderParam("X-Auth-Version") String version, + @PathParam("side") Side side, + @PathParam("pair") BitstampV2.Pair pair, + @FormParam("amount") BigDecimal amount, + @FormParam("amount_in_counter") boolean amountInCounter) + throws BitstampException, IOException; + enum Side { buy, sell @@ -1176,4 +1226,4 @@ enum BankCurrency { ZMW, ZWL } -} +} \ No newline at end of file diff --git a/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/dto/trade/BitstampOrderFlags.java b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/dto/trade/BitstampOrderFlags.java new file mode 100644 index 00000000000..2df4bedf17c --- /dev/null +++ b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/dto/trade/BitstampOrderFlags.java @@ -0,0 +1,18 @@ +package org.knowm.xchange.bitstamp.dto.trade; + +import org.knowm.xchange.dto.Order.IOrderFlags; + +public enum BitstampOrderFlags implements IOrderFlags { + /** + * This type of market order allows setting the amount in either counter or base currency, + * (the opposite of the usual market order) + * For buys, the amount is in the counter currency. + * For sells, the amount is in the base currency. + */ + INSTANT_MARKET, + + /** + * Set amount in counter currency (only supported for sell orders). + */ + INSTANT_AMOUNT_IN_COUNTER +} diff --git a/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampAccountServiceRaw.java b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampAccountServiceRaw.java index a32fd3ae8cd..2b2bd571574 100644 --- a/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampAccountServiceRaw.java +++ b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampAccountServiceRaw.java @@ -113,7 +113,21 @@ public BitstampWithdrawal withdrawBitstampFunds( } else if (currency.equals(Currency.XLM)) { response = withdrawXLM(amount, address, tag); } else { - response = withdrawAddrAmount(currency, amount, address); + response = checkAndReturnWithdrawal(bitstampAuthenticatedV2.withdrawCrypto(apiKeyForV2Requests, + signatureCreatorV2, + uuidNonceFactory, + timestampFactory, + API_VERSION, + currency.getCurrencyCode().toLowerCase(), + address, + amount, + null, + null, + null, + null, + null, + null, + null)); } if (response.error != null) { @@ -127,42 +141,6 @@ public BitstampWithdrawal withdrawBitstampFunds( return response; } - /** To prevent code repetition we try to resolve client method */ - public BitstampWithdrawal withdrawAddrAmount( - Currency currency, BigDecimal amount, String address) { - try { - Class clientClass = bitstampAuthenticatedV2.getClass(); - Method withdrawMethod = - clientClass.getMethod( - "withdraw" + currency.getCurrencyCode(), - String.class, - ParamsDigest.class, - SynchronizedValueFactory.class, - SynchronizedValueFactory.class, - String.class, - BigDecimal.class, - String.class); - - BitstampWithdrawal response = - (BitstampWithdrawal) - withdrawMethod.invoke( - bitstampAuthenticatedV2, - apiKeyForV2Requests, - signatureCreatorV2, - uuidNonceFactory, - timestampFactory, - API_VERSION, - amount, - address); - return checkAndReturnWithdrawal(response); - } catch (BitstampException e) { - throw handleError(e); - } catch (Exception e) { - throw new RuntimeException( - "Failed to call bitstamp withdraw method on authenticated client", e); - } - } - public BitstampWithdrawal withdrawRippleFunds( BigDecimal amount, String address, Long destinationTag) throws IOException { BitstampRippleDepositAddress addressAndDt; diff --git a/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampTradeService.java b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampTradeService.java index b57e622afa0..ec6203eb4e0 100644 --- a/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampTradeService.java +++ b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampTradeService.java @@ -10,10 +10,12 @@ import org.knowm.xchange.Exchange; import org.knowm.xchange.bitstamp.BitstampAdapters; import org.knowm.xchange.bitstamp.BitstampAuthenticatedV2; +import org.knowm.xchange.bitstamp.BitstampAuthenticatedV2.Side; import org.knowm.xchange.bitstamp.BitstampUtils; import org.knowm.xchange.bitstamp.BitstampV2; import org.knowm.xchange.bitstamp.dto.BitstampException; import org.knowm.xchange.bitstamp.dto.trade.BitstampOrder; +import org.knowm.xchange.bitstamp.dto.trade.BitstampOrderFlags; import org.knowm.xchange.bitstamp.dto.trade.BitstampUserTransaction; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.Order; @@ -80,17 +82,48 @@ public OpenOrders getOpenOrders(OpenOrdersParams params) throws ExchangeExceptio return new OpenOrders(limitOrders); } - @Override + private String placeInstantMarketOrder(MarketOrder order) throws IOException, BitstampException { + BitstampAuthenticatedV2.Side side = + order.getType().equals(BID) + ? BitstampAuthenticatedV2.Side.buy + : BitstampAuthenticatedV2.Side.sell; + + BitstampOrder bitstampOrder; + if (side == Side.sell) { + boolean amountInCounter = order.hasFlag(BitstampOrderFlags.INSTANT_AMOUNT_IN_COUNTER); + bitstampOrder = + placeBitstampInstantSellMarketOrder(order.getCurrencyPair(), side, order.getOriginalAmount(), amountInCounter); + } else { + bitstampOrder = + placeBitstampInstantMarketOrder(order.getCurrencyPair(), side, order.getOriginalAmount()); + + } + + if (bitstampOrder.getErrorMessage() != null) { + throw new ExchangeException(bitstampOrder.getErrorMessage()); + } + + return Long.toString(bitstampOrder.getId()); + } + + @Override public String placeMarketOrder(MarketOrder order) throws IOException, BitstampException { BitstampAuthenticatedV2.Side side = order.getType().equals(BID) ? BitstampAuthenticatedV2.Side.buy : BitstampAuthenticatedV2.Side.sell; + + if (order.hasFlag(BitstampOrderFlags.INSTANT_MARKET)) { + return placeInstantMarketOrder(order); + } + BitstampOrder bitstampOrder = placeBitstampMarketOrder(order.getCurrencyPair(), side, order.getOriginalAmount()); + if (bitstampOrder.getErrorMessage() != null) { throw new ExchangeException(bitstampOrder.getErrorMessage()); } + return Long.toString(bitstampOrder.getId()); } diff --git a/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampTradeServiceRaw.java b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampTradeServiceRaw.java index d0e2fe85dcf..78636bb741a 100644 --- a/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampTradeServiceRaw.java +++ b/xchange-bitstamp/src/main/java/org/knowm/xchange/bitstamp/service/BitstampTradeServiceRaw.java @@ -112,6 +112,45 @@ public BitstampOrder placeBitstampOrder( } } + public BitstampOrder placeBitstampInstantSellMarketOrder( + CurrencyPair pair, BitstampAuthenticatedV2.Side side, BigDecimal amount, boolean amountInCounter) + throws IOException { + + try { + return bitstampAuthenticatedV2.placeInstantSellMarketOrder( + apiKeyForV2Requests, + signatureCreatorV2, + uuidNonceFactory, + timestampFactory, + API_VERSION, + side, + new BitstampV2.Pair(pair), + amount, + amountInCounter); + } catch (BitstampException e) { + throw handleError(e); + } + } + + public BitstampOrder placeBitstampInstantMarketOrder( + CurrencyPair pair, BitstampAuthenticatedV2.Side side, BigDecimal amount) + throws IOException { + + try { + return bitstampAuthenticatedV2.placeInstantMarketOrder( + apiKeyForV2Requests, + signatureCreatorV2, + uuidNonceFactory, + timestampFactory, + API_VERSION, + side, + new BitstampV2.Pair(pair), + amount); + } catch (BitstampException e) { + throw handleError(e); + } + } + public boolean cancelBitstampOrder(long orderId) throws IOException { try { diff --git a/xchange-bitstamp/src/test/java/org/knowm/xchange/bitstamp/dto/trade/PlaceInstantOrderJSONTest.java b/xchange-bitstamp/src/test/java/org/knowm/xchange/bitstamp/dto/trade/PlaceInstantOrderJSONTest.java new file mode 100644 index 00000000000..e3d23772ff8 --- /dev/null +++ b/xchange-bitstamp/src/test/java/org/knowm/xchange/bitstamp/dto/trade/PlaceInstantOrderJSONTest.java @@ -0,0 +1,29 @@ +package org.knowm.xchange.bitstamp.dto.trade; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import org.junit.Test; + +public class PlaceInstantOrderJSONTest { + @Test + public void testUnmarshal() throws IOException { + + // Read in the JSON from the example resources + InputStream is = + PlaceLimitOrderJSONTest.class.getResourceAsStream( + "/org/knowm/xchange/bitstamp/dto/trade/example-place-instant-order.json"); + + // Use Jackson to parse it + ObjectMapper mapper = new ObjectMapper(); + BitstampOrder newOrder = mapper.readValue(is, BitstampOrder.class); + + assertThat(newOrder.getId()).isEqualTo(1273070); + assertThat(newOrder.getAmount()).isEqualTo(BigDecimal.ONE); + assertThat(newOrder.getPrice()).isEqualTo(new BigDecimal("1.25")); + assertThat(newOrder.getType()).isEqualTo(0); + } +} diff --git a/xchange-bitstamp/src/test/resources/org/knowm/xchange/bitstamp/dto/trade/example-place-instant-order.json b/xchange-bitstamp/src/test/resources/org/knowm/xchange/bitstamp/dto/trade/example-place-instant-order.json new file mode 100644 index 00000000000..3f59b1a327a --- /dev/null +++ b/xchange-bitstamp/src/test/resources/org/knowm/xchange/bitstamp/dto/trade/example-place-instant-order.json @@ -0,0 +1,7 @@ +{ + "id": "1273070", + "datetime": "2022-01-31 14:43:15.796000", + "type": "0", + "price": "1.25", + "amount": "1" +} \ No newline at end of file diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAdapters.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAdapters.java index a34ce5881e3..032a3928a53 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAdapters.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAdapters.java @@ -7,6 +7,7 @@ import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.Locale; import org.knowm.xchange.bybit.dto.BybitCategory; import org.knowm.xchange.bybit.dto.BybitResult; import org.knowm.xchange.bybit.dto.account.allcoins.BybitAllCoinBalance; @@ -44,7 +45,7 @@ public class BybitAdapters { - private static final SimpleDateFormat OPTION_DATE_FORMAT = new SimpleDateFormat("ddMMMyy"); + private static final SimpleDateFormat OPTION_DATE_FORMAT = new SimpleDateFormat("ddMMMyy", Locale.US); public static final List QUOTE_CURRENCIES = Arrays.asList("USDT", "USDC", "BTC", "DAI"); public static Wallet adaptBybitBalances(List coinWalletBalances) { diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAuthenticated.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAuthenticated.java index 9b39862e6ed..16fb0dea3d8 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAuthenticated.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitAuthenticated.java @@ -4,7 +4,7 @@ import static org.knowm.xchange.bybit.service.BybitDigest.X_BAPI_SIGN; import static org.knowm.xchange.bybit.service.BybitDigest.X_BAPI_TIMESTAMP; -import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; @@ -13,7 +13,7 @@ import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import java.io.IOException; -import java.math.BigDecimal; +import org.knowm.xchange.bybit.dto.trade.BybitPlaceOrderPayload; import org.knowm.xchange.bybit.dto.BybitResult; import org.knowm.xchange.bybit.dto.account.allcoins.BybitAllCoinsBalance; import org.knowm.xchange.bybit.dto.account.feerates.BybitFeeRates; @@ -84,16 +84,12 @@ BybitResult> getOpenOrders( */ @POST @Path("/order/create") + @Consumes(MediaType.APPLICATION_JSON) BybitResult placeMarketOrder( @HeaderParam(X_BAPI_API_KEY) String apiKey, @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, - @FormParam("category") String category, - @FormParam("symbol") String symbol, - @FormParam("side") String side, - @FormParam("orderType") String orderType, - @FormParam("qty") BigDecimal qty, - @FormParam("orderLinkId") String orderLinkId) + BybitPlaceOrderPayload payload) throws IOException, BybitException; /** @@ -101,18 +97,13 @@ BybitResult placeMarketOrder( */ @POST @Path("/order/create") + @Consumes(MediaType.APPLICATION_JSON) BybitResult placeLimitOrder( @HeaderParam(X_BAPI_API_KEY) String apiKey, @HeaderParam(X_BAPI_SIGN) ParamsDigest signature, @HeaderParam(X_BAPI_TIMESTAMP) SynchronizedValueFactory timestamp, - @FormParam("category") String category, - @FormParam("symbol") String symbol, - @FormParam("side") String side, - @FormParam("orderType") String orderType, - @FormParam("qty") BigDecimal qty, - @FormParam("price") BigDecimal price, - @FormParam("positionIdx") Integer positionIdx, - @FormParam("orderLinkId") String orderLinkId, - @FormParam("reduceOnly") Boolean reduceOnly) - throws IOException, BybitException; + BybitPlaceOrderPayload payload) +// @FormParam("positionIdx") +// @FormParam("reduceOnly") + throws IOException,BybitException; } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitExchange.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitExchange.java index 5adb148d243..c69031864eb 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitExchange.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/BybitExchange.java @@ -2,6 +2,7 @@ import java.io.IOException; import org.knowm.xchange.BaseExchange; +import org.knowm.xchange.Exchange; import org.knowm.xchange.ExchangeSpecification; import org.knowm.xchange.bybit.dto.BybitCategory; import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountType; @@ -17,6 +18,8 @@ public class BybitExchange extends BaseExchange { public static final String SPECIFIC_PARAM_ACCOUNT_TYPE = "accountType"; + private static final String BASE_URL = "https://api.bybit.com"; + private static final String DEMO_URL = "https://api-demo.bybit.com"; @Override protected void initServices() { @@ -33,13 +36,15 @@ protected void initServices() { @Override public ExchangeSpecification getDefaultExchangeSpecification() { ExchangeSpecification exchangeSpecification = new ExchangeSpecification(this.getClass()); - exchangeSpecification.setSslUri("https://api.bybit.com"); + exchangeSpecification.setSslUri(BASE_URL); exchangeSpecification.setHost("bybit.com"); exchangeSpecification.setPort(80); exchangeSpecification.setExchangeName("Bybit"); exchangeSpecification.setExchangeDescription("BYBIT"); exchangeSpecification.setExchangeSpecificParametersItem( SPECIFIC_PARAM_ACCOUNT_TYPE, BybitAccountType.UNIFIED); + exchangeSpecification.setExchangeSpecificParametersItem( + Exchange.USE_SANDBOX, false); return exchangeSpecification; } @@ -97,4 +102,14 @@ public void remoteInit() throws IOException, ExchangeException { BybitAdapters.symbolToCurrencyPairMetaData( (BybitOptionInstrumentInfo) instrumentInfo))); } + + @Override + public void applySpecification(ExchangeSpecification exchangeSpecification) { + if (exchangeSpecification + .getExchangeSpecificParametersItem(Exchange.USE_SANDBOX) + .equals(true)) { + exchangeSpecification.setSslUri(DEMO_URL); + } + super.applySpecification(exchangeSpecification); + } } diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitPlaceOrderPayload.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitPlaceOrderPayload.java new file mode 100644 index 00000000000..3a870d035d9 --- /dev/null +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/dto/trade/BybitPlaceOrderPayload.java @@ -0,0 +1,37 @@ +package org.knowm.xchange.bybit.dto.trade; + +import java.math.BigDecimal; +import lombok.Getter; + +@Getter +public class BybitPlaceOrderPayload { + + private String category; + private String symbol; + private String side; + private String orderType; + private String qty; + private String orderLinkId; + private String price; + + public BybitPlaceOrderPayload(String category, String symbol, String side, String orderType, + BigDecimal qty, + String orderLinkId) { + this.category = category; + this.symbol = symbol; + this.side = side; + this.orderType = orderType; + this.qty = qty.toString(); + this.orderLinkId = orderLinkId; + } + public BybitPlaceOrderPayload(String category, String symbol, String side, String orderType, + BigDecimal qty, String orderLinkId, BigDecimal price) { + this.category = category; + this.symbol = symbol; + this.side = side; + this.orderType = orderType; + this.qty = qty.toString(); + this.orderLinkId = orderLinkId; + this.price = price.toString(); + } +} diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitDigest.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitDigest.java index bb1e26e4ff3..e1642efa265 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitDigest.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitDigest.java @@ -28,7 +28,11 @@ public BybitDigest(String secretKeyBase64) { } public static ParamsDigest createInstance(String secretKeyBase64) { - return new BybitDigest(secretKeyBase64); + if (secretKeyBase64 != null) { + return new BybitDigest(secretKeyBase64); + } else { + return null; + } } @SneakyThrows diff --git a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeServiceRaw.java b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeServiceRaw.java index db89d7312e0..37e2f4f430f 100644 --- a/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeServiceRaw.java +++ b/xchange-bybit/src/main/java/org/knowm/xchange/bybit/service/BybitTradeServiceRaw.java @@ -6,6 +6,7 @@ import java.math.BigDecimal; import org.knowm.xchange.Exchange; import org.knowm.xchange.bybit.dto.BybitCategory; +import org.knowm.xchange.bybit.dto.trade.BybitPlaceOrderPayload; import org.knowm.xchange.bybit.dto.BybitResult; import org.knowm.xchange.bybit.dto.trade.BybitOrderResponse; import org.knowm.xchange.bybit.dto.trade.BybitOrderType; @@ -33,17 +34,14 @@ public BybitResult> getBybitOrder( public BybitResult placeMarketOrder( BybitCategory category, String symbol, BybitSide side, BigDecimal qty, String orderLinkId) throws IOException { + BybitPlaceOrderPayload payload = new BybitPlaceOrderPayload(category.getValue(), + symbol, side.getValue(), BybitOrderType.MARKET.getValue(), qty, orderLinkId); BybitResult placeOrder = bybitAuthenticated.placeMarketOrder( apiKey, signatureCreator, nonceFactory, - category.getValue(), - symbol, - side.getValue(), - BybitOrderType.MARKET.getValue(), - qty, - orderLinkId); + payload); if (!placeOrder.isSuccess()) { throw createBybitExceptionFromResult(placeOrder); } @@ -51,27 +49,17 @@ public BybitResult placeMarketOrder( } public BybitResult placeLimitOrder( - BybitCategory category, - String symbol, - BybitSide side, - BigDecimal qty, - BigDecimal limitPrice, + BybitCategory category, String symbol, BybitSide side, BigDecimal qty, BigDecimal limitPrice, String orderLinkId) throws IOException { + BybitPlaceOrderPayload payload = new BybitPlaceOrderPayload(category.getValue(), + symbol, side.getValue(), BybitOrderType.LIMIT.getValue(), qty, orderLinkId, limitPrice); BybitResult placeOrder = bybitAuthenticated.placeLimitOrder( apiKey, signatureCreator, nonceFactory, - category.getValue(), - symbol, - side.getValue(), - BybitOrderType.LIMIT.getValue(), - qty, - limitPrice, - 0, - orderLinkId, - false); + payload); if (!placeOrder.isSuccess()) { throw createBybitExceptionFromResult(placeOrder); } diff --git a/xchange-bybit/src/test/java/org/knowm/xchange/bybit/TradeExample.java b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/TradeExample.java new file mode 100644 index 00000000000..401da4889c1 --- /dev/null +++ b/xchange-bybit/src/test/java/org/knowm/xchange/bybit/TradeExample.java @@ -0,0 +1,94 @@ +package org.knowm.xchange.bybit; + +import static org.knowm.xchange.bybit.BybitExchange.SPECIFIC_PARAM_ACCOUNT_TYPE; + +import java.io.IOException; +import java.math.BigDecimal; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountType; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.derivative.FuturesContract; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.dto.trade.LimitOrder; +import org.knowm.xchange.dto.trade.MarketOrder; +import org.knowm.xchange.instrument.Instrument; + +public class TradeExample { + + public static void main(String[] args) throws InterruptedException { + try { + testTrade(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static void testTrade() throws IOException { + ExchangeSpecification exchangeSpecification = + new BybitExchange().getDefaultExchangeSpecification(); + exchangeSpecification.setApiKey(System.getProperty("test_api_key")); + exchangeSpecification.setSecretKey(System.getProperty("test_secret_key")); + exchangeSpecification.setExchangeSpecificParametersItem( + SPECIFIC_PARAM_ACCOUNT_TYPE, BybitAccountType.UNIFIED); // or FUND + Exchange exchange = ExchangeFactory.INSTANCE.createExchange( + exchangeSpecification); + Instrument ETH_USDT = new CurrencyPair("ETH/USDT"); + Instrument BTC_USDT_PERP = new FuturesContract(new CurrencyPair("BTC/USDT"), "PERP"); + Instrument ETH_USDT_PERP = new FuturesContract(new CurrencyPair("ETH/USDT"), "PERP"); + + System.out.printf("Wallets: %n%s%n", + exchange.getAccountService().getAccountInfo().getWallets()); + Ticker ticker = exchange + .getMarketDataService() + .getTicker(ETH_USDT_PERP); + System.out.println(ticker.toString()); + + System.out.printf("Instrument %s:%n %s", ETH_USDT, exchange.getExchangeMetaData() + .getInstruments().get(ETH_USDT)); + System.out.printf("Instrument %s:%n %s", ETH_USDT_PERP, exchange.getExchangeMetaData() + .getInstruments().get(ETH_USDT_PERP)); + + BigDecimal minAmountSpot = exchange.getExchangeMetaData().getInstruments().get(ETH_USDT) + .getMinimumAmount(); + BigDecimal minUSDTSpot = minAmountSpot.multiply(ticker.getLast()); + //buy + String marketSpotOrderId = + exchange + .getTradeService() + .placeMarketOrder( + new MarketOrder(OrderType.BID, minUSDTSpot, ETH_USDT)); + System.out.println("Market Spot order id: " + marketSpotOrderId); + //sell + marketSpotOrderId = + exchange + .getTradeService() + .placeMarketOrder( + new MarketOrder(OrderType.ASK, minAmountSpot, ETH_USDT)); + + System.out.println("Market Spot order id: " + marketSpotOrderId); + + BigDecimal minAmountFuture = exchange.getExchangeMetaData().getInstruments().get(ETH_USDT_PERP) + .getMinimumAmount(); + + //long + String marketFutureOrderId = + exchange + .getTradeService() + .placeMarketOrder( + new MarketOrder(OrderType.BID, minAmountFuture, ETH_USDT_PERP)); + System.out.println("Market Future order id: " + marketFutureOrderId); + + //short + String limitFutureOrderId = + exchange + .getTradeService() + .placeLimitOrder( + new LimitOrder(OrderType.ASK, minAmountFuture, ETH_USDT_PERP, "123213", null, + ticker.getLast())); + System.out.println("Limit Future order id: " + limitFutureOrderId); + } + +} diff --git a/xchange-bybit/src/test/resources/logback.xml b/xchange-bybit/src/test/resources/logback.xml index b193ec04c03..2a586567a6e 100644 --- a/xchange-bybit/src/test/resources/logback.xml +++ b/xchange-bybit/src/test/resources/logback.xml @@ -18,6 +18,6 @@ - + \ No newline at end of file diff --git a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/CoinmateAuthenticated.java b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/CoinmateAuthenticated.java index e494e772c57..7c0342114ce 100644 --- a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/CoinmateAuthenticated.java +++ b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/CoinmateAuthenticated.java @@ -31,11 +31,27 @@ import jakarta.ws.rs.core.MediaType; import java.io.IOException; import java.math.BigDecimal; +import org.knowm.xchange.coinmate.dto.account.AmountType; import org.knowm.xchange.coinmate.dto.account.CoinmateBalance; import org.knowm.xchange.coinmate.dto.account.CoinmateDepositAddresses; import org.knowm.xchange.coinmate.dto.account.CoinmateTradingFeesResponse; import org.knowm.xchange.coinmate.dto.account.FeePriority; -import org.knowm.xchange.coinmate.dto.trade.*; +import org.knowm.xchange.coinmate.dto.account.TransferHistoryOrder; +import org.knowm.xchange.coinmate.dto.account.UnconfirmedDepositsResponse; +import org.knowm.xchange.coinmate.dto.trade.CoinmateBuyFixRateResponse; +import org.knowm.xchange.coinmate.dto.trade.CoinmateCancelOrderResponse; +import org.knowm.xchange.coinmate.dto.trade.CoinmateCancelOrderWithInfoResponse; +import org.knowm.xchange.coinmate.dto.trade.CoinmateOpenOrders; +import org.knowm.xchange.coinmate.dto.trade.CoinmateOrder; +import org.knowm.xchange.coinmate.dto.trade.CoinmateOrderHistory; +import org.knowm.xchange.coinmate.dto.trade.CoinmateOrders; +import org.knowm.xchange.coinmate.dto.trade.CoinmateReplaceResponse; +import org.knowm.xchange.coinmate.dto.trade.CoinmateSellFixRateResponse; +import org.knowm.xchange.coinmate.dto.trade.CoinmateTradeHistory; +import org.knowm.xchange.coinmate.dto.trade.CoinmateTradeResponse; +import org.knowm.xchange.coinmate.dto.trade.CoinmateTransactionHistory; +import org.knowm.xchange.coinmate.dto.trade.CoinmateTransferDetail; +import org.knowm.xchange.coinmate.dto.trade.CoinmateTransferHistory; import si.mazi.rescu.ParamsDigest; import si.mazi.rescu.SynchronizedValueFactory; @@ -266,6 +282,42 @@ CoinmateTradeResponse sellQuickFix( // withdrawal and deposits // bitcoin + + @POST + @Path("withdrawVirtualCurrency") + CoinmateTradeResponse withdrawVirtualCurrency( + @FormParam("publicKey") String publicKey, + @FormParam("clientId") String clientId, + @FormParam("signature") ParamsDigest signer, + @FormParam("nonce") SynchronizedValueFactory nonce, + @FormParam("currencyName") String currencyName, + @FormParam("amount") BigDecimal amount, + @FormParam("destinationTag") String destinationTag, + @FormParam("amountType") AmountType amountType, + @FormParam("address") String address, + @FormParam("feePriority") FeePriority feePriority) + throws IOException; + + @POST + @Path("virtualCurrencyDepositAddresses") + CoinmateDepositAddresses virtualCurrencyDepositAddresses( + @FormParam("publicKey") String publicKey, + @FormParam("clientId") String clientId, + @FormParam("signature") ParamsDigest signer, + @FormParam("nonce") SynchronizedValueFactory nonce, + @FormParam("currencyName") String currencyName) + throws IOException; + + @POST + @Path("unconfirmedVirtualCurrencyDeposits") + UnconfirmedDepositsResponse unconfirmedVirtualCurrencyDeposits( + @FormParam("publicKey") String publicKey, + @FormParam("clientId") String clientId, + @FormParam("signature") ParamsDigest signer, + @FormParam("nonce") SynchronizedValueFactory nonce, + @FormParam("currencyName") String currencyName) + throws IOException; + @POST @Path("bitcoinWithdrawal") CoinmateTradeResponse bitcoinWithdrawal( @@ -352,28 +404,6 @@ CoinmateDepositAddresses rippleDepositAddresses( @FormParam("nonce") SynchronizedValueFactory nonce) throws IOException; - // dash - - @POST - @Path("dashWithdrawal") - CoinmateTradeResponse dashWithdrawal( - @FormParam("publicKey") String publicKey, - @FormParam("clientId") String clientId, - @FormParam("signature") ParamsDigest signer, - @FormParam("nonce") SynchronizedValueFactory nonce, - @FormParam("amount") BigDecimal amount, - @FormParam("address") String address) - throws IOException; - - @POST - @Path("dashDepositAddresses") - CoinmateDepositAddresses dashDepositAddresses( - @FormParam("publicKey") String publicKey, - @FormParam("clientId") String clientId, - @FormParam("signature") ParamsDigest signer, - @FormParam("nonce") SynchronizedValueFactory nonce) - throws IOException; - @POST @Path("adaWithdrawal") CoinmateTradeResponse adaWithdrawal( @@ -452,7 +482,7 @@ CoinmateTransferHistory getTransferHistory( @FormParam("nonce") SynchronizedValueFactory nonce, @FormParam("limit") Integer limit, @FormParam("lastId") Integer lastId, - @FormParam("sort") String sort, + @FormParam("sort") TransferHistoryOrder sort, @FormParam("timestampFrom") Long timestampFrom, @FormParam("timestampTo") Long timestampTo, @FormParam("currency") String currency) diff --git a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/AmountType.java b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/AmountType.java new file mode 100644 index 00000000000..e5d8dbd2fba --- /dev/null +++ b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/AmountType.java @@ -0,0 +1,6 @@ +package org.knowm.xchange.coinmate.dto.account; + +public enum AmountType { + NET, + GROSS +} diff --git a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/TransferHistoryOrder.java b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/TransferHistoryOrder.java new file mode 100644 index 00000000000..914b4fc40d1 --- /dev/null +++ b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/TransferHistoryOrder.java @@ -0,0 +1,5 @@ +package org.knowm.xchange.coinmate.dto.account; + +public enum TransferHistoryOrder { + ASC, DESC +} diff --git a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/UnconfirmedDeposits.java b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/UnconfirmedDeposits.java new file mode 100644 index 00000000000..b9ef8a98aff --- /dev/null +++ b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/UnconfirmedDeposits.java @@ -0,0 +1,41 @@ +package org.knowm.xchange.coinmate.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; + +public class UnconfirmedDeposits { + private final Long id; + + private final BigDecimal amount; + + private final String address; + + private final Long confirmations; + + public UnconfirmedDeposits( + @JsonProperty Long id, + @JsonProperty BigDecimal amount, + @JsonProperty String address, + @JsonProperty Long confirmations) { + this.id = id; + this.amount = amount; + this.address = address; + this.confirmations = confirmations; + } + + public Long getId() { + return id; + } + + public BigDecimal getAmount() { + return amount; + } + + public String getAddress() { + return address; + } + + public Long getConfirmations() { + return confirmations; + } +} diff --git a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/UnconfirmedDepositsResponse.java b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/UnconfirmedDepositsResponse.java new file mode 100644 index 00000000000..4f16605c40a --- /dev/null +++ b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/account/UnconfirmedDepositsResponse.java @@ -0,0 +1,16 @@ +package org.knowm.xchange.coinmate.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import org.knowm.xchange.coinmate.dto.CoinmateBaseResponse; + +public class UnconfirmedDepositsResponse + extends CoinmateBaseResponse> { + public UnconfirmedDepositsResponse( + @JsonProperty("error") boolean error, + @JsonProperty("errorMessage") String errorMessage, + @JsonProperty("data") ArrayList data) { + + super(error, errorMessage, data); + } +} diff --git a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/trade/CoinmateTransferHistoryEntry.java b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/trade/CoinmateTransferHistoryEntry.java index 201c7bb7adb..f10d6b49655 100644 --- a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/trade/CoinmateTransferHistoryEntry.java +++ b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/dto/trade/CoinmateTransferHistoryEntry.java @@ -14,6 +14,7 @@ public class CoinmateTransferHistoryEntry { private final String walletType; private final String destination; private final String destinationTag; + private final String txid; public CoinmateTransferHistoryEntry( @JsonProperty("transactionId") long id, @@ -25,8 +26,9 @@ public CoinmateTransferHistoryEntry( @JsonProperty("amountCurrency") String amountCurrency, @JsonProperty("walletType") String walletType, @JsonProperty("destination") String destination, - @JsonProperty("destinationTag") String destinationTag) { - + @JsonProperty("destinationTag") String destinationTag, + @JsonProperty("txid") String txid + ) { this.fee = fee; this.transferType = transferType; this.timestamp = timestamp; @@ -37,6 +39,7 @@ public CoinmateTransferHistoryEntry( this.walletType = walletType; this.destination = destination; this.destinationTag = destinationTag; + this.txid = txid; } public long getId() { @@ -78,4 +81,6 @@ public String getDestination() { public String getDestinationTag() { return destinationTag; } + + public String getTxid() { return txid; } } diff --git a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/service/CoinmateAccountService.java b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/service/CoinmateAccountService.java index 78e37dc1dee..e268cdb5a7d 100644 --- a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/service/CoinmateAccountService.java +++ b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/service/CoinmateAccountService.java @@ -34,14 +34,17 @@ import org.knowm.xchange.Exchange; import org.knowm.xchange.coinmate.CoinmateAdapters; import org.knowm.xchange.coinmate.CoinmateUtils; +import org.knowm.xchange.coinmate.dto.account.AmountType; import org.knowm.xchange.coinmate.dto.account.CoinmateDepositAddresses; import org.knowm.xchange.coinmate.dto.account.CoinmateTradingFeesResponseData; +import org.knowm.xchange.coinmate.dto.account.FeePriority; import org.knowm.xchange.coinmate.dto.trade.CoinmateTradeResponse; import org.knowm.xchange.coinmate.dto.trade.CoinmateTransactionHistory; import org.knowm.xchange.coinmate.dto.trade.CoinmateTransferDetail; import org.knowm.xchange.coinmate.dto.trade.CoinmateTransferHistory; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.dto.account.AccountInfo; +import org.knowm.xchange.dto.account.AddressWithTag; import org.knowm.xchange.dto.account.Fee; import org.knowm.xchange.dto.account.FundingRecord; import org.knowm.xchange.instrument.Instrument; @@ -97,12 +100,13 @@ public String withdrawFunds(Currency currency, BigDecimal amount, String address response = coinmateEthereumWithdrawal(amount, address); } else if (currency.equals(Currency.XRP)) { response = coinmateRippleWithdrawal(amount, address); - } else if (currency.equals(Currency.DASH)) { - response = coinmateDashWithdrawal(amount, address); } else if (currency.equals(Currency.ADA)) { response = coinmateCardanoWithdrawal(amount, address); } else if (currency.equals(Currency.SOL)) { response = coinmateSolanaWithdrawal(amount, address); + } else if (currency.equals(Currency.USDT)) { + Long tradeId = coinmateWithdrawVirtualCurrency(amount, address, Currency.USDT.getCurrencyCode(), AmountType.GROSS, FeePriority.HIGH, null); + return Long.toString(tradeId); } else { throw new IOException( "Wallet for currency" + currency.getCurrencyCode() + " is currently not supported"); @@ -111,10 +115,27 @@ public String withdrawFunds(Currency currency, BigDecimal amount, String address return Long.toString(response.getData()); } + @Override + public String withdrawFunds(Currency currency, BigDecimal amount, AddressWithTag address) + throws IOException { + if (currency.equals(Currency.XRP)) { + Long tradeId = coinmateWithdrawVirtualCurrency(amount, address.getAddress(), currency.getCurrencyCode(), AmountType.GROSS, FeePriority.HIGH, address.getAddressTag()); + return Long.toString(tradeId); + } else { + return withdrawFunds(currency, amount, address.getAddress()); + } + } + @Override public String withdrawFunds(WithdrawFundsParams params) throws IOException { if (params instanceof DefaultWithdrawFundsParams) { DefaultWithdrawFundsParams defaultParams = (DefaultWithdrawFundsParams) params; + + if (defaultParams.getCurrency().equals(Currency.XRP)) { + Long tradeId = coinmateWithdrawVirtualCurrency(defaultParams.getAmount(), defaultParams.getAddress(), defaultParams.getCurrency().getCurrencyCode(), AmountType.GROSS, FeePriority.HIGH, defaultParams.getAddressTag()); + return Long.toString(tradeId); + } + return withdrawFunds( defaultParams.getCurrency(), defaultParams.getAmount(), defaultParams.getAddress()); } @@ -132,12 +153,16 @@ public String requestDepositAddress(Currency currency, String... args) throws IO addresses = coinmateEthereumDepositAddresses(); } else if (currency.equals(Currency.XRP)) { addresses = coinmateRippleDepositAddresses(); - } else if (currency.equals(Currency.DASH)) { - addresses = coinmateDashDepositAddresses(); } else if (currency.equals(Currency.ADA)) { addresses = coinmateCardanoDepositAddresses(); } else if (currency.equals(Currency.SOL)) { addresses = coinmateSolanaDepositAddresses(); + } else if (currency.equals(Currency.USDT)) { + List addressesAll = coinmateVirtualCurrencyDepositAddresses(currency.getCurrencyCode()); + if (addressesAll.isEmpty()) { + return null; + } + return addressesAll.get(0); } else { throw new IOException( "Wallet for currency" + currency.getCurrencyCode() + " is currently not supported"); diff --git a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/service/CoinmateAccountServiceRaw.java b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/service/CoinmateAccountServiceRaw.java index 5c2579ac18d..e89d6dd016f 100644 --- a/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/service/CoinmateAccountServiceRaw.java +++ b/xchange-coinmate/src/main/java/org/knowm/xchange/coinmate/service/CoinmateAccountServiceRaw.java @@ -25,14 +25,20 @@ import java.io.IOException; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; import org.knowm.xchange.Exchange; import org.knowm.xchange.client.ExchangeRestProxyBuilder; import org.knowm.xchange.coinmate.CoinmateAuthenticated; +import org.knowm.xchange.coinmate.dto.account.AmountType; import org.knowm.xchange.coinmate.dto.account.CoinmateBalance; import org.knowm.xchange.coinmate.dto.account.CoinmateDepositAddresses; import org.knowm.xchange.coinmate.dto.account.CoinmateTradingFeesResponse; import org.knowm.xchange.coinmate.dto.account.CoinmateTradingFeesResponseData; import org.knowm.xchange.coinmate.dto.account.FeePriority; +import org.knowm.xchange.coinmate.dto.account.TransferHistoryOrder; +import org.knowm.xchange.coinmate.dto.account.UnconfirmedDeposits; +import org.knowm.xchange.coinmate.dto.account.UnconfirmedDepositsResponse; import org.knowm.xchange.coinmate.dto.trade.CoinmateTradeResponse; import org.knowm.xchange.coinmate.dto.trade.CoinmateTransactionHistory; import org.knowm.xchange.coinmate.dto.trade.CoinmateTransferDetail; @@ -89,6 +95,65 @@ public CoinmateTradingFeesResponseData getCoinmateTraderFees(String currencyPair return response.getData(); } + public Long coinmateWithdrawVirtualCurrency( + BigDecimal amount, + String address, + String currencyName, + AmountType amountType, + FeePriority feePriority, + String destinationTag + ) + throws IOException { + CoinmateTradeResponse response =coinmateAuthenticated.withdrawVirtualCurrency( + exchange.getExchangeSpecification().getApiKey(), + exchange.getExchangeSpecification().getUserName(), + signatureCreator, + exchange.getNonceFactory(), + currencyName, + amount, + destinationTag, + amountType, + address, + feePriority + ); + + throwExceptionIfError(response); + + return response.getData(); + } + + public ArrayList coinmateVirtualCurrencyDepositAddresses(String currencyName) + throws IOException { + CoinmateDepositAddresses response = + coinmateAuthenticated.virtualCurrencyDepositAddresses( + exchange.getExchangeSpecification().getApiKey(), + exchange.getExchangeSpecification().getUserName(), + signatureCreator, + exchange.getNonceFactory(), + currencyName + ); + + throwExceptionIfError(response); + + return response.getData(); + } + + public List coinmateUnconfirmedVirtualCurrencyDeposits(String currencyName) + throws IOException { + UnconfirmedDepositsResponse response = + coinmateAuthenticated.unconfirmedVirtualCurrencyDeposits( + exchange.getExchangeSpecification().getApiKey(), + exchange.getExchangeSpecification().getUserName(), + signatureCreator, + exchange.getNonceFactory(), + currencyName + ); + + throwExceptionIfError(response); + + return response.getData(); + } + public CoinmateTradeResponse coinmateBitcoinWithdrawal(BigDecimal amount, String address) throws IOException { return coinmateBitcoinWithdrawal(amount, address, FeePriority.HIGH); @@ -211,35 +276,6 @@ public CoinmateDepositAddresses coinmateRippleDepositAddresses() throws IOExcept return addresses; } - public CoinmateTradeResponse coinmateDashWithdrawal(BigDecimal amount, String address) - throws IOException { - CoinmateTradeResponse response = - coinmateAuthenticated.dashWithdrawal( - exchange.getExchangeSpecification().getApiKey(), - exchange.getExchangeSpecification().getUserName(), - signatureCreator, - exchange.getNonceFactory(), - amount, - address); - - throwExceptionIfError(response); - - return response; - } - - public CoinmateDepositAddresses coinmateDashDepositAddresses() throws IOException { - CoinmateDepositAddresses addresses = - coinmateAuthenticated.dashDepositAddresses( - exchange.getExchangeSpecification().getApiKey(), - exchange.getExchangeSpecification().getUserName(), - signatureCreator, - exchange.getNonceFactory()); - - throwExceptionIfError(addresses); - - return addresses; - } - public CoinmateTradeResponse coinmateCardanoWithdrawal(BigDecimal amount, String address) throws IOException { CoinmateTradeResponse response = @@ -327,7 +363,7 @@ public CoinmateTransferHistory getTransfersData( public CoinmateTransferHistory getCoinmateTransferHistory( Integer limit, Integer lastId, - String sort, + TransferHistoryOrder sort, Long timestampFrom, Long timestampTo, String currency) diff --git a/xchange-coinmate/src/main/resources/coinmate.json b/xchange-coinmate/src/main/resources/coinmate.json index cc880fea8c0..381f91a4735 100644 --- a/xchange-coinmate/src/main/resources/coinmate.json +++ b/xchange-coinmate/src/main/resources/coinmate.json @@ -1,164 +1,157 @@ { - "currency_pairs" : { - "DASH/BTC" : { - "price_scale" : 5, - "min_amount" : 0.001, - "base_scale" : 8 - }, - "DASH/CZK" : { - "price_scale" : 1, - "min_amount" : 0.001, - "base_scale" : 8 - }, - "ETH/DAI" : { - "price_scale" : 2, - "min_amount" : 0.001, - "base_scale" : 8 - }, - "XRP/EUR" : { - "price_scale" : 5, - "min_amount" : 1, - "base_scale" : 8 - }, - "BCH/EUR" : { - "price_scale" : 2, - "min_amount" : 0.001, - "base_scale" : 8 - }, - "ETH/BTC" : { - "price_scale" : 5, - "min_amount" : 0.001, - "base_scale" : 8 - }, - "LTC/BTC" : { - "price_scale" : 6, - "min_amount" : 0.01, - "base_scale" : 8 - }, - "ADA/CZK" : { - "price_scale" : 4, - "min_amount" : 1, - "base_scale" : 6 - }, - "ETH/CZK" : { - "price_scale" : 1, - "min_amount" : 0.001, - "base_scale" : 8 - }, - "SOL/CZK" : { - "price_scale" : 4, - "min_amount" : 0.01, - "base_scale" : 8 - }, - "BTC/CZK" : { - "price_scale" : 0, - "min_amount" : 0.0001, - "base_scale" : 8 - }, - "BCH/CZK" : { - "price_scale" : 1, - "min_amount" : 0.001, - "base_scale" : 8 - }, - "DASH/EUR" : { - "price_scale" : 2, - "min_amount" : 0.001, - "base_scale" : 8 - }, - "DAI/EUR" : { - "price_scale" : 4, - "min_amount" : 1, - "base_scale" : 2 - }, - "XRP/CZK" : { - "price_scale" : 4, - "min_amount" : 1, - "base_scale" : 8 - }, - "BCH/BTC" : { - "price_scale" : 5, - "min_amount" : 0.001, - "base_scale" : 8 - }, - "BTC/DAI" : { - "price_scale" : 2, - "min_amount" : 0.0001, - "base_scale" : 8 - }, - "LTC/CZK" : { - "price_scale" : 1, - "min_amount" : 0.01, - "base_scale" : 8 - }, - "XRP/BTC" : { - "price_scale" : 8, - "min_amount" : 1, - "base_scale" : 8 - }, - "LTC/EUR" : { - "price_scale" : 2, - "min_amount" : 0.01, - "base_scale" : 8 - }, - "ADA/EUR" : { - "price_scale" : 5, - "min_amount" : 1, - "base_scale" : 6 - }, - "SOL/EUR" : { - "price_scale" : 5, - "min_amount" : 0.01, - "base_scale" : 8 - }, - "BTC/EUR" : { - "price_scale" : 1, - "min_amount" : 0.0001, - "base_scale" : 8 - }, - "ETH/EUR" : { - "price_scale" : 2, - "min_amount" : 0.001, - "base_scale" : 8 + "currency_pairs": { + "XRP/EUR": { + "price_scale": 5, + "min_amount": 1, + "base_scale": 8 + }, + "BTC/USDT": { + "price_scale": 1, + "min_amount": 0.0001, + "base_scale": 8 + }, + "USDT/EUR": { + "price_scale": 4, + "min_amount": 1, + "base_scale": 2 + }, + "ETH/BTC": { + "price_scale": 5, + "min_amount": 0.001, + "base_scale": 8 + }, + "LTC/BTC": { + "price_scale": 6, + "min_amount": 0.01, + "base_scale": 8 + }, + "ADA/CZK": { + "price_scale": 4, + "min_amount": 1, + "base_scale": 6 + }, + "ETH/CZK": { + "price_scale": 1, + "min_amount": 0.001, + "base_scale": 8 + }, + "SOL/CZK": { + "price_scale": 4, + "min_amount": 0.01, + "base_scale": 8 + }, + "BTC/CZK": { + "price_scale": 0, + "min_amount": 0.0001, + "base_scale": 8 + }, + "XRP/CZK": { + "price_scale": 4, + "min_amount": 1, + "base_scale": 8 + }, + "LTC/CZK": { + "price_scale": 1, + "min_amount": 0.01, + "base_scale": 8 + }, + "XRP/BTC": { + "price_scale": 8, + "min_amount": 1, + "base_scale": 8 + }, + "ADA/EUR": { + "price_scale": 5, + "min_amount": 1, + "base_scale": 6 + }, + "LTC/EUR": { + "price_scale": 2, + "min_amount": 0.01, + "base_scale": 8 + }, + "SOL/EUR": { + "price_scale": 5, + "min_amount": 0.01, + "base_scale": 8 + }, + "USDT/CZK": { + "price_scale": 3, + "min_amount": 1, + "base_scale": 2 + }, + "BTC/EUR": { + "price_scale": 1, + "min_amount": 0.0001, + "base_scale": 8 + }, + "ETH/EUR": { + "price_scale": 2, + "min_amount": 0.001, + "base_scale": 8 } }, - "currencies" : { - "BTC" : { - "scale" : 8 + "currencies": { + "EUR": { + "scale": 2 }, - "SOL" : { - "scale" : 8 + "BCH": { + "scale": 8 }, - "EUR" : { - "scale" : 2 + "USD": { + "scale": 2 }, - "CZK" : { - "scale" : 2 + "USDT": { + "scale": 2 }, - "BCH" : { - "scale" : 8 + "DASH": { + "scale": 8 }, - "XRP" : { - "scale" : 8 + "DAI": { + "scale": 2 }, - "ETH" : { - "scale" : 8 + "BTC": { + "scale": 8 }, - "LTC" : { - "scale" : 8 + "SOL": { + "scale": 8 }, - "DASH" : { - "scale" : 8 + "PLN": { + "scale": 2 }, - "DAI" : { - "scale" : 2 + "GBP": { + "scale": 2 }, - "ADA" : { - "scale" : 6 + "CZK": { + "scale": 2 + }, + "XRP": { + "scale": 8 + }, + "ETH": { + "scale": 8 + }, + "HUF": { + "scale": 2 + }, + "LTC": { + "scale": 8 + }, + "TRX": { + "scale": 8 + }, + "ADA": { + "scale": 6 + }, + "LUNA": { + "scale": 8 } }, - "private_rate_limits" : [ { - "calls" : 100, - "time_unit" : "minutes" - } ], - "share_rate_limits" : true + "private_rate_limits": [ + { + "calls": 100, + "time_unit": "minutes" + } + ], + "share_rate_limits": true } \ No newline at end of file diff --git a/xchange-core/src/main/java/org/knowm/xchange/currency/CurrencyPair.java b/xchange-core/src/main/java/org/knowm/xchange/currency/CurrencyPair.java index ea54d40fbb8..2cbe5ade84c 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/currency/CurrencyPair.java +++ b/xchange-core/src/main/java/org/knowm/xchange/currency/CurrencyPair.java @@ -364,7 +364,7 @@ public class CurrencyPair extends Instrument implements Comparable public static final CurrencyPair ETC_7D = new CurrencyPair(Currency.ETC, Currency.getInstance("7D")); - // Bankera Exchange pairs + // Bankera pairs public static final CurrencyPair BNK_BTC = new CurrencyPair(Currency.BNK, Currency.BTC); public static final CurrencyPair BNK_ETH = new CurrencyPair(Currency.BNK, Currency.ETH); public static final CurrencyPair BNK_USDT = new CurrencyPair(Currency.BNK, Currency.USDT); diff --git a/xchange-core/src/main/java/org/knowm/xchange/exceptions/DepositAddressAmbiguousException.java b/xchange-core/src/main/java/org/knowm/xchange/exceptions/DepositAddressAmbiguousException.java new file mode 100644 index 00000000000..663cfc28698 --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/exceptions/DepositAddressAmbiguousException.java @@ -0,0 +1,25 @@ +package org.knowm.xchange.exceptions; + +import java.util.List; +import lombok.Getter; + +/** Exception indicating a requested deposit address has multiple networks and network required */ +@Getter +public class DepositAddressAmbiguousException extends ExchangeException { + private final List networks; + + public DepositAddressAmbiguousException(List networks) { + super("Deposit Address Not Found"); + this.networks = networks; + } + + public DepositAddressAmbiguousException(List networks, String message) { + super(message); + this.networks = networks; + } + + public DepositAddressAmbiguousException(List networks, String message, Throwable cause) { + super(message, cause); + this.networks = networks; + } +} diff --git a/xchange-core/src/main/java/org/knowm/xchange/exceptions/DepositAddressCreationException.java b/xchange-core/src/main/java/org/knowm/xchange/exceptions/DepositAddressCreationException.java new file mode 100644 index 00000000000..aaf34d8e2bb --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/exceptions/DepositAddressCreationException.java @@ -0,0 +1,17 @@ +package org.knowm.xchange.exceptions; + +/** Exception indicating a deposit address could not be created */ +public class DepositAddressCreationException extends ExchangeException { + + public DepositAddressCreationException() { + super("Deposit Address Could Not Be Created"); + } + + public DepositAddressCreationException(String message) { + super(message); + } + + public DepositAddressCreationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/xchange-core/src/main/java/org/knowm/xchange/exceptions/DepositAddressNotFoundException.java b/xchange-core/src/main/java/org/knowm/xchange/exceptions/DepositAddressNotFoundException.java new file mode 100644 index 00000000000..2575deb6b8e --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/exceptions/DepositAddressNotFoundException.java @@ -0,0 +1,17 @@ +package org.knowm.xchange.exceptions; + +/** Exception indicating a requested deposit address was not found */ +public class DepositAddressNotFoundException extends ExchangeException { + + public DepositAddressNotFoundException() { + super("Deposit Address Not Found"); + } + + public DepositAddressNotFoundException(String message) { + super(message); + } + + public DepositAddressNotFoundException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/account/AccountService.java b/xchange-core/src/main/java/org/knowm/xchange/service/account/AccountService.java index b10c6ef0ab7..5a3aa9c4bd4 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/service/account/AccountService.java +++ b/xchange-core/src/main/java/org/knowm/xchange/service/account/AccountService.java @@ -15,6 +15,7 @@ import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException; import org.knowm.xchange.instrument.Instrument; import org.knowm.xchange.service.BaseService; +import org.knowm.xchange.service.account.params.RequestDepositAddressParams; import org.knowm.xchange.service.trade.params.DefaultWithdrawFundsParams; import org.knowm.xchange.service.trade.params.TradeHistoryParams; import org.knowm.xchange.service.trade.params.WithdrawFundsParams; @@ -125,6 +126,24 @@ default String requestDepositAddress(Currency currency, String... args) throws I throw new NotYetImplementedForExchangeException("requestDepositAddress"); } + /** + * Request a digital currency address to fund this account. Allows to fund the exchange account + * with digital currency from an external address + * + * @param params The deposit address request parameters + * @return the internal deposit address to send funds to + * @throws ExchangeException - Indication that the exchange reported some kind of error with the + * request or response + * @throws NotAvailableFromExchangeException - Indication that the exchange does not support the + * requested function or data + * @throws NotYetImplementedForExchangeException - Indication that the exchange supports the + * requested function or data, but it has not yet been implemented + * @throws IOException - Indication that a networking error occurred while fetching JSON data + */ + default String requestDepositAddress(RequestDepositAddressParams params) throws IOException { + return requestDepositAddress(params.getCurrency(), params.getExtraArguments()); + } + /** * Request a digital currency address to fund this account. Allows to fund the exchange account * with digital currency from an external address @@ -144,6 +163,25 @@ default AddressWithTag requestDepositAddressData(Currency currency, String... ar throw new NotYetImplementedForExchangeException("requestDepositAddressData"); } + /** + * Request a digital currency address to fund this account. Allows to fund the exchange account + * with digital currency from an external address + * + * @param params The deposit address request parameters + * @return the internal deposit address to send funds to + * @throws ExchangeException - Indication that the exchange reported some kind of error with the + * request or response + * @throws NotAvailableFromExchangeException - Indication that the exchange does not support the + * requested function or data + * @throws NotYetImplementedForExchangeException - Indication that the exchange supports the + * requested function or data, but it has not yet been implemented + * @throws IOException - Indication that a networking error occurred while fetching JSON data + */ + default AddressWithTag requestDepositAddressData(RequestDepositAddressParams params) + throws IOException { + return requestDepositAddressData(params.getCurrency(), params.getExtraArguments()); + } + /** * Create {@link TradeHistoryParams} object specific to this exchange. Object created by this * method may be used to discover supported and required {@link diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/account/params/DefaultRequestDepositAddressParams.java b/xchange-core/src/main/java/org/knowm/xchange/service/account/params/DefaultRequestDepositAddressParams.java new file mode 100644 index 00000000000..291b31e3a5c --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/service/account/params/DefaultRequestDepositAddressParams.java @@ -0,0 +1,27 @@ +package org.knowm.xchange.service.account.params; + +import lombok.Builder; +import lombok.Value; +import lombok.experimental.NonFinal; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.Currency; + +@Value +@NonFinal +@Builder +@Jacksonized +public class DefaultRequestDepositAddressParams implements RequestDepositAddressParams { + Currency currency; + String network; + + @Builder.Default boolean newAddress = false; + + String[] extraArguments; + + public static DefaultRequestDepositAddressParams create(Currency currency, String... args) { + return DefaultRequestDepositAddressParams.builder() + .currency(currency) + .extraArguments(args) + .build(); + } +} diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/account/params/RequestDepositAddressParams.java b/xchange-core/src/main/java/org/knowm/xchange/service/account/params/RequestDepositAddressParams.java new file mode 100644 index 00000000000..9061ad96216 --- /dev/null +++ b/xchange-core/src/main/java/org/knowm/xchange/service/account/params/RequestDepositAddressParams.java @@ -0,0 +1,13 @@ +package org.knowm.xchange.service.account.params; + +import org.knowm.xchange.currency.Currency; + +public interface RequestDepositAddressParams { + Currency getCurrency(); + + String getNetwork(); + + boolean isNewAddress(); + + String[] getExtraArguments(); +} diff --git a/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/DefaultWithdrawFundsParams.java b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/DefaultWithdrawFundsParams.java index c5f0842f545..27e46e1b61c 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/DefaultWithdrawFundsParams.java +++ b/xchange-core/src/main/java/org/knowm/xchange/service/trade/params/DefaultWithdrawFundsParams.java @@ -3,11 +3,13 @@ import java.math.BigDecimal; import lombok.Value; import lombok.experimental.NonFinal; +import lombok.experimental.SuperBuilder; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.dto.account.AddressWithTag; @Value @NonFinal +@SuperBuilder public class DefaultWithdrawFundsParams implements WithdrawFundsParams { String address; diff --git a/xchange-core/src/main/java/org/knowm/xchange/utils/BigDecimalUtils.java b/xchange-core/src/main/java/org/knowm/xchange/utils/BigDecimalUtils.java index 8f90c2e25c8..8248e049dda 100644 --- a/xchange-core/src/main/java/org/knowm/xchange/utils/BigDecimalUtils.java +++ b/xchange-core/src/main/java/org/knowm/xchange/utils/BigDecimalUtils.java @@ -14,7 +14,7 @@ public static BigDecimal roundToStepSize(BigDecimal value, BigDecimal stepSize) public static BigDecimal roundToStepSize( BigDecimal value, BigDecimal stepSize, RoundingMode roundingMode) { - BigDecimal divided = value.divide(stepSize, MathContext.DECIMAL32).setScale(0, roundingMode); - return divided.multiply(stepSize, MathContext.DECIMAL32).stripTrailingZeros(); + BigDecimal divided = value.divide(stepSize, MathContext.DECIMAL64).setScale(0, roundingMode); + return divided.multiply(stepSize, MathContext.DECIMAL64).stripTrailingZeros(); } } diff --git a/xchange-deribit/src/main/java/org/knowm/xchange/deribit/v2/DeribitAdapters.java b/xchange-deribit/src/main/java/org/knowm/xchange/deribit/v2/DeribitAdapters.java index ea5fcdb43ef..8ccc526757e 100644 --- a/xchange-deribit/src/main/java/org/knowm/xchange/deribit/v2/DeribitAdapters.java +++ b/xchange-deribit/src/main/java/org/knowm/xchange/deribit/v2/DeribitAdapters.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.TreeMap; import java.util.stream.Collectors; import org.knowm.xchange.currency.Currency; @@ -53,7 +54,7 @@ public class DeribitAdapters { private static final String PERPETUAL = "PERPETUAL"; private static final int CURRENCY_SCALE = 8; private static final ThreadLocal DATE_PARSER = - ThreadLocal.withInitial(() -> new SimpleDateFormat("ddMMMyy")); + ThreadLocal.withInitial(() -> new SimpleDateFormat("ddMMMyy", Locale.US)); public static String adaptInstrumentName(Instrument instrument) { if (instrument instanceof FuturesContract) { diff --git a/xchange-examples/pom.xml b/xchange-examples/pom.xml index 54d9e0345c4..a2213ffe8c0 100755 --- a/xchange-examples/pom.xml +++ b/xchange-examples/pom.xml @@ -342,11 +342,6 @@ xchange-dvchain ${project.version} - - ${project.groupId} - xchange-bankera - ${project.version} - org.knowm.xchange xchange-enigma diff --git a/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraAccountServiceDemo.java b/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraAccountServiceDemo.java deleted file mode 100644 index 1d58e5bb7b1..00000000000 --- a/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraAccountServiceDemo.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.knowm.xchange.examples.bankera; - -import java.io.IOException; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.dto.account.AccountInfo; -import org.knowm.xchange.service.account.AccountService; - -public class BankeraAccountServiceDemo { - public static void main(String[] args) throws IOException { - Exchange exchange = BankeraDemoUtils.createExchange(); - AccountService accountService = exchange.getAccountService(); - - // Get the account information - AccountInfo accountInfo = accountService.getAccountInfo(); - System.out.println("AccountInfo as String: " + accountInfo.toString()); - } -} diff --git a/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraDemoUtils.java b/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraDemoUtils.java deleted file mode 100644 index f7abd15a013..00000000000 --- a/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraDemoUtils.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.knowm.xchange.examples.bankera; - -import org.knowm.xchange.Exchange; -import org.knowm.xchange.ExchangeFactory; -import org.knowm.xchange.ExchangeSpecification; -import org.knowm.xchange.bankera.BankeraExchange; - -public class BankeraDemoUtils { - public static Exchange createExchange() { - - ExchangeSpecification exSpec = new ExchangeSpecification(BankeraExchange.class); - - exSpec.setExchangeSpecificParametersItem("clientId", ""); - exSpec.setExchangeSpecificParametersItem("clientSecret", ""); - - return ExchangeFactory.INSTANCE.createExchange(exSpec); - } -} diff --git a/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraMarketDataServiceDemo.java b/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraMarketDataServiceDemo.java deleted file mode 100644 index f6de7644bc9..00000000000 --- a/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraMarketDataServiceDemo.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.knowm.xchange.examples.bankera; - -import java.io.IOException; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.marketdata.Ticker; -import org.knowm.xchange.service.marketdata.MarketDataService; - -public class BankeraMarketDataServiceDemo { - public static void main(String[] args) throws IOException { - Exchange exchange = BankeraDemoUtils.createExchange(); - MarketDataService marketDataService = exchange.getMarketDataService(); - - // Get the market data ticker - Ticker ticker = marketDataService.getTicker(CurrencyPair.ETH_BTC); - System.out.println("Ticker as String: " + ticker.toString()); - } -} diff --git a/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraTradeServiceDemo.java b/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraTradeServiceDemo.java deleted file mode 100644 index b50cecde8e2..00000000000 --- a/xchange-examples/src/main/java/org/knowm/xchange/examples/bankera/BankeraTradeServiceDemo.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.knowm.xchange.examples.bankera; - -import java.io.IOException; -import org.knowm.xchange.Exchange; -import org.knowm.xchange.dto.trade.OpenOrders; -import org.knowm.xchange.service.trade.TradeService; - -public class BankeraTradeServiceDemo { - public static void main(String[] args) throws IOException { - Exchange exchange = BankeraDemoUtils.createExchange(); - TradeService tradeService = exchange.getTradeService(); - - // Get the open orders - OpenOrders openOrders = tradeService.getOpenOrders(); - System.out.println("OpenOrders as String: " + openOrders.toString()); - } -} diff --git a/xchange-gateio-v4/.gitignore b/xchange-gateio-v4/.gitignore new file mode 100644 index 00000000000..42c7d2cd0eb --- /dev/null +++ b/xchange-gateio-v4/.gitignore @@ -0,0 +1 @@ +http-client.private.env.json \ No newline at end of file diff --git a/xchange-gateio-v4/api-specification.txt b/xchange-gateio-v4/api-specification.txt new file mode 100644 index 00000000000..c57083d9d90 --- /dev/null +++ b/xchange-gateio-v4/api-specification.txt @@ -0,0 +1,8 @@ +Gate.io Exchange API V4 specification +================================ + +Public API + +Documentation +------------- +https://www.gate.io/docs/developers/apiv4 \ No newline at end of file diff --git a/xchange-gateio-v4/example.http-client.private.env.json b/xchange-gateio-v4/example.http-client.private.env.json new file mode 100644 index 00000000000..977c974ecbe --- /dev/null +++ b/xchange-gateio-v4/example.http-client.private.env.json @@ -0,0 +1,6 @@ +{ + "default": { + "api_key": "replace_me", + "api_secret": "replace_me" + } +} \ No newline at end of file diff --git a/xchange-gateio-v4/http-client.env.json b/xchange-gateio-v4/http-client.env.json new file mode 100644 index 00000000000..3038f5935b5 --- /dev/null +++ b/xchange-gateio-v4/http-client.env.json @@ -0,0 +1,5 @@ +{ + "default": { + "api_v4": "https://api.gateio.ws/api/v4" + } +} \ No newline at end of file diff --git a/xchange-gateio-v4/lombok.config b/xchange-gateio-v4/lombok.config new file mode 100644 index 00000000000..22b090cc4b6 --- /dev/null +++ b/xchange-gateio-v4/lombok.config @@ -0,0 +1,2 @@ +lombok.equalsAndHashCode.callSuper = call +lombok.tostring.callsuper = call \ No newline at end of file diff --git a/xchange-gateio-v4/pom.xml b/xchange-gateio-v4/pom.xml new file mode 100644 index 00000000000..1a8805f5a3f --- /dev/null +++ b/xchange-gateio-v4/pom.xml @@ -0,0 +1,69 @@ + + + + 4.0.0 + + + org.knowm.xchange + xchange-parent + 5.1.2-SNAPSHOT + + + xchange-gateio-v4 + + XChange Gate.io V4 + XChange implementation for the Gate.io Exchange + + + 5.3.1 + + + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${version.fasterxml} + + + + io.github.resilience4j + resilience4j-ratelimiter + ${version.resilience4j} + + + + io.github.resilience4j + resilience4j-retry + ${version.resilience4j} + + + + org.junit.jupiter + junit-jupiter-engine + test + + + + org.knowm.xchange + xchange-core + ${project.version} + + + + org.mockito + mockito-junit-jupiter + ${version.mockito} + test + + + + org.wiremock + wiremock + test + + + + + + diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/Gateio.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/Gateio.java new file mode 100644 index 00000000000..fc805d69428 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/Gateio.java @@ -0,0 +1,52 @@ +package org.knowm.xchange.gateio; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import org.knowm.xchange.gateio.dto.GateioException; +import org.knowm.xchange.gateio.dto.marketdata.*; + +import java.io.IOException; +import java.util.List; + +@Path("api/v4") +@Produces(MediaType.APPLICATION_JSON) +public interface Gateio { + + @GET + @Path("spot/currencies") + List getCurrencies() throws IOException, GateioException; + + + @GET + @Path("spot/order_book") + GateioOrderBook getOrderBook( + @QueryParam("currency_pair") String currencyPair, + @QueryParam("with_id") Boolean withId + ) throws IOException, GateioException; + + + @GET + @Path("wallet/currency_chains") + List getCurrencyChains(@QueryParam("currency") String currency) throws IOException, GateioException; + + + @GET + @Path("spot/currency_pairs") + List getCurrencyPairDetails() throws IOException, GateioException; + + + @GET + @Path("spot/currency_pairs/{currency_pair}") + GateioCurrencyPairDetails getCurrencyPairDetails(@PathParam("currency_pair") String currencyPair) throws IOException, GateioException; + + + @GET + @Path("spot/tickers") + List getTickers(@QueryParam("currency_pair") String currencyPair) throws IOException, GateioException; + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioAdapters.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioAdapters.java new file mode 100644 index 00000000000..8cfa3ae57fd --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioAdapters.java @@ -0,0 +1,217 @@ +package org.knowm.xchange.gateio; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; +import lombok.experimental.UtilityClass; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order; +import org.knowm.xchange.dto.Order.OrderStatus; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.marketdata.OrderBook; +import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.dto.meta.InstrumentMetaData; +import org.knowm.xchange.dto.trade.LimitOrder; +import org.knowm.xchange.dto.trade.MarketOrder; +import org.knowm.xchange.dto.trade.UserTrade; +import org.knowm.xchange.gateio.dto.account.GateioAccountBookRecord; +import org.knowm.xchange.gateio.dto.account.GateioOrder; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawalRequest; +import org.knowm.xchange.gateio.dto.marketdata.GateioCurrencyPairDetails; +import org.knowm.xchange.gateio.dto.marketdata.GateioOrderBook; +import org.knowm.xchange.gateio.dto.marketdata.GateioTicker; +import org.knowm.xchange.gateio.dto.trade.GateioUserTrade; +import org.knowm.xchange.gateio.dto.trade.GateioUserTradeRaw; +import org.knowm.xchange.gateio.service.params.GateioWithdrawFundsParams; +import org.knowm.xchange.instrument.Instrument; + + +@UtilityClass +public class GateioAdapters { + + + public String toString(Instrument instrument) { + if (instrument == null) { + return null; + } + else { + return String.format("%s_%s", + instrument.getBase().getCurrencyCode(), + instrument.getCounter().getCurrencyCode()) + .toUpperCase(Locale.ROOT); + } + } + + + public OrderBook toOrderBook(GateioOrderBook gateioOrderBook, Instrument instrument) { + List asks = gateioOrderBook.getAsks().stream() + .map(priceSizeEntry -> new LimitOrder(OrderType.ASK, priceSizeEntry.getSize(), instrument, null, null, priceSizeEntry.getPrice())) + .collect(Collectors.toList()); + + List bids = gateioOrderBook.getBids().stream() + .map(priceSizeEntry -> new LimitOrder(OrderType.BID, priceSizeEntry.getSize(), instrument, null, null, priceSizeEntry.getPrice())) + .collect(Collectors.toList()); + + return new OrderBook(Date.from(gateioOrderBook.getGeneratedAt()), asks, bids); + } + + + public InstrumentMetaData toInstrumentMetaData(GateioCurrencyPairDetails gateioCurrencyPairDetails) { + return new InstrumentMetaData.Builder() + .tradingFee(gateioCurrencyPairDetails.getFee()) + .minimumAmount(gateioCurrencyPairDetails.getMinAssetAmount()) + .counterMinimumAmount(gateioCurrencyPairDetails.getMinQuoteAmount()) + .volumeScale(gateioCurrencyPairDetails.getAssetScale()) + .priceScale(gateioCurrencyPairDetails.getQuoteScale()) + .build(); + } + + + public String toString(OrderStatus orderStatus) { + switch (orderStatus) { + case OPEN: + return "open"; + case CLOSED: + return "finished"; + default: + throw new IllegalArgumentException("Can't map " + orderStatus); + } + } + + + public OrderStatus toOrderStatus(String gateioOrderStatus) { + switch (gateioOrderStatus) { + case "open": + return OrderStatus.OPEN; + case "filled": + case "closed": + return OrderStatus.FILLED; + case "cancelled": + case "stp": + return OrderStatus.CANCELED; + default: + throw new IllegalArgumentException("Can't map " + gateioOrderStatus); + } + } + + + public GateioOrder toGateioOrder(MarketOrder marketOrder) { + return GateioOrder.builder() + .currencyPair((CurrencyPair) marketOrder.getInstrument()) + .side(marketOrder.getType()) + .clientOrderId(marketOrder.getUserReference()) + .account("spot") + .type("market") + .timeInForce("ioc") + .amount(marketOrder.getOriginalAmount()) + .build(); + } + + + public GateioOrder toGateioOrder(LimitOrder limitOrder) { + return GateioOrder.builder() + .currencyPair((CurrencyPair) limitOrder.getInstrument()) + .side(limitOrder.getType()) + .clientOrderId(limitOrder.getUserReference()) + .account("spot") + .type("limit") + .timeInForce("gtc") + .price(limitOrder.getLimitPrice()) + .amount(limitOrder.getOriginalAmount()) + .build(); + } + + + public Order toOrder(GateioOrder gateioOrder) { + Order.Builder builder; + Instrument instrument = gateioOrder.getCurrencyPair(); + OrderType orderType = gateioOrder.getSide(); + + switch (gateioOrder.getType()) { + case "market": + builder = new MarketOrder.Builder(orderType, instrument); + break; + case "limit": + builder = new LimitOrder.Builder(orderType, instrument) + .limitPrice(gateioOrder.getPrice()); + break; + default: + throw new IllegalArgumentException("Can't map " + gateioOrder.getType()); + } + + if (orderType == OrderType.BID) { + builder.cumulativeAmount(gateioOrder.getFilledTotalQuote()); + } + else if (orderType == OrderType.ASK) { + BigDecimal filledAssetAmount = gateioOrder.getFilledTotalQuote().divide(gateioOrder.getAvgDealPrice(), MathContext.DECIMAL32); + builder.cumulativeAmount(filledAssetAmount); + } + else { + throw new IllegalArgumentException("Can't map " + orderType); + } + + return builder + .id(gateioOrder.getId()) + .originalAmount(gateioOrder.getAmount()) + .userReference(gateioOrder.getClientOrderId()) + .timestamp(Date.from(gateioOrder.getCreatedAt())) + .orderStatus(toOrderStatus(gateioOrder.getStatus())) + .averagePrice(gateioOrder.getAvgDealPrice()) + .fee(gateioOrder.getFee()) + .build(); + } + + + public UserTrade toUserTrade(GateioUserTradeRaw gateioUserTradeRaw) { + return new GateioUserTrade(gateioUserTradeRaw.getSide(), gateioUserTradeRaw.getAmount(), gateioUserTradeRaw.getCurrencyPair(), + gateioUserTradeRaw.getPrice(), Date.from(gateioUserTradeRaw.getTimeMs()), String.valueOf( + gateioUserTradeRaw.getId()), + String.valueOf(gateioUserTradeRaw.getOrderId()), gateioUserTradeRaw.getFee(), gateioUserTradeRaw.getFeeCurrency(), + gateioUserTradeRaw.getRemark(), gateioUserTradeRaw.getRole()); + } + + + public GateioWithdrawalRequest toGateioWithdrawalRequest(GateioWithdrawFundsParams p) { + return GateioWithdrawalRequest.builder() + .clientRecordId(p.getClientRecordId()) + .address(p.getAddress()) + .tag(p.getAddressTag()) + .chain(p.getChain()) + .amount(p.getAmount()) + .currency(p.getCurrency()) + .build(); + + } + + + public Ticker toTicker(GateioTicker gateioTicker) { + return new Ticker.Builder() + .instrument(gateioTicker.getCurrencyPair()) + .last(gateioTicker.getLastPrice()) + .bid(gateioTicker.getHighestBid()) + .ask(gateioTicker.getLowestAsk()) + .high(gateioTicker.getMaxPrice24h()) + .low(gateioTicker.getMinPrice24h()) + .volume(gateioTicker.getAssetVolume()) + .quoteVolume(gateioTicker.getQuoteVolume()) + .percentageChange(gateioTicker.getChangePercentage24h()) + .build(); + } + + + public FundingRecord toFundingRecords(GateioAccountBookRecord gateioAccountBookRecord) { + return new FundingRecord.Builder() + .setInternalId(gateioAccountBookRecord.getId()) + .setDate(Date.from(gateioAccountBookRecord.getTimestamp())) + .setCurrency(gateioAccountBookRecord.getCurrency()) + .setBalance(gateioAccountBookRecord.getBalance()) + .setType(gateioAccountBookRecord.getType()) + .setAmount(gateioAccountBookRecord.getChange().abs()) + .setDescription(gateioAccountBookRecord.getTypeDescription()) + .build(); + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioErrorAdapter.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioErrorAdapter.java new file mode 100644 index 00000000000..11324b523f1 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioErrorAdapter.java @@ -0,0 +1,58 @@ +package org.knowm.xchange.gateio; + +import lombok.experimental.UtilityClass; +import org.knowm.xchange.exceptions.*; +import org.knowm.xchange.gateio.dto.GateioException; + +@UtilityClass +public class GateioErrorAdapter { + + public final String INVALID_SIGNATURE = "INVALID_SIGNATURE"; + public final String INVALID_KEY = "INVALID_KEY"; + public final String FORBIDDEN = "FORBIDDEN"; + public final String INVALID_CURRENCY = "INVALID_CURRENCY"; + public final String INVALID_CURRENCY_PAIR = "INVALID_CURRENCY_PAIR"; + public final String BALANCE_NOT_ENOUGH = "BALANCE_NOT_ENOUGH"; + public final String TOO_FAST = "TOO_FAST"; + public final String TOO_MANY_REQUESTS = "TOO_MANY_REQUESTS"; + public final String INVALID_PARAM_VALUE = "INVALID_PARAM_VALUE"; + public final String SERVER_ERROR = "SERVER_ERROR"; + + + public ExchangeException adapt(GateioException e) { + + switch (e.getLabel()) { + case INVALID_SIGNATURE: + case INVALID_KEY: + case FORBIDDEN: + return new ExchangeSecurityException(e.getMessage(), e); + + case INVALID_CURRENCY: + case INVALID_CURRENCY_PAIR: + return new InstrumentNotValidException(e.getMessage(), e); + + case BALANCE_NOT_ENOUGH: + return new FundsExceededException(e.getMessage(), e); + + case TOO_FAST: + case TOO_MANY_REQUESTS: + return new RateLimitExceededException(e.getMessage(), e); + + case SERVER_ERROR: + return new InternalServerException(e.getMessage(), e); + + case INVALID_PARAM_VALUE: + if (e.getMessage().contains("below minimum")) { + return new OrderAmountUnderMinimumException(e.getMessage(), e); + } + else { + return new OrderNotValidException(e.getMessage(), e); + } + + default: + return new ExchangeException(e.getMessage(), e); + } + + } + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioExchange.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioExchange.java new file mode 100644 index 00000000000..5225fbab925 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioExchange.java @@ -0,0 +1,53 @@ +package org.knowm.xchange.gateio; + +import org.knowm.xchange.BaseExchange; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.dto.meta.ExchangeMetaData; +import org.knowm.xchange.dto.meta.InstrumentMetaData; +import org.knowm.xchange.gateio.service.GateioAccountService; +import org.knowm.xchange.gateio.service.GateioMarketDataService; +import org.knowm.xchange.gateio.service.GateioTradeService; +import org.knowm.xchange.instrument.Instrument; +import org.knowm.xchange.utils.nonce.CurrentTimeIncrementalNonceFactory; +import si.mazi.rescu.SynchronizedValueFactory; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class GateioExchange extends BaseExchange { + + private final SynchronizedValueFactory nonceFactory = new CurrentTimeIncrementalNonceFactory(TimeUnit.SECONDS); + + + @Override + protected void initServices() { + marketDataService = new GateioMarketDataService(this); + accountService = new GateioAccountService(this); + tradeService = new GateioTradeService(this); + } + + @Override + public ExchangeSpecification getDefaultExchangeSpecification() { + + ExchangeSpecification specification = new ExchangeSpecification(this.getClass()); + specification.setSslUri("https://api.gateio.ws"); + specification.setHost("gate.io"); + specification.setExchangeName("Gateio"); + + return specification; + } + + @Override + public SynchronizedValueFactory getNonceFactory() { + return nonceFactory; + } + + + @Override + public void remoteInit() throws IOException { + Map instruments = ((GateioMarketDataService) marketDataService).getMetaDataByInstrument(); + + exchangeMetaData = new ExchangeMetaData(instruments, null, null, null, null); + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioV4Authenticated.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioV4Authenticated.java new file mode 100644 index 00000000000..84ad6bc5b18 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/GateioV4Authenticated.java @@ -0,0 +1,202 @@ +package org.knowm.xchange.gateio; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import java.io.IOException; +import java.util.List; +import org.knowm.xchange.gateio.dto.GateioException; +import org.knowm.xchange.gateio.dto.account.GateioAccountBookRecord; +import org.knowm.xchange.gateio.dto.account.GateioAddressRecord; +import org.knowm.xchange.gateio.dto.account.GateioCurrencyBalance; +import org.knowm.xchange.gateio.dto.account.GateioDepositAddress; +import org.knowm.xchange.gateio.dto.account.GateioDepositRecord; +import org.knowm.xchange.gateio.dto.account.GateioOrder; +import org.knowm.xchange.gateio.dto.account.GateioSubAccountTransfer; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawStatus; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawalRecord; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawalRequest; +import org.knowm.xchange.gateio.dto.trade.GateioUserTradeRaw; +import si.mazi.rescu.ParamsDigest; +import si.mazi.rescu.SynchronizedValueFactory; + +@Path("api/v4") +@Produces(MediaType.APPLICATION_JSON) +public interface GateioV4Authenticated { + + @GET + @Path("wallet/deposit_address") + GateioDepositAddress getDepositAddress( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @QueryParam("currency") String currency + ) throws IOException, GateioException; + + + @GET + @Path("wallet/withdraw_status") + List getWithdrawStatus( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @QueryParam("currency") String currency + ) throws IOException, GateioException; + + + @GET + @Path("spot/accounts") + List getSpotAccounts( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @QueryParam("currency") String currency + ) throws IOException, GateioException; + + + @GET + @Path("spot/account_book") + List getAccountBookRecords( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @QueryParam("currency") String currency, + @QueryParam("from") Long from, + @QueryParam("to") Long to, + @QueryParam("limit") Integer pageLength, + @QueryParam("page") Integer pageNumber, + @QueryParam("type") String type + ) throws IOException, GateioException; + + + @GET + @Path("spot/orders") + List listOrders( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @QueryParam("currency_pair") String currencyPair, + @QueryParam("status") String status + ) throws IOException, GateioException; + + + @GET + @Path("spot/orders/{order_id}") + GateioOrder getOrder( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @PathParam("order_id") String orderId, + @QueryParam("currency_pair") String currencyPair + ) throws IOException, GateioException; + + + @DELETE + @Path("spot/orders/{order_id}") + GateioOrder cancelOrder( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @PathParam("order_id") String orderId, + @QueryParam("currency_pair") String currencyPair + ) throws IOException, GateioException; + + + @POST + @Path("spot/orders") + @Consumes(MediaType.APPLICATION_JSON) + GateioOrder createOrder( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + GateioOrder gateioOrder + ) throws IOException, GateioException; + + + @GET + @Path("spot/my_trades") + List getTradingHistory( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @QueryParam("currency_pair") String currencyPair, + @QueryParam("limit") Integer pageLength, + @QueryParam("page") Integer pageNumber, + @QueryParam("order_id") String orderId, + @QueryParam("account") String account, + @QueryParam("from") Long from, + @QueryParam("to") Long to + ) throws IOException, GateioException; + + + @GET + @Path("wallet/saved_address") + List getSavedAddresses( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @QueryParam("currency") String currency + ) throws IOException, GateioException; + + + @GET + @Path("wallet/sub_account_transfers") + List getSubAccountTransfers( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @QueryParam("sub_uid") String subAccountId, + @QueryParam("from") Long from, + @QueryParam("to") Long to, + @QueryParam("limit") Integer pageLength, + @QueryParam("offset") Integer zeroBasedPageNumber + ) throws IOException, GateioException; + + + @GET + @Path("wallet/withdrawals") + List getWithdrawals( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @QueryParam("currency") String currency, + @QueryParam("from") Long from, + @QueryParam("to") Long to, + @QueryParam("limit") Integer pageLength, + @QueryParam("offset") Integer zeroBasedPageNumber + ) throws IOException, GateioException; + + + @GET + @Path("wallet/deposits") + List getDeposits( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + @QueryParam("currency") String currency, + @QueryParam("from") Long from, + @QueryParam("to") Long to, + @QueryParam("limit") Integer pageLength, + @QueryParam("offset") Integer zeroBasedPageNumber + ) throws IOException, GateioException; + + + @POST + @Path("withdrawals") + @Consumes(MediaType.APPLICATION_JSON) + GateioWithdrawalRecord withdraw( + @HeaderParam("KEY") String apiKey, + @HeaderParam("Timestamp") SynchronizedValueFactory timestamp, + @HeaderParam("SIGN") ParamsDigest signer, + GateioWithdrawalRequest gateioWithdrawalRequest + ) throws IOException, GateioException; + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/Config.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/Config.java new file mode 100644 index 00000000000..e99b200e06b --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/Config.java @@ -0,0 +1,18 @@ +package org.knowm.xchange.gateio.config; + +import lombok.Data; +import si.mazi.rescu.IRestProxyFactory; +import si.mazi.rescu.RestProxyFactoryImpl; + +@Data +public class Config { + + private Class restProxyFactoryClass = RestProxyFactoryImpl.class; + + private static Config instance = new Config(); + + public static Config getInstance() { + return instance; + } + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/GateioJacksonObjectMapperFactory.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/GateioJacksonObjectMapperFactory.java new file mode 100644 index 00000000000..c1ade2df5e1 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/GateioJacksonObjectMapperFactory.java @@ -0,0 +1,20 @@ +package org.knowm.xchange.gateio.config; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import si.mazi.rescu.serialization.jackson.DefaultJacksonObjectMapperFactory; + +public class GateioJacksonObjectMapperFactory extends DefaultJacksonObjectMapperFactory { + + @Override + public void configureObjectMapper(ObjectMapper objectMapper) { + super.configureObjectMapper(objectMapper); + + // by default read timetamps as milliseconds + objectMapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false); + + // enable parsing to Instant + objectMapper.registerModule(new JavaTimeModule()); + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/CurrencyPairToStringConverter.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/CurrencyPairToStringConverter.java new file mode 100644 index 00000000000..de64a9e0dab --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/CurrencyPairToStringConverter.java @@ -0,0 +1,15 @@ +package org.knowm.xchange.gateio.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; +import org.knowm.xchange.currency.CurrencyPair; + +/** + * Converts {@code CurrencyPair} to string + */ +public class CurrencyPairToStringConverter extends StdConverter { + + @Override + public String convert(CurrencyPair value) { + return value.getBase() + "_" + value.getCounter(); + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/DoubleToInstantConverter.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/DoubleToInstantConverter.java new file mode 100644 index 00000000000..fe2a7078702 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/DoubleToInstantConverter.java @@ -0,0 +1,15 @@ +package org.knowm.xchange.gateio.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; +import java.time.Instant; + +/** + * Converts timestamp as double in milliseconds to {@code Instant} + */ +public class DoubleToInstantConverter extends StdConverter { + + @Override + public Instant convert(final Double value) { + return Instant.ofEpochMilli(value.longValue()); + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/OrderTypeToStringConverter.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/OrderTypeToStringConverter.java new file mode 100644 index 00000000000..9fa46e700eb --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/OrderTypeToStringConverter.java @@ -0,0 +1,22 @@ +package org.knowm.xchange.gateio.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; +import org.knowm.xchange.dto.Order.OrderType; + +/** + * Converts {@code OrderType} to string + */ +public class OrderTypeToStringConverter extends StdConverter { + + @Override + public String convert(OrderType value) { + switch (value) { + case BID: + return "buy"; + case ASK: + return "sell"; + default: + throw new IllegalArgumentException("Can't map " + value); + } + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToBooleanConverter.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToBooleanConverter.java new file mode 100644 index 00000000000..2cb8ae6738a --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToBooleanConverter.java @@ -0,0 +1,14 @@ +package org.knowm.xchange.gateio.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; + +/** + * Converts string value "1" to {@code true}, rest to {@code false} + */ +public class StringToBooleanConverter extends StdConverter { + + @Override + public Boolean convert(final String value) { + return "1".equals(value); + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToCurrencyConverter.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToCurrencyConverter.java new file mode 100644 index 00000000000..a6445b19cbb --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToCurrencyConverter.java @@ -0,0 +1,15 @@ +package org.knowm.xchange.gateio.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; +import org.knowm.xchange.currency.Currency; + +/** + * Converts string value {@code Currency} + */ +public class StringToCurrencyConverter extends StdConverter { + + @Override + public Currency convert(String value) { + return Currency.getInstance(value); + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToCurrencyPairConverter.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToCurrencyPairConverter.java new file mode 100644 index 00000000000..a9b4cf104de --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToCurrencyPairConverter.java @@ -0,0 +1,15 @@ +package org.knowm.xchange.gateio.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; +import org.knowm.xchange.currency.CurrencyPair; + +/** + * Converts string to {@code CurrencyPair} + */ +public class StringToCurrencyPairConverter extends StdConverter { + + @Override + public CurrencyPair convert(String value) { + return new CurrencyPair(value.replace('_', '/')); + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToOrderTypeConverter.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToOrderTypeConverter.java new file mode 100644 index 00000000000..418888c54c0 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/StringToOrderTypeConverter.java @@ -0,0 +1,22 @@ +package org.knowm.xchange.gateio.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; +import org.knowm.xchange.dto.Order.OrderType; + +/** + * Converts string to {@code OrderType} + */ +public class StringToOrderTypeConverter extends StdConverter { + + @Override + public OrderType convert(String value) { + switch (value) { + case "buy": + return OrderType.BID; + case "sell": + return OrderType.ASK; + default: + throw new IllegalArgumentException("Can't map " + value); + } + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/TimestampSecondsToInstantConverter.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/TimestampSecondsToInstantConverter.java new file mode 100644 index 00000000000..a4142f1f0ae --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/config/converter/TimestampSecondsToInstantConverter.java @@ -0,0 +1,15 @@ +package org.knowm.xchange.gateio.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; +import java.time.Instant; + +/** + * Converts timestamp in seconds to {@code Instant} + */ +public class TimestampSecondsToInstantConverter extends StdConverter { + + @Override + public Instant convert(final Long value) { + return Instant.ofEpochSecond(value); + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/GateioException.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/GateioException.java new file mode 100644 index 00000000000..e87b1a2dcd0 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/GateioException.java @@ -0,0 +1,19 @@ +package org.knowm.xchange.gateio.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; + +@Value +@Jacksonized +@Builder +public class GateioException extends RuntimeException { + + @JsonProperty("label") + String label; + + @JsonProperty("message") + String message; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioAccountBookRecord.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioAccountBookRecord.java new file mode 100644 index 00000000000..47f6ce16e8f --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioAccountBookRecord.java @@ -0,0 +1,52 @@ +package org.knowm.xchange.gateio.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Type; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyConverter; + +@Data +@Builder +@Jacksonized +public class GateioAccountBookRecord { + + @JsonProperty("id") + String id; + + @JsonProperty("time") + Instant timestamp; + + @JsonProperty("currency") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + Currency currency; + + @JsonProperty("change") + BigDecimal change; + + @JsonProperty("balance") + BigDecimal balance; + + @JsonProperty("type") + String typeDescription; + + + public FundingRecord.Type getType() { + switch (typeDescription) { + case "withdraw": + return Type.WITHDRAWAL; + case "deposit": + return Type.DEPOSIT; + default: + return change.signum() > 0 ? Type.OTHER_INFLOW : Type.OTHER_OUTFLOW; + } + } + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioAddressRecord.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioAddressRecord.java new file mode 100644 index 00000000000..029fe1f9556 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioAddressRecord.java @@ -0,0 +1,37 @@ +package org.knowm.xchange.gateio.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.gateio.config.converter.StringToBooleanConverter; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyConverter; + +@Data +@Builder +@Jacksonized +public class GateioAddressRecord { + + @JsonProperty("currency") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + Currency currency; + + @JsonProperty("chain") + String chain; + + @JsonProperty("address") + String address; + + @JsonProperty("name") + String name; + + @JsonProperty("tag") + String tag; + + @JsonProperty("verified") + @JsonDeserialize(converter = StringToBooleanConverter.class) + Boolean verified; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioCurrencyBalance.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioCurrencyBalance.java new file mode 100644 index 00000000000..0b15a5a8d24 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioCurrencyBalance.java @@ -0,0 +1,27 @@ +package org.knowm.xchange.gateio.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyConverter; + +@Data +@Builder +@Jacksonized +public class GateioCurrencyBalance { + + @JsonProperty("currency") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + Currency currency; + + @JsonProperty("available") + BigDecimal available; + + @JsonProperty("locked") + BigDecimal locked; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioDepositAddress.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioDepositAddress.java new file mode 100644 index 00000000000..25eaf92343d --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioDepositAddress.java @@ -0,0 +1,48 @@ +package org.knowm.xchange.gateio.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.List; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyConverter; + +@Data +@Builder +@Jacksonized +public class GateioDepositAddress { + + @JsonProperty("currency") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + Currency currency; + + @JsonProperty("multichain_addresses") + List multichainAddresses; + + + @Data + @Builder + @Jacksonized + public static class MultichainAddress { + + @JsonProperty("chain") + String chain; + + @JsonProperty("address") + String address; + + @JsonProperty("payment_id") + String memo; + + @JsonProperty("payment_name") + String memoType; + + @JsonProperty("obtain_failed") + Boolean failed; + + } + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioDepositRecord.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioDepositRecord.java new file mode 100644 index 00000000000..bc039679da2 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioDepositRecord.java @@ -0,0 +1,75 @@ +package org.knowm.xchange.gateio.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyConverter; +import org.knowm.xchange.gateio.config.converter.TimestampSecondsToInstantConverter; + +@Data +@Builder +@Jacksonized +public class GateioDepositRecord { + + @JsonProperty("id") + String id; + + @JsonProperty("txid") + String txId; + + @JsonProperty("withdraw_order_id") + String clientRecordId; + + @JsonProperty("timestamp") + @JsonDeserialize(converter = TimestampSecondsToInstantConverter.class) + Instant createdAt; + + @JsonProperty("amount") + BigDecimal amount; + + @JsonProperty("currency") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + Currency currency; + + @JsonProperty("address") + String address; + + @JsonProperty("memo") + String tag; + + @JsonProperty("status") + Status status; + + @JsonProperty("chain") + String chain; + + + @Getter + @AllArgsConstructor + public static enum Status { + DONE("done"), + CANCEL("cancelled"), + REQUEST("requesting"), + MANUAL("pending manual approval"), + BCODE("GateCode operation"), + EXTPEND("pending confirm after sending"), + FAIL("pending confirm when fail"), + INVALID("invalid order"), + VERIFY("verifying"), + PROCES("processing"), + PEND("pending"), + DMOVE("required manual approval"), + SPLITPEND("the order is automatically split due to large amount"); + + private final String description; + + } + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioOrder.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioOrder.java new file mode 100644 index 00000000000..e655a618f2f --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioOrder.java @@ -0,0 +1,124 @@ +package org.knowm.xchange.gateio.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.gateio.config.converter.CurrencyPairToStringConverter; +import org.knowm.xchange.gateio.config.converter.OrderTypeToStringConverter; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyConverter; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyPairConverter; +import org.knowm.xchange.gateio.config.converter.StringToOrderTypeConverter; + +@Data +@Builder +@Jacksonized +public class GateioOrder { + + @JsonProperty("id") + String id; + + @JsonProperty("text") + String clientOrderId; + + @JsonProperty("amend_text") + String amendText; + + @JsonProperty("create_time_ms") + Instant createdAt; + + @JsonProperty("update_time_ms") + Instant updatedAt; + + @JsonProperty("status") + String status; + + @JsonProperty("currency_pair") + @JsonDeserialize(converter = StringToCurrencyPairConverter.class) + @JsonSerialize(converter = CurrencyPairToStringConverter.class) + CurrencyPair currencyPair; + + @JsonProperty("type") + String type; + + @JsonProperty("account") + String account; + + @JsonProperty("side") + @JsonDeserialize(converter = StringToOrderTypeConverter.class) + @JsonSerialize(converter = OrderTypeToStringConverter.class) + OrderType side; + + @JsonProperty("amount") + BigDecimal amount; + + @JsonProperty("price") + BigDecimal price; + + @JsonProperty("time_in_force") + String timeInForce; + + @JsonProperty("iceberg") + BigDecimal icebergAmount; + + @JsonProperty("auto_borrow") + Boolean autoBorrow; + + @JsonProperty("auto_repay") + Boolean autoRepay; + + @JsonProperty("left") + BigDecimal amountLeftToFill; + + @JsonProperty("filled_total") + BigDecimal filledTotalQuote; + + @JsonProperty("avg_deal_price") + BigDecimal avgDealPrice; + + @JsonProperty("fee") + BigDecimal fee; + + @JsonProperty("fee_currency") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + Currency feeCurrency; + + @JsonProperty("point_fee") + BigDecimal pointFee; + + @JsonProperty("gt_fee") + BigDecimal gtFee; + + @JsonProperty("gt_maker_fee") + BigDecimal gtMakerFee; + + @JsonProperty("gt_taker_fee") + BigDecimal gtTakerFee; + + @JsonProperty("gt_discount") + Boolean gtDiscount; + + @JsonProperty("rebated_fee") + BigDecimal rebatedFee; + + @JsonProperty("rebated_fee_currency") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + Currency rebatedFeeCurrency; + + @JsonProperty("stp_id") + Integer stpId; + + @JsonProperty("stp_act") + String stpAction; + + @JsonProperty("finish_as") + String finishAs; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioSubAccountTransfer.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioSubAccountTransfer.java new file mode 100644 index 00000000000..f8a203174ff --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioSubAccountTransfer.java @@ -0,0 +1,48 @@ +package org.knowm.xchange.gateio.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyConverter; +import org.knowm.xchange.gateio.config.converter.TimestampSecondsToInstantConverter; + +@Data +@Builder +@Jacksonized +public class GateioSubAccountTransfer { + + @JsonProperty("uid") + Integer mainAccountId; + + @JsonProperty("timest") + @JsonDeserialize(converter = TimestampSecondsToInstantConverter.class) + private Instant timestamp; + + @JsonProperty("source") + String source; + + @JsonProperty("client_order_id") + String clientOrderId; + + @JsonProperty("currency") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + Currency currency; + + @JsonProperty("sub_account") + Integer subAccountId; + + @JsonProperty("direction") + String direction; + + @JsonProperty("amount") + BigDecimal amount; + + @JsonProperty("sub_account_type") + String subAccountType; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioWithdrawStatus.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioWithdrawStatus.java new file mode 100644 index 00000000000..befd5c281e8 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioWithdrawStatus.java @@ -0,0 +1,61 @@ +package org.knowm.xchange.gateio.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import java.util.Map; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.apache.commons.lang3.StringUtils; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyConverter; + +@Data +@Builder +@Jacksonized +public class GateioWithdrawStatus { + + @JsonProperty("currency") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + Currency currency; + + @JsonProperty("name") + String name; + + @JsonProperty("name_cn") + String nameCN; + + @JsonProperty("deposit") + BigDecimal depositFee; + + @JsonProperty("withdraw_percent") + String withdrawPercent; + + @JsonProperty("withdraw_fix") + BigDecimal withdrawFee; + + @JsonProperty("withdraw_day_limit") + BigDecimal withdrawDailyLimit; + + @JsonProperty("withdraw_day_limit_remain") + BigDecimal withdrawDailyLimitLeft; + + @JsonProperty("withdraw_amount_mini") + BigDecimal minWithdrawAmount; + + @JsonProperty("withdraw_eachtime_limit") + BigDecimal maxWithdrawAmount; + + @JsonProperty("withdraw_fix_on_chains") + Map withdrawFeeByChain; + + + /** + * @return withdraw rate converted from percentage + */ + public BigDecimal getWithdrawRate() { + return new BigDecimal(StringUtils.removeEnd(withdrawPercent, "%")).multiply(new BigDecimal("0.01")); + } + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioWithdrawalRecord.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioWithdrawalRecord.java new file mode 100644 index 00000000000..c199e49ecfb --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioWithdrawalRecord.java @@ -0,0 +1,78 @@ +package org.knowm.xchange.gateio.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyConverter; +import org.knowm.xchange.gateio.config.converter.TimestampSecondsToInstantConverter; + +@Data +@Builder +@Jacksonized +public class GateioWithdrawalRecord { + + @JsonProperty("id") + String id; + + @JsonProperty("txid") + String txId; + + @JsonProperty("withdraw_order_id") + String clientRecordId; + + @JsonProperty("timestamp") + @JsonDeserialize(converter = TimestampSecondsToInstantConverter.class) + Instant createdAt; + + @JsonProperty("amount") + BigDecimal amount; + + @JsonProperty("currency") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + Currency currency; + + @JsonProperty("address") + String address; + + @JsonProperty("memo") + String tag; + + @JsonProperty("status") + Status status; + + @JsonProperty("chain") + String chain; + + @JsonProperty("fee") + BigDecimal fee; + + + @Getter + @AllArgsConstructor + public static enum Status { + DONE("done"), + CANCEL("cancelled"), + REQUEST("requesting"), + MANUAL("pending manual approval"), + BCODE("GateCode operation"), + EXTPEND("pending confirm after sending"), + FAIL("pending confirm when fail"), + INVALID("invalid order"), + VERIFY("verifying"), + PROCES("processing"), + PEND("pending"), + DMOVE("required manual approval"), + SPLITPEND("the order is automatically split due to large amount"); + + private final String description; + + } + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioWithdrawalRequest.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioWithdrawalRequest.java new file mode 100644 index 00000000000..8c4afd3e3d5 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/GateioWithdrawalRequest.java @@ -0,0 +1,33 @@ +package org.knowm.xchange.gateio.dto.account; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.Currency; + +@Data +@Builder +@Jacksonized +public class GateioWithdrawalRequest { + + @JsonProperty("withdraw_order_id") + String clientRecordId; + + @JsonProperty("amount") + BigDecimal amount; + + @JsonProperty("currency") + Currency currency; + + @JsonProperty("address") + String address; + + @JsonProperty("memo") + String tag; + + @JsonProperty("chain") + String chain; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/params/GateioSubAccountTransfersParams.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/params/GateioSubAccountTransfersParams.java new file mode 100644 index 00000000000..d2f19ad882a --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/account/params/GateioSubAccountTransfersParams.java @@ -0,0 +1,21 @@ +package org.knowm.xchange.gateio.dto.account.params; + +import java.time.Instant; +import lombok.Data; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +public class GateioSubAccountTransfersParams { + + private String subAccountId; + + private Integer pageLength; + + private Integer zeroBasedPageNumber; + + private Instant startTime; + + private Instant endTime; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyChain.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyChain.java new file mode 100644 index 00000000000..aa0d4fdb4be --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyChain.java @@ -0,0 +1,47 @@ +package org.knowm.xchange.gateio.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; + +@Data +@Builder +@Jacksonized +public class GateioCurrencyChain { + + @JsonProperty("chain") + String chain; + + @JsonProperty("name_cn") + String chainNameCN; + + @JsonProperty("name_en") + String chainNameEN; + + @JsonProperty("is_disabled") + Boolean disabled; + + @JsonProperty("is_deposit_disabled") + Boolean depositDisabled; + + @JsonProperty("is_withdraw_disabled") + Boolean withdrawDisabled; + + @JsonProperty("contract_address") + String contractAddress; + + public boolean isWithdrawEnabled() { + return isEnabled() && (withdrawDisabled != null) && !withdrawDisabled; + } + + public boolean isDepositEnabled() { + return isEnabled() && (depositDisabled != null) && !depositDisabled; + } + + public boolean isEnabled() { + return (disabled != null) && !disabled; + } + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyInfo.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyInfo.java new file mode 100644 index 00000000000..7a66bda9bcb --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyInfo.java @@ -0,0 +1,44 @@ +package org.knowm.xchange.gateio.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; + +@Data +@Builder +@Jacksonized +public class GateioCurrencyInfo { + + @JsonProperty("currency") + String currencyWithChain; + + @JsonProperty("delisted") + Boolean delisted; + + @JsonProperty("withdraw_disabled") + Boolean withdrawDisabled; + + @JsonProperty("withdraw_delayed") + Boolean withdrawDelayed; + + @JsonProperty("deposit_disabled") + Boolean depositDisabled; + + @JsonProperty("trade_disabled") + Boolean tradeDisabled; + + @JsonProperty("chain") + String chain; + + + public boolean isWithdrawEnabled() { + return (withdrawDisabled != null) && !withdrawDisabled; + } + + + public boolean isDepositEnabled() { + return (depositDisabled != null) && !depositDisabled; + } + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyPairDetails.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyPairDetails.java new file mode 100644 index 00000000000..d3438f52240 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyPairDetails.java @@ -0,0 +1,52 @@ +package org.knowm.xchange.gateio.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.gateio.config.converter.TimestampSecondsToInstantConverter; + +@Data +@Builder +@Jacksonized +public class GateioCurrencyPairDetails { + + @JsonProperty("id") + String id; + + @JsonProperty("base") + String asset; + + @JsonProperty("quote") + String quote; + + @JsonProperty("fee") + BigDecimal fee; + + @JsonProperty("min_base_amount") + BigDecimal minAssetAmount; + + @JsonProperty("min_quote_amount") + BigDecimal minQuoteAmount; + + @JsonProperty("amount_precision") + Integer assetScale; + + @JsonProperty("precision") + Integer quoteScale; + + @JsonProperty("trade_status") + String tradeStatus; + + @JsonProperty("sell_start") + @JsonDeserialize(converter = TimestampSecondsToInstantConverter.class) + Instant startOfSells; + + @JsonProperty("buy_start") + @JsonDeserialize(converter = TimestampSecondsToInstantConverter.class) + Instant startOfBuys; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioOrderBook.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioOrderBook.java new file mode 100644 index 00000000000..e55129efc68 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioOrderBook.java @@ -0,0 +1,45 @@ +package org.knowm.xchange.gateio.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.List; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; + +@Data +@Builder +@Jacksonized +public class GateioOrderBook { + + @JsonProperty("id") + Long id; + + @JsonProperty("current") + Instant generatedAt; + + @JsonProperty("update") + Instant updatedAt; + + @JsonProperty("asks") + List asks; + + @JsonProperty("bids") + List bids; + + + @Data + @Builder + @Jacksonized + @JsonFormat(shape = JsonFormat.Shape.ARRAY) + public static class PriceSizeEntry { + + BigDecimal price; + + BigDecimal size; + + } + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioTicker.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioTicker.java new file mode 100644 index 00000000000..209a3e1bca9 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/marketdata/GateioTicker.java @@ -0,0 +1,64 @@ +package org.knowm.xchange.gateio.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyPairConverter; + +@Data +@Builder +@Jacksonized +public class GateioTicker { + + @JsonProperty("currency_pair") + @JsonDeserialize(converter = StringToCurrencyPairConverter.class) + CurrencyPair currencyPair; + + @JsonProperty("last") + BigDecimal lastPrice; + + @JsonProperty("lowest_ask") + BigDecimal lowestAsk; + + @JsonProperty("highest_bid") + BigDecimal highestBid; + + @JsonProperty("change_percentage") + BigDecimal changePercentage24h; + + @JsonProperty("change_utc0") + BigDecimal changePercentage24hUTC0; + + @JsonProperty("change_utc8") + BigDecimal changePercentage24hUTC8; + + @JsonProperty("base_volume") + BigDecimal assetVolume; + + @JsonProperty("quote_volume") + BigDecimal quoteVolume; + + @JsonProperty("high_24h") + BigDecimal maxPrice24h; + + @JsonProperty("low_24h") + BigDecimal minPrice24h; + + @JsonProperty("etf_net_value") + BigDecimal etfNetValue; + + @JsonProperty("etf_pre_net_value") + BigDecimal etfPreNetValue; + + @JsonProperty("etf_pre_timestamp") + Instant etfPreTimestamp; + + @JsonProperty("etf_leverage") + BigDecimal etfLeverage; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/trade/GateioUserTrade.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/trade/GateioUserTrade.java new file mode 100644 index 00000000000..7e5ffe5902f --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/trade/GateioUserTrade.java @@ -0,0 +1,40 @@ +package org.knowm.xchange.gateio.dto.trade; + +import java.math.BigDecimal; +import java.util.Date; +import lombok.Value; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.dto.trade.UserTrade; +import org.knowm.xchange.instrument.Instrument; + +@Value +public class GateioUserTrade extends UserTrade { + + Role role; + + /** + * @param type The trade type (BID side or ASK side) + * @param originalAmount The depth of this trade + * @param instrument The exchange identifier (e.g. "BTC/USD") + * @param price The price (either the bid or the ask) + * @param timestamp The timestamp of the trade + * @param id The id of the trade + * @param orderId The id of the order responsible for execution of this trade + * @param feeAmount The fee that was charged by the exchange for this trade + * @param feeCurrency The symbol of the currency in which the fee was charged + * @param orderUserReference The id that the user has insert to the trade + * @param role Trade role + */ + public GateioUserTrade(OrderType type, + BigDecimal originalAmount, Instrument instrument, + BigDecimal price, Date timestamp, String id, String orderId, + BigDecimal feeAmount, Currency feeCurrency, + String orderUserReference, Role role) { + super(type, originalAmount, instrument, price, timestamp, id, orderId, feeAmount, feeCurrency, + orderUserReference); + this.role = role; + } + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/trade/GateioUserTradeRaw.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/trade/GateioUserTradeRaw.java new file mode 100644 index 00000000000..e0a67fd79e2 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/trade/GateioUserTradeRaw.java @@ -0,0 +1,74 @@ +package org.knowm.xchange.gateio.dto.trade; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.gateio.config.converter.DoubleToInstantConverter; +import org.knowm.xchange.gateio.config.converter.OrderTypeToStringConverter; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyConverter; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyPairConverter; +import org.knowm.xchange.gateio.config.converter.StringToOrderTypeConverter; +import org.knowm.xchange.gateio.config.converter.TimestampSecondsToInstantConverter; + +@Data +@Builder +@Jacksonized +public class GateioUserTradeRaw { + + @JsonProperty("id") + Long id; + + @JsonProperty("create_time") + @JsonDeserialize(converter = TimestampSecondsToInstantConverter.class) + Instant time; + + @JsonProperty("create_time_ms") + @JsonDeserialize(converter = DoubleToInstantConverter.class) + Instant timeMs; + + @JsonProperty("currency_pair") + @JsonDeserialize(converter = StringToCurrencyPairConverter.class) + CurrencyPair currencyPair; + + @JsonProperty("side") + @JsonDeserialize(converter = StringToOrderTypeConverter.class) + @JsonSerialize(converter = OrderTypeToStringConverter.class) + OrderType side; + + @JsonProperty("role") + Role role; + + @JsonProperty("amount") + BigDecimal amount; + + @JsonProperty("price") + BigDecimal price; + + @JsonProperty("order_id") + Long orderId; + + @JsonProperty("fee") + BigDecimal fee; + + @JsonProperty("fee_currency") + @JsonDeserialize(converter = StringToCurrencyConverter.class) + Currency feeCurrency; + + @JsonProperty("point_fee") + BigDecimal pointFee; + + @JsonProperty("gt_fee") + BigDecimal gtFee; + + @JsonProperty("amend_text") + String remark; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/trade/Role.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/trade/Role.java new file mode 100644 index 00000000000..db867b5f499 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/dto/trade/Role.java @@ -0,0 +1,17 @@ +package org.knowm.xchange.gateio.dto.trade; + +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum Role { + TAKER("taker"), + MAKER("maker"); + + @JsonValue + private final String value; + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioAccountService.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioAccountService.java new file mode 100644 index 00000000000..9b79d7b0dff --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioAccountService.java @@ -0,0 +1,86 @@ +package org.knowm.xchange.gateio.service; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.commons.lang3.Validate; +import org.knowm.xchange.dto.account.AccountInfo; +import org.knowm.xchange.dto.account.Balance; +import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.Wallet; +import org.knowm.xchange.gateio.GateioAdapters; +import org.knowm.xchange.gateio.GateioErrorAdapter; +import org.knowm.xchange.gateio.GateioExchange; +import org.knowm.xchange.gateio.dto.GateioException; +import org.knowm.xchange.gateio.dto.account.GateioCurrencyBalance; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawalRecord; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawalRequest; +import org.knowm.xchange.gateio.service.params.GateioWithdrawFundsParams; +import org.knowm.xchange.service.account.AccountService; +import org.knowm.xchange.service.trade.params.TradeHistoryParams; +import org.knowm.xchange.service.trade.params.WithdrawFundsParams; + +public class GateioAccountService extends GateioAccountServiceRaw implements AccountService { + + public GateioAccountService(GateioExchange exchange) { + super(exchange); + } + + + @Override + public AccountInfo getAccountInfo() throws IOException { + + try { + List spotBalances = getSpotBalances(null); + + List balances = spotBalances.stream() + .map(balance -> new Balance.Builder() + .currency(balance.getCurrency()) + .available(balance.getAvailable()) + .frozen(balance.getLocked()) + .build()) + .collect(Collectors.toList()); + + Wallet wallet = Wallet.Builder + .from(balances) + .id("spot") + .build(); + + return new AccountInfo(wallet); + + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + + @Override + public String withdrawFunds(WithdrawFundsParams params) throws IOException { + Validate.isInstanceOf(GateioWithdrawFundsParams.class, params); + GateioWithdrawFundsParams p = (GateioWithdrawFundsParams) params; + + GateioWithdrawalRequest gateioWithdrawalRequest = GateioAdapters.toGateioWithdrawalRequest(p); + + try { + GateioWithdrawalRecord gateioWithdrawalRecord = withdraw(gateioWithdrawalRequest); + return gateioWithdrawalRecord.getId(); + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + + @Override + public List getFundingHistory(TradeHistoryParams params) throws IOException { + try { + return getAccountBookRecords(params).stream() + .map(GateioAdapters::toFundingRecords) + .collect(Collectors.toList()); + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioAccountServiceRaw.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioAccountServiceRaw.java new file mode 100644 index 00000000000..9f5bddfb8f6 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioAccountServiceRaw.java @@ -0,0 +1,123 @@ +package org.knowm.xchange.gateio.service; + +import java.io.IOException; +import java.util.List; +import org.apache.commons.lang3.Validate; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.gateio.GateioErrorAdapter; +import org.knowm.xchange.gateio.GateioExchange; +import org.knowm.xchange.gateio.dto.GateioException; +import org.knowm.xchange.gateio.dto.account.GateioAccountBookRecord; +import org.knowm.xchange.gateio.dto.account.GateioAddressRecord; +import org.knowm.xchange.gateio.dto.account.GateioCurrencyBalance; +import org.knowm.xchange.gateio.dto.account.GateioDepositAddress; +import org.knowm.xchange.gateio.dto.account.GateioDepositRecord; +import org.knowm.xchange.gateio.dto.account.GateioSubAccountTransfer; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawStatus; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawalRecord; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawalRequest; +import org.knowm.xchange.gateio.dto.account.params.GateioSubAccountTransfersParams; +import org.knowm.xchange.gateio.service.params.GateioDepositsParams; +import org.knowm.xchange.gateio.service.params.GateioFundingHistoryParams; +import org.knowm.xchange.gateio.service.params.GateioWithdrawalsParams; +import org.knowm.xchange.service.trade.params.TradeHistoryParamPaging; +import org.knowm.xchange.service.trade.params.TradeHistoryParams; +import org.knowm.xchange.service.trade.params.TradeHistoryParamsTimeSpan; + +public class GateioAccountServiceRaw extends GateioBaseService { + + public GateioAccountServiceRaw(GateioExchange exchange) { + super(exchange); + } + + public GateioDepositAddress getDepositAddress(Currency currency) throws IOException { + String currencyCode = currency == null ? null : currency.getCurrencyCode(); + + try { + return gateioV4Authenticated.getDepositAddress(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, currencyCode); + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + + } + + + public List getWithdrawStatus(Currency currency) throws IOException { + String currencyCode = currency == null ? null : currency.getCurrencyCode(); + + try { + return gateioV4Authenticated.getWithdrawStatus(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, currencyCode); + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + + } + + + public List getSpotBalances(Currency currency) throws IOException { + String currencyCode = currency == null ? null : currency.getCurrencyCode(); + return gateioV4Authenticated.getSpotAccounts(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, currencyCode); + } + + + public List getWithdrawals(GateioWithdrawalsParams params) throws IOException { + String currency = params.getCurrency() != null ? params.getCurrency().toString() : null; + Long from = params.getStartTime() != null ? params.getStartTime().getEpochSecond() : null; + Long to = params.getEndTime() != null ? params.getEndTime().getEpochSecond() : null; + return gateioV4Authenticated.getWithdrawals(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, + currency, from, to, params.getPageLength(), params.getZeroBasedPageNumber()); + } + + + public List getDeposits(GateioDepositsParams params) throws IOException { + String currency = params.getCurrency() != null ? params.getCurrency().toString() : null; + Long from = params.getStartTime() != null ? params.getStartTime().getEpochSecond() : null; + Long to = params.getEndTime() != null ? params.getEndTime().getEpochSecond() : null; + return gateioV4Authenticated.getDeposits(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, + currency, from, to, params.getPageLength(), params.getZeroBasedPageNumber()); + } + + + public GateioWithdrawalRecord withdraw(GateioWithdrawalRequest gateioWithdrawalRequest) throws IOException { + return gateioV4Authenticated.withdraw(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, gateioWithdrawalRequest); + } + + + public List getSavedAddresses(Currency currency) throws IOException { + Validate.notNull(currency); + return gateioV4Authenticated.getSavedAddresses(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, currency.getCurrencyCode()); + } + + + public List getAccountBookRecords(TradeHistoryParams params) throws IOException { + // get arguments + Currency currency = params instanceof GateioFundingHistoryParams ? ((GateioFundingHistoryParams) params).getCurrency() : null; + String currencyCode = currency != null ? currency.toString() : null; + String type = params instanceof GateioFundingHistoryParams ? ((GateioFundingHistoryParams) params).getType() : null; + Integer pageLength = params instanceof TradeHistoryParamPaging ? ((TradeHistoryParamPaging) params).getPageLength() : null; + Integer pageNumber = params instanceof TradeHistoryParamPaging ? ((TradeHistoryParamPaging) params).getPageNumber() : null; + Long from = null; + Long to = null; + if (params instanceof TradeHistoryParamsTimeSpan) { + TradeHistoryParamsTimeSpan paramsTimeSpan = ((TradeHistoryParamsTimeSpan) params); + from = paramsTimeSpan.getStartTime() != null ? paramsTimeSpan.getStartTime().getTime() / 1000 : null; + to = paramsTimeSpan.getEndTime() != null ? paramsTimeSpan.getEndTime().getTime() / 1000 : null; + } + + return gateioV4Authenticated.getAccountBookRecords(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, + currencyCode, from, to, pageLength, pageNumber, type); + } + + + public List getSubAccountTransfers(GateioSubAccountTransfersParams params) throws IOException { + Long from = params.getStartTime() != null ? params.getStartTime().getEpochSecond() : null; + Long to = params.getEndTime() != null ? params.getEndTime().getEpochSecond() : null; + + return gateioV4Authenticated.getSubAccountTransfers(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, + params.getSubAccountId(), from, to, params.getPageLength(), params.getZeroBasedPageNumber()); + } + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioBaseService.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioBaseService.java new file mode 100644 index 00000000000..9be973588ef --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioBaseService.java @@ -0,0 +1,43 @@ +package org.knowm.xchange.gateio.service; + +import lombok.SneakyThrows; +import org.knowm.xchange.client.ExchangeRestProxyBuilder; +import org.knowm.xchange.gateio.Gateio; +import org.knowm.xchange.gateio.GateioExchange; +import org.knowm.xchange.gateio.GateioV4Authenticated; +import org.knowm.xchange.gateio.config.Config; +import org.knowm.xchange.gateio.config.GateioJacksonObjectMapperFactory; +import org.knowm.xchange.service.BaseExchangeService; +import org.knowm.xchange.service.BaseService; +import si.mazi.rescu.ParamsDigest; + +public class GateioBaseService extends BaseExchangeService implements BaseService { + + protected final String apiKey; + protected final Gateio gateio; + protected final GateioV4Authenticated gateioV4Authenticated; + protected final ParamsDigest gateioV4ParamsDigest; + + + @SneakyThrows + public GateioBaseService(GateioExchange exchange) { + super(exchange); + + gateio = ExchangeRestProxyBuilder + .forInterface(Gateio.class, exchange.getExchangeSpecification()) + .clientConfigCustomizer(clientConfig -> clientConfig.setJacksonObjectMapperFactory(new GateioJacksonObjectMapperFactory())) + .restProxyFactory(Config.getInstance().getRestProxyFactoryClass().getDeclaredConstructor().newInstance()) + .build(); + apiKey = exchange.getExchangeSpecification().getApiKey(); + + gateioV4Authenticated = ExchangeRestProxyBuilder + .forInterface(GateioV4Authenticated.class, exchange.getExchangeSpecification()) + .clientConfigCustomizer(clientConfig -> clientConfig.setJacksonObjectMapperFactory(new GateioJacksonObjectMapperFactory())) + .restProxyFactory(Config.getInstance().getRestProxyFactoryClass().getDeclaredConstructor().newInstance()) + .build(); + + gateioV4ParamsDigest = + GateioV4Digest.createInstance(exchange.getExchangeSpecification().getSecretKey()); + } + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioMarketDataService.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioMarketDataService.java new file mode 100644 index 00000000000..70a785c6973 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioMarketDataService.java @@ -0,0 +1,127 @@ +package org.knowm.xchange.gateio.service; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.marketdata.OrderBook; +import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.dto.meta.InstrumentMetaData; +import org.knowm.xchange.gateio.GateioAdapters; +import org.knowm.xchange.gateio.GateioErrorAdapter; +import org.knowm.xchange.gateio.GateioExchange; +import org.knowm.xchange.gateio.dto.GateioException; +import org.knowm.xchange.gateio.dto.marketdata.GateioCurrencyInfo; +import org.knowm.xchange.gateio.dto.marketdata.GateioCurrencyPairDetails; +import org.knowm.xchange.gateio.dto.marketdata.GateioOrderBook; +import org.knowm.xchange.gateio.dto.marketdata.GateioTicker; +import org.knowm.xchange.instrument.Instrument; +import org.knowm.xchange.service.marketdata.MarketDataService; +import org.knowm.xchange.service.marketdata.params.Params; + +public class GateioMarketDataService extends GateioMarketDataServiceRaw implements MarketDataService { + + public GateioMarketDataService(GateioExchange exchange) { + super(exchange); + } + + @Override + public Ticker getTicker(CurrencyPair currencyPair, Object... args) throws IOException { + return getTicker((Instrument) currencyPair, args); + } + + + @Override + public Ticker getTicker(Instrument instrument, Object... args) throws IOException { + Validate.notNull(instrument); + try { + List tickers = getGateioTickers(instrument); + Validate.validState(tickers.size() == 1); + + return GateioAdapters.toTicker(tickers.get(0)); + } catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + + @Override + public List getTickers(Params params) throws IOException { + try { + List tickers = getGateioTickers(null); + + return tickers.stream() + .map(GateioAdapters::toTicker) + .collect(Collectors.toList()); + } catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + @Override + public OrderBook getOrderBook(CurrencyPair currencyPair, Object... args) throws IOException { + return getOrderBook((Instrument) currencyPair, args); + } + + @Override + public OrderBook getOrderBook(Instrument instrument, Object... args) throws IOException { + try { + GateioOrderBook gateioOrderBook = getGateioOrderBook(instrument); + return GateioAdapters.toOrderBook(gateioOrderBook, instrument); + } catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + + public List getCurrencies() throws IOException { + try { + List currencyInfos = getGateioCurrencyInfos(); + return currencyInfos.stream() + .filter(gateioCurrencyInfo -> !gateioCurrencyInfo.getDelisted()) + .map(o -> StringUtils.removeEnd(o.getCurrencyWithChain(), "_" + o.getChain())) + .distinct() + .map(Currency::getInstance) + .collect(Collectors.toList()); + } catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + + public List getCurrencyPairs() throws IOException { + try { + List metadata = getCurrencyPairDetails(); + + return metadata.stream() + .filter(details -> "tradable".equals(details.getTradeStatus())) + .map(details -> new CurrencyPair(details.getAsset(), details.getQuote())) + .collect(Collectors.toList()); + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + + public Map getMetaDataByInstrument() throws IOException { + try { + List metadata = getCurrencyPairDetails(); + + return metadata.stream() + .collect(Collectors.toMap( + gateioCurrencyPairDetails -> new CurrencyPair(gateioCurrencyPairDetails.getAsset(), gateioCurrencyPairDetails.getQuote()), + GateioAdapters::toInstrumentMetaData + )); + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioMarketDataServiceRaw.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioMarketDataServiceRaw.java new file mode 100644 index 00000000000..fbf38b5f7ee --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioMarketDataServiceRaw.java @@ -0,0 +1,52 @@ +package org.knowm.xchange.gateio.service; + +import java.io.IOException; +import java.util.List; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.gateio.GateioAdapters; +import org.knowm.xchange.gateio.GateioExchange; +import org.knowm.xchange.gateio.dto.marketdata.GateioCurrencyChain; +import org.knowm.xchange.gateio.dto.marketdata.GateioCurrencyInfo; +import org.knowm.xchange.gateio.dto.marketdata.GateioCurrencyPairDetails; +import org.knowm.xchange.gateio.dto.marketdata.GateioOrderBook; +import org.knowm.xchange.gateio.dto.marketdata.GateioTicker; +import org.knowm.xchange.instrument.Instrument; + +public class GateioMarketDataServiceRaw extends GateioBaseService { + + public GateioMarketDataServiceRaw(GateioExchange exchange) { + super(exchange); + } + + + public List getGateioTickers(Instrument instrument) throws IOException { + return gateio.getTickers(GateioAdapters.toString(instrument)); + } + + + public List getGateioCurrencyInfos() throws IOException { + return gateio.getCurrencies(); + } + + + public GateioOrderBook getGateioOrderBook(Instrument instrument) throws IOException { + return gateio.getOrderBook(GateioAdapters.toString(instrument), false); + } + + + public List getCurrencyChains(Currency currency) throws IOException { + return gateio.getCurrencyChains(currency.getCurrencyCode()); + } + + + public List getCurrencyPairDetails() throws IOException { + return gateio.getCurrencyPairDetails(); + } + + + public GateioCurrencyPairDetails getCurrencyPairDetails(Instrument instrument) throws IOException { + return gateio.getCurrencyPairDetails(GateioAdapters.toString(instrument)); + } + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioTradeService.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioTradeService.java new file mode 100644 index 00000000000..44e55432b64 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioTradeService.java @@ -0,0 +1,129 @@ +package org.knowm.xchange.gateio.service; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.commons.lang3.Validate; +import org.knowm.xchange.dto.Order; +import org.knowm.xchange.dto.Order.OrderStatus; +import org.knowm.xchange.dto.marketdata.Trades.TradeSortType; +import org.knowm.xchange.dto.trade.LimitOrder; +import org.knowm.xchange.dto.trade.MarketOrder; +import org.knowm.xchange.dto.trade.UserTrade; +import org.knowm.xchange.dto.trade.UserTrades; +import org.knowm.xchange.gateio.GateioAdapters; +import org.knowm.xchange.gateio.GateioErrorAdapter; +import org.knowm.xchange.gateio.GateioExchange; +import org.knowm.xchange.gateio.dto.GateioException; +import org.knowm.xchange.gateio.dto.account.GateioOrder; +import org.knowm.xchange.gateio.service.params.GateioTradeHistoryParams; +import org.knowm.xchange.instrument.Instrument; +import org.knowm.xchange.service.trade.TradeService; +import org.knowm.xchange.service.trade.params.CancelOrderParams; +import org.knowm.xchange.service.trade.params.DefaultCancelOrderByInstrumentAndIdParams; +import org.knowm.xchange.service.trade.params.TradeHistoryParams; +import org.knowm.xchange.service.trade.params.orders.OrderQueryParamInstrument; +import org.knowm.xchange.service.trade.params.orders.OrderQueryParams; + +public class GateioTradeService extends GateioTradeServiceRaw implements TradeService { + + public GateioTradeService(GateioExchange exchange) { + super(exchange); + } + + + @Override + public String placeMarketOrder(MarketOrder marketOrder) throws IOException { + try { + GateioOrder order = createOrder(GateioAdapters.toGateioOrder(marketOrder)); + return order.getId(); + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + + @Override + public String placeLimitOrder(LimitOrder limitOrder) throws IOException { + try { + GateioOrder order = createOrder(GateioAdapters.toGateioOrder(limitOrder)); + return order.getId(); + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + + @Override + public Collection getOrder(OrderQueryParams... orderQueryParams) throws IOException { + // todo: implement getting of several orders + Validate.validState(orderQueryParams.length == 1); + Validate.isInstanceOf(OrderQueryParamInstrument.class, orderQueryParams[0]); + + OrderQueryParamInstrument params = (OrderQueryParamInstrument) orderQueryParams[0]; + + try { + GateioOrder gateioOrder = getOrder(params.getOrderId(), params.getInstrument()); + return Collections.singletonList(GateioAdapters.toOrder(gateioOrder)); + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + + public Order cancelOrder(String orderId, Instrument instrument) throws IOException { + try { + GateioOrder gateioOrder = cancelOrderRaw(orderId, instrument); + return GateioAdapters.toOrder(gateioOrder); + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + + @Override + public boolean cancelOrder(CancelOrderParams orderParams) throws IOException { + Validate.isInstanceOf(DefaultCancelOrderByInstrumentAndIdParams.class, orderParams); + DefaultCancelOrderByInstrumentAndIdParams params = (DefaultCancelOrderByInstrumentAndIdParams) orderParams; + + try { + Order order = cancelOrder(params.getOrderId(), params.getInstrument()); + return order.getStatus() == OrderStatus.CANCELED; + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + @Override + public UserTrades getTradeHistory(TradeHistoryParams params) throws IOException { + try { + List userTradeList = getGateioUserTrades(params).stream() + .map(GateioAdapters::toUserTrade) + .collect(Collectors.toList()); + return new UserTrades(userTradeList, TradeSortType.SortByID); + } + catch (GateioException e) { + throw GateioErrorAdapter.adapt(e); + } + } + + @Override + public Class[] getRequiredCancelOrderParamClasses() { + return new Class[] {DefaultCancelOrderByInstrumentAndIdParams.class}; + } + + + @Override + public TradeHistoryParams createTradeHistoryParams() { + return GateioTradeHistoryParams.builder().build(); + } + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioTradeServiceRaw.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioTradeServiceRaw.java new file mode 100644 index 00000000000..94e516240d5 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioTradeServiceRaw.java @@ -0,0 +1,100 @@ +package org.knowm.xchange.gateio.service; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.Validate; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order.OrderStatus; +import org.knowm.xchange.gateio.GateioAdapters; +import org.knowm.xchange.gateio.GateioExchange; +import org.knowm.xchange.gateio.dto.account.GateioOrder; +import org.knowm.xchange.gateio.dto.trade.GateioUserTradeRaw; +import org.knowm.xchange.instrument.Instrument; +import org.knowm.xchange.service.trade.params.CurrencyPairParam; +import org.knowm.xchange.service.trade.params.TradeHistoryParamCurrencyPair; +import org.knowm.xchange.service.trade.params.TradeHistoryParamPaging; +import org.knowm.xchange.service.trade.params.TradeHistoryParamTransactionId; +import org.knowm.xchange.service.trade.params.TradeHistoryParams; +import org.knowm.xchange.service.trade.params.TradeHistoryParamsTimeSpan; + +public class GateioTradeServiceRaw extends GateioBaseService { + + public GateioTradeServiceRaw(GateioExchange exchange) { + super(exchange); + } + + + public List listOrders(Instrument instrument, OrderStatus orderStatus) throws IOException { + // validate arguments + Validate.notNull(orderStatus); + Set allowedOrderStatuses = EnumSet.of(OrderStatus.OPEN, OrderStatus.CLOSED); + Validate.validState(allowedOrderStatuses.contains(orderStatus), "Allowed order statuses are: {}", allowedOrderStatuses); + Validate.notNull(instrument); + + return gateioV4Authenticated.listOrders(apiKey, exchange.getNonceFactory(), + gateioV4ParamsDigest, GateioAdapters.toString(instrument), GateioAdapters.toString(orderStatus) + ); + + } + + + public List getGateioUserTrades(TradeHistoryParams params) throws IOException { + // get arguments + CurrencyPair currencyPair = params instanceof TradeHistoryParamCurrencyPair ? ((CurrencyPairParam) params).getCurrencyPair() : null; + Integer pageLength = params instanceof TradeHistoryParamPaging ? ((TradeHistoryParamPaging) params).getPageLength() : null; + Integer pageNumber = params instanceof TradeHistoryParamPaging ? ((TradeHistoryParamPaging) params).getPageNumber() : null; + String orderId = params instanceof TradeHistoryParamTransactionId ? ((TradeHistoryParamTransactionId) params).getTransactionId() : null; + Long from = null; + Long to = null; + if (params instanceof TradeHistoryParamsTimeSpan) { + TradeHistoryParamsTimeSpan paramsTimeSpan = ((TradeHistoryParamsTimeSpan) params); + from = paramsTimeSpan.getStartTime() != null ? paramsTimeSpan.getStartTime().getTime() / 1000 : null; + to = paramsTimeSpan.getEndTime() != null ? paramsTimeSpan.getEndTime().getTime() / 1000 : null; + } + + // if no pagination is given, get all records in chunks + if (ObjectUtils.allNull(pageLength, pageNumber)) { + List result = new ArrayList<>(); + List chunk; + Integer currentPageNumber = 1; + + do { + chunk = gateioV4Authenticated.getTradingHistory(apiKey, exchange.getNonceFactory(), + gateioV4ParamsDigest, GateioAdapters.toString(currencyPair), + 1000, currentPageNumber, orderId, null, from, to); + currentPageNumber++; + result.addAll(chunk); + } + while (!chunk.isEmpty()); + + return result; + } + + return gateioV4Authenticated.getTradingHistory(apiKey, exchange.getNonceFactory(), + gateioV4ParamsDigest, GateioAdapters.toString(currencyPair), + pageLength, pageNumber, orderId, null, from, to); + } + + + public GateioOrder createOrder(GateioOrder gateioOrder) throws IOException { + return gateioV4Authenticated.createOrder(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, gateioOrder); + } + + + public GateioOrder getOrder(String orderId, Instrument instrument) throws IOException { + return gateioV4Authenticated.getOrder(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, + orderId, GateioAdapters.toString(instrument)); + } + + + public GateioOrder cancelOrderRaw(String orderId, Instrument instrument) throws IOException { + return gateioV4Authenticated.cancelOrder(apiKey, exchange.getNonceFactory(), gateioV4ParamsDigest, + orderId, GateioAdapters.toString(instrument)); + } + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioV4Digest.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioV4Digest.java new file mode 100644 index 00000000000..3625ccc6ff1 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/GateioV4Digest.java @@ -0,0 +1,42 @@ +package org.knowm.xchange.gateio.service; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import javax.crypto.Mac; +import lombok.SneakyThrows; +import org.knowm.xchange.service.BaseParamsDigest; +import org.knowm.xchange.utils.DigestUtils; +import si.mazi.rescu.RestInvocation; + +public final class GateioV4Digest extends BaseParamsDigest { + + private GateioV4Digest(String secretKeyBase64) { + + super(secretKeyBase64, HMAC_SHA_512); + } + + public static GateioV4Digest createInstance(String secretKeyBase64) { + return secretKeyBase64 == null ? null : new GateioV4Digest(secretKeyBase64); + } + + @SneakyThrows + @Override + public String digestParams(RestInvocation restInvocation) { + String method = restInvocation.getHttpMethod(); + String path = restInvocation.getPath(); + + String query = restInvocation.getQueryString(); + String body = restInvocation.getRequestBody(); + MessageDigest md = MessageDigest.getInstance("SHA-512"); + String hexedHashedBody = DigestUtils.bytesToHex(md.digest(body.getBytes(StandardCharsets.UTF_8))); + + String timestamp = restInvocation.getHttpHeadersFromParams().get("Timestamp"); + + String payloadToSign = String.format("%s\n/%s\n%s\n%s\n%s", method, path, query, hexedHashedBody, timestamp); + + Mac mac = getMac(); + mac.update(payloadToSign.getBytes(StandardCharsets.UTF_8)); + + return DigestUtils.bytesToHex(mac.doFinal()); + } +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioDepositsParams.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioDepositsParams.java new file mode 100644 index 00000000000..88678aecc4b --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioDepositsParams.java @@ -0,0 +1,26 @@ +package org.knowm.xchange.gateio.service.params; + +import java.time.Instant; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.knowm.xchange.currency.Currency; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class GateioDepositsParams { + + private Currency currency; + + private Integer pageLength; + + private Integer zeroBasedPageNumber; + + private Instant startTime; + + private Instant endTime; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioFundingHistoryParams.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioFundingHistoryParams.java new file mode 100644 index 00000000000..8980c09239c --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioFundingHistoryParams.java @@ -0,0 +1,26 @@ +package org.knowm.xchange.gateio.service.params; + +import java.util.Date; +import lombok.Builder; +import lombok.Data; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.service.trade.params.TradeHistoryParamPaging; +import org.knowm.xchange.service.trade.params.TradeHistoryParamsTimeSpan; + +@Data +@Builder +public class GateioFundingHistoryParams implements TradeHistoryParamPaging, TradeHistoryParamsTimeSpan { + + private Currency currency; + + private Integer pageLength; + + private Integer pageNumber; + + private Date startTime; + + private Date endTime; + + private String type; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioTradeHistoryParams.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioTradeHistoryParams.java new file mode 100644 index 00000000000..d0f708c4b46 --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioTradeHistoryParams.java @@ -0,0 +1,30 @@ +package org.knowm.xchange.gateio.service.params; + +import java.util.Date; +import lombok.Data; +import lombok.experimental.SuperBuilder; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.service.trade.params.TradeHistoryParamCurrencyPair; +import org.knowm.xchange.service.trade.params.TradeHistoryParamPaging; +import org.knowm.xchange.service.trade.params.TradeHistoryParamTransactionId; +import org.knowm.xchange.service.trade.params.TradeHistoryParamsTimeSpan; + +@Data +@SuperBuilder +public class GateioTradeHistoryParams implements TradeHistoryParamCurrencyPair, + TradeHistoryParamPaging, TradeHistoryParamTransactionId, TradeHistoryParamsTimeSpan { + + private CurrencyPair currencyPair; + + private Integer pageLength; + + private Integer pageNumber; + + private String transactionId; + + private Date startTime; + + private Date endTime; + + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioWithdrawFundsParams.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioWithdrawFundsParams.java new file mode 100644 index 00000000000..1ebcb3b235a --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioWithdrawFundsParams.java @@ -0,0 +1,15 @@ +package org.knowm.xchange.gateio.service.params; + +import lombok.Value; +import lombok.experimental.SuperBuilder; +import org.knowm.xchange.service.trade.params.DefaultWithdrawFundsParams; + +@Value +@SuperBuilder +public class GateioWithdrawFundsParams extends DefaultWithdrawFundsParams { + + String clientRecordId; + + String chain; + +} diff --git a/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioWithdrawalsParams.java b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioWithdrawalsParams.java new file mode 100644 index 00000000000..ce906635abb --- /dev/null +++ b/xchange-gateio-v4/src/main/java/org/knowm/xchange/gateio/service/params/GateioWithdrawalsParams.java @@ -0,0 +1,26 @@ +package org.knowm.xchange.gateio.service.params; + +import java.time.Instant; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.knowm.xchange.currency.Currency; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class GateioWithdrawalsParams { + + private Currency currency; + + private Integer pageLength; + + private Integer zeroBasedPageNumber; + + private Instant startTime; + + private Instant endTime; + +} diff --git a/xchange-bankera/src/main/resources/bankera.json b/xchange-gateio-v4/src/main/resources/gateio.json similarity index 100% rename from xchange-bankera/src/main/resources/bankera.json rename to xchange-gateio-v4/src/main/resources/gateio.json diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/GateioExchangeTest.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/GateioExchangeTest.java new file mode 100644 index 00000000000..2f76b5e060c --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/GateioExchangeTest.java @@ -0,0 +1,28 @@ +package org.knowm.xchange.gateio; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.math.BigDecimal; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.meta.InstrumentMetaData; + +public class GateioExchangeTest extends GateioExchangeWiremock { + + @Test + void metadata_present() { + InstrumentMetaData expected = new InstrumentMetaData.Builder() + .tradingFee(new BigDecimal("0.2")) + .minimumAmount(new BigDecimal("0.0001")) + .counterMinimumAmount(BigDecimal.ONE) + .volumeScale(4) + .priceScale(1) + .marketOrderEnabled(false) + .build(); + + InstrumentMetaData actual = exchange.getExchangeMetaData().getInstruments().get(CurrencyPair.BTC_USDT); + + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/GateioExchangeWiremock.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/GateioExchangeWiremock.java new file mode 100644 index 00000000000..2ddf34b0d82 --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/GateioExchangeWiremock.java @@ -0,0 +1,62 @@ +package org.knowm.xchange.gateio; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.recording.RecordSpecBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.ExchangeSpecification; + +/** + * Sets up the wiremock for exchange + */ +public abstract class GateioExchangeWiremock { + + protected static GateioExchange exchange; + +// private static final boolean IS_RECORDING = true; + private static final boolean IS_RECORDING = false; + + private static WireMockServer wireMockServer; + + + @BeforeAll + public static void initExchange() { + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + + ExchangeSpecification exSpec = new ExchangeSpecification(GateioExchange.class); + exSpec.setSslUri("http://localhost:" + wireMockServer.port()); + exSpec.setApiKey("a"); + exSpec.setSecretKey("b"); + + + if (IS_RECORDING) { + // use default url and record the requests + wireMockServer.startRecording( + new RecordSpecBuilder() + .forTarget("https://api.gateio.ws") + .matchRequestBodyWithEqualToJson() + .extractTextBodiesOver(1L) + .chooseBodyMatchTypeAutomatically() + ); + + } + + exchange = (GateioExchange) ExchangeFactory.INSTANCE.createExchange(exSpec); + + } + + + @AfterAll + public static void stop() { + if (IS_RECORDING) { + wireMockServer.stopRecording(); + } + wireMockServer.stop(); + } + + +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyChainTest.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyChainTest.java new file mode 100644 index 00000000000..10e0f6506da --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyChainTest.java @@ -0,0 +1,28 @@ +package org.knowm.xchange.gateio.dto.marketdata; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class GateioCurrencyChainTest { + + @Test + void valid_isWithdrawEnabled() { + GateioCurrencyChain gateioCurrencyChain = GateioCurrencyChain.builder().build(); + assertThat(gateioCurrencyChain.isWithdrawEnabled()).isFalse(); + + gateioCurrencyChain.setDisabled(false); + gateioCurrencyChain.setWithdrawDisabled(false); + assertThat(gateioCurrencyChain.isWithdrawEnabled()).isTrue(); + } + + @Test + void valid_isDepositEnabled() { + GateioCurrencyChain gateioCurrencyChain = GateioCurrencyChain.builder().build(); + assertThat(gateioCurrencyChain.isDepositEnabled()).isFalse(); + + gateioCurrencyChain.setDisabled(false); + gateioCurrencyChain.setDepositDisabled(false); + assertThat(gateioCurrencyChain.isDepositEnabled()).isTrue(); + } +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyInfoTest.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyInfoTest.java new file mode 100644 index 00000000000..4dd47e5eb50 --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/dto/marketdata/GateioCurrencyInfoTest.java @@ -0,0 +1,27 @@ +package org.knowm.xchange.gateio.dto.marketdata; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class GateioCurrencyInfoTest { + + @Test + void valid_isWithdrawEnabled() { + GateioCurrencyInfo gateioCurrencyInfo = GateioCurrencyInfo.builder().build(); + assertThat(gateioCurrencyInfo.isWithdrawEnabled()).isFalse(); + + gateioCurrencyInfo.setWithdrawDisabled(false); + assertThat(gateioCurrencyInfo.isWithdrawEnabled()).isTrue(); + } + + + @Test + void valid_isDepositEnabled() { + GateioCurrencyInfo gateioCurrencyInfo = GateioCurrencyInfo.builder().build(); + assertThat(gateioCurrencyInfo.isDepositEnabled()).isFalse(); + + gateioCurrencyInfo.setDepositDisabled(false); + assertThat(gateioCurrencyInfo.isDepositEnabled()).isTrue(); + } +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioAccountServiceRawTest.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioAccountServiceRawTest.java new file mode 100644 index 00000000000..93e7c0c44bb --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioAccountServiceRawTest.java @@ -0,0 +1,244 @@ +package org.knowm.xchange.gateio.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.exceptions.ExchangeSecurityException; +import org.knowm.xchange.exceptions.InstrumentNotValidException; +import org.knowm.xchange.exceptions.InternalServerException; +import org.knowm.xchange.gateio.GateioExchangeWiremock; +import org.knowm.xchange.gateio.dto.account.GateioAddressRecord; +import org.knowm.xchange.gateio.dto.account.GateioDepositAddress; +import org.knowm.xchange.gateio.dto.account.GateioDepositAddress.MultichainAddress; +import org.knowm.xchange.gateio.dto.account.GateioDepositRecord; +import org.knowm.xchange.gateio.dto.account.GateioDepositRecord.Status; +import org.knowm.xchange.gateio.dto.account.GateioSubAccountTransfer; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawStatus; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawalRecord; +import org.knowm.xchange.gateio.dto.account.GateioWithdrawalRequest; +import org.knowm.xchange.gateio.dto.account.params.GateioSubAccountTransfersParams; +import org.knowm.xchange.gateio.service.params.GateioDepositsParams; +import org.knowm.xchange.gateio.service.params.GateioWithdrawalsParams; + +public class GateioAccountServiceRawTest extends GateioExchangeWiremock { + + GateioAccountServiceRaw gateioAccountServiceRaw = ((GateioAccountServiceRaw) exchange.getAccountService()); + + @Test + void getWithdrawStatus_valid() throws IOException { + Map expectedWithdrawFeeByChain = new HashMap<>(); + expectedWithdrawFeeByChain.put("ETH", new BigDecimal("0.93")); + expectedWithdrawFeeByChain.put("GTEVM", new BigDecimal("0.0049")); + GateioWithdrawStatus expected = GateioWithdrawStatus.builder() + .currency(Currency.getInstance("GT")) + .name("GateToken") + .nameCN("狗头") + .depositFee(BigDecimal.ZERO) + .withdrawPercent("0%") + .withdrawFee(new BigDecimal("0.02")) + .withdrawDailyLimit(new BigDecimal("500000")) + .withdrawDailyLimitLeft(new BigDecimal("499999")) + .minWithdrawAmount(new BigDecimal("0.12")) + .maxWithdrawAmount(new BigDecimal("499999")) + .withdrawFeeByChain(expectedWithdrawFeeByChain) + .build(); + + List status = gateioAccountServiceRaw.getWithdrawStatus(null); + + assertThat(status).hasSize(2); + GateioWithdrawStatus actualGt = status.get(0); + assertThat(actualGt).isEqualTo(expected); + assertThat(actualGt.getWithdrawRate()).isEqualTo(new BigDecimal("0.00")); + + } + + + @Test + void http_401_exception_mapped() { + assertThatExceptionOfType(ExchangeSecurityException.class) + .isThrownBy(() -> gateioAccountServiceRaw.getWithdrawStatus(Currency.getInstance("THROW_401"))); + assertThatExceptionOfType(ExchangeSecurityException.class) + .isThrownBy(() -> gateioAccountServiceRaw.getWithdrawStatus(Currency.getInstance("INVALID_KEY"))); + } + + + @Test + void http_403_exception_mapped() { + assertThatExceptionOfType(ExchangeSecurityException.class) + .isThrownBy(() -> gateioAccountServiceRaw.getWithdrawStatus(Currency.getInstance("RETURN-FORBIDDEN"))); + } + + + @Test + void http_500_exception_mapped() { + assertThatExceptionOfType(InternalServerException.class) + .isThrownBy(() -> gateioAccountServiceRaw.getWithdrawStatus(Currency.getInstance("INTERNAL-SERVER-ERROR"))); + } + + + @Test + void invalid_currency_exception_mapped() { + assertThatExceptionOfType(InstrumentNotValidException.class) + .isThrownBy(() -> gateioAccountServiceRaw.getWithdrawStatus(Currency.getInstance("invalid-currency"))); + } + + + @Test + void pending_deposit_address() throws IOException { + GateioDepositAddress a = gateioAccountServiceRaw.getDepositAddress(Currency.getInstance("ITA")); + + MultichainAddress expected = MultichainAddress.builder() + .chain("CHZ") + .failed(true) + .address("") + .memo("") + .memoType("") + .build(); + + assertThat(a.getMultichainAddresses()).hasSize(1); + assertThat(a.getMultichainAddresses().get(0)).isEqualTo(expected); + } + + + @Test + void valid_deposit_address() throws IOException { + GateioDepositAddress a = gateioAccountServiceRaw.getDepositAddress(Currency.getInstance("ABBC")); + + MultichainAddress expected = MultichainAddress.builder() + .chain("ABBC") + .failed(false) + .address("gateioioabbc") + .memo("00cceed3f9cb5069") + .memoType("Memo") + .build(); + + assertThat(a.getMultichainAddresses()).hasSize(1); + assertThat(a.getMultichainAddresses().get(0)).isEqualTo(expected); + } + + + @Test + void withdrawal_records() throws IOException { + List actual = gateioAccountServiceRaw.getWithdrawals(GateioWithdrawalsParams.builder().build()); + + GateioWithdrawalRecord expected = GateioWithdrawalRecord.builder() + .id("w35874123") + .currency(Currency.getInstance("LUFFY")) + .address("0x3dca2ae4d1d065220a731cf69f5a934914afc435") + .amount(new BigDecimal("1030645.8587")) + .fee(new BigDecimal("10000")) + .txId("0x8f72d42b016a2b7b543149e707ff37fadded2ff3ef6767bee30b6003330f604b") + .chain("ETH") + .createdAt(Instant.parse("2023-06-01T11:34:15Z")) + .status(GateioWithdrawalRecord.Status.DONE) + .clientRecordId("a") + .tag("b") + .build(); + + assertThat(actual).hasSize(1); + assertThat(actual).first().usingRecursiveComparison().isEqualTo(expected); + } + + + @Test + void deposit_records() throws IOException { + List actual = gateioAccountServiceRaw.getDeposits(GateioDepositsParams.builder() + .startTime(Instant.ofEpochSecond(1685833987)) + .endTime(Instant.ofEpochSecond(1688339587)) + .build()); + + GateioDepositRecord expected = GateioDepositRecord.builder() + .id("d95198946") + .currency(Currency.getInstance("OMN")) + .address("0x04A09F5712F527584BAC0C7be40Ae7946f9A9587") + .amount(new BigDecimal("2353")) + .txId("0x34a89edf400a85a9a6a2747ebc2074ef65895345b9b7400f164602085e9b4a2e") + .chain("OMN") + .createdAt(Instant.parse("2023-07-01T14:12:26Z")) + .status(Status.DONE) + .tag("") + .build(); + + assertThat(actual).hasSize(2); + assertThat(actual).first().usingRecursiveComparison().isEqualTo(expected); + } + + + @Test + void withdraw() throws IOException { + GateioWithdrawalRequest gateioWithdrawalRequest = GateioWithdrawalRequest.builder() + .clientRecordId("valid-withdrawal-id") + .address("6vLyxJ9dBziamyaw2vDcs9n2NwQdW1uk3aooJwrEscnA") + .tag("") + .chain("SOL") + .amount(BigDecimal.valueOf(3)) + .currency(Currency.USDT) + .build(); + + GateioWithdrawalRecord actual = gateioAccountServiceRaw.withdraw(gateioWithdrawalRequest); + + GateioWithdrawalRecord expected = GateioWithdrawalRecord.builder() + .id("w35980955") + .clientRecordId("valid-withdrawal-id") + .address("6vLyxJ9dBziamyaw2vDcs9n2NwQdW1uk3aooJwrEscnA ") + .tag("") + .chain("SOL") + .amount(BigDecimal.valueOf(3)) + .currency(Currency.USDT) + .status(GateioWithdrawalRecord.Status.REQUEST) + .build(); + + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + + } + + + @Test + void saved_addresses() throws IOException { + List actual = gateioAccountServiceRaw.getSavedAddresses(Currency.USDT); + + GateioAddressRecord expected = GateioAddressRecord.builder() + .address("6vLyxJ9dBziamyaw2vDcs9n2NwQdW1uk3aooJwrEscnA") + .tag("") + .chain("SOL") + .currency(Currency.USDT) + .name("stuff") + .verified(true) + .build(); + + assertThat(actual).hasSize(1); + assertThat(actual).first().usingRecursiveComparison().isEqualTo(expected); + } + + + @Test + void sub_account_transfers() throws IOException { + + List actual = gateioAccountServiceRaw.getSubAccountTransfers( + GateioSubAccountTransfersParams.builder().build()); + + GateioSubAccountTransfer expected = GateioSubAccountTransfer.builder() + .mainAccountId(10001) + .timestamp(Instant.ofEpochSecond(1592809000)) + .source("web") + .clientOrderId("da3ce7a088c8b0372b741419c7829033") + .currency(Currency.BTC) + .subAccountId(10002) + .direction("to") + .amount(BigDecimal.ONE) + .subAccountType("spot") + .build(); + + assertThat(actual).hasSize(1); + assertThat(actual).first().usingRecursiveComparison().isEqualTo(expected); + } + +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioAccountServiceTest.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioAccountServiceTest.java new file mode 100644 index 00000000000..1107887502e --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioAccountServiceTest.java @@ -0,0 +1,139 @@ +package org.knowm.xchange.gateio.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.dto.account.AccountInfo; +import org.knowm.xchange.dto.account.Balance; +import org.knowm.xchange.dto.account.FundingRecord; +import org.knowm.xchange.dto.account.FundingRecord.Type; +import org.knowm.xchange.exceptions.OrderAmountUnderMinimumException; +import org.knowm.xchange.exceptions.OrderNotValidException; +import org.knowm.xchange.exceptions.RateLimitExceededException; +import org.knowm.xchange.gateio.GateioExchangeWiremock; +import org.knowm.xchange.gateio.config.Config; +import org.knowm.xchange.gateio.service.params.GateioFundingHistoryParams; +import org.knowm.xchange.gateio.service.params.GateioWithdrawFundsParams; +import si.mazi.rescu.CustomRestProxyFactoryImpl; + +class GateioAccountServiceTest extends GateioExchangeWiremock { + + + static { + Config.getInstance().setRestProxyFactoryClass(CustomRestProxyFactoryImpl.class); + } + + GateioAccountService gateioAccountService = ((GateioAccountService) exchange.getAccountService()); + + + @Test + void getAccountInfo() throws IOException { + + AccountInfo accountInfo = gateioAccountService.getAccountInfo(); + + assertThat(accountInfo.getWallet("spot").balances()).hasSize(1); + + Balance usdtBalance = accountInfo.getWallet().getBalance(Currency.USDT); + assertThat(usdtBalance.getTotal()).isEqualTo(new BigDecimal("5.2")); + assertThat(usdtBalance.getAvailable()).isEqualTo(new BigDecimal("4.1")); + assertThat(usdtBalance.getFrozen()).isEqualTo(new BigDecimal("1.1")); + } + + @Test + void normal_withdraw() throws IOException { + GateioWithdrawFundsParams params = GateioWithdrawFundsParams.builder() + .clientRecordId("valid-withdrawal-id") + .address("6vLyxJ9dBziamyaw2vDcs9n2NwQdW1uk3aooJwrEscnA") + .addressTag("") + .chain("SOL") + .amount(BigDecimal.valueOf(3)) + .currency(Currency.USDT) + .build(); + + String withdrawalId = gateioAccountService.withdrawFunds(params); + assertThat(withdrawalId).isEqualTo("w35980955"); + } + + + @Test + void rate_limited_withdraw() { + GateioWithdrawFundsParams params = GateioWithdrawFundsParams.builder() + .clientRecordId("rate-limited-id") + .address("6vLyxJ9dBziamyaw2vDcs9n2NwQdW1uk3aooJwrEscnA") + .addressTag("") + .chain("SOL") + .amount(BigDecimal.valueOf(3)) + .currency(Currency.USDT) + .build(); + + assertThatExceptionOfType(RateLimitExceededException.class) + .isThrownBy(() -> gateioAccountService.withdrawFunds(params)); + } + + + @Test + void zero_amount_withdraw() { + GateioWithdrawFundsParams params = GateioWithdrawFundsParams.builder() + .clientRecordId("zero-amount-id") + .address("6vLyxJ9dBziamyaw2vDcs9n2NwQdW1uk3aooJwrEscnA") + .addressTag("") + .chain("SOL") + .amount(BigDecimal.ZERO) + .currency(Currency.USDT) + .build(); + + assertThatExceptionOfType(OrderAmountUnderMinimumException.class) + .isThrownBy(() -> gateioAccountService.withdrawFunds(params)); + } + + + @Test + void invalid_address_withdraw() { + GateioWithdrawFundsParams params = GateioWithdrawFundsParams.builder() + .clientRecordId("invalid-address-id") + .address("invalid-address") + .addressTag("") + .chain("SOL") + .amount(BigDecimal.ZERO) + .currency(Currency.USDT) + .build(); + + assertThatExceptionOfType(OrderNotValidException.class) + .isThrownBy(() -> gateioAccountService.withdrawFunds(params)); + } + + + @Test + void funding_history() throws IOException { + List actual = gateioAccountService.getFundingHistory(GateioFundingHistoryParams.builder() + .currency(Currency.USDT) + .startTime(Date.from(Instant.ofEpochSecond(1691447482))) + .endTime(Date.from(Instant.ofEpochSecond(1691533882))) + .pageLength(2) + .pageNumber(1) + .type("order_fee") + .build()); + + FundingRecord expected = new FundingRecord.Builder() + .setInternalId("40558668441") + .setDate(Date.from(Instant.ofEpochMilli(1691510538067L))) + .setCurrency(Currency.USDT) + .setBalance(new BigDecimal("16.00283141582979715942")) + .setType(Type.OTHER_OUTFLOW) + .setAmount(new BigDecimal("0.0113918056")) + .setDescription("order_fee") + .build(); + + assertThat(actual).hasSize(2); + assertThat(actual).first().usingRecursiveComparison().isEqualTo(expected); + } + + +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioBaseServiceTest.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioBaseServiceTest.java new file mode 100644 index 00000000000..e7b7fbb410d --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioBaseServiceTest.java @@ -0,0 +1,32 @@ +package org.knowm.xchange.gateio.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.lang.reflect.Proxy; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.gateio.GateioExchangeWiremock; +import org.knowm.xchange.gateio.config.Config; +import si.mazi.rescu.BodyLoggingRestInvocationHandler; +import si.mazi.rescu.CustomRestProxyFactoryImpl; + +/** + * Contains the example of overriding of RestProxyFactory for exchange for some specific logic + */ +class GateioBaseServiceTest extends GateioExchangeWiremock { + + // set custom proxy factory before creating the exchange + static { + Config.getInstance().setRestProxyFactoryClass(CustomRestProxyFactoryImpl.class); + } + + + @Disabled + @Test + void correct_proxy_factory() { + GateioBaseService service = ((GateioBaseService) exchange.getAccountService()); + assertThat(Proxy.getInvocationHandler(service.gateio) instanceof BodyLoggingRestInvocationHandler).isTrue(); + } + + +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioMarketDataServiceRawTest.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioMarketDataServiceRawTest.java new file mode 100644 index 00000000000..b2b00fc470f --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioMarketDataServiceRawTest.java @@ -0,0 +1,153 @@ +package org.knowm.xchange.gateio.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.gateio.GateioExchangeWiremock; +import org.knowm.xchange.gateio.dto.marketdata.GateioCurrencyChain; +import org.knowm.xchange.gateio.dto.marketdata.GateioCurrencyInfo; +import org.knowm.xchange.gateio.dto.marketdata.GateioCurrencyPairDetails; +import org.knowm.xchange.gateio.dto.marketdata.GateioOrderBook; +import org.knowm.xchange.gateio.dto.marketdata.GateioOrderBook.PriceSizeEntry; + +public class GateioMarketDataServiceRawTest extends GateioExchangeWiremock { + + GateioMarketDataServiceRaw gateioMarketDataServiceRaw = (GateioMarketDataServiceRaw) exchange.getMarketDataService(); + + + @Test + public void getCurrencies_valid() throws IOException { + List actual = gateioMarketDataServiceRaw.getGateioCurrencyInfos(); + + assertThat(actual).hasSize(5); + + GateioCurrencyInfo actualBtc = actual.get(0); + + GateioCurrencyInfo expectedBtc = GateioCurrencyInfo.builder() + .currencyWithChain("BTC") + .delisted(false) + .withdrawDisabled(false) + .withdrawDelayed(false) + .depositDisabled(false) + .tradeDisabled(false) + .chain("BTC") + .build(); + + assertThat(actualBtc).usingRecursiveComparison().isEqualTo(expectedBtc); + } + + + @Test + public void getGateioOrderBook_valid() throws IOException { + List expectedAsks = new ArrayList<>(); + expectedAsks.add(PriceSizeEntry.builder() + .price(new BigDecimal("200")) + .size(BigDecimal.ONE) + .build()); + expectedAsks.add(PriceSizeEntry.builder() + .price(new BigDecimal("250")) + .size(BigDecimal.TEN) + .build()); + + List expectedBids = new ArrayList<>(); + expectedBids.add(PriceSizeEntry.builder() + .price(new BigDecimal("150")) + .size(BigDecimal.ONE) + .build()); + expectedBids.add(PriceSizeEntry.builder() + .price(new BigDecimal("100")) + .size(BigDecimal.TEN) + .build()); + + GateioOrderBook expected = GateioOrderBook.builder() + .generatedAt(Instant.parse("2023-05-14T22:10:10.493Z")) + .updatedAt(Instant.parse("2023-05-14T22:10:10.263Z")) + .asks(expectedAsks) + .bids(expectedBids) + .build(); + + GateioOrderBook actual = gateioMarketDataServiceRaw.getGateioOrderBook(CurrencyPair.BTC_USDT); + + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + + @Test + public void getCurrencyChains_valid_result() throws IOException { + List expected = Arrays.asList( + GateioCurrencyChain.builder() + .chain("BTC") + .chainNameCN("比特币 BRC20/Ordinals") + .chainNameEN("Bitcoin BRC20/Ordinals") + .disabled(false) + .depositDisabled(false) + .withdrawDisabled(false) + .contractAddress("") + .build(), + GateioCurrencyChain.builder() + .chain("HT") + .chainNameCN("Heco") + .chainNameEN("Heco") + .disabled(true) + .depositDisabled(true) + .withdrawDisabled(true) + .contractAddress("0x66a79d23e58475d2738179ca52cd0b41d73f0bea") + .build() + ); + + List actual = gateioMarketDataServiceRaw.getCurrencyChains(Currency.BTC); + + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + + @Test + void valid_currencypairs_details() throws IOException { + List details = gateioMarketDataServiceRaw.getCurrencyPairDetails(); + assertThat(details).hasSize(3); + GateioCurrencyPairDetails expectedChz = GateioCurrencyPairDetails.builder() + .id("CHZ_USDT") + .asset("CHZ") + .quote("USDT") + .fee(new BigDecimal("0.2")) + .minQuoteAmount(BigDecimal.ONE) + .assetScale(2) + .quoteScale(5) + .tradeStatus("tradable") + .startOfSells(Instant.parse("2020-12-24T04:00:00.000Z")) + .startOfBuys(Instant.parse("2020-12-24T06:00:00.000Z")) + .build(); + + GateioCurrencyPairDetails actualChz = details.get(1); + + assertThat(actualChz).isEqualTo(expectedChz); + } + + + @Test + void valid_single_currencypair_details() throws IOException { + GateioCurrencyPairDetails actualChz = gateioMarketDataServiceRaw.getCurrencyPairDetails(new CurrencyPair("CHZ/USDT")); + GateioCurrencyPairDetails expectedChz = GateioCurrencyPairDetails.builder() + .id("CHZ_USDT") + .asset("CHZ") + .quote("USDT") + .fee(new BigDecimal("0.2")) + .minQuoteAmount(BigDecimal.ONE) + .assetScale(2) + .quoteScale(5) + .tradeStatus("tradable") + .startOfSells(Instant.parse("2020-12-24T04:00:00.000Z")) + .startOfBuys(Instant.parse("2020-12-24T06:00:00.000Z")) + .build(); + + assertThat(actualChz).isEqualTo(expectedChz); + } +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioMarketDataServiceTest.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioMarketDataServiceTest.java new file mode 100644 index 00000000000..5a5f5fbd8f4 --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioMarketDataServiceTest.java @@ -0,0 +1,103 @@ +package org.knowm.xchange.gateio.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.dto.marketdata.OrderBook; +import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.dto.trade.LimitOrder; +import org.knowm.xchange.gateio.GateioExchangeWiremock; + +public class GateioMarketDataServiceTest extends GateioExchangeWiremock { + + GateioMarketDataService gateioMarketDataService = (GateioMarketDataService) exchange.getMarketDataService(); + + + @Test + void getOrderBook_valid() throws IOException { + OrderBook actual = gateioMarketDataService.getOrderBook(CurrencyPair.BTC_USDT); + + List expectedAsks = new ArrayList<>(); + expectedAsks.add(new LimitOrder.Builder(OrderType.ASK, CurrencyPair.BTC_USDT) + .limitPrice(new BigDecimal("200")) + .originalAmount(BigDecimal.ONE) + .build()); + expectedAsks.add(new LimitOrder.Builder(OrderType.ASK, CurrencyPair.BTC_USDT) + .limitPrice(new BigDecimal("250")) + .originalAmount(BigDecimal.TEN) + .build()); + + List expectedBids = new ArrayList<>(); + expectedBids.add(new LimitOrder.Builder(OrderType.BID, CurrencyPair.BTC_USDT) + .limitPrice(new BigDecimal("150")) + .originalAmount(BigDecimal.ONE) + .build()); + expectedBids.add(new LimitOrder.Builder(OrderType.BID, CurrencyPair.BTC_USDT) + .limitPrice(new BigDecimal("100")) + .originalAmount(BigDecimal.TEN) + .build()); + Date expectedTimestamp = Date.from(Instant.parse("2023-05-14T22:10:10.493Z")); + + OrderBook expected = new OrderBook(expectedTimestamp, expectedAsks, expectedBids); + + assertThat(actual) + .usingRecursiveComparison() + .ignoringFieldsMatchingRegexes(".*userReference") + .isEqualTo(expected); + } + + + @Test + void getTicker_valid() throws IOException { + Ticker actual = gateioMarketDataService.getTicker(CurrencyPair.BTC_USDT); + + Ticker expected = new Ticker.Builder() + .instrument(CurrencyPair.BTC_USDT) + .last(new BigDecimal("26028.7")) + .ask(new BigDecimal("26026.8")) + .bid(new BigDecimal("26026.7")) + .high(new BigDecimal("26202.4")) + .low(new BigDecimal("25606.3")) + .volume(new BigDecimal("4726.066213526")) + .quoteVolume(new BigDecimal("122407105.4829")) + .percentageChange(new BigDecimal("1.1")) + .build(); + + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + + @Test + void getTickers_valid() throws IOException { + List actual = gateioMarketDataService.getTickers(null); + + assertThat(actual).hasSize(2); + } + + + @Test + void getCurrencies_valid() throws IOException { + List actual = gateioMarketDataService.getCurrencies(); + + assertThat(actual).containsOnly(Currency.BTC, Currency.ETH); + } + + + @Test + void getCurrencyPairs_valid() throws IOException { + List actual = gateioMarketDataService.getCurrencyPairs(); + + assertThat(actual).containsOnly(CurrencyPair.BTC_USDT, CurrencyPair.ETH_USDT, new CurrencyPair("CHZ/USDT")); + } + + +} diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioTradeServiceRawTest.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioTradeServiceRawTest.java new file mode 100644 index 00000000000..19271898ae0 --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioTradeServiceRawTest.java @@ -0,0 +1,140 @@ +package org.knowm.xchange.gateio.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order.OrderStatus; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.gateio.GateioExchangeWiremock; +import org.knowm.xchange.gateio.dto.account.GateioOrder; + +class GateioTradeServiceRawTest extends GateioExchangeWiremock { + + GateioTradeServiceRaw gateioTradeServiceRaw = (GateioTradeServiceRaw) exchange.getTradeService(); + + GateioOrder sampleMarketOrder = GateioOrder.builder() + .id("342251629898") + .currencyPair(CurrencyPair.BTC_USDT) + .clientOrderId("t-valid-market-buy-order") + .amendText("-") + .type("market") + .account("spot") + .side(OrderType.BID) + .timeInForce("ioc") + .amount(BigDecimal.valueOf(20)) + .createdAt(Instant.parse("2023-06-03T22:07:38.451Z")) + .updatedAt(Instant.parse("2023-06-03T22:07:38.451Z")) + .status("closed") + .icebergAmount(BigDecimal.ZERO) + .amountLeftToFill(new BigDecimal("1.07319")) + .filledTotalQuote(new BigDecimal("18.92681")) + .avgDealPrice(new BigDecimal("27038.3")) + .fee(new BigDecimal("0.0000014")) + .price(BigDecimal.ZERO) + .feeCurrency(Currency.BTC) + .pointFee(BigDecimal.ZERO) + .gtFee(BigDecimal.ZERO) + .gtMakerFee(BigDecimal.ZERO) + .gtTakerFee(BigDecimal.ZERO) + .rebatedFee(BigDecimal.ZERO) + .gtDiscount(false) + .rebatedFeeCurrency(Currency.USDT) + .finishAs("filled") + .build(); + + + + @Test + void listOrders() throws IOException { + List orders = gateioTradeServiceRaw.listOrders(CurrencyPair.BTC_USDT, OrderStatus.OPEN); + + assertThat(orders).hasSize(1); + assertThat(orders.get(0).getId()).isEqualTo("339440374909"); + assertThat(orders.get(0).getStatus()).isEqualTo("open"); + + } + + + @Test + void valid_market_buy_order() throws IOException { + GateioOrder gateioOrder = GateioOrder.builder() + .currencyPair(CurrencyPair.BTC_USDT) + .clientOrderId("t-valid-market-buy-order") + .type("market") + .account("spot") + .side(OrderType.BID) + .timeInForce("ioc") + .amount(BigDecimal.valueOf(20)) + .build(); + + GateioOrder actualResponse = gateioTradeServiceRaw.createOrder(gateioOrder); + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(sampleMarketOrder); + + } + + + @Test + void valid_market_sell_order() throws IOException { + GateioOrder gateioOrder = GateioOrder.builder() + .currencyPair(CurrencyPair.BTC_USDT) + .clientOrderId("t-valid-market-sell-order") + .type("market") + .account("spot") + .side(OrderType.ASK) + .timeInForce("ioc") + .amount(new BigDecimal("0.0007")) + .build(); + + + GateioOrder actualResponse = gateioTradeServiceRaw.createOrder(gateioOrder); + + GateioOrder expectedResponse = GateioOrder.builder() + .id("342260949533") + .currencyPair(CurrencyPair.BTC_USDT) + .clientOrderId("t-valid-market-sell-order") + .amendText("-") + .type("market") + .account("spot") + .side(OrderType.ASK) + .timeInForce("ioc") + .amount(new BigDecimal("0.0007")) + .createdAt(Instant.parse("2023-06-03T22:33:21.743Z")) + .updatedAt(Instant.parse("2023-06-03T22:33:21.743Z")) + .status("closed") + .icebergAmount(BigDecimal.ZERO) + .amountLeftToFill(BigDecimal.ZERO) + .filledTotalQuote(new BigDecimal("18.94382")) + .avgDealPrice(new BigDecimal("27062.6")) + .fee(new BigDecimal("0.03788764")) + .price(BigDecimal.ZERO) + .feeCurrency(Currency.USDT) + .pointFee(BigDecimal.ZERO) + .gtFee(BigDecimal.ZERO) + .gtMakerFee(BigDecimal.ZERO) + .gtTakerFee(BigDecimal.ZERO) + .rebatedFee(BigDecimal.ZERO) + .gtDiscount(false) + .rebatedFeeCurrency(Currency.BTC) + .finishAs("filled") + .build(); + + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); + + } + + + @Test + void order_details() throws IOException { + GateioOrder actualResponse = gateioTradeServiceRaw.getOrder("342251629898", CurrencyPair.BTC_USDT); + + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(sampleMarketOrder); + } + + +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioTradeServiceTest.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioTradeServiceTest.java new file mode 100644 index 00000000000..87af88e91f5 --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioTradeServiceTest.java @@ -0,0 +1,165 @@ +package org.knowm.xchange.gateio.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Collection; +import java.util.Date; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order; +import org.knowm.xchange.dto.Order.OrderStatus; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.dto.trade.LimitOrder; +import org.knowm.xchange.dto.trade.MarketOrder; +import org.knowm.xchange.dto.trade.UserTrade; +import org.knowm.xchange.dto.trade.UserTrades; +import org.knowm.xchange.exceptions.FundsExceededException; +import org.knowm.xchange.gateio.GateioExchangeWiremock; +import org.knowm.xchange.gateio.dto.trade.GateioUserTrade; +import org.knowm.xchange.gateio.dto.trade.Role; +import org.knowm.xchange.gateio.service.params.GateioTradeHistoryParams; +import org.knowm.xchange.service.trade.params.DefaultCancelOrderByInstrumentAndIdParams; +import org.knowm.xchange.service.trade.params.orders.DefaultQueryOrderParamInstrument; + +class GateioTradeServiceTest extends GateioExchangeWiremock { + + GateioTradeService gateioTradeService = (GateioTradeService) exchange.getTradeService(); + + + @Test + void place_order_not_enough_balance() { + MarketOrder marketOrder = new MarketOrder.Builder(OrderType.BID, CurrencyPair.BTC_USDT) + .userReference("t-balance-test") + .originalAmount(BigDecimal.valueOf(100)) + .build(); + + assertThatExceptionOfType(FundsExceededException.class) + .isThrownBy(() -> gateioTradeService.placeMarketOrder(marketOrder)); + } + + + @Test + void valid_market_buy_order() throws IOException { + MarketOrder marketOrder = new MarketOrder.Builder(OrderType.BID, CurrencyPair.BTC_USDT) + .userReference("t-valid-market-buy-order") + .originalAmount(BigDecimal.valueOf(20)) + .build(); + + String actualResponse = gateioTradeService.placeMarketOrder(marketOrder); + assertThat(actualResponse).isEqualTo("342251629898"); + + } + + + @Test + void valid_market_sell_order() throws IOException { + MarketOrder marketOrder = new MarketOrder.Builder(OrderType.ASK, CurrencyPair.BTC_USDT) + .userReference("t-valid-market-sell-order") + .originalAmount(new BigDecimal("0.0007")) + .build(); + + String actualResponse = gateioTradeService.placeMarketOrder(marketOrder); + assertThat(actualResponse).isEqualTo("342260949533"); + + } + + + @Test + void valid_limit_sell_order() throws IOException { + LimitOrder limitOrder = new LimitOrder.Builder(OrderType.ASK, CurrencyPair.BTC_USDT) + .userReference("t-valid-limit-sell-order") + .originalAmount(new BigDecimal("0.00068")) + .limitPrice(new BigDecimal("29240.7")) + .build(); + + String actualResponse = gateioTradeService.placeLimitOrder(limitOrder); + assertThat(actualResponse).isEqualTo("373824296029"); + } + + + @Test + void valid_cancel_order() throws IOException { + boolean actual = gateioTradeService.cancelOrder(new DefaultCancelOrderByInstrumentAndIdParams(CurrencyPair.BTC_USDT, "376835979523")); + assertThat(actual).isTrue(); + } + + + @Test + void valid_limit_buy_order() throws IOException { + LimitOrder limitOrder = new LimitOrder.Builder(OrderType.BID, CurrencyPair.BTC_USDT) + .userReference("t-valid-limit-buy-order") + .originalAmount(new BigDecimal("0.00068")) + .limitPrice(new BigDecimal("10000.7")) + .build(); + + String actualResponse = gateioTradeService.placeLimitOrder(limitOrder); + assertThat(actualResponse).isEqualTo("376835979523"); + } + + + @Test + void buy_order_details() throws IOException { + MarketOrder expected = new MarketOrder.Builder(OrderType.BID, CurrencyPair.BTC_USDT) + .id("342251629898") + .userReference("t-valid-market-buy-order") + .timestamp(Date.from(Instant.parse("2023-06-03T22:07:38.451Z"))) + .originalAmount(BigDecimal.valueOf(20)) + .orderStatus(OrderStatus.FILLED) + .cumulativeAmount(new BigDecimal("18.92681")) + .averagePrice(new BigDecimal("27038.3")) + .fee(new BigDecimal("0.0000014")) + .build(); + + Collection orders = gateioTradeService.getOrder(new DefaultQueryOrderParamInstrument(CurrencyPair.BTC_USDT, "342251629898")); + assertThat(orders).hasSize(1); + assertThat(orders).first().usingRecursiveComparison().isEqualTo(expected); + } + + + @Test + void sell_order_details() throws IOException { + MarketOrder expected = new MarketOrder.Builder(OrderType.ASK, new CurrencyPair("VAI/USDT")) + .id("425539509181") + .userReference("t-valid-market-sell-order") + .timestamp(Date.from(Instant.parse("2023-10-28T18:40:02.006Z"))) + .originalAmount(new BigDecimal("105.58")) + .orderStatus(OrderStatus.FILLED) + .cumulativeAmount(new BigDecimal("105.58")) + .averagePrice(new BigDecimal("0.079888")) + .fee(new BigDecimal("0.01686915008")) + .build(); + + Collection orders = gateioTradeService.getOrder(new DefaultQueryOrderParamInstrument(new CurrencyPair("VAI/USDT"), "425539509181")); + assertThat(orders).hasSize(1); + assertThat(orders).first().usingRecursiveComparison().isEqualTo(expected); + } + + + @Test + void trade_history() throws IOException { + UserTrades userTrades = gateioTradeService.getTradeHistory(GateioTradeHistoryParams.builder() + .currencyPair(CurrencyPair.BTC_USDT) + .pageLength(2) + .pageNumber(2) + .startTime(Date.from(Instant.ofEpochSecond(1691617924))) + .endTime(Date.from(Instant.ofEpochSecond(1691704324))) + .build()); + + assertThat(userTrades.getUserTrades()).hasSize(2); + + GateioUserTrade expected = new GateioUserTrade(OrderType.ASK, new BigDecimal("0.00005"), CurrencyPair.BTC_USDT, + new BigDecimal("29447.2"), Date.from(Instant.ofEpochMilli(1691702286356L)), "6068789332", + "381064942553", new BigDecimal("0.00294472"), Currency.USDT, "-", Role.TAKER); + + UserTrade actual = userTrades.getUserTrades().get(0); + + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioV4DigestTest.java b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioV4DigestTest.java new file mode 100644 index 00000000000..472a6c6cb73 --- /dev/null +++ b/xchange-gateio-v4/src/test/java/org/knowm/xchange/gateio/service/GateioV4DigestTest.java @@ -0,0 +1,38 @@ +package org.knowm.xchange.gateio.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import si.mazi.rescu.RestInvocation; + +@ExtendWith(MockitoExtension.class) +class GateioV4DigestTest { + + @Mock + RestInvocation restInvocation; + + @Test + void signature() { + GateioV4Digest gateioV4Digest = GateioV4Digest.createInstance("b"); + + when(restInvocation.getHttpMethod()).thenReturn("POST"); + when(restInvocation.getPath()).thenReturn("a"); + when(restInvocation.getQueryString()).thenReturn("?b=c"); + when(restInvocation.getRequestBody()).thenReturn("{a:1}"); + Map headers = new HashMap<>(); + headers.put("Timestamp", "1691707273890"); + when(restInvocation.getHttpHeadersFromParams()).thenReturn(headers); + + String actual = gateioV4Digest.digestParams(restInvocation); + String expected = "de31e211a60623ba2c41e65a3c21e550400ffccfef55578173e09f2b34cf46b426c587f01b4c12474608dc856b1ba226a71004f7989603236c885c23275d5577"; + + assertThat(actual).isEqualTo(expected); + } + +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/java/si/mazi/rescu/BodyLoggingRestInvocationHandler.java b/xchange-gateio-v4/src/test/java/si/mazi/rescu/BodyLoggingRestInvocationHandler.java new file mode 100644 index 00000000000..d955cd17b8c --- /dev/null +++ b/xchange-gateio-v4/src/test/java/si/mazi/rescu/BodyLoggingRestInvocationHandler.java @@ -0,0 +1,24 @@ +package si.mazi.rescu; + +import java.io.IOException; +import lombok.extern.slf4j.Slf4j; + +/** + * Invocation handler that logs the raw textual response body + */ +@Slf4j +public class BodyLoggingRestInvocationHandler extends RestInvocationHandler { + + BodyLoggingRestInvocationHandler(Class restInterface, String url, ClientConfig config) { + super(restInterface, url, config); + } + + @Override + protected Object mapInvocationResult(InvocationResult invocationResult, RestMethodMetadata methodMetadata) throws IOException { + // log the body + log.info(invocationResult.getHttpBody()); + + // do the normal processing + return super.mapInvocationResult(invocationResult, methodMetadata); + } +} diff --git a/xchange-gateio-v4/src/test/java/si/mazi/rescu/CustomRestProxyFactoryImpl.java b/xchange-gateio-v4/src/test/java/si/mazi/rescu/CustomRestProxyFactoryImpl.java new file mode 100644 index 00000000000..6594f8ec2d1 --- /dev/null +++ b/xchange-gateio-v4/src/test/java/si/mazi/rescu/CustomRestProxyFactoryImpl.java @@ -0,0 +1,17 @@ +package si.mazi.rescu; + +/** + * The implementation of {@link IRestProxyFactory} that instantiates {@link BodyLoggingRestInvocationHandler} + */ +public class CustomRestProxyFactoryImpl implements IRestProxyFactory { + + @Override + public I createProxy(Class restInterface, String baseUrl, ClientConfig config, Interceptor... interceptors) { + return RestProxyFactory.createProxy(restInterface, RestProxyFactory.wrap(new BodyLoggingRestInvocationHandler(restInterface, baseUrl, config), interceptors)); + } + + @Override + public I createProxy(Class restInterface, String baseUrl) { + return createProxy(restInterface, baseUrl, null); + } +} diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_account_book.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_account_book.json new file mode 100644 index 00000000000..fd07274dc7d --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_account_book.json @@ -0,0 +1,18 @@ +[ + { + "id": "40558668441", + "time": 1691510538067, + "currency": "USDT", + "change": "-0.0113918056", + "balance": "16.00283141582979715942", + "type": "order_fee" + }, + { + "id": "40558668438", + "time": 1691510538067, + "currency": "USDT", + "change": "-0.0187363344", + "balance": "10.31832042142979715942", + "type": "order_fee" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_accounts.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_accounts.json new file mode 100644 index 00000000000..7cb352ebaf2 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_accounts.json @@ -0,0 +1,7 @@ +[ + { + "currency": "USDT", + "available": "4.1", + "locked": "1.1" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_currencies.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_currencies.json new file mode 100644 index 00000000000..3883d788716 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_currencies.json @@ -0,0 +1,47 @@ +[ + { + "currency": "BTC", + "delisted": false, + "withdraw_disabled": false, + "withdraw_delayed": false, + "deposit_disabled": false, + "trade_disabled": false, + "chain": "BTC" + }, + { + "currency": "BTC_BSC", + "delisted": false, + "withdraw_disabled": false, + "withdraw_delayed": false, + "deposit_disabled": false, + "trade_disabled": false, + "chain": "BSC" + }, + { + "currency": "BTC_HT", + "delisted": false, + "withdraw_disabled": true, + "withdraw_delayed": false, + "deposit_disabled": true, + "trade_disabled": false, + "chain": "HT" + }, + { + "currency": "ETH", + "delisted": false, + "withdraw_disabled": false, + "withdraw_delayed": false, + "deposit_disabled": false, + "trade_disabled": false, + "chain": "ETH" + }, + { + "currency": "ETH_BSC", + "delisted": false, + "withdraw_disabled": false, + "withdraw_delayed": false, + "deposit_disabled": false, + "trade_disabled": false, + "chain": "BSC" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_currency_pairs.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_currency_pairs.json new file mode 100644 index 00000000000..b4d06c20ae8 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_currency_pairs.json @@ -0,0 +1,40 @@ +[ + { + "id": "BTC_USDT", + "base": "BTC", + "quote": "USDT", + "fee": "0.2", + "min_base_amount": "0.0001", + "min_quote_amount": "1", + "amount_precision": 4, + "precision": 1, + "trade_status": "tradable", + "sell_start": 0, + "buy_start": 0 + }, + { + "id": "CHZ_USDT", + "base": "CHZ", + "quote": "USDT", + "fee": "0.2", + "min_quote_amount": "1", + "amount_precision": 2, + "precision": 5, + "trade_status": "tradable", + "sell_start": 1608782400, + "buy_start": 1608789600 + }, + { + "id": "ETH_USDT", + "base": "ETH", + "quote": "USDT", + "fee": "0.2", + "min_base_amount": "0.001", + "min_quote_amount": "1", + "amount_precision": 4, + "precision": 2, + "trade_status": "tradable", + "sell_start": 0, + "buy_start": 0 + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_currency_pairs_chz_usdt.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_currency_pairs_chz_usdt.json new file mode 100644 index 00000000000..3a1bcc29a6b --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_currency_pairs_chz_usdt.json @@ -0,0 +1,12 @@ +{ + "id": "CHZ_USDT", + "base": "CHZ", + "quote": "USDT", + "fee": "0.2", + "min_quote_amount": "1", + "amount_precision": 2, + "precision": 5, + "trade_status": "tradable", + "sell_start": 1608782400, + "buy_start": 1608789600 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_my_trades.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_my_trades.json new file mode 100644 index 00000000000..1fbc87c560f --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_my_trades.json @@ -0,0 +1,34 @@ +[ + { + "id": "6068816979", + "create_time": "1691702924", + "create_time_ms": "1691702924010.071000", + "currency_pair": "BTC_USDT", + "side": "buy", + "role": "taker", + "amount": "0.00003", + "price": "29454.6", + "order_id": "381068734893", + "fee": "0.00000006", + "fee_currency": "BTC", + "point_fee": "0", + "gt_fee": "0", + "amend_text": "-" + }, + { + "id": "6068789332", + "create_time": "1691702286", + "create_time_ms": "1691702286356.911000", + "currency_pair": "BTC_USDT", + "side": "sell", + "role": "taker", + "amount": "0.00005", + "price": "29447.2", + "order_id": "381064942553", + "fee": "0.00294472", + "fee_currency": "USDT", + "point_fee": "0", + "gt_fee": "0", + "amend_text": "-" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_order_book.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_order_book.json new file mode 100644 index 00000000000..e902ce2b312 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_order_book.json @@ -0,0 +1,24 @@ +{ + "current": 1684102210493, + "update": 1684102210263, + "asks": [ + [ + "200", + "1" + ], + [ + "250", + "10" + ] + ], + "bids": [ + [ + "150", + "1" + ], + [ + "100", + "10" + ] + ] +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-btc-open.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-btc-open.json new file mode 100644 index 00000000000..710f1a2b209 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-btc-open.json @@ -0,0 +1,34 @@ +[ + { + "id": "339440374909", + "text": "3", + "amend_text": "-", + "create_time": "1685391045", + "update_time": "1685391045", + "create_time_ms": 1685391045952, + "update_time_ms": 1685391045952, + "status": "open", + "currency_pair": "BTC_USDT", + "type": "limit", + "account": "spot", + "side": "buy", + "amount": "0.0001", + "price": "22053", + "biz_info": "-", + "time_in_force": "gtc", + "iceberg": "0", + "left": "0.0001", + "fill_price": "0", + "filled_total": "0", + "fee": "0", + "fee_currency": "BTC", + "point_fee": "0", + "gt_fee": "0", + "gt_maker_fee": "0", + "gt_taker_fee": "0", + "gt_discount": false, + "rebated_fee": "0", + "rebated_fee_currency": "USDT", + "finish_as": "open" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-cancel-order.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-cancel-order.json new file mode 100644 index 00000000000..2987151a468 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-cancel-order.json @@ -0,0 +1,32 @@ +{ + "id": "376835979523", + "text": "t-valid-limit-sell-order", + "amend_text": "-", + "create_time": "1691015713", + "update_time": "1691016008", + "create_time_ms": 1691015713247, + "update_time_ms": 1691016008216, + "status": "cancelled", + "currency_pair": "BTC_USDT", + "type": "limit", + "account": "spot", + "side": "buy", + "amount": "0.00068", + "price": "10000.7", + "biz_info": "-", + "time_in_force": "gtc", + "iceberg": "0", + "left": "0.00068", + "fill_price": "0", + "filled_total": "0", + "fee": "0", + "fee_currency": "BTC", + "point_fee": "0", + "gt_fee": "0", + "gt_maker_fee": "0", + "gt_taker_fee": "0", + "gt_discount": false, + "rebated_fee": "0", + "rebated_fee_currency": "USDT", + "finish_as": "cancelled" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-limit-buy-order.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-limit-buy-order.json new file mode 100644 index 00000000000..bb7b1ed43a0 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-limit-buy-order.json @@ -0,0 +1,32 @@ +{ + "id": "376835979523", + "text": "t-valid-limit-buy-order", + "amend_text": "-", + "create_time": "1691015713", + "update_time": "1691015713", + "create_time_ms": 1691015713247, + "update_time_ms": 1691015713247, + "status": "open", + "currency_pair": "BTC_USDT", + "type": "limit", + "account": "spot", + "side": "buy", + "amount": "0.00068", + "price": "10000.7", + "biz_info": "-", + "time_in_force": "gtc", + "iceberg": "0", + "left": "0.00068", + "fill_price": "0", + "filled_total": "0", + "fee": "0", + "fee_currency": "BTC", + "point_fee": "0", + "gt_fee": "0", + "gt_maker_fee": "0", + "gt_taker_fee": "0", + "gt_discount": false, + "rebated_fee": "0", + "rebated_fee_currency": "USDT", + "finish_as": "open" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-limit-sell-order.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-limit-sell-order.json new file mode 100644 index 00000000000..be96cefa616 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-limit-sell-order.json @@ -0,0 +1,32 @@ +{ + "id": "373824296029", + "text": "t-valid-limit-sell-order", + "amend_text": "-", + "create_time": "1690498746", + "update_time": "1690498746", + "create_time_ms": 1690498746773, + "update_time_ms": 1690498746773, + "status": "open", + "currency_pair": "BTC_USDT", + "type": "limit", + "account": "spot", + "side": "sell", + "amount": "0.00068", + "price": "29240.7", + "biz_info": "-", + "time_in_force": "gtc", + "iceberg": "0", + "left": "0.00068", + "fill_price": "0", + "filled_total": "0", + "fee": "0", + "fee_currency": "USDT", + "point_fee": "0", + "gt_fee": "0", + "gt_maker_fee": "0", + "gt_taker_fee": "0", + "gt_discount": false, + "rebated_fee": "0", + "rebated_fee_currency": "BTC", + "finish_as": "open" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-market-buy-order.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-market-buy-order.json new file mode 100644 index 00000000000..d2b39003982 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-market-buy-order.json @@ -0,0 +1,33 @@ +{ + "id": "342251629898", + "text": "t-valid-market-buy-order", + "amend_text": "-", + "create_time": "1685830058", + "update_time": "1685830058", + "create_time_ms": 1685830058451, + "update_time_ms": 1685830058451, + "status": "closed", + "currency_pair": "BTC_USDT", + "type": "market", + "account": "spot", + "side": "buy", + "amount": "20", + "price": "0", + "biz_info": "-", + "time_in_force": "ioc", + "iceberg": "0", + "left": "1.07319", + "fill_price": "18.92681", + "filled_total": "18.92681", + "avg_deal_price": "27038.3", + "fee": "0.0000014", + "fee_currency": "BTC", + "point_fee": "0", + "gt_fee": "0", + "gt_maker_fee": "0", + "gt_taker_fee": "0", + "gt_discount": false, + "rebated_fee": "0", + "rebated_fee_currency": "USDT", + "finish_as": "filled" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-market-sell-order.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-market-sell-order.json new file mode 100644 index 00000000000..69906f63344 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders-valid-market-sell-order.json @@ -0,0 +1,33 @@ +{ + "id": "342260949533", + "text": "t-valid-market-sell-order", + "amend_text": "-", + "create_time": "1685831601", + "update_time": "1685831601", + "create_time_ms": 1685831601743, + "update_time_ms": 1685831601743, + "status": "closed", + "currency_pair": "BTC_USDT", + "type": "market", + "account": "spot", + "side": "sell", + "amount": "0.0007", + "price": "0", + "biz_info": "-", + "time_in_force": "ioc", + "iceberg": "0", + "left": "0", + "fill_price": "18.94382", + "filled_total": "18.94382", + "avg_deal_price": "27062.6", + "fee": "0.03788764", + "fee_currency": "USDT", + "point_fee": "0", + "gt_fee": "0", + "gt_maker_fee": "0", + "gt_taker_fee": "0", + "gt_discount": false, + "rebated_fee": "0", + "rebated_fee_currency": "BTC", + "finish_as": "filled" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders_buy-order-details.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders_buy-order-details.json new file mode 100644 index 00000000000..d2b39003982 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders_buy-order-details.json @@ -0,0 +1,33 @@ +{ + "id": "342251629898", + "text": "t-valid-market-buy-order", + "amend_text": "-", + "create_time": "1685830058", + "update_time": "1685830058", + "create_time_ms": 1685830058451, + "update_time_ms": 1685830058451, + "status": "closed", + "currency_pair": "BTC_USDT", + "type": "market", + "account": "spot", + "side": "buy", + "amount": "20", + "price": "0", + "biz_info": "-", + "time_in_force": "ioc", + "iceberg": "0", + "left": "1.07319", + "fill_price": "18.92681", + "filled_total": "18.92681", + "avg_deal_price": "27038.3", + "fee": "0.0000014", + "fee_currency": "BTC", + "point_fee": "0", + "gt_fee": "0", + "gt_maker_fee": "0", + "gt_taker_fee": "0", + "gt_discount": false, + "rebated_fee": "0", + "rebated_fee_currency": "USDT", + "finish_as": "filled" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders_sell-order-details.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders_sell-order-details.json new file mode 100644 index 00000000000..d2f9df60cf1 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_orders_sell-order-details.json @@ -0,0 +1,33 @@ +{ + "id": "425539509181", + "text": "t-valid-market-sell-order", + "amend_text": "-", + "create_time": "1698518402", + "update_time": "1698518402", + "create_time_ms": 1698518402006, + "update_time_ms": 1698518402006, + "status": "closed", + "currency_pair": "VAI_USDT", + "type": "market", + "account": "spot", + "side": "sell", + "amount": "105.58", + "price": "0", + "biz_info": "-", + "time_in_force": "ioc", + "iceberg": "0", + "left": "0.00", + "fill_price": "8.43457504", + "filled_total": "8.43457504", + "avg_deal_price": "0.079888", + "fee": "0.01686915008", + "fee_currency": "USDT", + "point_fee": "0", + "gt_fee": "0", + "gt_maker_fee": "0", + "gt_taker_fee": "0", + "gt_discount": false, + "rebated_fee": "0", + "rebated_fee_currency": "VAI", + "finish_as": "filled" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_tickers.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_tickers.json new file mode 100644 index 00000000000..2608edf1357 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_tickers.json @@ -0,0 +1,28 @@ +[ + { + "currency_pair": "BTC_USDT", + "last": "26019.8", + "lowest_ask": "26019.9", + "highest_bid": "26019.8", + "change_percentage": "1.09", + "change_utc0": "0.36", + "change_utc8": "1.04", + "base_volume": "4722.912133756", + "quote_volume": "122324034.68892", + "high_24h": "26202.4", + "low_24h": "25606.3" + }, + { + "currency_pair": "ETH_USDT", + "last": "1752.96", + "lowest_ask": "1752.97", + "highest_bid": "1752.96", + "change_percentage": "0.24", + "change_utc0": "0", + "change_utc8": "0.66", + "base_volume": "34842.614438438", + "quote_volume": "60887901.935971", + "high_24h": "1777.84", + "low_24h": "1719.5" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_tickers_btc-usdt.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_tickers_btc-usdt.json new file mode 100644 index 00000000000..b0f99c48378 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_spot_tickers_btc-usdt.json @@ -0,0 +1,13 @@ +[ + { + "currency_pair": "BTC_USDT", + "last": "26028.7", + "lowest_ask": "26026.8", + "highest_bid": "26026.7", + "change_percentage": "1.1", + "base_volume": "4726.066213526", + "quote_volume": "122407105.4829", + "high_24h": "26202.4", + "low_24h": "25606.3" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_currency_chains.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_currency_chains.json new file mode 100644 index 00000000000..522f3f762a5 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_currency_chains.json @@ -0,0 +1,20 @@ +[ + { + "chain": "BTC", + "name_cn": "比特币 BRC20\/Ordinals", + "name_en": "Bitcoin BRC20\/Ordinals", + "is_disabled": 0, + "is_deposit_disabled": 0, + "is_withdraw_disabled": 0, + "contract_address": "" + }, + { + "chain": "HT", + "name_cn": "Heco", + "name_en": "Heco", + "is_disabled": 1, + "is_deposit_disabled": 1, + "is_withdraw_disabled": 1, + "contract_address": "0x66a79d23e58475d2738179ca52cd0b41d73f0bea" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_deposit_address-pending.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_deposit_address-pending.json new file mode 100644 index 00000000000..1b77bda7d40 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_deposit_address-pending.json @@ -0,0 +1,13 @@ +{ + "currency": "ITA", + "address": "New address is being generated for you, please wait a moment and refresh this page. ", + "multichain_addresses": [ + { + "chain": "CHZ", + "address": "", + "payment_id": "", + "payment_name": "", + "obtain_failed": 1 + } + ] +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_deposit_address-valid.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_deposit_address-valid.json new file mode 100644 index 00000000000..6ab9d153529 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_deposit_address-valid.json @@ -0,0 +1,13 @@ +{ + "currency": "ABBC", + "address": "gateioioabbc 00cceed3f9cb5069", + "multichain_addresses": [ + { + "chain": "ABBC", + "address": "gateioioabbc", + "payment_id": "00cceed3f9cb5069", + "payment_name": "Memo", + "obtain_failed": 0 + } + ] +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_deposits.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_deposits.json new file mode 100644 index 00000000000..51061679bef --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_deposits.json @@ -0,0 +1,24 @@ +[ + { + "id": "d95198946", + "currency": "OMN", + "address": "0x04A09F5712F527584BAC0C7be40Ae7946f9A9587", + "amount": "2353", + "txid": "0x34a89edf400a85a9a6a2747ebc2074ef65895345b9b7400f164602085e9b4a2e", + "chain": "OMN", + "timestamp": "1688220746", + "status": "DONE", + "memo": "" + }, + { + "id": "d95194166", + "currency": "NAP", + "address": "0x04A09F5712F527584BAC0C7be40Ae7946f9A9587", + "amount": "5", + "txid": "0xe4145981e135f2742fe5b08ab4fed5cc6f18bd8fc9e47feb7923f39a87a21ec3", + "chain": "CHZ", + "timestamp": "1688213581", + "status": "DONE", + "memo": "" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_saved_address.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_saved_address.json new file mode 100644 index 00000000000..82fcddf8577 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_saved_address.json @@ -0,0 +1,10 @@ +[ + { + "currency": "USDT", + "chain": "SOL", + "address": "6vLyxJ9dBziamyaw2vDcs9n2NwQdW1uk3aooJwrEscnA", + "name": "stuff", + "tag": "", + "verified": "1" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_sub_account_transfers.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_sub_account_transfers.json new file mode 100644 index 00000000000..36a1df1da6f --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_sub_account_transfers.json @@ -0,0 +1,13 @@ +[ + { + "uid": "10001", + "timest": "1592809000", + "source": "web", + "client_order_id": "da3ce7a088c8b0372b741419c7829033", + "currency": "BTC", + "sub_account": "10002", + "direction": "to", + "amount": "1", + "sub_account_type": "spot" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_withdraw_status.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_withdraw_status.json new file mode 100644 index 00000000000..2d3910834e7 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_withdraw_status.json @@ -0,0 +1,48 @@ +[ + { + "currency": "GT", + "name": "GateToken", + "name_cn": "狗头", + "deposit": "0", + "withdraw_percent": "0%", + "withdraw_fix": "0.02", + "withdraw_day_limit": "500000", + "withdraw_day_limit_remain": "499999", + "withdraw_amount_mini": "0.12", + "withdraw_eachtime_limit": "499999", + "withdraw_fix_on_chains": { + "ETH": "0.93", + "GTEVM": "0.0049" + } + }, + { + "currency": "USDT", + "name": "Tether", + "name_cn": "Tether", + "deposit": "0", + "withdraw_percent": "0%", + "withdraw_fix": "65", + "withdraw_day_limit": "300000", + "withdraw_day_limit_remain": "299999", + "withdraw_amount_mini": "66", + "withdraw_eachtime_limit": "299999", + "withdraw_fix_on_chains": { + "BTC": "65", + "ETH": "5", + "TRX": "1", + "BSC": "1", + "GTEVM": "0.024", + "EOS": "10", + "HT": "0.5", + "SOL": "0.1", + "ALGO": "5", + "NEAR": "0.5", + "MATIC": "1", + "ARBEVM": "1", + "OPETH": "1", + "KSMSM": "2", + "DOTSM": "2", + "OKT": "1" + } + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_withdrawals-records.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_withdrawals-records.json new file mode 100644 index 00000000000..7eea85bc078 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_wallet_withdrawals-records.json @@ -0,0 +1,15 @@ +[ + { + "id": "w35874123", + "currency": "LUFFY", + "address": "0x3dca2ae4d1d065220a731cf69f5a934914afc435", + "amount": "1030645.8587", + "fee": "10000", + "txid": "0x8f72d42b016a2b7b543149e707ff37fadded2ff3ef6767bee30b6003330f604b", + "chain": "ETH", + "timestamp": "1685619255", + "status": "DONE", + "withdraw_order_id": "a", + "memo": "b" + } +] \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/api_v4_withdrawals-create-new.json b/xchange-gateio-v4/src/test/resources/__files/api_v4_withdrawals-create-new.json new file mode 100644 index 00000000000..e1e91e4d738 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/api_v4_withdrawals-create-new.json @@ -0,0 +1,10 @@ +{ + "id": "w35980955", + "currency": "USDT", + "amount": "3", + "address": "6vLyxJ9dBziamyaw2vDcs9n2NwQdW1uk3aooJwrEscnA ", + "memo": "", + "status": "REQUEST", + "chain": "SOL", + "withdraw_order_id": "valid-withdrawal-id" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_spot_orders-balance-not-enough.json b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_spot_orders-balance-not-enough.json new file mode 100644 index 00000000000..47e030a5f86 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_spot_orders-balance-not-enough.json @@ -0,0 +1,4 @@ +{ + "label": "BALANCE_NOT_ENOUGH", + "message": "Not enough balance" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-forbidden.json b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-forbidden.json new file mode 100644 index 00000000000..d090b2c43df --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-forbidden.json @@ -0,0 +1,4 @@ +{ + "message": "Request IP not in whitelist: 1.1.1.1", + "label": "FORBIDDEN" +} diff --git a/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-invalid_currency.json b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-invalid_currency.json new file mode 100644 index 00000000000..021ac2176de --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-invalid_currency.json @@ -0,0 +1,4 @@ +{ + "label": "INVALID_CURRENCY", + "message": "Invalid currency" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-invalid_key.json b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-invalid_key.json new file mode 100644 index 00000000000..85589cd3b7f --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-invalid_key.json @@ -0,0 +1,4 @@ +{ + "message": "Invalid key provided", + "label": "INVALID_KEY" +} diff --git a/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-invalid_signature.json b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-invalid_signature.json new file mode 100644 index 00000000000..b2acf1dd1cd --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status-invalid_signature.json @@ -0,0 +1,4 @@ +{ + "label": "INVALID_SIGNATURE", + "message": "Signature mismatch" +} diff --git a/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status_internal-server-error.json b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status_internal-server-error.json new file mode 100644 index 00000000000..5cd34cdc249 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_wallet_withdraw_status_internal-server-error.json @@ -0,0 +1,4 @@ +{ + "label": "SERVER_ERROR", + "message": "Internal server error" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_withdrawals_invalid-address.json b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_withdrawals_invalid-address.json new file mode 100644 index 00000000000..3cf6d04f722 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_withdrawals_invalid-address.json @@ -0,0 +1,4 @@ +{ + "label": "INVALID_PARAM_VALUE", + "message": "Error: only used addresses or verified addresses are allowed for api withdrawal" +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_withdrawals_min-amount.json b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_withdrawals_min-amount.json new file mode 100644 index 00000000000..fae74d67544 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_withdrawals_min-amount.json @@ -0,0 +1,4 @@ +{ + "label": "INVALID_PARAM_VALUE", + "message": "Error: Invalid amount or below minimum amount limit." +} diff --git a/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_withdrawals_rate-limit.json b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_withdrawals_rate-limit.json new file mode 100644 index 00000000000..a7a2a421dc0 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/__files/exceptions/api_v4_withdrawals_rate-limit.json @@ -0,0 +1,4 @@ +{ + "label": "TOO_FAST", + "message": "Withdrawal frequency is limited to 10s127.0.0.1" +} diff --git a/xchange-bankera/src/test/resources/logback.xml b/xchange-gateio-v4/src/test/resources/logback.xml similarity index 96% rename from xchange-bankera/src/test/resources/logback.xml rename to xchange-gateio-v4/src/test/resources/logback.xml index 5809b573500..c823ac40a99 100644 --- a/xchange-bankera/src/test/resources/logback.xml +++ b/xchange-gateio-v4/src/test/resources/logback.xml @@ -11,7 +11,7 @@ - + diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_account_book.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_account_book.json new file mode 100644 index 00000000000..ad55b447d4c --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_account_book.json @@ -0,0 +1,15 @@ +{ + "id" : "0f4c0848-df10-4f1e-b5e7-1b5213fa2f18", + "name" : "api_v4_spot_account_book", + "request" : { + "url" : "/api/v4/spot/account_book?currency=USDT&from=1691447482&to=1691533882&limit=2&page=1&type=order_fee", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_account_book.json" + }, + "uuid" : "0f4c0848-df10-4f1e-b5e7-1b5213fa2f18", + "persistent" : true, + "insertionIndex" : 35 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_accounts.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_accounts.json new file mode 100644 index 00000000000..475e7b19905 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_accounts.json @@ -0,0 +1,15 @@ +{ + "id" : "2e902638-c6b5-450a-911c-d501d9a5ee44", + "name" : "api_v4_spot_accounts", + "request" : { + "url" : "/api/v4/spot/accounts", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_accounts.json" + }, + "uuid" : "2e902638-c6b5-450a-911c-d501d9a5ee44", + "persistent" : true, + "insertionIndex" : 10 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_currencies.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_currencies.json new file mode 100644 index 00000000000..e9addb37ed1 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_currencies.json @@ -0,0 +1,15 @@ +{ + "id" : "2ad660f8-d92d-4f29-98d2-6e1b1acde382", + "name" : "api_v4_spot_currencies", + "request" : { + "url" : "/api/v4/spot/currencies", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_currencies.json" + }, + "uuid" : "2ad660f8-d92d-4f29-98d2-6e1b1acde382", + "persistent" : true, + "insertionIndex" : 2 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_currency_pairs.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_currency_pairs.json new file mode 100644 index 00000000000..288f999c9d9 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_currency_pairs.json @@ -0,0 +1,15 @@ +{ + "id" : "6a2422dc-c1e6-4f5b-95e9-89a626210111", + "name" : "api_v4_spot_currency_pairs", + "request" : { + "url" : "/api/v4/spot/currency_pairs", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_currency_pairs.json" + }, + "uuid" : "6a2422dc-c1e6-4f5b-95e9-89a626210111", + "persistent" : true, + "insertionIndex" : 11 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_currency_pairs_chz_usdt.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_currency_pairs_chz_usdt.json new file mode 100644 index 00000000000..07a16b375fa --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_currency_pairs_chz_usdt.json @@ -0,0 +1,15 @@ +{ + "id" : "9cb9e274-4bf7-419e-9b28-ed6c7dbf8f52", + "name" : "api_v4_spot_currency_pairs_chz_usdt", + "request" : { + "url" : "/api/v4/spot/currency_pairs/CHZ_USDT", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_currency_pairs_chz_usdt.json" + }, + "uuid" : "9cb9e274-4bf7-419e-9b28-ed6c7dbf8f52", + "persistent" : true, + "insertionIndex" : 13 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_my_trades.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_my_trades.json new file mode 100644 index 00000000000..bdc77e06f16 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_my_trades.json @@ -0,0 +1,15 @@ +{ + "id" : "8542120c-f98f-4b9a-9230-b81ee6ffe102", + "name" : "api_v4_spot_my_trades", + "request" : { + "url" : "/api/v4/spot/my_trades?currency_pair=BTC_USDT&limit=2&page=2&from=1691617924&to=1691704324", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_my_trades.json" + }, + "uuid" : "8542120c-f98f-4b9a-9230-b81ee6ffe102", + "persistent" : true, + "insertionIndex" : 32 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_order_book.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_order_book.json new file mode 100644 index 00000000000..c1e119453fb --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_order_book.json @@ -0,0 +1,15 @@ +{ + "id" : "b24dfa78-b1a7-4468-b85c-fa299b82170c", + "name" : "api_v4_spot_order_book", + "request" : { + "url" : "/api/v4/spot/order_book?currency_pair=BTC_USDT&with_id=false", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_order_book.json" + }, + "uuid" : "b24dfa78-b1a7-4468-b85c-fa299b82170c", + "persistent" : true, + "insertionIndex" : 3 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-btc-open.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-btc-open.json new file mode 100644 index 00000000000..ad26c081532 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-btc-open.json @@ -0,0 +1,15 @@ +{ + "id" : "8597ffe9-f28d-40d6-9a47-5f378dd53515", + "name" : "api_v4_spot_orders", + "request" : { + "url" : "/api/v4/spot/orders?currency_pair=BTC_USDT&status=open", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_orders-btc-open.json" + }, + "uuid" : "8597ffe9-f28d-40d6-9a47-5f378dd53515", + "persistent" : true, + "insertionIndex" : 16 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-cancel-order.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-cancel-order.json new file mode 100644 index 00000000000..378a102ab95 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-cancel-order.json @@ -0,0 +1,15 @@ +{ + "id" : "89473a34-023a-4591-9820-e2b5cbebc2ad", + "name" : "api_v4_spot_orders_376835979523", + "request" : { + "url" : "/api/v4/spot/orders/376835979523?currency_pair=BTC_USDT", + "method" : "DELETE" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_orders-valid-cancel-order.json" + }, + "uuid" : "89473a34-023a-4591-9820-e2b5cbebc2ad", + "persistent" : true, + "insertionIndex" : 31 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-limit-buy-order.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-limit-buy-order.json new file mode 100644 index 00000000000..656848548e2 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-limit-buy-order.json @@ -0,0 +1,20 @@ +{ + "id" : "de1a7911-c7dd-4151-b92e-7f3654aaf1d1", + "name" : "api_v4_spot_orders", + "request" : { + "url" : "/api/v4/spot/orders", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"id\":null,\"text\":\"t-valid-limit-buy-order\",\"amend_text\":null,\"create_time_ms\":null,\"update_time_ms\":null,\"status\":null,\"currency_pair\":\"BTC_USDT\",\"type\":\"limit\",\"account\":\"spot\",\"side\":\"buy\",\"amount\":0.00068,\"price\":10000.7,\"time_in_force\":\"gtc\",\"iceberg\":null,\"auto_borrow\":null,\"auto_repay\":null,\"left\":null,\"filled_total\":null,\"avg_deal_price\":null,\"fee\":null,\"fee_currency\":null,\"point_fee\":null,\"gt_fee\":null,\"gt_maker_fee\":null,\"gt_taker_fee\":null,\"gt_discount\":null,\"rebated_fee\":null,\"rebated_fee_currency\":null,\"stp_id\":null,\"stp_act\":null,\"finish_as\":null}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 201, + "bodyFileName" : "api_v4_spot_orders-valid-limit-buy-order.json" + }, + "uuid" : "de1a7911-c7dd-4151-b92e-7f3654aaf1d1", + "persistent" : true, + "insertionIndex" : 30 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-limit-sell-order.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-limit-sell-order.json new file mode 100644 index 00000000000..ec557a33834 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-limit-sell-order.json @@ -0,0 +1,20 @@ +{ + "id" : "ab0ea1a7-de0a-4ddf-b66d-0791d9fd5532", + "name" : "api_v4_spot_orders", + "request" : { + "url" : "/api/v4/spot/orders", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"id\":null,\"text\":\"t-valid-limit-sell-order\",\"amend_text\":null,\"create_time_ms\":null,\"update_time_ms\":null,\"status\":null,\"currency_pair\":\"BTC_USDT\",\"type\":\"limit\",\"account\":\"spot\",\"side\":\"sell\",\"amount\":0.00068,\"price\":29240.7,\"time_in_force\":\"gtc\",\"iceberg\":null,\"auto_borrow\":null,\"auto_repay\":null,\"left\":null,\"filled_total\":null,\"avg_deal_price\":null,\"fee\":null,\"fee_currency\":null,\"point_fee\":null,\"gt_fee\":null,\"gt_maker_fee\":null,\"gt_taker_fee\":null,\"gt_discount\":null,\"rebated_fee\":null,\"rebated_fee_currency\":null,\"stp_id\":null,\"stp_act\":null,\"finish_as\":null}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 201, + "bodyFileName" : "api_v4_spot_orders-valid-limit-sell-order.json" + }, + "uuid" : "ab0ea1a7-de0a-4ddf-b66d-0791d9fd5532", + "persistent" : true, + "insertionIndex" : 31 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-market-buy-order.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-market-buy-order.json new file mode 100644 index 00000000000..7fee2f05c07 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-market-buy-order.json @@ -0,0 +1,20 @@ +{ + "id" : "3311168b-7ef3-44cc-b356-ffa5737b3bfe", + "name" : "api_v4_spot_orders", + "request" : { + "url" : "/api/v4/spot/orders", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"text\":\"t-valid-market-buy-order\",\"currency_pair\":\"BTC_USDT\",\"type\":\"market\",\"account\":\"spot\",\"side\":\"buy\",\"amount\":20,\"time_in_force\":\"ioc\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 201, + "bodyFileName" : "api_v4_spot_orders-valid-market-buy-order.json" + }, + "uuid" : "3311168b-7ef3-44cc-b356-ffa5737b3bfe", + "persistent" : true, + "insertionIndex" : 18 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-market-sell-order.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-market-sell-order.json new file mode 100644 index 00000000000..950980d3253 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders-valid-market-sell-order.json @@ -0,0 +1,20 @@ +{ + "id" : "d63d7d0f-8d8b-496e-a00c-db2a6b1305bb", + "name" : "api_v4_spot_orders", + "request" : { + "url" : "/api/v4/spot/orders", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"text\":\"t-valid-market-sell-order\",\"currency_pair\":\"BTC_USDT\",\"type\":\"market\",\"account\":\"spot\",\"side\":\"sell\",\"amount\":0.0007,\"time_in_force\":\"ioc\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 201, + "bodyFileName" : "api_v4_spot_orders-valid-market-sell-order.json" + }, + "uuid" : "d63d7d0f-8d8b-496e-a00c-db2a6b1305bb", + "persistent" : true, + "insertionIndex" : 20 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders_buy-order-details.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders_buy-order-details.json new file mode 100644 index 00000000000..62a00cc036d --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders_buy-order-details.json @@ -0,0 +1,15 @@ +{ + "id" : "da0d205d-94ee-4760-bfd1-f4e5871d23e4", + "name" : "api_v4_spot_orders_342251629898", + "request" : { + "url" : "/api/v4/spot/orders/342251629898?currency_pair=BTC_USDT", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_orders_buy-order-details.json" + }, + "uuid" : "da0d205d-94ee-4760-bfd1-f4e5871d23e4", + "persistent" : true, + "insertionIndex" : 19 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders_sell-order-details.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders_sell-order-details.json new file mode 100644 index 00000000000..53c9adb8878 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_orders_sell-order-details.json @@ -0,0 +1,15 @@ +{ + "id" : "b1d0529b-1196-4a9c-a74b-fe474197b70f", + "name" : "api_v4_spot_orders_425539509181", + "request" : { + "url" : "/api/v4/spot/orders/425539509181?currency_pair=VAI_USDT", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_orders_sell-order-details.json" + }, + "uuid" : "b1d0529b-1196-4a9c-a74b-fe474197b70f", + "persistent" : true, + "insertionIndex" : 35 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_tickers.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_tickers.json new file mode 100644 index 00000000000..8ca4adae167 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_tickers.json @@ -0,0 +1,15 @@ +{ + "id" : "b2066635-52eb-404f-880b-0d0b6f6714f3", + "name" : "api_v4_spot_tickers", + "request" : { + "url" : "/api/v4/spot/tickers", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_tickers.json" + }, + "uuid" : "b2066635-52eb-404f-880b-0d0b6f6714f3", + "persistent" : true, + "insertionIndex" : 26 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_tickers_btc-usdt.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_tickers_btc-usdt.json new file mode 100644 index 00000000000..af9e50793f5 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_spot_tickers_btc-usdt.json @@ -0,0 +1,15 @@ +{ + "id" : "9330316c-b079-4bea-9c63-186a5d5bc943", + "name" : "api_v4_spot_tickers", + "request" : { + "url" : "/api/v4/spot/tickers?currency_pair=BTC_USDT", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_spot_tickers_btc-usdt.json" + }, + "uuid" : "9330316c-b079-4bea-9c63-186a5d5bc943", + "persistent" : true, + "insertionIndex" : 27 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_currency_chains.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_currency_chains.json new file mode 100644 index 00000000000..cd0f446a281 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_currency_chains.json @@ -0,0 +1,15 @@ +{ + "id" : "19776ad5-01d9-4071-a458-530f6b562dd2", + "name" : "api_v4_wallet_currency_chains", + "request" : { + "url" : "/api/v4/wallet/currency_chains?currency=BTC", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_wallet_currency_chains.json" + }, + "uuid" : "19776ad5-01d9-4071-a458-530f6b562dd2", + "persistent" : true, + "insertionIndex" : 5 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_deposit_address-pending.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_deposit_address-pending.json new file mode 100644 index 00000000000..e12e789b1f8 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_deposit_address-pending.json @@ -0,0 +1,15 @@ +{ + "id" : "2cfb7047-1b43-4d6a-af84-d2cc987e16c0", + "name" : "api_v4_wallet_deposit_address", + "request" : { + "url" : "/api/v4/wallet/deposit_address?currency=ITA", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_wallet_deposit_address-pending.json" + }, + "uuid" : "2cfb7047-1b43-4d6a-af84-d2cc987e16c0", + "persistent" : true, + "insertionIndex" : 20 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_deposit_address-valid.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_deposit_address-valid.json new file mode 100644 index 00000000000..6f2b00e943a --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_deposit_address-valid.json @@ -0,0 +1,15 @@ +{ + "id" : "9dee83de-0703-43c6-a41d-4e74c90b6635", + "name" : "api_v4_wallet_deposit_address", + "request" : { + "url" : "/api/v4/wallet/deposit_address?currency=ABBC", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_wallet_deposit_address-valid.json" + }, + "uuid" : "9dee83de-0703-43c6-a41d-4e74c90b6635", + "persistent" : true, + "insertionIndex" : 28 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_deposits.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_deposits.json new file mode 100644 index 00000000000..1e7a7152db9 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_deposits.json @@ -0,0 +1,15 @@ +{ + "id" : "ace4cfb1-40e9-42ca-b7c9-eb6b97d70351", + "name" : "api_v4_wallet_deposits", + "request" : { + "url" : "/api/v4/wallet/deposits?from=1685833987&to=1688339587", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_wallet_deposits.json" + }, + "uuid" : "ace4cfb1-40e9-42ca-b7c9-eb6b97d70351", + "persistent" : true, + "insertionIndex" : 34 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_saved_address.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_saved_address.json new file mode 100644 index 00000000000..3b3671f9bed --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_saved_address.json @@ -0,0 +1,15 @@ +{ + "id" : "59a47010-ed6f-4950-9c65-ab57f2e23af3", + "name" : "api_v4_wallet_saved_address", + "request" : { + "url" : "/api/v4/wallet/saved_address?currency=USDT", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_wallet_saved_address.json" + }, + "uuid" : "59a47010-ed6f-4950-9c65-ab57f2e23af3", + "persistent" : true, + "insertionIndex" : 28 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_sub_account_transfers.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_sub_account_transfers.json new file mode 100644 index 00000000000..c05901d39dc --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_sub_account_transfers.json @@ -0,0 +1,15 @@ +{ + "id" : "0fa9195b-5201-4082-9876-cd2285cc96c2", + "name" : "api_v4_wallet_sub_account_transfers", + "request" : { + "url" : "/api/v4/wallet/sub_account_transfers", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_wallet_sub_account_transfers.json" + }, + "uuid" : "0fa9195b-5201-4082-9876-cd2285cc96c2", + "persistent" : true, + "insertionIndex" : 33 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_withdraw_status.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_withdraw_status.json new file mode 100644 index 00000000000..77870809d20 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_withdraw_status.json @@ -0,0 +1,15 @@ +{ + "id" : "4b5f7040-4613-4a00-b049-d313e9898515", + "name" : "api_v4_wallet_withdraw_status", + "request" : { + "url" : "/api/v4/wallet/withdraw_status", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_wallet_withdraw_status.json" + }, + "uuid" : "4b5f7040-4613-4a00-b049-d313e9898515", + "persistent" : true, + "insertionIndex" : 5 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_withdrawals-records.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_withdrawals-records.json new file mode 100644 index 00000000000..9f71f98cd27 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_wallet_withdrawals-records.json @@ -0,0 +1,15 @@ +{ + "id" : "27c5026f-0655-4b5a-a621-04dbc34684e5", + "name" : "api_v4_wallet_withdrawals", + "request" : { + "url" : "/api/v4/wallet/withdrawals", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_wallet_withdrawals-records.json" + }, + "uuid" : "27c5026f-0655-4b5a-a621-04dbc34684e5", + "persistent" : true, + "insertionIndex" : 21 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/api_v4_withdrawals-create-new.json b/xchange-gateio-v4/src/test/resources/mappings/api_v4_withdrawals-create-new.json new file mode 100644 index 00000000000..a46d87b5de3 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/api_v4_withdrawals-create-new.json @@ -0,0 +1,20 @@ +{ + "id" : "cb6973a2-5831-427d-9430-d5a7d01b9a4d", + "name" : "api_v4_withdrawals", + "request" : { + "url" : "/api/v4/withdrawals", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"withdraw_order_id\":\"valid-withdrawal-id\",\"amount\":3,\"currency\":\"USDT\",\"address\":\"6vLyxJ9dBziamyaw2vDcs9n2NwQdW1uk3aooJwrEscnA\",\"memo\":\"\",\"chain\":\"SOL\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_v4_withdrawals-create-new.json" + }, + "uuid" : "cb6973a2-5831-427d-9430-d5a7d01b9a4d", + "persistent" : true, + "insertionIndex" : 22 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_spot_orders-balance-not-enough.json b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_spot_orders-balance-not-enough.json new file mode 100644 index 00000000000..a20ad412554 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_spot_orders-balance-not-enough.json @@ -0,0 +1,20 @@ +{ + "id" : "87f03a6d-42bc-45a3-81c2-f817403bda44", + "name" : "api_v4_spot_orders", + "request" : { + "url" : "/api/v4/spot/orders", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"text\":\"t-balance-test\",\"currency_pair\":\"BTC_USDT\",\"type\":\"market\",\"side\":\"buy\",\"amount\":100,\"time_in_force\":\"ioc\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 400, + "bodyFileName" : "exceptions/api_v4_spot_orders-balance-not-enough.json" + }, + "uuid" : "87f03a6d-42bc-45a3-81c2-f817403bda44", + "persistent" : true, + "insertionIndex" : 17 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-forbidden.json b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-forbidden.json new file mode 100644 index 00000000000..0186263e651 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-forbidden.json @@ -0,0 +1,15 @@ +{ + "id" : "d6adff40-80fe-4dd1-827a-57de56020d50", + "name" : "api_v4_wallet_withdraw_status", + "request" : { + "url" : "/api/v4/wallet/withdraw_status?currency=RETURN-FORBIDDEN", + "method" : "GET" + }, + "response" : { + "status" : 403, + "bodyFileName" : "exceptions/api_v4_wallet_withdraw_status-forbidden.json" + }, + "uuid" : "d6adff40-80fe-4dd1-827a-57de56020d50", + "persistent" : true, + "insertionIndex" : 16 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-invalid_currency.json b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-invalid_currency.json new file mode 100644 index 00000000000..4bb6a9d94ed --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-invalid_currency.json @@ -0,0 +1,15 @@ +{ + "id" : "850454cc-bd9d-41de-ba6e-34717cddc6ac", + "name" : "api_v4_wallet_withdraw_status", + "request" : { + "url" : "/api/v4/wallet/withdraw_status?currency=INVALID-CURRENCY", + "method" : "GET" + }, + "response" : { + "status" : 400, + "bodyFileName" : "exceptions/api_v4_wallet_withdraw_status-invalid_currency.json" + }, + "uuid" : "850454cc-bd9d-41de-ba6e-34717cddc6ac", + "persistent" : true, + "insertionIndex" : 7 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-invalid_key.json b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-invalid_key.json new file mode 100644 index 00000000000..6372d8d758e --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-invalid_key.json @@ -0,0 +1,15 @@ +{ + "id" : "b6ca7675-7276-4e62-adb6-bc7a73e4769a", + "name" : "api_v4_wallet_withdraw_status", + "request" : { + "url" : "/api/v4/wallet/withdraw_status?currency=INVALID_KEY", + "method" : "GET" + }, + "response" : { + "status" : 401, + "bodyFileName" : "exceptions/api_v4_wallet_withdraw_status-invalid_key.json" + }, + "uuid" : "b6ca7675-7276-4e62-adb6-bc7a73e4769a", + "persistent" : true, + "insertionIndex" : 8 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-invalid_signature.json b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-invalid_signature.json new file mode 100644 index 00000000000..291318a8ef6 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status-invalid_signature.json @@ -0,0 +1,15 @@ +{ + "id" : "56aff0c3-822e-4e4c-adbd-45c89cbd5aad", + "name" : "api_v4_wallet_withdraw_status", + "request" : { + "url" : "/api/v4/wallet/withdraw_status?currency=THROW_401", + "method" : "GET" + }, + "response" : { + "status" : 401, + "bodyFileName" : "exceptions/api_v4_wallet_withdraw_status-invalid_signature.json" + }, + "uuid" : "56aff0c3-822e-4e4c-adbd-45c89cbd5aad", + "persistent" : true, + "insertionIndex" : 9 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status_internal-server-error.json b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status_internal-server-error.json new file mode 100644 index 00000000000..c6ea26a7c6e --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_wallet_withdraw_status_internal-server-error.json @@ -0,0 +1,15 @@ +{ + "id" : "000454cc-bd9d-41de-ba6e-34717cddc6ac", + "name" : "api_v4_spot_orders_internal-server-error", + "request" : { + "url" : "/api/v4/wallet/withdraw_status?currency=INTERNAL-SERVER-ERROR", + "method" : "GET" + }, + "response" : { + "status" : 500, + "bodyFileName" : "exceptions/api_v4_wallet_withdraw_status_internal-server-error.json" + }, + "uuid" : "000454cc-bd9d-41de-ba6e-34717cddc6ac", + "persistent" : true, + "insertionIndex" : 29 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_withdrawals_invalid-address.json b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_withdrawals_invalid-address.json new file mode 100644 index 00000000000..41f073e3a19 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_withdrawals_invalid-address.json @@ -0,0 +1,20 @@ +{ + "id" : "abed53fd-b0fb-46df-9bc7-07cb19f48a48", + "name" : "api_v4_withdrawals", + "request" : { + "url" : "/api/v4/withdrawals", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"withdraw_order_id\":\"invalid-address-id\",\"amount\":0,\"currency\":\"USDT\",\"address\":\"invalid-address\",\"memo\":\"\",\"chain\":\"SOL\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 400, + "bodyFileName" : "exceptions/api_v4_withdrawals_invalid-address.json" + }, + "uuid" : "abed53fd-b0fb-46df-9bc7-07cb19f48a48", + "persistent" : true, + "insertionIndex" : 25 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_withdrawals_min-amount.json b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_withdrawals_min-amount.json new file mode 100644 index 00000000000..b21d2339a29 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_withdrawals_min-amount.json @@ -0,0 +1,20 @@ +{ + "id" : "0a1e28bd-882a-47a2-b5fc-1b8b67e3c61c", + "name" : "api_v4_withdrawals", + "request" : { + "url" : "/api/v4/withdrawals", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"withdraw_order_id\":\"zero-amount-id\",\"amount\":0,\"currency\":\"USDT\",\"address\":\"6vLyxJ9dBziamyaw2vDcs9n2NwQdW1uk3aooJwrEscnA\",\"memo\":\"\",\"chain\":\"SOL\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 400, + "bodyFileName" : "exceptions/api_v4_withdrawals_min-amount.json" + }, + "uuid" : "0a1e28bd-882a-47a2-b5fc-1b8b67e3c61c", + "persistent" : true, + "insertionIndex" : 24 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_withdrawals_rate-limit.json b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_withdrawals_rate-limit.json new file mode 100644 index 00000000000..8a3805064c9 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/mappings/exceptions/api_v4_withdrawals_rate-limit.json @@ -0,0 +1,20 @@ +{ + "id" : "cb6973a2-5831-427d-9430-d5a7d01b9a4a", + "name" : "api_v4_withdrawals", + "request" : { + "url" : "/api/v4/withdrawals", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"withdraw_order_id\":\"rate-limited-id\",\"amount\":3,\"currency\":\"USDT\",\"address\":\"6vLyxJ9dBziamyaw2vDcs9n2NwQdW1uk3aooJwrEscnA\",\"memo\":\"\",\"chain\":\"SOL\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 400, + "bodyFileName" : "exceptions/api_v4_withdrawals_rate-limit.json" + }, + "uuid" : "cb6973a2-5831-427d-9430-d5a7d01b9a4a", + "persistent" : true, + "insertionIndex" : 23 +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/rest/sign.js b/xchange-gateio-v4/src/test/resources/rest/sign.js new file mode 100644 index 00000000000..3f84077e02d --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/rest/sign.js @@ -0,0 +1,16 @@ +export function gen_sign(method, request) { + const pattern = RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"); + const url = request.url.tryGetSubstituted(); + const matches = url.match(pattern); + + const path = matches[5]; + const query = matches[7] || ""; + const hexedHashedBody = crypto.sha512().updateWithText(request.body.tryGetSubstituted() || "").digest().toHex(); + const timestamp = Math.floor(Date.now() / 1000).toFixed(); + const payloadToSign = `${method}\n${path}\n${query}\n${hexedHashedBody}\n${timestamp}`; + const apiSecret = request.environment.get("api_secret"); + const sign = crypto.hmac.sha512().withTextSecret(apiSecret).updateWithText(payloadToSign).digest().toHex(); + + request.variables.set("timestamp", timestamp); + request.variables.set("sign", sign); +} \ No newline at end of file diff --git a/xchange-gateio-v4/src/test/resources/rest/spot.http b/xchange-gateio-v4/src/test/resources/rest/spot.http new file mode 100644 index 00000000000..f698d9c9e4e --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/rest/spot.http @@ -0,0 +1,38 @@ +### List personal trading history +< {% + import {gen_sign} from 'sign.js' + gen_sign("GET", request); +%} + +GET {{api_v4}}/spot/my_trades +KEY: {{api_key}} +SIGN: {{sign}} +Timestamp: {{timestamp}} +Content-Type: application/json + + +### Query account book +< {% + import {gen_sign} from 'sign.js' + gen_sign("GET", request); +%} + +GET {{api_v4}}/spot/account_book +KEY: {{api_key}} +SIGN: {{sign}} +Timestamp: {{timestamp}} +Content-Type: application/json + + +### List orders +< {% + import {gen_sign} from 'sign.js' + gen_sign("GET", request); +%} + +GET {{api_v4}}/spot/orders?currency_pair=VAI_USDT&status=finished +KEY: {{api_key}} +SIGN: {{sign}} +Timestamp: {{timestamp}} +Content-Type: application/json + diff --git a/xchange-gateio-v4/src/test/resources/rest/wallet.http b/xchange-gateio-v4/src/test/resources/rest/wallet.http new file mode 100644 index 00000000000..f267392faa5 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/rest/wallet.http @@ -0,0 +1,29 @@ +### Retrieve withdrawal status +< {% + import {gen_sign} from 'sign.js' + gen_sign("GET", request); +%} + +GET {{api_v4}}/wallet/withdraw_status?currency=usdt +KEY: {{api_key}} +SIGN: {{sign}} +Timestamp: {{timestamp}} +Content-Type: application/json + + +### Retrieve transfer records between main and sub accounts +< {% + import {gen_sign} from 'sign.js' + gen_sign("GET", request); +%} + +GET {{api_v4}}/wallet/sub_account_transfers +KEY: {{api_key}} +SIGN: {{sign}} +Timestamp: {{timestamp}} +Content-Type: application/json + + +### List chains supported for specified currency +GET {{api_v4}}/wallet/currency_chains?currency=BTC +Content-Type: application/json diff --git a/xchange-gateio-v4/src/test/resources/rest/withdraw.http b/xchange-gateio-v4/src/test/resources/rest/withdraw.http new file mode 100644 index 00000000000..081f29c2976 --- /dev/null +++ b/xchange-gateio-v4/src/test/resources/rest/withdraw.http @@ -0,0 +1,19 @@ +### Withdraw +< {% + import {gen_sign} from 'sign.js' + gen_sign("POST", request); +%} + +POST {{api_v4}}/withdrawals +KEY: {{api_key}} +SIGN: {{sign}} +Timestamp: {{timestamp}} +Content-Type: application/json + +{ + "amount": 0, + "currency": "usdt", + "address": "0x3dca2ae4d1d065220a731cf69f5a934914afc435", + "memo": "", + "chain": "BSC" +} diff --git a/xchange-kraken/src/main/java/org/knowm/xchange/kraken/KrakenAdapters.java b/xchange-kraken/src/main/java/org/knowm/xchange/kraken/KrakenAdapters.java index 111e6bd6995..5967272aa76 100644 --- a/xchange-kraken/src/main/java/org/knowm/xchange/kraken/KrakenAdapters.java +++ b/xchange-kraken/src/main/java/org/knowm/xchange/kraken/KrakenAdapters.java @@ -18,6 +18,7 @@ import org.knowm.xchange.dto.Order; import org.knowm.xchange.dto.Order.OrderStatus; import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.dto.account.AddressWithTag; import org.knowm.xchange.dto.account.Balance; import org.knowm.xchange.dto.account.Fee; import org.knowm.xchange.dto.account.FundingRecord; @@ -332,8 +333,12 @@ public static OrderType adaptOrderType(KrakenType krakenType) { return krakenType.equals(KrakenType.BUY) ? OrderType.BID : OrderType.ASK; } - public static String adaptKrakenDepositAddress(KrakenDepositAddress[] krakenDepositAddress) { - return krakenDepositAddress[0].getAddress(); + public static AddressWithTag adaptKrakenDepositAddress( + KrakenDepositAddress[] krakenDepositAddress) { + return AddressWithTag.builder() + .address(krakenDepositAddress[0].getAddress()) + .addressTag(krakenDepositAddress[0].getTag()) + .build(); } public static String adaptOrderId(KrakenOrderResponse orderResponse) { diff --git a/xchange-kraken/src/main/java/org/knowm/xchange/kraken/dto/account/LedgerType.java b/xchange-kraken/src/main/java/org/knowm/xchange/kraken/dto/account/LedgerType.java index 6f72ec6d4b9..bef033d0493 100644 --- a/xchange-kraken/src/main/java/org/knowm/xchange/kraken/dto/account/LedgerType.java +++ b/xchange-kraken/src/main/java/org/knowm/xchange/kraken/dto/account/LedgerType.java @@ -23,7 +23,10 @@ public enum LedgerType { STAKING, ROLLOVER, TRANSFER, - ADJUSTMENT; + ADJUSTMENT, + SALE, + SPEND, + RECEIVE; private static final Map fromString = new HashMap<>(); diff --git a/xchange-kraken/src/main/java/org/knowm/xchange/kraken/service/KrakenAccountService.java b/xchange-kraken/src/main/java/org/knowm/xchange/kraken/service/KrakenAccountService.java index 047f92aba3f..cf48b98301a 100644 --- a/xchange-kraken/src/main/java/org/knowm/xchange/kraken/service/KrakenAccountService.java +++ b/xchange-kraken/src/main/java/org/knowm/xchange/kraken/service/KrakenAccountService.java @@ -11,9 +11,12 @@ import org.knowm.xchange.currency.Currency; import org.knowm.xchange.currency.CurrencyPair; import org.knowm.xchange.dto.account.AccountInfo; +import org.knowm.xchange.dto.account.AddressWithTag; import org.knowm.xchange.dto.account.Fee; import org.knowm.xchange.dto.account.FundingRecord; import org.knowm.xchange.dto.account.Wallet; +import org.knowm.xchange.exceptions.DepositAddressCreationException; +import org.knowm.xchange.exceptions.DepositAddressNotFoundException; import org.knowm.xchange.instrument.Instrument; import org.knowm.xchange.kraken.KrakenAdapters; import org.knowm.xchange.kraken.KrakenUtils; @@ -22,6 +25,8 @@ import org.knowm.xchange.kraken.dto.account.KrakenTradeBalanceInfo; import org.knowm.xchange.kraken.dto.account.LedgerType; import org.knowm.xchange.service.account.AccountService; +import org.knowm.xchange.service.account.params.DefaultRequestDepositAddressParams; +import org.knowm.xchange.service.account.params.RequestDepositAddressParams; import org.knowm.xchange.service.trade.params.DefaultTradeHistoryParamsTimeSpan; import org.knowm.xchange.service.trade.params.DefaultWithdrawFundsParams; import org.knowm.xchange.service.trade.params.HistoryParamsFundingType; @@ -89,50 +94,88 @@ public String withdrawFunds(WithdrawFundsParams params) throws IOException { throw new IllegalStateException("Don't know how to withdraw: " + params); } + @Override + public AddressWithTag requestDepositAddressData(Currency currency, String... args) + throws IOException { + return requestDepositAddressData(DefaultRequestDepositAddressParams.create(currency, args)); + } + @Override public String requestDepositAddress(Currency currency, String... args) throws IOException { + return requestDepositAddressData(DefaultRequestDepositAddressParams.create(currency, args)) + .getAddress(); + } + + @Override + public String requestDepositAddress(RequestDepositAddressParams requestDepositAddressParams) + throws IOException { + return requestDepositAddressData(requestDepositAddressParams).getAddress(); + } + + @Override + public AddressWithTag requestDepositAddressData( + RequestDepositAddressParams requestDepositAddressParams) throws IOException { + Currency currency = requestDepositAddressParams.getCurrency(); + boolean newAddress = requestDepositAddressParams.isNewAddress(); + String depositMethod = null; + KrakenDepositAddress[] depositAddresses; if (Currency.BTC.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "Bitcoin", false); + depositAddresses = getDepositAddresses(currency.toString(), "Bitcoin", newAddress); } else if (Currency.LTC.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "Litecoin", false); + depositAddresses = getDepositAddresses(currency.toString(), "Litecoin", newAddress); } else if (Currency.ETH.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "Ethereum (ERC20)", false); + depositAddresses = getDepositAddresses(currency.toString(), "Ethereum (ERC20)", newAddress); } else if (Currency.ZEC.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "Zcash (Transparent)", false); + depositAddresses = + getDepositAddresses(currency.toString(), "Zcash (Transparent)", newAddress); } else if (Currency.ADA.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "ADA", false); + depositAddresses = getDepositAddresses(currency.toString(), "ADA", newAddress); } else if (Currency.XMR.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "Monero", false); + depositAddresses = getDepositAddresses(currency.toString(), "Monero", newAddress); } else if (Currency.XRP.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "Ripple XRP", false); + depositAddresses = getDepositAddresses(currency.toString(), "Ripple XRP", newAddress); } else if (Currency.XLM.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "Stellar XLM", false); + depositAddresses = getDepositAddresses(currency.toString(), "Stellar XLM", newAddress); } else if (Currency.BCH.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "Bitcoin Cash", false); + depositAddresses = getDepositAddresses(currency.toString(), "Bitcoin Cash", newAddress); } else if (Currency.REP.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "REP", false); + depositAddresses = getDepositAddresses(currency.toString(), "REP", newAddress); } else if (Currency.USD.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "SynapsePay (US Wire)", false); + depositAddresses = + getDepositAddresses(currency.toString(), "SynapsePay (US Wire)", newAddress); } else if (Currency.XDG.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "Dogecoin", false); + depositAddresses = getDepositAddresses(currency.toString(), "Dogecoin", newAddress); } else if (Currency.MLN.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "MLN", false); + depositAddresses = getDepositAddresses(currency.toString(), "MLN", newAddress); } else if (Currency.GNO.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "GNO", false); + depositAddresses = getDepositAddresses(currency.toString(), "GNO", newAddress); } else if (Currency.QTUM.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "QTUM", false); + depositAddresses = getDepositAddresses(currency.toString(), "QTUM", newAddress); } else if (Currency.XTZ.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "XTZ", false); + depositAddresses = getDepositAddresses(currency.toString(), "XTZ", newAddress); } else if (Currency.ATOM.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "Cosmos", false); + depositAddresses = getDepositAddresses(currency.toString(), "Cosmos", newAddress); } else if (Currency.EOS.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "EOS", false); + depositAddresses = getDepositAddresses(currency.toString(), "EOS", newAddress); } else if (Currency.DASH.equals(currency)) { - depositAddresses = getDepositAddresses(currency.toString(), "Dash", false); + depositAddresses = getDepositAddresses(currency.toString(), "Dash", newAddress); } else { - throw new RuntimeException("Not implemented yet, Kraken works only for BTC and LTC"); + depositMethod = findDepositMethod(currency, requestDepositAddressParams.getNetwork()); + depositAddresses = getDepositAddresses(currency.toString(), depositMethod, newAddress); + } + + if (depositAddresses.length == 0 && !newAddress) { + throw new DepositAddressNotFoundException( + String.format("No deposit addresses found for %s method: %s", currency, depositMethod)); + } + + if (depositAddresses.length == 0) { + throw new DepositAddressCreationException( + String.format( + "Deposit address could not be created for %s method: %s", currency, depositMethod)); } + return KrakenAdapters.adaptKrakenDepositAddress(depositAddresses); } diff --git a/xchange-kraken/src/main/java/org/knowm/xchange/kraken/service/KrakenAccountServiceRaw.java b/xchange-kraken/src/main/java/org/knowm/xchange/kraken/service/KrakenAccountServiceRaw.java index c816d4297f6..726690c2fc9 100644 --- a/xchange-kraken/src/main/java/org/knowm/xchange/kraken/service/KrakenAccountServiceRaw.java +++ b/xchange-kraken/src/main/java/org/knowm/xchange/kraken/service/KrakenAccountServiceRaw.java @@ -2,12 +2,17 @@ import java.io.IOException; import java.math.BigDecimal; +import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; import org.knowm.xchange.Exchange; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.exceptions.DepositAddressAmbiguousException; import org.knowm.xchange.kraken.KrakenUtils; import org.knowm.xchange.kraken.dto.account.DepostitStatus; import org.knowm.xchange.kraken.dto.account.KrakenDepositAddress; @@ -23,7 +28,6 @@ import org.knowm.xchange.kraken.dto.account.results.DepositStatusResult; import org.knowm.xchange.kraken.dto.account.results.KrakenBalanceResult; import org.knowm.xchange.kraken.dto.account.results.KrakenDepositAddressResult; -import org.knowm.xchange.kraken.dto.account.results.KrakenDepositMethodsResults; import org.knowm.xchange.kraken.dto.account.results.KrakenLedgerResult; import org.knowm.xchange.kraken.dto.account.results.KrakenQueryLedgerResult; import org.knowm.xchange.kraken.dto.account.results.KrakenTradeBalanceInfoResult; @@ -37,15 +41,18 @@ /** * @author jamespedwards42 */ +@Slf4j public class KrakenAccountServiceRaw extends KrakenBaseService { + private ConcurrentHashMap depositMethods = + new ConcurrentHashMap<>(); + /** * Constructor * * @param exchange */ public KrakenAccountServiceRaw(Exchange exchange) { - super(exchange); } @@ -82,14 +89,64 @@ public KrakenDepositAddress[] getDepositAddresses( public KrakenDepositMethods[] getDepositMethods(String assetPairs, String assets) throws IOException { - KrakenDepositMethodsResults depositMethods = + if (shouldCacheDepositMethods()) { + try { + return depositMethods.computeIfAbsent( + String.format("%s%s", assetPairs, assets), + k -> { + try { + return getDepositMethodsFromRemote(assetPairs, assets); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } catch (RuntimeException e) { + if (e.getCause() instanceof IOException) { + throw (IOException) e.getCause(); + } + throw e; + } + } + + return getDepositMethodsFromRemote(assetPairs, assets); + } + + private KrakenDepositMethods[] getDepositMethodsFromRemote(String assetPairs, String assets) + throws IOException { + return checkResult( kraken.getDepositMethods( assetPairs, assets, exchange.getExchangeSpecification().getApiKey(), signatureCreator, - exchange.getNonceFactory()); - return checkResult(depositMethods); + exchange.getNonceFactory())); + } + + protected String findDepositMethod(Currency currency, String network) throws IOException { + KrakenDepositMethods[] depositMethods = getDepositMethods(null, currency.toString()); + + if (depositMethods == null || depositMethods.length == 0) { + return network; + } + + if (depositMethods.length == 1) { + return depositMethods[0].getMethod(); + } + + log.warn( + "Multiple methods for currency {} {}", + currency, + Arrays.stream(depositMethods).map(KrakenDepositMethods::getMethod).toArray()); + + if (network == null) { + throw new DepositAddressAmbiguousException( + Arrays.stream(depositMethods) + .map(KrakenDepositMethods::getMethod) + .collect(Collectors.toList()), + "Multiple deposit methods available for " + currency + ", require to specify network"); + } + + return network; // Use network if requested } public WithdrawInfo getWithdrawInfo( @@ -305,4 +362,12 @@ public KrakenTradeVolume getTradeVolume(CurrencyPair... currencyPairs) throws IO exchange.getNonceFactory()); return checkResult(result); } + + private boolean shouldCacheDepositMethods() { + return (boolean) + exchange + .getExchangeSpecification() + .getExchangeSpecificParameters() + .getOrDefault("cacheDepositMethods", false); + } } diff --git a/xchange-kraken/src/test/java/org/knowm/xchange/kraken/service/BaseWiremockTest.java b/xchange-kraken/src/test/java/org/knowm/xchange/kraken/service/BaseWiremockTest.java index 68fb2ee2515..6c5edf6f182 100644 --- a/xchange-kraken/src/test/java/org/knowm/xchange/kraken/service/BaseWiremockTest.java +++ b/xchange-kraken/src/test/java/org/knowm/xchange/kraken/service/BaseWiremockTest.java @@ -2,8 +2,11 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import com.fasterxml.jackson.databind.ObjectMapper; import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.google.common.collect.ImmutableMap; +import lombok.SneakyThrows; +import org.apache.commons.io.IOUtils; import org.junit.Rule; import org.knowm.xchange.Exchange; import org.knowm.xchange.ExchangeFactory; @@ -17,6 +20,8 @@ public class BaseWiremockTest { @Rule public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort()); + private final ObjectMapper objectMapper = new ObjectMapper(); + public Exchange createExchange() { KrakenUtils.setKrakenAssets(ASSETS); KrakenUtils.setKrakenAssetPairs(ASSET_PAIRS); @@ -31,6 +36,11 @@ public Exchange createExchange() { return exchange; } + @SneakyThrows + protected byte[] loadFile(String path) { + return IOUtils.toByteArray(getClass().getResourceAsStream(path)); + } + public static final ImmutableMap ASSETS = ImmutableMap.of( "XXBT", new KrakenAsset("XBT", "currency", 8, 6), diff --git a/xchange-kraken/src/test/java/org/knowm/xchange/kraken/service/KrakenAccountServiceTest.java b/xchange-kraken/src/test/java/org/knowm/xchange/kraken/service/KrakenAccountServiceTest.java new file mode 100644 index 00000000000..8f1d262a402 --- /dev/null +++ b/xchange-kraken/src/test/java/org/knowm/xchange/kraken/service/KrakenAccountServiceTest.java @@ -0,0 +1,213 @@ +package org.knowm.xchange.kraken.service; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.knowm.xchange.Exchange; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.dto.account.AddressWithTag; +import org.knowm.xchange.exceptions.DepositAddressAmbiguousException; +import org.knowm.xchange.service.account.params.DefaultRequestDepositAddressParams; + +@Slf4j +public class KrakenAccountServiceTest extends BaseWiremockTest { + + private KrakenAccountService classUnderTest; + private Exchange exchange; + + @Before + public void setup() { + exchange = createExchange(); + classUnderTest = (KrakenAccountService) exchange.getAccountService(); + } + + @Test + @SneakyThrows + public void testRequestDepositAddressUnknownCurrency() { + stubFor( + post(urlPathEqualTo("/0/private/DepositMethods")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + loadFile( + "/org/knowm/xchange/kraken/dto/account/example-deposit-methods-trx.json")))); + + stubFor( + post(urlPathEqualTo("/0/private/DepositAddresses")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + loadFile( + "/org/knowm/xchange/kraken/dto/account/example-deposit-addresses-trx.json")))); + + DefaultRequestDepositAddressParams params = + DefaultRequestDepositAddressParams.builder().currency(Currency.TRX).build(); + + String address = classUnderTest.requestDepositAddress(params); + + assertThat(address).isEqualTo("TYAnp8VW1aq5Jbtxgoai7BDo3jKSRe6VNR"); + } + + @Test + @SneakyThrows + public void testRequestDepositAddressKnownCurrency() { + stubFor( + post(urlPathEqualTo("/0/private/DepositAddresses")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + loadFile( + "/org/knowm/xchange/kraken/dto/account/example-deposit-addresses.json")))); + + DefaultRequestDepositAddressParams params = + DefaultRequestDepositAddressParams.builder().currency(Currency.BTC).build(); + + String address = classUnderTest.requestDepositAddress(params); + + assertThat(address).isEqualTo("testBtcAddress"); + } + + @Test + @SneakyThrows + public void testRequestDepositAddressUnknownCurrencyMultipleMethods() { + stubFor( + post(urlPathEqualTo("/0/private/DepositMethods")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + loadFile( + "/org/knowm/xchange/kraken/dto/account/example-deposit-methods-usdt.json")))); + + assertThatThrownBy( + () -> { + DefaultRequestDepositAddressParams params = + DefaultRequestDepositAddressParams.builder().currency(Currency.USDT).build(); + + classUnderTest.requestDepositAddress(params); + }) + .isInstanceOf(DepositAddressAmbiguousException.class); + } + + @Test + @SneakyThrows + public void testRequestDepositAddressUnknownCurrencyMultipleMethodsWithNetwork() { + stubFor( + post(urlPathEqualTo("/0/private/DepositMethods")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + loadFile( + "/org/knowm/xchange/kraken/dto/account/example-deposit-methods-xrp.json")))); + + stubFor( + post(urlPathEqualTo("/0/private/DepositAddresses")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + loadFile( + "/org/knowm/xchange/kraken/dto/account/example-deposit-addresses-xrp.json")))); + + DefaultRequestDepositAddressParams params = + DefaultRequestDepositAddressParams.builder().currency(Currency.XRP).build(); + + AddressWithTag address = classUnderTest.requestDepositAddressData(params); + + assertThat(address.getAddress()).isEqualTo("testXrpAddress"); + assertThat(address.getAddressTag()).isEqualTo("123"); + } + + @Test + @SneakyThrows + public void testRequestDepositMethodCaching() { + stubFor( + post(urlPathEqualTo("/0/private/DepositMethods")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + loadFile( + "/org/knowm/xchange/kraken/dto/account/example-deposit-methods-trx.json")))); + + stubFor( + post(urlPathEqualTo("/0/private/DepositAddresses")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + loadFile( + "/org/knowm/xchange/kraken/dto/account/example-deposit-addresses-trx.json")))); + + exchange + .getExchangeSpecification() + .setExchangeSpecificParametersItem("cacheDepositMethods", true); + + DefaultRequestDepositAddressParams params = + DefaultRequestDepositAddressParams.builder().currency(Currency.TRX).build(); + + classUnderTest.requestDepositAddress(params); + classUnderTest.requestDepositAddress(params); + + verify(1, postRequestedFor(urlEqualTo("/0/private/DepositMethods"))); + } + + @Test + @SneakyThrows + public void testRequestDepositMethodNoCache() { + stubFor( + post(urlPathEqualTo("/0/private/DepositMethods")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + loadFile( + "/org/knowm/xchange/kraken/dto/account/example-deposit-methods-trx.json")))); + + stubFor( + post(urlPathEqualTo("/0/private/DepositAddresses")) + .willReturn( + aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + loadFile( + "/org/knowm/xchange/kraken/dto/account/example-deposit-addresses-trx.json")))); + + DefaultRequestDepositAddressParams params = + DefaultRequestDepositAddressParams.builder().currency(Currency.TRX).build(); + + exchange + .getExchangeSpecification() + .setExchangeSpecificParametersItem("cacheDepositMethods", false); + + classUnderTest.requestDepositAddress(params); + classUnderTest.requestDepositAddress(params); + + verify(2, postRequestedFor(urlEqualTo("/0/private/DepositMethods"))); + } +} diff --git a/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-addresses-trx.json b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-addresses-trx.json new file mode 100644 index 00000000000..406a9f0af40 --- /dev/null +++ b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-addresses-trx.json @@ -0,0 +1,10 @@ +{ + "error": [], + "result": [ + { + "address": "TYAnp8VW1aq5Jbtxgoai7BDo3jKSRe6VNR", + "expiretm": "0", + "new": true + } + ] +} \ No newline at end of file diff --git a/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-addresses-xrp.json b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-addresses-xrp.json new file mode 100644 index 00000000000..2ba6c97210f --- /dev/null +++ b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-addresses-xrp.json @@ -0,0 +1,11 @@ +{ + "error": [], + "result": [ + { + "address": "testXrpAddress", + "expiretm": "0", + "new": true, + "tag": "123" + } + ] +} \ No newline at end of file diff --git a/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-addresses.json b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-addresses.json new file mode 100644 index 00000000000..531c0c67317 --- /dev/null +++ b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-addresses.json @@ -0,0 +1,10 @@ +{ + "error": [], + "result": [ + { + "address": "testBtcAddress", + "expiretm": "0", + "new": true + } + ] +} \ No newline at end of file diff --git a/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-methods-trx.json b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-methods-trx.json new file mode 100644 index 00000000000..48e4bd47000 --- /dev/null +++ b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-methods-trx.json @@ -0,0 +1,11 @@ +{ + "error": [], + "result": [ + { + "method": "Tron", + "limit": false, + "gen-address": true, + "minimum": "2.000000" + } + ] +} \ No newline at end of file diff --git a/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-methods-usdt.json b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-methods-usdt.json new file mode 100644 index 00000000000..8a5498cef1d --- /dev/null +++ b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-methods-usdt.json @@ -0,0 +1,42 @@ +{ + "error": [], + "result": [ + { + "method": "USDT - Ethereum (Unified)", + "limit": false, + "gen-address": true, + "minimum": "7.11000000" + }, + { + "method": "Tether USD (TRC20)", + "limit": false, + "gen-address": true, + "minimum": "5.00000000" + }, + { + "method": "Tether USD (SPL)", + "limit": false, + "fee": "0.00000000", + "gen-address": true, + "minimum": "0.40000000" + }, + { + "method": "USDT - Polygon (Unified)", + "limit": false, + "gen-address": true, + "minimum": "2.00000000" + }, + { + "method": "USDT - Arbitrum One (Unified)", + "limit": false, + "gen-address": true, + "minimum": "2.50000000" + }, + { + "method": "USDT - Optimism (Unified)", + "limit": false, + "gen-address": true, + "minimum": "2.50000000" + } + ] +} diff --git a/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-methods-xrp.json b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-methods-xrp.json new file mode 100644 index 00000000000..e435b6cb859 --- /dev/null +++ b/xchange-kraken/src/test/resources/org/knowm/xchange/kraken/dto/account/example-deposit-methods-xrp.json @@ -0,0 +1,11 @@ +{ + "error": [], + "result": [ + { + "method": "Ripple XRP", + "limit": false, + "gen-address": true, + "minimum": "1" + } + ] +} diff --git a/xchange-stream-bankera/pom.xml b/xchange-stream-bankera/pom.xml deleted file mode 100644 index ca312e01464..00000000000 --- a/xchange-stream-bankera/pom.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - 4.0.0 - - - org.knowm.xchange - xchange-parent - 5.1.2-SNAPSHOT - - - XChange Bankera Stream - xchange-stream-bankera - - - - org.knowm.xchange - xchange-stream-core - ${project.parent.version} - - - org.knowm.xchange - xchange-stream-service-netty - ${project.parent.version} - - - org.knowm.xchange - xchange-bankera - ${project.parent.version} - - - \ No newline at end of file diff --git a/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/BankeraStreamingExchange.java b/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/BankeraStreamingExchange.java deleted file mode 100644 index 3dfa3052408..00000000000 --- a/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/BankeraStreamingExchange.java +++ /dev/null @@ -1,78 +0,0 @@ -package info.bitrich.xchangestream.bankera; - -import info.bitrich.xchangestream.core.ProductSubscription; -import info.bitrich.xchangestream.core.StreamingExchange; -import info.bitrich.xchangestream.core.StreamingMarketDataService; -import info.bitrich.xchangestream.service.netty.ConnectionStateModel.State; -import io.reactivex.Completable; -import io.reactivex.Observable; -import org.knowm.xchange.ExchangeSpecification; -import org.knowm.xchange.bankera.BankeraExchange; -import org.knowm.xchange.bankera.service.BankeraMarketDataService; - -public class BankeraStreamingExchange extends BankeraExchange implements StreamingExchange { - - private static final String WS_URI = "wss://api-exchange.bankera.com/ws"; - private final BankeraStreamingService streamingService; - private BankeraStreamingMarketDataService streamingMarketDataService; - - public BankeraStreamingExchange() { - this.streamingService = new BankeraStreamingService(WS_URI); - } - - @Override - protected void initServices() { - super.initServices(); - streamingMarketDataService = - new BankeraStreamingMarketDataService( - streamingService, (BankeraMarketDataService) marketDataService); - } - - @Override - public Completable connect(ProductSubscription... args) { - return streamingService.connect(); - } - - @Override - public Completable disconnect() { - return streamingService.disconnect(); - } - - @Override - public boolean isAlive() { - return streamingService.isSocketOpen(); - } - - @Override - public Observable reconnectFailure() { - return streamingService.subscribeReconnectFailure(); - } - - @Override - public Observable connectionSuccess() { - return streamingService.subscribeConnectionSuccess(); - } - - @Override - public Observable connectionStateObservable() { - return streamingService.subscribeConnectionState(); - } - - @Override - public ExchangeSpecification getDefaultExchangeSpecification() { - ExchangeSpecification spec = super.getDefaultExchangeSpecification(); - spec.setShouldLoadRemoteMetaData(false); - - return spec; - } - - @Override - public StreamingMarketDataService getStreamingMarketDataService() { - return streamingMarketDataService; - } - - @Override - public void useCompressedMessages(boolean compressedMessages) { - streamingService.useCompressedMessages(compressedMessages); - } -} diff --git a/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/BankeraStreamingMarketDataService.java b/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/BankeraStreamingMarketDataService.java deleted file mode 100644 index 77c88da4eda..00000000000 --- a/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/BankeraStreamingMarketDataService.java +++ /dev/null @@ -1,104 +0,0 @@ -package info.bitrich.xchangestream.bankera; - -import info.bitrich.xchangestream.core.StreamingMarketDataService; -import io.reactivex.Observable; -import java.io.IOException; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Optional; -import org.knowm.xchange.bankera.BankeraAdapters; -import org.knowm.xchange.bankera.dto.BankeraException; -import org.knowm.xchange.bankera.dto.marketdata.BankeraMarket; -import org.knowm.xchange.bankera.dto.marketdata.BankeraMarketInfo; -import org.knowm.xchange.bankera.dto.marketdata.BankeraOrderBook; -import org.knowm.xchange.bankera.service.BankeraMarketDataService; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.Order; -import org.knowm.xchange.dto.marketdata.OrderBook; -import org.knowm.xchange.dto.marketdata.Ticker; -import org.knowm.xchange.dto.marketdata.Trade; -import org.knowm.xchange.exceptions.NotAvailableFromExchangeException; - -public class BankeraStreamingMarketDataService implements StreamingMarketDataService { - - private final BankeraStreamingService service; - private final BankeraMarketDataService marketDataService; - - public BankeraStreamingMarketDataService( - BankeraStreamingService service, BankeraMarketDataService marketDataService) { - this.service = service; - this.marketDataService = marketDataService; - } - - @Override - public Observable getOrderBook(CurrencyPair currencyPair, Object... args) { - BankeraMarket market = getMarketInfo(currencyPair); - return service - .subscribeChannel("market-orderbook", market.getId()) - .map( - o -> { - List listBids = new ArrayList<>(); - List listAsks = new ArrayList<>(); - o.get("data") - .get("bids") - .forEach( - b -> - listBids.add( - new BankeraOrderBook.OrderBookOrder( - 0, b.get("price").asText(), b.get("amount").asText()))); - o.get("data") - .get("asks") - .forEach( - b -> - listAsks.add( - new BankeraOrderBook.OrderBookOrder( - 0, b.get("price").asText(), b.get("amount").asText()))); - return BankeraAdapters.adaptOrderBook( - new BankeraOrderBook(listBids, listAsks), currencyPair); - }); - } - - @Override - public Observable getTicker(CurrencyPair currencyPair, Object... args) { - throw new NotAvailableFromExchangeException(); - } - - @Override - public Observable getTrades(CurrencyPair currencyPair, Object... args) { - BankeraMarket market = getMarketInfo(currencyPair); - return service - .subscribeChannel("market-trade", market.getId()) - .map( - t -> - new Trade.Builder() - .currencyPair(currencyPair) - .id("-1") - .price(new BigDecimal(t.get("data").get("price").asText())) - .originalAmount(new BigDecimal(t.get("data").get("amount").asText())) - .timestamp(new Date(t.get("data").get("time").asLong())) - .type( - t.get("data").get("side").asText().equals("SELL") - ? Order.OrderType.ASK - : Order.OrderType.BID) - .build()); - } - - private BankeraMarket getMarketInfo(CurrencyPair currencyPair) { - try { - BankeraMarketInfo info = this.marketDataService.getMarketInfo(); - Optional market = - info.getMarkets().stream() - .filter(m -> m.getName().equals(currencyPair.toString().replace("/", "-"))) - .findFirst(); - - if (market.isPresent()) { - return market.get(); - } - throw new BankeraException(404, "Unable to find market."); - } catch (IOException e) { - throw new BankeraException(404, "Unable to find market."); - } - } -} diff --git a/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/BankeraStreamingService.java b/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/BankeraStreamingService.java deleted file mode 100644 index 44f8fe64558..00000000000 --- a/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/BankeraStreamingService.java +++ /dev/null @@ -1,37 +0,0 @@ -package info.bitrich.xchangestream.bankera; - -import com.fasterxml.jackson.databind.JsonNode; -import info.bitrich.xchangestream.bankera.dto.BankeraWebSocketSubscriptionMessage; -import info.bitrich.xchangestream.service.netty.JsonNettyStreamingService; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler; -import java.io.IOException; - -public class BankeraStreamingService extends JsonNettyStreamingService { - - public BankeraStreamingService(String uri) { - super(uri, Integer.MAX_VALUE); - } - - @Override - protected String getChannelNameFromMessage(JsonNode message) throws IOException { - return message.get("type").asText(); - } - - @Override - public String getSubscribeMessage(String channelName, Object... args) throws IOException { - if (args.length != 1) throw new IOException("SubscribeMessage: Insufficient arguments"); - BankeraWebSocketSubscriptionMessage subscribeMessage = - new BankeraWebSocketSubscriptionMessage(String.valueOf(args[0])); - return objectMapper.writeValueAsString(subscribeMessage); - } - - @Override - public String getUnsubscribeMessage(String channelName, Object... args) throws IOException { - return null; - } - - @Override - protected WebSocketClientExtensionHandler getWebSocketClientExtensionHandler() { - return null; - } -} diff --git a/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/dto/BankeraWebSocketSubscriptionMessage.java b/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/dto/BankeraWebSocketSubscriptionMessage.java deleted file mode 100644 index 3ad9d635e9e..00000000000 --- a/xchange-stream-bankera/src/main/java/info/bitrich/xchangestream/bankera/dto/BankeraWebSocketSubscriptionMessage.java +++ /dev/null @@ -1,39 +0,0 @@ -package info.bitrich.xchangestream.bankera.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class BankeraWebSocketSubscriptionMessage { - - @JsonProperty("e") - private String event; - - @JsonProperty("marketId") - private String marketId; - - @JsonProperty("chartInterval") - private String chartInterval; - - public BankeraWebSocketSubscriptionMessage(String marketId) { - this.event = "market"; - this.marketId = marketId; - this.chartInterval = "1m"; - } - - public BankeraWebSocketSubscriptionMessage(String marketId, String chartInterval) { - this.event = "market"; - this.marketId = marketId; - this.chartInterval = chartInterval; - } - - public String getEvent() { - return event; - } - - public String getMarketId() { - return marketId; - } - - public String getChartInterval() { - return chartInterval; - } -} diff --git a/xchange-stream-bankera/src/test/java/info/bitrich/xchangestream/bankera/BankeraManualExample.java b/xchange-stream-bankera/src/test/java/info/bitrich/xchangestream/bankera/BankeraManualExample.java deleted file mode 100644 index e98b7605eb1..00000000000 --- a/xchange-stream-bankera/src/test/java/info/bitrich/xchangestream/bankera/BankeraManualExample.java +++ /dev/null @@ -1,39 +0,0 @@ -package info.bitrich.xchangestream.bankera; - -import info.bitrich.xchangestream.core.StreamingExchange; -import info.bitrich.xchangestream.core.StreamingExchangeFactory; -import org.knowm.xchange.currency.CurrencyPair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class BankeraManualExample { - private static final Logger LOGGER = LoggerFactory.getLogger(BankeraManualExample.class); - - public static void main(String[] args) { - StreamingExchange exchange = - StreamingExchangeFactory.INSTANCE.createExchange(BankeraStreamingExchange.class); - - exchange.connect().blockingAwait(); - exchange - .getStreamingMarketDataService() - .getOrderBook(CurrencyPair.ETH_BTC) - .subscribe( - orderBook -> LOGGER.debug("ORDERBOOK: {}", orderBook.toString()), - throwable -> LOGGER.error("ERROR in getting order book: ", throwable)); - - exchange - .getStreamingMarketDataService() - .getTrades(CurrencyPair.ETH_BTC) - .subscribe( - trade -> LOGGER.debug("TRADES: {}", trade.toString()), - throwable -> LOGGER.error("ERROR in getting trade ", throwable)); - - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - exchange.disconnect().subscribe(() -> LOGGER.info("Disconnected")); - } -} diff --git a/xchange-stream-bankera/src/test/resources/logback.xml b/xchange-stream-bankera/src/test/resources/logback.xml deleted file mode 100644 index 5809b573500..00000000000 --- a/xchange-stream-bankera/src/test/resources/logback.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - %d{HH:mm:ss.SSS} [%contextName] [%thread] %-5level %logger{36} - %msg %xEx%n - - - - - - - - - - - - - - diff --git a/xchange-stream-bybit/pom.xml b/xchange-stream-bybit/pom.xml new file mode 100644 index 00000000000..71b2943843b --- /dev/null +++ b/xchange-stream-bybit/pom.xml @@ -0,0 +1,31 @@ + + + + 4.0.0 + + + org.knowm.xchange + xchange-parent + 5.1.2-SNAPSHOT + + + XChange Bybit Stream + xchange-stream-bybit + + + + org.knowm.xchange + xchange-stream-core + ${project.parent.version} + + + org.knowm.xchange + xchange-bybit + ${project.parent.version} + + + + + \ No newline at end of file diff --git a/xchange-stream-bybit/src/main/java/dto/BybitSubscribeMessage.java b/xchange-stream-bybit/src/main/java/dto/BybitSubscribeMessage.java new file mode 100644 index 00000000000..e3c01595d52 --- /dev/null +++ b/xchange-stream-bybit/src/main/java/dto/BybitSubscribeMessage.java @@ -0,0 +1,13 @@ +package dto; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class BybitSubscribeMessage { + private final String op; + private final List args; + } + diff --git a/xchange-stream-bybit/src/main/java/dto/marketdata/BybitOrderbook.java b/xchange-stream-bybit/src/main/java/dto/marketdata/BybitOrderbook.java new file mode 100644 index 00000000000..7ba6df89560 --- /dev/null +++ b/xchange-stream-bybit/src/main/java/dto/marketdata/BybitOrderbook.java @@ -0,0 +1,22 @@ +package dto.marketdata; + +import java.beans.ConstructorProperties; +import lombok.Getter; + +@Getter +public class BybitOrderbook { + + private final String topic; + private final String dataType; + private final String ts; + private final BybitOrderbookData data; + + @ConstructorProperties({"topic","type","ts","data"}) + public BybitOrderbook(String topic, String dataType, String ts, BybitOrderbookData data) { + this.topic = topic; + this.dataType = dataType; + this.ts = ts; + this.data = data; + } + +} diff --git a/xchange-stream-bybit/src/main/java/dto/marketdata/BybitOrderbookData.java b/xchange-stream-bybit/src/main/java/dto/marketdata/BybitOrderbookData.java new file mode 100644 index 00000000000..fc613a392ce --- /dev/null +++ b/xchange-stream-bybit/src/main/java/dto/marketdata/BybitOrderbookData.java @@ -0,0 +1,33 @@ +package dto.marketdata; + +import java.beans.ConstructorProperties; +import java.util.List; +import lombok.Getter; + +@Getter +public class BybitOrderbookData { + + private final String symbolName; + private final List bid; + private final List ask; + // Update ID. Is a sequence. Occasionally, you'll receive "u"=1, which is a snapshot data due to + // the restart of the service. So please overwrite your local orderbook + private final Integer u; + // Cross sequence + // You can use this field to compare different levels orderbook data, and for the smaller seq, + // then it means the data is generated earlier. + // in docs says than it is Integer, but in fact, we get in response bigger numbers + private final Long seq; + + @ConstructorProperties({"s", "b", "a", "u", "seq"}) + public BybitOrderbookData(String symbolName, List bid, + List ask, + Integer u, + Long seq) { + this.symbolName = symbolName; + this.bid = bid; + this.ask = ask; + this.u = u; + this.seq = seq; + } +} diff --git a/xchange-stream-bybit/src/main/java/dto/marketdata/BybitPublicOrder.java b/xchange-stream-bybit/src/main/java/dto/marketdata/BybitPublicOrder.java new file mode 100644 index 00000000000..55954ca82a1 --- /dev/null +++ b/xchange-stream-bybit/src/main/java/dto/marketdata/BybitPublicOrder.java @@ -0,0 +1,18 @@ +package dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonCreator; +import lombok.Getter; + +@Getter +public class BybitPublicOrder { + + private final String price; + private final String size; + + @JsonCreator + public BybitPublicOrder(String[] data) { + this.price = data[0]; + this.size = data[1]; + } +} + diff --git a/xchange-stream-bybit/src/main/java/dto/trade/BybitTrade.java b/xchange-stream-bybit/src/main/java/dto/trade/BybitTrade.java new file mode 100644 index 00000000000..a0ac503f2ef --- /dev/null +++ b/xchange-stream-bybit/src/main/java/dto/trade/BybitTrade.java @@ -0,0 +1,42 @@ +package dto.trade; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Getter; +import org.knowm.xchange.bybit.dto.trade.BybitSide; + +@Getter +public class BybitTrade { + + //https://bybit-exchange.github.io/docs/v5/websocket/public/trad + //The timestamp (ms) that the order is filled + private final Date timestamp; + //Symbol name + private final String instId; + //Side of taker. Buy,Sell + private final BybitSide side; + private final BigDecimal tradeSize; + private final BigDecimal tradePrice; + //L string Direction of price change. Unique field for future + private final String direction; + private final String tradeId; + //boolean Whether it is a block trade order or not + private final boolean bT; + + + public BybitTrade(@JsonProperty("T") Date timestamp, @JsonProperty("s") String instId, + @JsonProperty("S") String side, @JsonProperty("v") BigDecimal tradeSize, + @JsonProperty("p") BigDecimal tradePrice, @JsonProperty("L") String direction, + @JsonProperty("i") String tradeId, @JsonProperty("BT") boolean bT) { + this.timestamp = timestamp; + this.instId = instId; + this.side = BybitSide.valueOf(side.toUpperCase()); + this.tradeSize = tradeSize; + this.tradePrice = tradePrice; + this.direction = direction; + this.tradeId = tradeId; + this.bT = bT; + } +} + diff --git a/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamAdapters.java b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamAdapters.java new file mode 100644 index 00000000000..d5b1808cc01 --- /dev/null +++ b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamAdapters.java @@ -0,0 +1,62 @@ +package info.bitrich.xchangestream.bybit; + +import static org.knowm.xchange.bybit.BybitAdapters.getOrderType; + +import dto.marketdata.BybitPublicOrder; +import dto.trade.BybitTrade; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import dto.marketdata.BybitOrderbook; +import org.knowm.xchange.dto.Order; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.dto.marketdata.OrderBook; +import org.knowm.xchange.dto.marketdata.Trade; +import org.knowm.xchange.dto.marketdata.Trades; +import org.knowm.xchange.dto.trade.LimitOrder; +import org.knowm.xchange.instrument.Instrument; + +public class BybitStreamAdapters { + public static OrderBook adaptOrderBook(BybitOrderbook bybitOrderbooks, Instrument instrument) { + List asks = new ArrayList<>(); + List bids = new ArrayList<>(); + Date timestamp = new Date(Long.parseLong(bybitOrderbooks.getTs())); + bybitOrderbooks.getData().getAsk() + .forEach(bybitAsk -> asks.add( + adaptOrderBookOrder(bybitAsk, instrument, OrderType.ASK,timestamp))); + + bybitOrderbooks.getData().getBid() + .forEach(bybitBid -> bids.add( + adaptOrderBookOrder(bybitBid, instrument, OrderType.BID,timestamp))); + + return new OrderBook(timestamp,asks, bids); + } + + public static Trades adaptTrades(List bybitTrades, Instrument instrument) { + List trades = new ArrayList<>(); + + bybitTrades.forEach( + bybitTrade -> + trades.add( + new Trade.Builder() + .id(bybitTrade.getTradeId()) + .instrument(instrument) + .originalAmount(bybitTrade.getTradeSize()) + .price(bybitTrade.getTradePrice()) + .timestamp(bybitTrade.getTimestamp()) + .type(getOrderType(bybitTrade.getSide())) + .build())); + + return new Trades(trades); + } + + public static LimitOrder adaptOrderBookOrder(BybitPublicOrder bybitPublicOrder, + Instrument instrument, Order.OrderType orderType, Date timestamp) { + + return new LimitOrder(orderType, new BigDecimal(bybitPublicOrder.getSize()), instrument, "", + timestamp, new BigDecimal(bybitPublicOrder.getPrice())); + } + + +} diff --git a/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingExchange.java b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingExchange.java new file mode 100644 index 00000000000..5482484bb0e --- /dev/null +++ b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingExchange.java @@ -0,0 +1,76 @@ +package info.bitrich.xchangestream.bybit; + +import info.bitrich.xchangestream.core.ProductSubscription; +import info.bitrich.xchangestream.core.StreamingExchange; +import io.reactivex.Completable; +import org.knowm.xchange.bybit.BybitExchange; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BybitStreamingExchange extends BybitExchange implements StreamingExchange { + + private final Logger LOG = LoggerFactory.getLogger(BybitStreamingExchange.class); + + //https://bybit-exchange.github.io/docs/v5/ws/connect + public static final String URI = "wss://stream.bybit.com/v5/public"; + public static final String TESTNET_URI = "wss://stream-testnet.bybit.com/v5/public"; + public static final String AUTH_URI = "wss://stream.bybit.com/v5/private"; + public static final String TESTNET_AUTH_URI = "wss://stream-testnet.bybit.com/v5/private"; + + //spot, linear, inverse or option + public static final String EXCHANGE_TYPE = "EXCHANGE_TYPE"; + + private BybitStreamingService streamingService; + private BybitStreamingMarketDataService streamingMarketDataService; + + @Override + protected void initServices() { + super.initServices(); + this.streamingService = new BybitStreamingService(getApiUrl(), + exchangeSpecification.getExchangeSpecificParametersItem(EXCHANGE_TYPE)); + this.streamingMarketDataService = new BybitStreamingMarketDataService(streamingService); + } + + private String getApiUrl() { + String apiUrl = null; + if (exchangeSpecification.getApiKey() == null) { + if (Boolean.TRUE.equals( + exchangeSpecification.getExchangeSpecificParametersItem(USE_SANDBOX))) { + apiUrl = TESTNET_URI; + } else { + apiUrl = URI; + } + apiUrl += "/" + exchangeSpecification.getExchangeSpecificParametersItem(EXCHANGE_TYPE); + } +// TODO auth + return apiUrl; + } + + @Override + public Completable connect(ProductSubscription... args) { + LOG.info("Connect to BybitStream"); + return streamingService.connect(); + } + + @Override + public Completable disconnect() { + streamingService.pingPongDisconnectIfConnected(); + return streamingService.disconnect(); + } + + @Override + public boolean isAlive() { + return streamingService != null && streamingService.isSocketOpen(); + } + + @Override + public void useCompressedMessages(boolean compressedMessages) { + streamingService.useCompressedMessages(compressedMessages); + } + + @Override + public BybitStreamingMarketDataService getStreamingMarketDataService() { + return streamingMarketDataService; + } + +} diff --git a/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingMarketDataService.java b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingMarketDataService.java new file mode 100644 index 00000000000..2fd375b44c5 --- /dev/null +++ b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingMarketDataService.java @@ -0,0 +1,171 @@ +package info.bitrich.xchangestream.bybit; + +import static org.knowm.xchange.bybit.BybitAdapters.convertToBybitSymbol; + +import com.fasterxml.jackson.databind.ObjectMapper; +import dto.marketdata.BybitPublicOrder; +import info.bitrich.xchangestream.core.StreamingMarketDataService; +import info.bitrich.xchangestream.service.netty.StreamingObjectMapperHelper; +import io.reactivex.Observable; +import io.reactivex.subjects.PublishSubject; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import dto.marketdata.BybitOrderbook; +import dto.trade.BybitTrade; +import java.util.concurrent.atomic.AtomicLong; +import org.knowm.xchange.dto.Order; +import org.knowm.xchange.dto.marketdata.OrderBook; +import org.knowm.xchange.dto.marketdata.OrderBookUpdate; +import org.knowm.xchange.dto.marketdata.Trade; +import org.knowm.xchange.instrument.Instrument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BybitStreamingMarketDataService implements StreamingMarketDataService { + + private final Logger LOG = LoggerFactory.getLogger(BybitStreamingMarketDataService.class); + private final BybitStreamingService streamingService; + private final ObjectMapper mapper = StreamingObjectMapperHelper.getObjectMapper(); + public static final String TRADE = "publicTrade."; + public static final String ORDERBOOK = "orderbook."; + public static final String TICKER = "tickers."; + + private final Map orderBookMap = new HashMap<>(); + private final Map>> + orderBookUpdatesSubscriptions; + + public BybitStreamingMarketDataService(BybitStreamingService streamingService) { + this.streamingService = streamingService; + this.orderBookUpdatesSubscriptions = new ConcurrentHashMap<>(); + } + + /** + * Linear & inverse: Level 1 data, push frequency: 10ms Level 50 data, push frequency: 20ms Level + * 200 data, push frequency: 100ms Level 500 data, push frequency: 100ms Spot: Level 1 data, push + * frequency: 10ms Level 50 data, push frequency: 20ms Level 200 data, push frequency: 200ms + * + * @param args - orderbook depth + **/ + @Override + public Observable getOrderBook(Instrument instrument, Object... args) { + String depth = "50"; + AtomicLong orderBookUpdateIdPrev = new AtomicLong(); + if (args.length > 0 && args[0] != null) { + depth = args[0].toString(); + } + String channelUniqueId = + ORDERBOOK + depth + "." + convertToBybitSymbol(instrument); + return streamingService + .subscribeChannel(channelUniqueId) + .flatMap( + jsonNode -> { + BybitOrderbook bybitOrderbooks = + mapper.treeToValue(jsonNode, BybitOrderbook.class); + String type = bybitOrderbooks.getDataType(); + if (type.equalsIgnoreCase("snapshot")) { + OrderBook orderBook = BybitStreamAdapters.adaptOrderBook(bybitOrderbooks, instrument); + orderBookUpdateIdPrev.set(bybitOrderbooks.getData().getU()); + orderBookMap.put(channelUniqueId, orderBook); + return Observable.just(orderBook); + } else if (type.equalsIgnoreCase("delta")) { + return applyDeltaSnapshot(channelUniqueId, instrument, bybitOrderbooks, orderBookUpdateIdPrev); + } + return Observable.fromIterable(new LinkedList<>()); + }); + } + + private Observable applyDeltaSnapshot(String channelUniqueId, Instrument instrument, + BybitOrderbook bybitOrderBookUpdate,AtomicLong orderBookUpdateIdPrev) { + OrderBook orderBook = orderBookMap.getOrDefault(channelUniqueId, null); + if (orderBook == null) { + LOG.error("Failed to get orderBook, channelUniqueId= {}", channelUniqueId); + return Observable.fromIterable(new LinkedList<>()); + } + if (orderBookUpdateIdPrev.incrementAndGet() == bybitOrderBookUpdate.getData().getU()) { + LOG.debug("orderBookUpdate id {}, seq {} ", bybitOrderBookUpdate.getData().getU(), + bybitOrderBookUpdate.getData().getSeq()); + List asks = bybitOrderBookUpdate.getData().getAsk(); + List bids = bybitOrderBookUpdate.getData().getBid(); + Date timestamp = new Date(Long.parseLong(bybitOrderBookUpdate.getTs())); + asks.forEach( + bybitPublicOrder -> + orderBook.update( + BybitStreamAdapters.adaptOrderBookOrder( + bybitPublicOrder, instrument, Order.OrderType.ASK, timestamp))); + bids.forEach( + bybitPublicOrder -> + orderBook.update( + BybitStreamAdapters.adaptOrderBookOrder( + bybitPublicOrder, instrument, Order.OrderType.BID, timestamp))); + if (orderBookUpdatesSubscriptions.get(instrument) != null) { + orderBookUpdatesSubscriptions(instrument, asks, bids, timestamp); + } + return Observable.just(orderBook); + } else { + LOG.error("orderBookUpdate id sequence failed, expected {}, in fact {}", + orderBookUpdateIdPrev, + bybitOrderBookUpdate.getData().getU()); + // resubscribe or what here? + return Observable.fromIterable(new LinkedList<>()); + } + } + + @Override + public Observable> getOrderBookUpdates(Instrument instrument,Object... args) { + return orderBookUpdatesSubscriptions.computeIfAbsent(instrument, v -> PublishSubject.create()); + } + + private void orderBookUpdatesSubscriptions( + Instrument instrument, List asks, List bids, Date date) { + List orderBookUpdates = new ArrayList<>(); + for (BybitPublicOrder ask : asks) { + OrderBookUpdate o = + new OrderBookUpdate( + Order.OrderType.ASK, + new BigDecimal(ask.getSize()), + instrument, + new BigDecimal(ask.getPrice()), + date, + new BigDecimal(ask.getSize())); + orderBookUpdates.add(o); + } + for (BybitPublicOrder bid : bids) { + OrderBookUpdate o = + new OrderBookUpdate( + Order.OrderType.BID, + new BigDecimal(bid.getSize()), + instrument, + new BigDecimal(bid.getPrice()), + date, + new BigDecimal(bid.getSize())); + orderBookUpdates.add(o); + } + orderBookUpdatesSubscriptions.get(instrument).onNext(orderBookUpdates); + } + + @Override + public Observable getTrades(Instrument instrument, Object... args) { + String channelUniqueId = + TRADE + convertToBybitSymbol(instrument); + + return streamingService + .subscribeChannel(channelUniqueId) + .filter(message -> message.has("data")) + .flatMap( + jsonNode -> { + List bybitTradeList = + mapper.treeToValue( + jsonNode.get("data"), + mapper.getTypeFactory() + .constructCollectionType(List.class, BybitTrade.class)); + return Observable.fromIterable( + BybitStreamAdapters.adaptTrades(bybitTradeList, instrument).getTrades()); + }); + } +} diff --git a/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingService.java b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingService.java new file mode 100644 index 00000000000..d1d0e57006b --- /dev/null +++ b/xchange-stream-bybit/src/main/java/info/bitrich/xchangestream/bybit/BybitStreamingService.java @@ -0,0 +1,107 @@ +package info.bitrich.xchangestream.bybit; + +import com.fasterxml.jackson.databind.JsonNode; +import dto.BybitSubscribeMessage; +import info.bitrich.xchangestream.service.netty.JsonNettyStreamingService; +import info.bitrich.xchangestream.service.netty.WebSocketClientCompressionAllowClientNoContextAndServerNoContextHandler; +import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler; +import io.reactivex.Completable; +import io.reactivex.CompletableSource; +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; +import java.io.IOException; +import java.util.Collections; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BybitStreamingService extends JsonNettyStreamingService { + + private final Logger LOG = LoggerFactory.getLogger(BybitStreamingService.class); + public final String exchange_type; + private final Observable pingPongSrc = Observable.interval(15, 20, TimeUnit.SECONDS); + private Disposable pingPongSubscription; + + public BybitStreamingService(String apiUrl, Object exchange_type) { + super(apiUrl); + this.exchange_type = (String) exchange_type; +// this.setEnableLoggingHandler(true); + } + + @Override + public Completable connect() { + Completable conn = super.connect(); + return conn.andThen( + (CompletableSource) + (completable) -> { + pingPongDisconnectIfConnected(); + pingPongSubscription = pingPongSrc.subscribe( + o -> this.sendMessage("{\"op\":\"ping\"}")); + completable.onComplete(); + }); + } + + @Override + protected String getChannelNameFromMessage(JsonNode message) { + if (message.has("topic")) { + return message.get("topic").asText(); + } + return ""; + } + + @Override + public String getSubscribeMessage(String channelName, Object... args) throws IOException { + LOG.info(" getSubscribeMessage {}", channelName); + return objectMapper.writeValueAsString( + new BybitSubscribeMessage("subscribe", Collections.singletonList(channelName))); + } + + @Override + public String getUnsubscribeMessage(String channelName, Object... args) throws IOException { + LOG.info(" getUnsubscribeMessage {}", channelName); + return objectMapper.writeValueAsString( + new BybitSubscribeMessage("unsubscribe", Collections.singletonList(channelName))); + } + + @Override + public void messageHandler(String message) { + LOG.debug("Received message: {}", message); + JsonNode jsonNode; + try { + jsonNode = objectMapper.readTree(message); + } catch (IOException e) { + LOG.error("Error parsing incoming message to JSON: {}", message); + return; + } + String op = ""; + boolean success = false; + if (jsonNode.has("op")) { + op = jsonNode.get("op").asText(); + } + if (jsonNode.has("success")) { + success = jsonNode.get("success").asBoolean(); + } + if (success) { + switch (op) { + case "pong": + case "subscribe": + case "unsubscribe": { + break; + } + } + return; + } + handleMessage(jsonNode); + } + + public void pingPongDisconnectIfConnected() { + if (pingPongSubscription != null && !pingPongSubscription.isDisposed()) { + pingPongSubscription.dispose(); + } + } + + @Override + protected WebSocketClientExtensionHandler getWebSocketClientExtensionHandler() { + return WebSocketClientCompressionAllowClientNoContextAndServerNoContextHandler.INSTANCE; + } +} diff --git a/xchange-stream-bybit/src/test/java/info/bitrich/xchangestream/bybit/BybitStreamExample.java b/xchange-stream-bybit/src/test/java/info/bitrich/xchangestream/bybit/BybitStreamExample.java new file mode 100644 index 00000000000..5a3dc135d6d --- /dev/null +++ b/xchange-stream-bybit/src/test/java/info/bitrich/xchangestream/bybit/BybitStreamExample.java @@ -0,0 +1,163 @@ +package info.bitrich.xchangestream.bybit; + +import static org.knowm.xchange.Exchange.USE_SANDBOX; +import static org.knowm.xchange.bybit.BybitExchange.SPECIFIC_PARAM_ACCOUNT_TYPE; + +import info.bitrich.xchangestream.core.StreamingExchange; +import info.bitrich.xchangestream.core.StreamingExchangeFactory; +import io.reactivex.disposables.Disposable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.bybit.dto.BybitCategory; +import org.knowm.xchange.bybit.dto.account.walletbalance.BybitAccountType; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.derivative.FuturesContract; +import org.knowm.xchange.derivative.OptionsContract; +import org.knowm.xchange.instrument.Instrument; + + +public class BybitStreamExample { + + public static void main(String[] args) { + try { + spot(); + } catch (IOException e) { + throw new RuntimeException(e); + } + futures(); + } + + private static final Instrument BTC_PERP = new FuturesContract("BTC/USDT/PERP"); + private static final Instrument ETH_PERP = new CurrencyPair("ETH/USDT/PERP"); + private static final Instrument BTC_SPOT = new CurrencyPair("BTC/USDT"); + private static final Instrument ETH_SPOT = new CurrencyPair("ETH/USDT"); + + public static void spot() throws IOException { + ExchangeSpecification exchangeSpecification = + new ExchangeSpecification(BybitStreamingExchange.class); + exchangeSpecification.setExchangeSpecificParametersItem(USE_SANDBOX, true); + exchangeSpecification.setExchangeSpecificParametersItem(BybitStreamingExchange.EXCHANGE_TYPE, + BybitCategory.SPOT.getValue()); + exchangeSpecification.setExchangeSpecificParametersItem(SPECIFIC_PARAM_ACCOUNT_TYPE, + BybitAccountType.UNIFIED); + StreamingExchange exchange = StreamingExchangeFactory.INSTANCE.createExchange( + exchangeSpecification); + exchange.connect().blockingAwait(); + int count_currencyPair = 0; + int count_futureContractPerp = 0; + int count_futureContractDate = 0; + List instruments = new ArrayList<>(exchange + .getExchangeMetaData() + .getInstruments().keySet()); + for (Instrument instrument : instruments) { + if (instrument instanceof CurrencyPair) { + count_currencyPair++; + } + if (instrument instanceof FuturesContract) { + count_futureContractPerp++; + } + if (instrument instanceof OptionsContract) { + count_futureContractDate++; + } + } + System.out.println("Currency pairs: " + count_currencyPair); + System.out.println("Futures: " + count_futureContractPerp); + System.out.println("Futures date: " + count_futureContractDate); + + System.out.println(exchange.getMarketDataService().getTicker(BTC_SPOT)); + System.out.println(exchange.getMarketDataService().getTicker(BTC_PERP)); + List tradesDisposable = new ArrayList<>(); + Disposable bookDisposable = + exchange + .getStreamingMarketDataService() + .getOrderBook(ETH_SPOT) + .subscribe(); + tradesDisposable.add(exchange + .getStreamingMarketDataService().getTrades(ETH_SPOT).subscribe( + trade -> System.out.println("trade: " + trade))); + tradesDisposable.add(exchange + .getStreamingMarketDataService().getTrades(BTC_SPOT).subscribe( + trade -> System.out.println("trade: " + trade))); + try { + Thread.sleep(2000); + } catch ( + InterruptedException ignored) { + } + bookDisposable.dispose(); + for (Disposable disposable : tradesDisposable) { + disposable.dispose(); + } + + try { + Thread.sleep(5000); + } catch ( + InterruptedException ignored) { + } + exchange.disconnect().blockingAwait(); + } + + + public static void futures() { + ExchangeSpecification exchangeSpecification = + new ExchangeSpecification(BybitStreamingExchange.class); + exchangeSpecification.setExchangeSpecificParametersItem(SPECIFIC_PARAM_ACCOUNT_TYPE, + BybitAccountType.UNIFIED); + exchangeSpecification.setExchangeSpecificParametersItem(BybitStreamingExchange.EXCHANGE_TYPE, + BybitCategory.LINEAR.getValue()); + StreamingExchange exchange = StreamingExchangeFactory.INSTANCE.createExchange( + exchangeSpecification); + exchange.connect().blockingAwait(); + List tradesDisposable = new ArrayList<>(); + List booksDisposable = new ArrayList<>(); + List booksUpdatesDisposable = new ArrayList<>(); + booksDisposable.add(exchange + .getStreamingMarketDataService() + .getOrderBook(BTC_PERP) + .subscribe()); + booksDisposable.add(exchange + .getStreamingMarketDataService() + .getOrderBook(ETH_PERP) + .subscribe()); + booksUpdatesDisposable.add(exchange + .getStreamingMarketDataService() + .getOrderBookUpdates(BTC_PERP) + .subscribe( + orderBookUpdates -> System.out.printf("orderBookUpdates: %s\n", orderBookUpdates) + )); + booksUpdatesDisposable.add(exchange + .getStreamingMarketDataService() + .getOrderBookUpdates(ETH_PERP) + .subscribe( + orderBookUpdates -> System.out.printf("orderBookUpdates: %s\n", orderBookUpdates) + )); + tradesDisposable.add(exchange + .getStreamingMarketDataService().getTrades(BTC_PERP).subscribe( + trade -> System.out.println("trade: " + trade))); + tradesDisposable.add(exchange + .getStreamingMarketDataService().getTrades(ETH_PERP).subscribe( + trade -> System.out.println("trade: " + trade))); + try { + Thread.sleep(2000); + } catch (InterruptedException ignored) { + + } + for (Disposable disposable : booksUpdatesDisposable) { + disposable.dispose(); + } + for (Disposable disposable : booksDisposable) { + disposable.dispose(); + } + for (Disposable disposable : tradesDisposable) { + disposable.dispose(); + } + try { + Thread.sleep(5000); + } catch (InterruptedException ignored) { + + } + exchange.disconnect().blockingAwait(); + } + +} diff --git a/xchange-stream-bybit/src/test/resources/logback.xml b/xchange-stream-bybit/src/test/resources/logback.xml new file mode 100644 index 00000000000..fabc864484c --- /dev/null +++ b/xchange-stream-bybit/src/test/resources/logback.xml @@ -0,0 +1,27 @@ + + + + + + + + + %d{HH:mm:ss.SSS} [%contextName] [%thread] %-5level %logger{36} - %msg %xEx%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xchange-stream-gateio/.gitignore b/xchange-stream-gateio/.gitignore new file mode 100644 index 00000000000..42c7d2cd0eb --- /dev/null +++ b/xchange-stream-gateio/.gitignore @@ -0,0 +1 @@ +http-client.private.env.json \ No newline at end of file diff --git a/xchange-stream-gateio/http-client.env.json b/xchange-stream-gateio/http-client.env.json new file mode 100644 index 00000000000..7d82f548325 --- /dev/null +++ b/xchange-stream-gateio/http-client.env.json @@ -0,0 +1,5 @@ +{ + "default": { + "base_url": "wss://api.gateio.ws/ws/v4/" + } +} \ No newline at end of file diff --git a/xchange-stream-gateio/http-client.private.env.json.example b/xchange-stream-gateio/http-client.private.env.json.example new file mode 100644 index 00000000000..11f254874ee --- /dev/null +++ b/xchange-stream-gateio/http-client.private.env.json.example @@ -0,0 +1,6 @@ +{ + "live": { + "api_key": "replace_me", + "api_secret": "replace_me" + } +} \ No newline at end of file diff --git a/xchange-stream-gateio/lombok.config b/xchange-stream-gateio/lombok.config new file mode 100644 index 00000000000..e572d96336a --- /dev/null +++ b/xchange-stream-gateio/lombok.config @@ -0,0 +1 @@ +lombok.equalsAndHashCode.callSuper = call \ No newline at end of file diff --git a/xchange-stream-gateio/pom.xml b/xchange-stream-gateio/pom.xml index 4f5ff80aa80..a8f8c5e5c9e 100644 --- a/xchange-stream-gateio/pom.xml +++ b/xchange-stream-gateio/pom.xml @@ -1,26 +1,54 @@ + 4.0.0 - xchange-parent org.knowm.xchange + xchange-parent 5.1.2-SNAPSHOT - 4.0.0 + xchange-stream-gateio XChange Gate.io Stream - xchange-stream-gateio + + + 5.3.1 + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${version.fasterxml} + + + + + org.junit.jupiter + junit-jupiter-engine + test + + org.knowm.xchange - xchange-stream-core + xchange-gateio-v4 ${project.parent.version} + org.knowm.xchange - xchange-gateio + xchange-stream-core ${project.parent.version} + + org.mockito + mockito-junit-jupiter + ${version.mockito} + test + + - \ No newline at end of file + + + diff --git a/xchange-stream-gateio/src/main/java/GateioStreamingExchange.java b/xchange-stream-gateio/src/main/java/GateioStreamingExchange.java deleted file mode 100644 index 16b1ae05677..00000000000 --- a/xchange-stream-gateio/src/main/java/GateioStreamingExchange.java +++ /dev/null @@ -1,55 +0,0 @@ -import info.bitrich.xchangestream.core.ProductSubscription; -import info.bitrich.xchangestream.core.StreamingExchange; -import info.bitrich.xchangestream.core.StreamingMarketDataService; -import io.reactivex.Completable; -import org.knowm.xchange.gateio.GateioExchange; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** Author: Max Gao (gaamox@tutanota.com) Created: 05-05-2021 */ -public class GateioStreamingExchange extends GateioExchange implements StreamingExchange { - private static final Logger LOG = LoggerFactory.getLogger(GateioStreamingExchange.class); - - private final String V4_URL = "wss://api.gateio.ws/ws/v4/"; - - private GateioStreamingService streamingService; - private StreamingMarketDataService streamingMarketDataService; - - public GateioStreamingExchange() {} - - @Override - public Completable connect(ProductSubscription... args) { - if (args == null || args.length == 0) - throw new UnsupportedOperationException("The ProductSubscription must be defined!"); - - this.streamingService = new GateioStreamingService(V4_URL, exchangeSpecification); - applyStreamingSpecification(getExchangeSpecification(), streamingService); - this.streamingMarketDataService = new GateioStreamingMarketDataService(streamingService); - - streamingService.subscribeMultipleCurrencyPairs(args); - return streamingService.connect(); - } - - @Override - public Completable disconnect() { - GateioStreamingService service = streamingService; - streamingService = null; - streamingMarketDataService = null; - return service.disconnect(); - } - - @Override - public StreamingMarketDataService getStreamingMarketDataService() { - return streamingMarketDataService; - } - - @Override - public boolean isAlive() { - return streamingService != null && streamingService.isSocketOpen(); - } - - @Override - public void useCompressedMessages(boolean compressedMessages) { - streamingService.useCompressedMessages(compressedMessages); - } -} diff --git a/xchange-stream-gateio/src/main/java/GateioStreamingMarketDataService.java b/xchange-stream-gateio/src/main/java/GateioStreamingMarketDataService.java deleted file mode 100644 index 9eb4ee34381..00000000000 --- a/xchange-stream-gateio/src/main/java/GateioStreamingMarketDataService.java +++ /dev/null @@ -1,59 +0,0 @@ -import dto.response.GateioOrderBookResponse; -import dto.response.GateioTradesResponse; -import info.bitrich.xchangestream.core.StreamingMarketDataService; -import io.reactivex.Observable; -import java.util.List; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.marketdata.OrderBook; -import org.knowm.xchange.dto.marketdata.Ticker; -import org.knowm.xchange.dto.marketdata.Trade; -import org.knowm.xchange.exceptions.NotYetImplementedForExchangeException; -import org.knowm.xchange.instrument.Instrument; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** Author: Max Gao (gaamox@tutanota.com) Created: 05-05-2021 */ -public class GateioStreamingMarketDataService implements StreamingMarketDataService { - private static final Logger LOGGER = - LoggerFactory.getLogger(GateioStreamingMarketDataService.class); - private final GateioStreamingService service; - - public GateioStreamingMarketDataService(GateioStreamingService service) { - this.service = service; - } - - private boolean containsPair(List pairs, CurrencyPair pair) { - return pairs.stream().anyMatch(p -> p.equals(pair)); - } - - /** - * Uses the limited-level snapshot method: - * https://www.gate.io/docs/apiv4/ws/index.html#limited-level-full-order-book-snapshot - * - * @param currencyPair Currency pair of the order book - * @param args Optional maxDepth, Optional msgInterval - * @return - */ - @Override - public Observable getOrderBook(CurrencyPair currencyPair, Object... args) { - if (!containsPair(service.getProduct().getOrderBook(), currencyPair)) - throw new UnsupportedOperationException( - String.format("The currency pair %s is not subscribed for orderbook", currencyPair)); - - return service - .getRawWebSocketTransactions(currencyPair, GateioStreamingService.SPOT_ORDERBOOK_CHANNEL) - .map(msg -> ((GateioOrderBookResponse) msg).toOrderBook(currencyPair)); - } - - @Override - public Observable getTicker(CurrencyPair currencyPair, Object... args) { - throw new NotYetImplementedForExchangeException("Not yet implemented!"); - } - - @Override - public Observable getTrades(CurrencyPair currencyPair, Object... args) { - return service - .getRawWebSocketTransactions(currencyPair, GateioStreamingService.SPOT_TRADES_CHANNEL) - .map(msg -> ((GateioTradesResponse) msg).toTrade()); - } -} diff --git a/xchange-stream-gateio/src/main/java/GateioStreamingService.java b/xchange-stream-gateio/src/main/java/GateioStreamingService.java deleted file mode 100644 index 878c2f13bee..00000000000 --- a/xchange-stream-gateio/src/main/java/GateioStreamingService.java +++ /dev/null @@ -1,161 +0,0 @@ -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import dto.GateioWebSocketSubscriptionMessage; -import dto.response.GateioOrderBookResponse; -import dto.response.GateioTradesResponse; -import dto.response.GateioWebSocketTransaction; -import info.bitrich.xchangestream.core.ProductSubscription; -import info.bitrich.xchangestream.service.netty.JsonNettyStreamingService; -import info.bitrich.xchangestream.service.netty.StreamingObjectMapperHelper; -import info.bitrich.xchangestream.service.netty.WebSocketClientCompressionAllowClientNoContextAndServerNoContextHandler; -import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler; -import io.reactivex.Observable; -import java.io.IOException; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import org.knowm.xchange.ExchangeSpecification; -import org.knowm.xchange.currency.CurrencyPair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** Author: Max Gao (gaamox@tutanota.com) Created: 05-05-2021 */ -public class GateioStreamingService extends JsonNettyStreamingService { - private static final Logger LOG = LoggerFactory.getLogger(GateioStreamingService.class); - private static final String SUBSCRIBE = "subscribe"; - private static final String UNSUBSCRIBE = "unsubscribe"; - private static final String CHANNEL_NAME_DELIMITER = "-"; - - public static final String SPOT_ORDERBOOK_CHANNEL = "spot.order_book"; - public static final String SPOT_TRADES_CHANNEL = "spot.trades"; - public static final String SPOT_TICKERS_CHANNEL = "spot.tickers"; - - private static final int MAX_DEPTH_DEFAULT = 5; - private static final int UPDATE_INTERVAL_DEFAULT = 100; - - private final String apiUri; - private ProductSubscription productSubscription; - private ExchangeSpecification exchangeSpecification; - - private final Map> subscriptions = new ConcurrentHashMap<>(); - private final Map channelSubscriptionMessages = new ConcurrentHashMap<>(); - - public GateioStreamingService(String apiUri, ExchangeSpecification exchangeSpecification) { - super(apiUri, Integer.MAX_VALUE); - this.apiUri = apiUri; - this.exchangeSpecification = exchangeSpecification; - } - - public Observable getRawWebSocketTransactions( - CurrencyPair currencyPair, String channelName, Object... args) { - final ObjectMapper mapper = StreamingObjectMapperHelper.getObjectMapper(); - - return subscribeChannel(channelName, currencyPair, args) - .map( - msg -> { - switch (channelName) { - case SPOT_ORDERBOOK_CHANNEL: - return mapper.treeToValue(msg, GateioOrderBookResponse.class); - case SPOT_TRADES_CHANNEL: - return mapper.treeToValue(msg, GateioTradesResponse.class); - } - return mapper.treeToValue(msg, GateioWebSocketTransaction.class); - }) - .filter(t -> currencyPair.equals(t.getCurrencyPair())); - } - - public void subscribeMultipleCurrencyPairs(ProductSubscription... products) { - this.productSubscription = products[0]; - } - - public ProductSubscription getProduct() { - return this.productSubscription; - } - - @Override - protected String getChannelNameFromMessage(JsonNode message) { - String channel = message.path("channel") != null ? message.path("channel").asText() : ""; - String currencyPairOrderBook = - message.path("result").path("s") != null ? message.path("result").path("s").asText() : ""; - String currencyPairTradesTickers = - message.path("result").path("currency_pair") != null - ? message.path("result").path("currency_pair").asText() - : ""; - - return new StringBuilder(channel) - .append(CHANNEL_NAME_DELIMITER) - .append(currencyPairOrderBook) - .append(currencyPairTradesTickers) - .toString(); - } - - @Override - public Observable subscribeChannel(String channelName, Object... args) { - final CurrencyPair currencyPair = - (args.length > 0 && args[0] instanceof CurrencyPair) ? ((CurrencyPair) args[0]) : null; - - String currencyPairChannelName = - String.format("%s-%s", channelName, currencyPair.toString().replace('/', '_')); - - try { - // Example channel name key: spot.order_book_update-ETH_USDT, spot.trades-BTC_USDT - if (!channels.containsKey(currencyPairChannelName) - && !subscriptions.containsKey(currencyPairChannelName)) { - subscriptions.put( - currencyPairChannelName, super.subscribeChannel(currencyPairChannelName, args)); - channelSubscriptionMessages.put( - currencyPairChannelName, getSubscribeMessage(currencyPairChannelName, currencyPair)); - } - } catch (IOException e) { - LOG.error("Failed to subscribe to channel: {}", currencyPairChannelName); - } - - return subscriptions.get(currencyPairChannelName); - } - - /** - * Returns a JSON String containing the subscription message. - * - * @param channelName - * @param args CurrencyPair to subscribe to - * @return - * @throws IOException - */ - @Override - public String getSubscribeMessage(String channelName, Object... args) throws IOException { - final CurrencyPair currencyPair = - (args.length > 0 && args[0] instanceof CurrencyPair) ? ((CurrencyPair) args[0]) : null; - - final int maxDepth = - exchangeSpecification.getExchangeSpecificParametersItem("maxDepth") != null - ? (int) exchangeSpecification.getExchangeSpecificParametersItem("maxDepth") - : MAX_DEPTH_DEFAULT; - final int msgInterval = - exchangeSpecification.getExchangeSpecificParametersItem("updateInterval") != null - ? (int) exchangeSpecification.getExchangeSpecificParametersItem("updateInterval") - : UPDATE_INTERVAL_DEFAULT; - - GateioWebSocketSubscriptionMessage subscribeMessage = - new GateioWebSocketSubscriptionMessage( - channelName.split(CHANNEL_NAME_DELIMITER)[0], - SUBSCRIBE, - currencyPair, - msgInterval, - maxDepth); - - return objectMapper.writeValueAsString(subscribeMessage); - } - - @Override - protected WebSocketClientExtensionHandler getWebSocketClientExtensionHandler() { - return WebSocketClientCompressionAllowClientNoContextAndServerNoContextHandler.INSTANCE; - } - - @Override - public String getUnsubscribeMessage(String channelName, Object... args) throws IOException { - GateioWebSocketSubscriptionMessage unsubscribeMessage = - objectMapper.readValue( - channelSubscriptionMessages.get(channelName), GateioWebSocketSubscriptionMessage.class); - unsubscribeMessage.setEvent(UNSUBSCRIBE); - return objectMapper.writeValueAsString(unsubscribeMessage); - } -} diff --git a/xchange-stream-gateio/src/main/java/dto/GateioWebSocketSubscriptionMessage.java b/xchange-stream-gateio/src/main/java/dto/GateioWebSocketSubscriptionMessage.java index 9d3b9b29106..e69de29bb2d 100644 --- a/xchange-stream-gateio/src/main/java/dto/GateioWebSocketSubscriptionMessage.java +++ b/xchange-stream-gateio/src/main/java/dto/GateioWebSocketSubscriptionMessage.java @@ -1,52 +0,0 @@ -package dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.time.Instant; -import java.util.Arrays; -import java.util.Objects; -import java.util.stream.Collectors; -import lombok.NoArgsConstructor; -import lombok.Setter; -import org.knowm.xchange.currency.CurrencyPair; - -/** Author: Max Gao (gaamox@tutanota.com) Created: 05-05-2021 */ -@Setter -@NoArgsConstructor -public class GateioWebSocketSubscriptionMessage { - @JsonProperty("time") - private int time; - - @JsonProperty("channel") - private String channel; - - @JsonProperty("event") - private String event; - - @JsonProperty("payload") - private String[] payload; - - public GateioWebSocketSubscriptionMessage( - String channelName, CurrencyPair currencyPair, Integer interval) {} - - public GateioWebSocketSubscriptionMessage( - String channelName, - String event, - CurrencyPair currencyPair, - Integer interval, - Integer depth) { - this.time = (int) (Instant.now().getEpochSecond()); - this.channel = channelName; - this.event = event; - this.payload = - Arrays.asList( - currencyPair.toString().replace('/', '_'), - (depth != null && channelName.contains("order_book")) - ? Integer.toString(depth) - : null, - (interval != null && channelName.contains("order_book")) ? interval + "ms" : null) - .stream() - .filter(Objects::nonNull) - .collect(Collectors.toList()) - .toArray(new String[] {}); - } -} diff --git a/xchange-stream-gateio/src/main/java/dto/response/GateioOrderBookResponse.java b/xchange-stream-gateio/src/main/java/dto/response/GateioOrderBookResponse.java deleted file mode 100644 index 854ba11f901..00000000000 --- a/xchange-stream-gateio/src/main/java/dto/response/GateioOrderBookResponse.java +++ /dev/null @@ -1,79 +0,0 @@ -package dto.response; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; -import java.time.Instant; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.stream.Collectors; -import lombok.Getter; -import lombok.Setter; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.Order; -import org.knowm.xchange.dto.marketdata.OrderBook; -import org.knowm.xchange.dto.trade.LimitOrder; - -@Getter -@Setter -/** Author: Max Gao (gaamox@tutanota.com) Created: 05-05-2021 */ -public class GateioOrderBookResponse extends GateioWebSocketTransaction { - @JsonProperty("result") - private Result result; - - @Getter - @Setter - public class Result { - @JsonProperty("t") - private long updateTimeMilliseconds; - - @JsonProperty("lastUpdateId") - private long lastUpdateId; - - @JsonProperty("s") - private String currencyPair; - - @JsonProperty("bids") - private String[][] bids; // Pre-sorted, ascending - [price, amount] - - @JsonProperty("asks") - private String[][] asks; // Pre-sorted, descending - [price, amount] - } - - @Override - public CurrencyPair getCurrencyPair() { - if (result.getCurrencyPair() == null) return null; // Expect to hit this on the success - return new CurrencyPair(result.getCurrencyPair().replace('_', '/')); - } - - public OrderBook toOrderBook(CurrencyPair currencyPair) { - List bids = - Arrays.stream(result.bids) - .map( - rawBid -> - new LimitOrder( - Order.OrderType.BID, - new BigDecimal(rawBid[1]), - currencyPair, - Long.toString(result.lastUpdateId), - null, - new BigDecimal(rawBid[0]))) - .collect(Collectors.toList()); - - List asks = - Arrays.stream(result.asks) - .map( - rawAsk -> - new LimitOrder( - Order.OrderType.ASK, - new BigDecimal(rawAsk[1]), - currencyPair, - Long.toString(result.lastUpdateId), - null, - new BigDecimal(rawAsk[0]))) - .collect(Collectors.toList()); - - return new OrderBook( - Date.from(Instant.ofEpochMilli(result.updateTimeMilliseconds)), asks, bids, false); - } -} diff --git a/xchange-stream-gateio/src/main/java/dto/response/GateioTradesResponse.java b/xchange-stream-gateio/src/main/java/dto/response/GateioTradesResponse.java deleted file mode 100644 index 38e751b8be3..00000000000 --- a/xchange-stream-gateio/src/main/java/dto/response/GateioTradesResponse.java +++ /dev/null @@ -1,60 +0,0 @@ -package dto.response; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; -import java.util.Date; -import lombok.Getter; -import lombok.Setter; -import org.knowm.xchange.currency.CurrencyPair; -import org.knowm.xchange.dto.Order; -import org.knowm.xchange.dto.marketdata.Trade; - -@Getter -@Setter -/** Author: Max Gao (gaamox@tutanota.com) Created: 05-05-2021 */ -public class GateioTradesResponse extends GateioWebSocketTransaction { - @JsonProperty("result") - private Result result; - - @Getter - @Setter - public class Result { - @JsonProperty("id") - private long id; - - @JsonProperty("create_time") - private long createTimeSeconds; - - @JsonProperty("create_time_ms") - private String createTimeMilliseconds; - - @JsonProperty("side") - private String side; - - @JsonProperty("currency_pair") - private String currencyPair; - - @JsonProperty("amount") - private String amount; - - @JsonProperty("price") - private String price; - } - - @Override - public CurrencyPair getCurrencyPair() { - return new CurrencyPair(result.getCurrencyPair().replace('_', '/')); - } - - public Trade toTrade() { - return new Trade( - "sell".equals(this.result.side) ? Order.OrderType.ASK : Order.OrderType.BID, - new BigDecimal(result.amount), - getCurrencyPair(), - new BigDecimal(result.price), - new Date(new BigDecimal(result.createTimeMilliseconds).longValue()), - Long.toString(result.id), - null, - null); - } -} diff --git a/xchange-stream-gateio/src/main/java/dto/response/GateioWebSocketTransaction.java b/xchange-stream-gateio/src/main/java/dto/response/GateioWebSocketTransaction.java deleted file mode 100644 index 93095f1a7ad..00000000000 --- a/xchange-stream-gateio/src/main/java/dto/response/GateioWebSocketTransaction.java +++ /dev/null @@ -1,22 +0,0 @@ -package dto.response; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Getter; -import lombok.Setter; -import org.knowm.xchange.currency.CurrencyPair; - -@Getter -@Setter -/** Author: Max Gao (gaamox@tutanota.com) Created: 05-05-2021 */ -public abstract class GateioWebSocketTransaction { - @JsonProperty("time") - private int time; - - @JsonProperty("channel") - private String channel; - - @JsonProperty("event") - private String event; - - public abstract CurrencyPair getCurrencyPair(); -} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingAccountService.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingAccountService.java new file mode 100644 index 00000000000..9e9fb7d0aba --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingAccountService.java @@ -0,0 +1,27 @@ +package info.bitrich.xchangestream.gateio; + +import info.bitrich.xchangestream.core.StreamingAccountService; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.balance.GateioSingleSpotBalanceNotification; +import io.reactivex.Observable; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.dto.account.Balance; + +public class GateioStreamingAccountService implements StreamingAccountService { + + private final GateioStreamingService service; + + public GateioStreamingAccountService(GateioStreamingService service) { + this.service = service; + } + + + @Override + public Observable getBalanceChanges(Currency currency, Object... args) { + return service + .subscribeChannel(Config.SPOT_BALANCES_CHANNEL) + .map(GateioSingleSpotBalanceNotification.class::cast) + .filter(notification -> (currency == null) || (notification.getResult().getCurrency().equals(currency))) + .map(GateioStreamingAdapters::toBalance); + } +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingAdapters.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingAdapters.java new file mode 100644 index 00000000000..e3da0b187d4 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingAdapters.java @@ -0,0 +1,104 @@ +package info.bitrich.xchangestream.gateio; + +import info.bitrich.xchangestream.gateio.dto.response.balance.BalancePayload; +import info.bitrich.xchangestream.gateio.dto.response.balance.GateioSingleSpotBalanceNotification; +import info.bitrich.xchangestream.gateio.dto.response.orderbook.GateioOrderBookNotification; +import info.bitrich.xchangestream.gateio.dto.response.orderbook.OrderBookPayload; +import info.bitrich.xchangestream.gateio.dto.response.ticker.GateioTickerNotification; +import info.bitrich.xchangestream.gateio.dto.response.ticker.TickerPayload; +import info.bitrich.xchangestream.gateio.dto.response.trade.GateioTradeNotification; +import info.bitrich.xchangestream.gateio.dto.response.trade.TradePayload; +import info.bitrich.xchangestream.gateio.dto.response.usertrade.GateioSingleUserTradeNotification; +import info.bitrich.xchangestream.gateio.dto.response.usertrade.UserTradePayload; +import java.util.Date; +import java.util.stream.Stream; +import lombok.experimental.UtilityClass; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.dto.account.Balance; +import org.knowm.xchange.dto.marketdata.OrderBook; +import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.dto.marketdata.Trade; +import org.knowm.xchange.dto.trade.LimitOrder; +import org.knowm.xchange.dto.trade.UserTrade; + +@UtilityClass +public class GateioStreamingAdapters { + + public Ticker toTicker(GateioTickerNotification notification) { + TickerPayload tickerPayload = notification.getResult(); + + return new Ticker.Builder() + .timestamp(Date.from(notification.getTimeMs())) + .instrument(tickerPayload.getCurrencyPair()) + .last(tickerPayload.getLastPrice()) + .ask(tickerPayload.getLowestAsk()) + .bid(tickerPayload.getHighestBid()) + .percentageChange(tickerPayload.getChangePercent24h()) + .volume(tickerPayload.getBaseVolume()) + .quoteVolume(tickerPayload.getQuoteVolume()) + .high(tickerPayload.getHighPrice24h()) + .low(tickerPayload.getLowPrice24h()) + .build(); + } + + + public Trade toTrade(GateioTradeNotification notification) { + TradePayload tradePayload = notification.getResult(); + + + return new Trade.Builder() + .type(tradePayload.getSide()) + .originalAmount(tradePayload.getAmount()) + .instrument(tradePayload.getCurrencyPair()) + .price(tradePayload.getPrice()) + .timestamp(Date.from(tradePayload.getTimeMs())) + .id(String.valueOf(tradePayload.getId())) + .build(); + } + + + public UserTrade toUserTrade(GateioSingleUserTradeNotification notification) { + UserTradePayload userTradePayload = notification.getResult(); + + return new UserTrade.Builder() + .type(userTradePayload.getSide()) + .originalAmount(userTradePayload.getAmount()) + .instrument(userTradePayload.getCurrencyPair()) + .price(userTradePayload.getPrice()) + .timestamp(Date.from(userTradePayload.getTimeMs())) + .id(String.valueOf(userTradePayload.getId())) + .orderId(String.valueOf(userTradePayload.getOrderId())) + .feeAmount(userTradePayload.getFee()) + .feeCurrency(userTradePayload.getFeeCurrency()) + .orderUserReference(userTradePayload.getRemark()) + .build(); + } + + + public Balance toBalance(GateioSingleSpotBalanceNotification notification) { + BalancePayload balancePayload = notification.getResult(); + + return new Balance.Builder() + .currency(balancePayload.getCurrency()) + .total(balancePayload.getTotal()) + .available(balancePayload.getAvailable()) + .frozen(balancePayload.getFreeze()) + .timestamp(Date.from(balancePayload.getTimeMs())) + .build(); + } + + + public OrderBook toOrderBook(GateioOrderBookNotification notification) { + OrderBookPayload orderBookPayload = notification.getResult(); + + Stream asks = orderBookPayload.getAsks().stream() + .map(priceSizeEntry -> new LimitOrder(OrderType.ASK, priceSizeEntry.getSize(), orderBookPayload.getCurrencyPair(), null, null, priceSizeEntry.getPrice())); + + Stream bids = orderBookPayload.getBids().stream() + .map(priceSizeEntry -> new LimitOrder(OrderType.BID, priceSizeEntry.getSize(), orderBookPayload.getCurrencyPair(), null, null, priceSizeEntry.getPrice())); + + return new OrderBook(Date.from(orderBookPayload.getTimestamp()), asks, bids); + } + + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingAuthHelper.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingAuthHelper.java new file mode 100644 index 00000000000..027bc25c7b0 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingAuthHelper.java @@ -0,0 +1,29 @@ +package info.bitrich.xchangestream.gateio; + +import java.nio.charset.StandardCharsets; +import javax.crypto.Mac; +import org.knowm.xchange.gateio.service.GateioV4Digest; +import org.knowm.xchange.utils.DigestUtils; + +public class GateioStreamingAuthHelper { + + private final GateioV4Digest gateioV4Digest; + + public GateioStreamingAuthHelper(String apiSecret) { + gateioV4Digest = GateioV4Digest.createInstance(apiSecret); + } + + + /** + * Generates signature based on payload + */ + public String sign(String channel, String event, String timestamp) { + Mac mac = gateioV4Digest.getMac(); + + String payloadToSign = String.format("channel=%s&event=%s&time=%s", channel, event, timestamp); + mac.update(payloadToSign.getBytes(StandardCharsets.UTF_8)); + + return DigestUtils.bytesToHex(mac.doFinal()); + } + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingExchange.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingExchange.java new file mode 100644 index 00000000000..c36b8f5a9e9 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingExchange.java @@ -0,0 +1,75 @@ +package info.bitrich.xchangestream.gateio; + +import info.bitrich.xchangestream.core.ProductSubscription; +import info.bitrich.xchangestream.core.StreamingAccountService; +import info.bitrich.xchangestream.core.StreamingExchange; +import info.bitrich.xchangestream.core.StreamingMarketDataService; +import info.bitrich.xchangestream.core.StreamingTradeService; +import info.bitrich.xchangestream.gateio.config.Config; +import io.reactivex.Completable; +import org.knowm.xchange.ExchangeSpecification; +import org.knowm.xchange.gateio.GateioExchange; + +public class GateioStreamingExchange extends GateioExchange implements StreamingExchange { + + private GateioStreamingService streamingService; + private StreamingMarketDataService streamingMarketDataService; + private StreamingTradeService streamingTradeService; + private StreamingAccountService streamingAccountService; + + public GateioStreamingExchange() {} + + @Override + public Completable connect(ProductSubscription... args) { + streamingService = new GateioStreamingService(exchangeSpecification.getSslUri(), exchangeSpecification.getApiKey(), exchangeSpecification.getSecretKey()); + applyStreamingSpecification(exchangeSpecification, streamingService); + streamingMarketDataService = new GateioStreamingMarketDataService(streamingService); + streamingTradeService = new GateioStreamingTradeService(streamingService); + streamingAccountService = new GateioStreamingAccountService(streamingService); + + return streamingService.connect(); + } + + @Override + public Completable disconnect() { + GateioStreamingService service = streamingService; + streamingService = null; + streamingMarketDataService = null; + streamingTradeService = null; + streamingAccountService = null; + return service.disconnect(); + } + + @Override + public StreamingMarketDataService getStreamingMarketDataService() { + return streamingMarketDataService; + } + + @Override + public StreamingTradeService getStreamingTradeService() { + return streamingTradeService; + } + + @Override + public StreamingAccountService getStreamingAccountService() { + return streamingAccountService; + } + + @Override + public boolean isAlive() { + return streamingService != null && streamingService.isSocketOpen(); + } + + @Override + public void useCompressedMessages(boolean compressedMessages) { + streamingService.useCompressedMessages(compressedMessages); + } + + @Override + public ExchangeSpecification getDefaultExchangeSpecification() { + ExchangeSpecification specification = super.getDefaultExchangeSpecification(); + specification.setShouldLoadRemoteMetaData(false); + specification.setSslUri(Config.V4_URL); + return specification; + } +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingMarketDataService.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingMarketDataService.java new file mode 100644 index 00000000000..462650cfb96 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingMarketDataService.java @@ -0,0 +1,62 @@ +package info.bitrich.xchangestream.gateio; + +import info.bitrich.xchangestream.core.StreamingMarketDataService; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.orderbook.GateioOrderBookNotification; +import info.bitrich.xchangestream.gateio.dto.response.ticker.GateioTickerNotification; +import info.bitrich.xchangestream.gateio.dto.response.trade.GateioTradeNotification; +import io.reactivex.Observable; +import java.time.Duration; +import org.apache.commons.lang3.ArrayUtils; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.marketdata.OrderBook; +import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.dto.marketdata.Trade; + +public class GateioStreamingMarketDataService implements StreamingMarketDataService { + + public static final int MAX_DEPTH_DEFAULT = 5; + public static final int UPDATE_INTERVAL_DEFAULT = 100; + private final GateioStreamingService service; + + public GateioStreamingMarketDataService(GateioStreamingService service) { + this.service = service; + } + + + /** + * Uses the limited-level snapshot method: + * https://www.gate.io/docs/apiv4/ws/index.html#limited-level-full-order-book-snapshot + * + * @param currencyPair Currency pair of the order book + * @param args Order book level: {@link Integer}, update speed: {@link Duration} + */ + + @Override + public Observable getOrderBook(CurrencyPair currencyPair, Object... args) { + Integer orderBookLevel = (Integer) ArrayUtils.get(args, 0, MAX_DEPTH_DEFAULT); + Duration updateSpeed = (Duration) ArrayUtils.get(args, 1, UPDATE_INTERVAL_DEFAULT); + return service + .subscribeChannel(Config.SPOT_ORDERBOOK_CHANNEL, new Object[]{currencyPair, orderBookLevel, updateSpeed}) + .map(GateioOrderBookNotification.class::cast) + .map(GateioStreamingAdapters::toOrderBook); + } + + + @Override + public Observable getTicker(CurrencyPair currencyPair, Object... args) { + return service + .subscribeChannel(Config.SPOT_TICKERS_CHANNEL, currencyPair) + .map(GateioTickerNotification.class::cast) + .map(GateioStreamingAdapters::toTicker); + } + + + @Override + public Observable getTrades(CurrencyPair currencyPair, Object... args) { + return service + .subscribeChannel(Config.SPOT_TRADES_CHANNEL, currencyPair) + .map(GateioTradeNotification.class::cast) + .map(GateioStreamingAdapters::toTrade); + } +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingService.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingService.java new file mode 100644 index 00000000000..38cb5ed145d --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingService.java @@ -0,0 +1,251 @@ +package info.bitrich.xchangestream.gateio; + + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.config.IdGenerator; +import info.bitrich.xchangestream.gateio.dto.Event; +import info.bitrich.xchangestream.gateio.dto.request.GateioWsRequest; +import info.bitrich.xchangestream.gateio.dto.request.GateioWsRequest.AuthInfo; +import info.bitrich.xchangestream.gateio.dto.request.payload.CurrencyPairLevelIntervalPayload; +import info.bitrich.xchangestream.gateio.dto.request.payload.CurrencyPairPayload; +import info.bitrich.xchangestream.gateio.dto.request.payload.EmptyPayload; +import info.bitrich.xchangestream.gateio.dto.request.payload.StringPayload; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import info.bitrich.xchangestream.gateio.dto.response.balance.GateioMultipleSpotBalanceNotification; +import info.bitrich.xchangestream.gateio.dto.response.usertrade.GateioMultipleUserTradeNotification; +import info.bitrich.xchangestream.gateio.dto.response.usertrade.GateioSingleUserTradeNotification; +import info.bitrich.xchangestream.service.netty.NettyStreamingService; +import info.bitrich.xchangestream.service.netty.WebSocketClientCompressionAllowClientNoContextAndServerNoContextHandler; +import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler; +import io.reactivex.Observable; +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.Validate; +import org.knowm.xchange.currency.CurrencyPair; + +@Slf4j +public class GateioStreamingService extends NettyStreamingService { + + private static final String USERTRADES_BROADCAST_CHANNEL_NAME = Config.SPOT_USER_TRADES_CHANNEL + Config.CHANNEL_NAME_DELIMITER + "null"; + + private final Map> subscriptions = new ConcurrentHashMap<>(); + + private final ObjectMapper objectMapper = Config.getInstance().getObjectMapper(); + + private final String apiKey; + + private final GateioStreamingAuthHelper gateioStreamingAuthHelper; + + public GateioStreamingService(String apiUri, String apiKey, String apiSecret) { + super(apiUri, Integer.MAX_VALUE); + this.apiKey = apiKey; + this.gateioStreamingAuthHelper = new GateioStreamingAuthHelper(apiSecret); + } + + @Override + protected String getChannelNameFromMessage(GateioWsNotification message) { + return message.getUniqueChannelName(); + } + + @Override + public Observable subscribeChannel(String channelName, Object... args) { + final CurrencyPair currencyPair = + (args.length > 0 && args[0] instanceof CurrencyPair) ? ((CurrencyPair) args[0]) : null; + + String uniqueChannelName = + String.format("%s%s%s", channelName, Config.CHANNEL_NAME_DELIMITER, currencyPair); + + // Example channel name key: spot.order_book-BTC/USDT + if (!channels.containsKey(uniqueChannelName) && !subscriptions.containsKey(uniqueChannelName)) { + + // subscribe + Observable observable = super.subscribeChannel(uniqueChannelName, args); + + // cache channel subscribtion + subscriptions.put(uniqueChannelName, observable); + } + + return subscriptions.get(uniqueChannelName); + } + + /** + * Returns a JSON String containing the subscription message. + * + * @param uniqueChannelName e.g. spot.order_book-BTC/USDT + * @param args CurrencyPair to subscribe and additional channel-specific arguments + * @return subscription message + */ + @Override + public String getSubscribeMessage(String uniqueChannelName, Object... args) throws IOException { + String generalChannelName = uniqueChannelName.split(Config.CHANNEL_NAME_DELIMITER)[0]; + GateioWsRequest request = getWsRequest(generalChannelName, Event.SUBSCRIBE , args); + return objectMapper.writeValueAsString(request); + } + + + private GateioWsRequest getWsRequest(String channelName, Event event, Object... args) { + // create request common part + + GateioWsRequest request = GateioWsRequest.builder() + .id(IdGenerator.getInstance().requestId()) + .channel(channelName) + .event(event) + .time(Instant.now(Config.getInstance().getClock())) + .build(); + + // create channel specific payload + Object payload; + switch (channelName) { + + // channels require only currency pair in payload + case Config.SPOT_TICKERS_CHANNEL: + case Config.SPOT_TRADES_CHANNEL: { + CurrencyPair currencyPair = (CurrencyPair) ArrayUtils.get(args, 0); + Validate.notNull(currencyPair); + + payload = CurrencyPairPayload.builder() + .currencyPair(currencyPair) + .build(); + break; + } + + // channel requires currency pair, level, interval in payload + case Config.SPOT_ORDERBOOK_CHANNEL: { + CurrencyPair currencyPair = (CurrencyPair) ArrayUtils.get(args, 0); + Integer orderBookLevel = (Integer) ArrayUtils.get(args, 1); + Duration updateSpeed = (Duration) ArrayUtils.get(args, 2); + Validate.noNullElements(new Object[]{currencyPair, orderBookLevel, updateSpeed}); + + payload = CurrencyPairLevelIntervalPayload.builder() + .currencyPair(currencyPair) + .orderBookLevel(orderBookLevel) + .updateSpeed(updateSpeed) + .build(); + break; + } + + // channel requires currency pair or default value for all + case Config.SPOT_USER_TRADES_CHANNEL: { + CurrencyPair currencyPair = (CurrencyPair) ArrayUtils.get(args, 0); + if (currencyPair == null) { + payload = StringPayload.builder() + .data("!all") + .build(); + } else { + payload = CurrencyPairPayload.builder() + .currencyPair(currencyPair) + .build(); + } + break; + } + + default: + payload = EmptyPayload.builder().build(); + } + + // add auth for private channels + if (Config.PRIVATE_CHANNELS.contains(channelName)) { + request.setAuthInfo(AuthInfo.builder() + .method("api_key") + .key(apiKey) + .sign(gateioStreamingAuthHelper.sign(channelName, event.getValue(), String.valueOf(request.getTime().getEpochSecond()))) + .build()); + } + + request.setPayload(payload); + return request; + } + + + @Override + protected WebSocketClientExtensionHandler getWebSocketClientExtensionHandler() { + return WebSocketClientCompressionAllowClientNoContextAndServerNoContextHandler.INSTANCE; + } + + + /** + * Returns a JSON String containing the unsubscribe message. + * + * @param uniqueChannelName e.g. spot.order_book-BTC/USDT + * @param args CurrencyPair to subscribe and additional channel-specific arguments + * @return unsubscribe message + */ + @Override + public String getUnsubscribeMessage(String uniqueChannelName, Object... args) throws IOException { + String generalChannelName = uniqueChannelName.split(Config.CHANNEL_NAME_DELIMITER)[0]; + GateioWsRequest unsubscribeMessage = getWsRequest(generalChannelName, Event.UNSUBSCRIBE, args); + return objectMapper.writeValueAsString(unsubscribeMessage); + } + + @Override + public void messageHandler(String message) { + log.debug("Received message: {}", message); + + // Parse incoming message + try { + + // process only update messages + JsonNode jsonNode = objectMapper.readTree(message); + String event = jsonNode.path("event") != null ? jsonNode.path("event").asText() : ""; + if (!"update".equals(event)) { + return; + } + + GateioWsNotification notification = objectMapper.treeToValue(jsonNode, GateioWsNotification.class); + + // process arrays in "result" field -> emit each item separately + if (notification instanceof GateioMultipleUserTradeNotification) { + GateioMultipleUserTradeNotification multipleNotification = (GateioMultipleUserTradeNotification) notification; + multipleNotification.toSingleNotifications().forEach(this::handleMessage); + } + else if (notification instanceof GateioMultipleSpotBalanceNotification) { + GateioMultipleSpotBalanceNotification multipleNotification = (GateioMultipleSpotBalanceNotification) notification; + multipleNotification.toSingleNotifications().forEach(this::handleMessage); + } + else { + handleMessage(notification); + } + } catch (IOException e) { + log.error("Error parsing incoming message to JSON: {}", message); + } + + } + + + @Override + protected void handleChannelMessage(String channel, GateioWsNotification message) { + if (channel == null) { + log.debug("Channel provided is null"); + return; + } + + // user trade can be emitted to 2 channels + if (message instanceof GateioSingleUserTradeNotification) { + + // subscription that listens to all currency pairs + NettyStreamingService.Subscription broadcast = channels.get( + USERTRADES_BROADCAST_CHANNEL_NAME); + if (broadcast != null && broadcast.getEmitter() != null) { + broadcast.getEmitter().onNext(message); + } + + // subscription that listens to specific currency pair + NettyStreamingService.Subscription specific = channels.get(channel); + if (specific != null && specific.getEmitter() != null) { + specific.getEmitter().onNext(message); + } + + } + else { + super.handleChannelMessage(channel, message); + } + + } +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingTradeService.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingTradeService.java new file mode 100644 index 00000000000..e375a9268b0 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/GateioStreamingTradeService.java @@ -0,0 +1,31 @@ +package info.bitrich.xchangestream.gateio; + +import info.bitrich.xchangestream.core.StreamingTradeService; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.usertrade.GateioSingleUserTradeNotification; +import io.reactivex.Observable; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.trade.UserTrade; + +public class GateioStreamingTradeService implements StreamingTradeService { + + private final GateioStreamingService service; + + public GateioStreamingTradeService(GateioStreamingService service) { + this.service = service; + } + + + @Override + public Observable getUserTrades(CurrencyPair currencyPair, Object... args) { + return service + .subscribeChannel(Config.SPOT_USER_TRADES_CHANNEL, currencyPair) + .map(GateioSingleUserTradeNotification.class::cast) + .map(GateioStreamingAdapters::toUserTrade); + } + + @Override + public Observable getUserTrades() { + return getUserTrades(null); + } +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/config/Config.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/config/Config.java new file mode 100644 index 00000000000..0d373586d85 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/config/Config.java @@ -0,0 +1,57 @@ +package info.bitrich.xchangestream.gateio.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.time.Clock; +import java.util.Arrays; +import java.util.List; +import lombok.Data; + +@Data +public final class Config { + + public static final String V4_URL = "wss://api.gateio.ws/ws/v4/"; + + public static final String SPOT_ORDERBOOK_CHANNEL = "spot.order_book"; + public static final String SPOT_TRADES_CHANNEL = "spot.trades"; + public static final String SPOT_TICKERS_CHANNEL = "spot.tickers"; + public static final String SPOT_BALANCES_CHANNEL = "spot.balances"; + public static final String SPOT_USER_TRADES_CHANNEL = "spot.usertrades"; + public static final List PRIVATE_CHANNELS = Arrays.asList(SPOT_BALANCES_CHANNEL, SPOT_USER_TRADES_CHANNEL); + + public static final String CHANNEL_NAME_DELIMITER = "-"; + + private ObjectMapper objectMapper; + private Clock clock; + + private static Config instance = new Config(); + + private Config() { + clock = Clock.systemDefaultZone(); + + objectMapper = new ObjectMapper(); + + // by default read and write timetamps as milliseconds + objectMapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false); + objectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false); + + // don't fail un unknown properties + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + // don't write nulls + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + // enable parsing to Instant + objectMapper.registerModule(new JavaTimeModule()); + } + + + public static Config getInstance() { + return instance; + } + + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/config/IdGenerator.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/config/IdGenerator.java new file mode 100644 index 00000000000..4be60bf6a8e --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/config/IdGenerator.java @@ -0,0 +1,22 @@ +package info.bitrich.xchangestream.gateio.config; + +import org.apache.commons.lang3.RandomUtils; + +public final class IdGenerator { + + private static IdGenerator instance = new IdGenerator(); + + private IdGenerator() { + } + + public static IdGenerator getInstance() { + return instance; + } + + + public Long requestId() { + return RandomUtils.nextLong(); + } +} + + diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/config/converter/InstantToTimestampSecondsConverter.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/config/converter/InstantToTimestampSecondsConverter.java new file mode 100644 index 00000000000..ff5e75d0a39 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/config/converter/InstantToTimestampSecondsConverter.java @@ -0,0 +1,15 @@ +package info.bitrich.xchangestream.gateio.config.converter; + +import com.fasterxml.jackson.databind.util.StdConverter; +import java.time.Instant; + +/** + * Converts {@code Instant} to timestamp in seconds + */ +public class InstantToTimestampSecondsConverter extends StdConverter { + + @Override + public Long convert(Instant value) { + return value.getEpochSecond(); + } +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/Event.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/Event.java new file mode 100644 index 00000000000..701149e1a90 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/Event.java @@ -0,0 +1,18 @@ +package info.bitrich.xchangestream.gateio.dto; + +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum Event { + SUBSCRIBE("subscribe"), + UNSUBSCRIBE("unsubscribe"), + UPDATE("update"); + + @JsonValue + private final String value; + + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/GateioWsRequest.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/GateioWsRequest.java new file mode 100644 index 00000000000..6c55d44ce4e --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/GateioWsRequest.java @@ -0,0 +1,58 @@ +package info.bitrich.xchangestream.gateio.dto.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import info.bitrich.xchangestream.gateio.config.converter.InstantToTimestampSecondsConverter; +import info.bitrich.xchangestream.gateio.dto.Event; +import java.time.Instant; +import lombok.Data; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.gateio.config.converter.TimestampSecondsToInstantConverter; + +@Data +@SuperBuilder +@Jacksonized +public class GateioWsRequest { + + @JsonProperty("time") + @JsonDeserialize(converter = TimestampSecondsToInstantConverter.class) + @JsonSerialize(converter = InstantToTimestampSecondsConverter.class) + private Instant time; + + @JsonProperty("id") + private Long id; + + @JsonProperty("channel") + private String channel; + + @JsonProperty("event") + private Event event; + + @JsonProperty("auth") + private AuthInfo authInfo; + + @JsonProperty("payload") + @JsonFormat(shape = JsonFormat.Shape.ARRAY) + private Object payload; + + + @Data + @SuperBuilder + @Jacksonized + public static class AuthInfo { + + @JsonProperty("method") + private String method; + + @JsonProperty("key") + private String key; + + @JsonProperty("sign") + private String sign; + + } + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/CurrencyPairLevelIntervalPayload.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/CurrencyPairLevelIntervalPayload.java new file mode 100644 index 00000000000..7a4256633f8 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/CurrencyPairLevelIntervalPayload.java @@ -0,0 +1,32 @@ +package info.bitrich.xchangestream.gateio.dto.request.payload; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Shape; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.time.Duration; +import lombok.Data; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.gateio.config.converter.CurrencyPairToStringConverter; + +@Data +@SuperBuilder +@Jacksonized +public class CurrencyPairLevelIntervalPayload { + + @JsonSerialize(converter = CurrencyPairToStringConverter.class) + private CurrencyPair currencyPair; + + @JsonFormat(shape = Shape.STRING) + private Integer orderBookLevel; + + private Duration updateSpeed; + + @JsonGetter("updateSpeed") + public String renderUpdateSpeed() { + return updateSpeed.toMillis() + "ms"; + } + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/CurrencyPairPayload.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/CurrencyPairPayload.java new file mode 100644 index 00000000000..6c329092686 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/CurrencyPairPayload.java @@ -0,0 +1,18 @@ +package info.bitrich.xchangestream.gateio.dto.request.payload; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Data; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.gateio.config.converter.CurrencyPairToStringConverter; + +@Data +@SuperBuilder +@Jacksonized +public class CurrencyPairPayload { + + @JsonSerialize(converter = CurrencyPairToStringConverter.class) + private CurrencyPair currencyPair; + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/EmptyPayload.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/EmptyPayload.java new file mode 100644 index 00000000000..8481d829958 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/EmptyPayload.java @@ -0,0 +1,12 @@ +package info.bitrich.xchangestream.gateio.dto.request.payload; + +import lombok.Data; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +@Data +@SuperBuilder +@Jacksonized +public class EmptyPayload { + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/StringPayload.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/StringPayload.java new file mode 100644 index 00000000000..4e3b484d6e7 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/request/payload/StringPayload.java @@ -0,0 +1,14 @@ +package info.bitrich.xchangestream.gateio.dto.request.payload; + +import lombok.Data; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +@Data +@SuperBuilder +@Jacksonized +public class StringPayload { + + private String data; + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/GateioWsNotification.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/GateioWsNotification.java new file mode 100644 index 00000000000..e983b8efea5 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/GateioWsNotification.java @@ -0,0 +1,58 @@ +package info.bitrich.xchangestream.gateio.dto.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.Event; +import info.bitrich.xchangestream.gateio.dto.response.balance.GateioMultipleSpotBalanceNotification; +import info.bitrich.xchangestream.gateio.dto.response.orderbook.GateioOrderBookNotification; +import info.bitrich.xchangestream.gateio.dto.response.ticker.GateioTickerNotification; +import info.bitrich.xchangestream.gateio.dto.response.trade.GateioTradeNotification; +import info.bitrich.xchangestream.gateio.dto.response.usertrade.GateioMultipleUserTradeNotification; +import java.time.Instant; +import lombok.Data; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.gateio.config.converter.TimestampSecondsToInstantConverter; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "channel", + visible = true) +@JsonSubTypes({ + @Type(value = GateioTradeNotification.class, name = Config.SPOT_TRADES_CHANNEL), + @Type(value = GateioTickerNotification.class, name = Config.SPOT_TICKERS_CHANNEL), + @Type(value = GateioOrderBookNotification.class, name = Config.SPOT_ORDERBOOK_CHANNEL), + @Type(value = GateioMultipleSpotBalanceNotification.class, name = Config.SPOT_BALANCES_CHANNEL), + @Type(value = GateioMultipleUserTradeNotification.class, name = Config.SPOT_USER_TRADES_CHANNEL) +}) +@Data +@SuperBuilder +@Jacksonized +public class GateioWsNotification { + + @JsonProperty("time") + @JsonDeserialize(converter = TimestampSecondsToInstantConverter.class) + private Instant time; + + @JsonProperty("time_ms") + private Instant timeMs; + + @JsonProperty("channel") + private String channel; + + @JsonProperty("event") + private Event event; + + @JsonProperty("error") + private String error; + + + public String getUniqueChannelName() { + return channel; + } + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/balance/BalancePayload.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/balance/BalancePayload.java new file mode 100644 index 00000000000..fc70c5eb980 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/balance/BalancePayload.java @@ -0,0 +1,46 @@ +package info.bitrich.xchangestream.gateio.dto.response.balance; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Data; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.gateio.config.converter.TimestampSecondsToInstantConverter; + +@Data +public class BalancePayload { + + @JsonProperty("timestamp") + @JsonDeserialize(converter = TimestampSecondsToInstantConverter.class) + private Instant time; + + @JsonProperty("timestamp_ms") + private Instant timeMs; + + @JsonProperty("user") + Long userId; + + @JsonProperty("currency") + Currency currency; + + @JsonProperty("change") + BigDecimal change; + + @JsonProperty("total") + BigDecimal total; + + @JsonProperty("available") + BigDecimal available; + + @JsonProperty("freeze") + BigDecimal freeze; + + @JsonProperty("freeze_change") + BigDecimal freezeChange; + + + @JsonProperty("change_type") + String changeType; + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/balance/GateioMultipleSpotBalanceNotification.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/balance/GateioMultipleSpotBalanceNotification.java new file mode 100644 index 00000000000..3d6e8617ef5 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/balance/GateioMultipleSpotBalanceNotification.java @@ -0,0 +1,32 @@ +package info.bitrich.xchangestream.gateio.dto.response.balance; + +import com.fasterxml.jackson.annotation.JsonProperty; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import java.util.List; +import java.util.stream.Collectors; +import lombok.Data; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +@Data +@SuperBuilder +@Jacksonized +public class GateioMultipleSpotBalanceNotification extends GateioWsNotification { + + @JsonProperty("result") + private List result; + + + public List toSingleNotifications() { + return result.stream() + .map(balancePayload -> GateioSingleSpotBalanceNotification.builder() + .result(balancePayload) + .time(getTime()) + .timeMs(getTimeMs()) + .channel(getChannel()) + .event(getEvent()) + .error(getError()) + .build()) + .collect(Collectors.toList()); + } +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/balance/GateioSingleSpotBalanceNotification.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/balance/GateioSingleSpotBalanceNotification.java new file mode 100644 index 00000000000..db22e18e605 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/balance/GateioSingleSpotBalanceNotification.java @@ -0,0 +1,21 @@ +package info.bitrich.xchangestream.gateio.dto.response.balance; + +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import lombok.Data; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +public class GateioSingleSpotBalanceNotification extends GateioWsNotification { + + private BalancePayload result; + + @Override + public String getUniqueChannelName() { + // there is no currency specific subscription + return super.getUniqueChannelName() + Config.CHANNEL_NAME_DELIMITER + "null"; + } + + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/orderbook/GateioOrderBookNotification.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/orderbook/GateioOrderBookNotification.java new file mode 100644 index 00000000000..2960524f807 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/orderbook/GateioOrderBookNotification.java @@ -0,0 +1,24 @@ +package info.bitrich.xchangestream.gateio.dto.response.orderbook; + +import com.fasterxml.jackson.annotation.JsonProperty; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import lombok.Data; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +@Data +@SuperBuilder +@Jacksonized +public class GateioOrderBookNotification extends GateioWsNotification { + + @JsonProperty("result") + private OrderBookPayload result; + + @Override + public String getUniqueChannelName() { + String suffix = result.getCurrencyPair() != null ? Config.CHANNEL_NAME_DELIMITER + result.getCurrencyPair() : ""; + return super.getUniqueChannelName() + suffix; + } + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/orderbook/OrderBookPayload.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/orderbook/OrderBookPayload.java new file mode 100644 index 00000000000..ebb8698a5ad --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/orderbook/OrderBookPayload.java @@ -0,0 +1,49 @@ +package info.bitrich.xchangestream.gateio.dto.response.orderbook; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.List; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyPairConverter; + +@Data +public class OrderBookPayload { + + @JsonProperty("t") + Instant timestamp; + + @JsonProperty("lastUpdateId") + Long lastUpdateId; + + @JsonProperty("s") + @JsonDeserialize(converter = StringToCurrencyPairConverter.class) + CurrencyPair currencyPair; + + + @JsonProperty("asks") + List asks; + + @JsonProperty("bids") + List bids; + + + @Data + @Builder + @Jacksonized + @JsonFormat(shape = JsonFormat.Shape.ARRAY) + public static class PriceSizeEntry { + + BigDecimal price; + + BigDecimal size; + + } + + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/ticker/GateioTickerNotification.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/ticker/GateioTickerNotification.java new file mode 100644 index 00000000000..c75e9141128 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/ticker/GateioTickerNotification.java @@ -0,0 +1,24 @@ +package info.bitrich.xchangestream.gateio.dto.response.ticker; + +import com.fasterxml.jackson.annotation.JsonProperty; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import lombok.Data; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +@Data +@SuperBuilder +@Jacksonized +public class GateioTickerNotification extends GateioWsNotification { + + @JsonProperty("result") + private TickerPayload result; + + @Override + public String getUniqueChannelName() { + String suffix = result.getCurrencyPair() != null ? Config.CHANNEL_NAME_DELIMITER + result.getCurrencyPair() : ""; + return super.getUniqueChannelName() + suffix; + } + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/ticker/TickerPayload.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/ticker/TickerPayload.java new file mode 100644 index 00000000000..b182cdf77f8 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/ticker/TickerPayload.java @@ -0,0 +1,41 @@ +package info.bitrich.xchangestream.gateio.dto.response.ticker; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.math.BigDecimal; +import lombok.Data; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyPairConverter; + +@Data +public class TickerPayload { + + @JsonProperty("currency_pair") + @JsonDeserialize(converter = StringToCurrencyPairConverter.class) + CurrencyPair currencyPair; + + @JsonProperty("last") + BigDecimal lastPrice; + + @JsonProperty("lowest_ask") + BigDecimal lowestAsk; + + @JsonProperty("highest_bid") + BigDecimal highestBid; + + @JsonProperty("change_percentage") + BigDecimal changePercent24h; + + @JsonProperty("base_volume") + BigDecimal baseVolume; + + @JsonProperty("quote_volume") + BigDecimal quoteVolume; + + @JsonProperty("high_24h") + BigDecimal highPrice24h; + + @JsonProperty("low_24h") + BigDecimal lowPrice24h; + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/trade/GateioTradeNotification.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/trade/GateioTradeNotification.java new file mode 100644 index 00000000000..12eae15ae9d --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/trade/GateioTradeNotification.java @@ -0,0 +1,23 @@ +package info.bitrich.xchangestream.gateio.dto.response.trade; + +import com.fasterxml.jackson.annotation.JsonProperty; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import lombok.Data; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +@Data +@SuperBuilder +@Jacksonized +public class GateioTradeNotification extends GateioWsNotification { + + @JsonProperty("result") + private TradePayload result; + + @Override + public String getUniqueChannelName() { + String suffix = result.getCurrencyPair() != null ? Config.CHANNEL_NAME_DELIMITER + result.getCurrencyPair() : ""; + return super.getUniqueChannelName() + suffix; + } +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/trade/TradePayload.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/trade/TradePayload.java new file mode 100644 index 00000000000..b4e1e8e366f --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/trade/TradePayload.java @@ -0,0 +1,46 @@ +package info.bitrich.xchangestream.gateio.dto.response.trade; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Data; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.gateio.config.converter.DoubleToInstantConverter; +import org.knowm.xchange.gateio.config.converter.OrderTypeToStringConverter; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyPairConverter; +import org.knowm.xchange.gateio.config.converter.StringToOrderTypeConverter; +import org.knowm.xchange.gateio.config.converter.TimestampSecondsToInstantConverter; + +@Data +public class TradePayload { + + @JsonProperty("id") + Long id; + + @JsonProperty("create_time") + @JsonDeserialize(converter = TimestampSecondsToInstantConverter.class) + Instant time; + + @JsonProperty("create_time_ms") + @JsonDeserialize(converter = DoubleToInstantConverter.class) + Instant timeMs; + + @JsonProperty("side") + @JsonDeserialize(converter = StringToOrderTypeConverter.class) + @JsonSerialize(converter = OrderTypeToStringConverter.class) + OrderType side; + + @JsonProperty("currency_pair") + @JsonDeserialize(converter = StringToCurrencyPairConverter.class) + CurrencyPair currencyPair; + + @JsonProperty("amount") + BigDecimal amount; + + @JsonProperty("price") + BigDecimal price; + +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/usertrade/GateioMultipleUserTradeNotification.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/usertrade/GateioMultipleUserTradeNotification.java new file mode 100644 index 00000000000..2ba4bb69064 --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/usertrade/GateioMultipleUserTradeNotification.java @@ -0,0 +1,32 @@ +package info.bitrich.xchangestream.gateio.dto.response.usertrade; + +import com.fasterxml.jackson.annotation.JsonProperty; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import java.util.List; +import java.util.stream.Collectors; +import lombok.Data; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +@Data +@SuperBuilder +@Jacksonized +public class GateioMultipleUserTradeNotification extends GateioWsNotification { + + @JsonProperty("result") + private List result; + + + public List toSingleNotifications() { + return result.stream() + .map(userTradePayload -> GateioSingleUserTradeNotification.builder() + .result(userTradePayload) + .time(getTime()) + .timeMs(getTimeMs()) + .channel(getChannel()) + .event(getEvent()) + .error(getError()) + .build()) + .collect(Collectors.toList()); + } +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/usertrade/GateioSingleUserTradeNotification.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/usertrade/GateioSingleUserTradeNotification.java new file mode 100644 index 00000000000..be5238c780d --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/usertrade/GateioSingleUserTradeNotification.java @@ -0,0 +1,19 @@ +package info.bitrich.xchangestream.gateio.dto.response.usertrade; + +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import lombok.Data; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +public class GateioSingleUserTradeNotification extends GateioWsNotification { + + private UserTradePayload result; + + @Override + public String getUniqueChannelName() { + String suffix = result.getCurrencyPair() != null ? Config.CHANNEL_NAME_DELIMITER + result.getCurrencyPair() : ""; + return super.getUniqueChannelName() + suffix; + } +} diff --git a/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/usertrade/UserTradePayload.java b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/usertrade/UserTradePayload.java new file mode 100644 index 00000000000..adb9f9caf2b --- /dev/null +++ b/xchange-stream-gateio/src/main/java/info/bitrich/xchangestream/gateio/dto/response/usertrade/UserTradePayload.java @@ -0,0 +1,72 @@ +package info.bitrich.xchangestream.gateio.dto.response.usertrade; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.math.BigDecimal; +import java.time.Instant; +import lombok.Data; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.gateio.config.converter.DoubleToInstantConverter; +import org.knowm.xchange.gateio.config.converter.OrderTypeToStringConverter; +import org.knowm.xchange.gateio.config.converter.StringToCurrencyPairConverter; +import org.knowm.xchange.gateio.config.converter.StringToOrderTypeConverter; +import org.knowm.xchange.gateio.config.converter.TimestampSecondsToInstantConverter; +import org.knowm.xchange.gateio.dto.trade.Role; + +@Data +public class UserTradePayload { + + @JsonProperty("id") + Long id; + + @JsonProperty("user_id") + Long userId; + + @JsonProperty("order_id") + Long orderId; + + @JsonProperty("currency_pair") + @JsonDeserialize(converter = StringToCurrencyPairConverter.class) + CurrencyPair currencyPair; + + @JsonProperty("create_time") + @JsonDeserialize(converter = TimestampSecondsToInstantConverter.class) + Instant time; + + @JsonProperty("create_time_ms") + @JsonDeserialize(converter = DoubleToInstantConverter.class) + Instant timeMs; + + @JsonProperty("side") + @JsonDeserialize(converter = StringToOrderTypeConverter.class) + @JsonSerialize(converter = OrderTypeToStringConverter.class) + OrderType side; + + @JsonProperty("amount") + BigDecimal amount; + + @JsonProperty("role") + Role role; + + @JsonProperty("price") + BigDecimal price; + + @JsonProperty("fee") + BigDecimal fee; + + @JsonProperty("fee_currency") + Currency feeCurrency; + + @JsonProperty("point_fee") + BigDecimal pointFee; + + @JsonProperty("gt_fee") + BigDecimal gtFee; + + @JsonProperty("text") + String remark; + +} diff --git a/xchange-stream-gateio/src/test/java/dto/GateioOrderBookResponseTest.java b/xchange-stream-gateio/src/test/java/dto/GateioOrderBookResponseTest.java deleted file mode 100644 index 286d57bf672..00000000000 --- a/xchange-stream-gateio/src/test/java/dto/GateioOrderBookResponseTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package dto; - -import com.google.common.io.CharStreams; -import dto.response.GateioOrderBookResponse; -import info.bitrich.xchangestream.service.netty.StreamingObjectMapperHelper; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import org.junit.Assert; -import org.junit.Test; - -public class GateioOrderBookResponseTest { - @Test - public void V4_OrderBookResponse_ParsingTest() throws IOException { - InputStream is = getClass().getClassLoader().getResourceAsStream("OrderBookResponse.json"); - String body = null; - try (final Reader reader = new InputStreamReader(is)) { - body = CharStreams.toString(reader); - } - - GateioOrderBookResponse message = - StreamingObjectMapperHelper.getObjectMapper() - .readValue(body, GateioOrderBookResponse.class); - - // Message Metadata - Assert.assertEquals(1606295412, message.getTime()); - Assert.assertEquals("spot.order_book", message.getChannel()); - Assert.assertEquals("update", message.getEvent()); - - // Result data - Assert.assertEquals(1606295412123L, message.getResult().getUpdateTimeMilliseconds()); - Assert.assertEquals(48791820, message.getResult().getLastUpdateId()); - Assert.assertEquals("BTC_USDT", message.getResult().getCurrencyPair()); - - // Bids - Assert.assertEquals("19079.55", message.getResult().getBids()[0][0]); - Assert.assertEquals("0.0195", message.getResult().getBids()[0][1]); - - // Asks - Assert.assertEquals("19080.24", message.getResult().getAsks()[0][0]); - Assert.assertEquals("0.1638", message.getResult().getAsks()[0][1]); - } -} diff --git a/xchange-stream-gateio/src/test/java/dto/GateioTradesResponseTest.java b/xchange-stream-gateio/src/test/java/dto/GateioTradesResponseTest.java deleted file mode 100644 index 694308375d2..00000000000 --- a/xchange-stream-gateio/src/test/java/dto/GateioTradesResponseTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package dto; - -import com.google.common.io.CharStreams; -import dto.response.GateioTradesResponse; -import info.bitrich.xchangestream.service.netty.StreamingObjectMapperHelper; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import org.junit.Assert; -import org.junit.Test; - -public class GateioTradesResponseTest { - @Test - public void V4_TradeResponse_ParsingTest() throws IOException { - InputStream is = getClass().getClassLoader().getResourceAsStream("TradeResponses.json"); - String body = null; - try (final Reader reader = new InputStreamReader(is)) { - body = CharStreams.toString(reader); - } - - GateioTradesResponse message = - StreamingObjectMapperHelper.getObjectMapper().readValue(body, GateioTradesResponse.class); - - // Message Metadata - Assert.assertEquals(1606292218, message.getTime()); - Assert.assertEquals("spot.trades", message.getChannel()); - Assert.assertEquals("update", message.getEvent()); - - // Result data - Assert.assertEquals(309143071, message.getResult().getId()); - Assert.assertEquals(1606292218, message.getResult().getCreateTimeSeconds()); - Assert.assertEquals("1606292218213.4578", message.getResult().getCreateTimeMilliseconds()); - Assert.assertEquals("GT_USDT", message.getResult().getCurrencyPair()); - Assert.assertEquals("sell", message.getResult().getSide()); - Assert.assertEquals("16.4700000000", message.getResult().getAmount()); - Assert.assertEquals("0.4705000000", message.getResult().getPrice()); - } -} diff --git a/xchange-stream-gateio/src/test/java/GateioManualExample.java b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioManualExample.java similarity index 56% rename from xchange-stream-gateio/src/test/java/GateioManualExample.java rename to xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioManualExample.java index be720222698..97624b63e49 100644 --- a/xchange-stream-gateio/src/test/java/GateioManualExample.java +++ b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioManualExample.java @@ -1,32 +1,25 @@ -import info.bitrich.xchangestream.core.ProductSubscription; +package info.bitrich.xchangestream.gateio; + import info.bitrich.xchangestream.core.StreamingExchangeFactory; import io.reactivex.disposables.Disposable; +import lombok.extern.slf4j.Slf4j; import org.knowm.xchange.ExchangeSpecification; import org.knowm.xchange.currency.CurrencyPair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +@Slf4j public class GateioManualExample { - private static final Logger LOG = LoggerFactory.getLogger(GateioManualExample.class); public static void main(String[] args) throws Exception { - ProductSubscription productSubscription = - ProductSubscription.create() - .addOrderbook(CurrencyPair.ETH_USDT) - .addOrderbook(CurrencyPair.BTC_USDT) - .addTrades(CurrencyPair.ETH_USDT) - .build(); ExchangeSpecification spec = StreamingExchangeFactory.INSTANCE .createExchangeWithoutSpecification(GateioStreamingExchange.class) .getDefaultExchangeSpecification(); - spec.setShouldLoadRemoteMetaData(false); GateioStreamingExchange exchange = (GateioStreamingExchange) StreamingExchangeFactory.INSTANCE.createExchange(spec); - exchange.connect(productSubscription).blockingAwait(); + exchange.connect().blockingAwait(); Disposable sub1 = exchange @@ -34,30 +27,30 @@ public static void main(String[] args) throws Exception { .getOrderBook(CurrencyPair.ETH_USDT) .subscribe( orderBook -> { - LOG.info("First ask: {}", orderBook.getAsks().get(0)); - LOG.info("First bid: {}", orderBook.getBids().get(0)); + log.info("First ask: {}", orderBook.getAsks().get(0)); + log.info("First bid: {}", orderBook.getBids().get(0)); }, - throwable -> LOG.error("ERROR in getting order book: ", throwable)); + throwable -> log.error("ERROR in getting order book: ", throwable)); Disposable sub2 = exchange .getStreamingMarketDataService() .getOrderBook(CurrencyPair.BTC_USDT) .subscribe( orderBook -> { - LOG.info("First ask: {}", orderBook.getAsks().get(0)); - LOG.info("First bid: {}", orderBook.getBids().get(0)); + log.info("First ask: {}", orderBook.getAsks().get(0)); + log.info("First bid: {}", orderBook.getBids().get(0)); }, - throwable -> LOG.error("ERROR in getting order book: ", throwable)); + throwable -> log.error("ERROR in getting order book: ", throwable)); Disposable sub3 = exchange .getStreamingMarketDataService() .getTrades(CurrencyPair.BTC_USDT) .subscribe( trade -> { - LOG.info("Trade Price: {}", trade.getPrice()); - LOG.info("Trade Amount: {}", trade.getOriginalAmount()); + log.info("Trade Price: {}", trade.getPrice()); + log.info("Trade Amount: {}", trade.getOriginalAmount()); }, - throwable -> LOG.error("ERROR in getting trade: ", throwable)); + throwable -> log.error("ERROR in getting trade: ", throwable)); Thread.sleep(1000); sub1.dispose(); diff --git a/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingAccountServiceIntegration.java b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingAccountServiceIntegration.java new file mode 100644 index 00000000000..aaf76323ccf --- /dev/null +++ b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingAccountServiceIntegration.java @@ -0,0 +1,46 @@ +package info.bitrich.xchangestream.gateio; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import io.reactivex.Observable; +import io.reactivex.observers.BaseTestConsumer.TestWaitStrategy; +import io.reactivex.observers.TestObserver; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.dto.account.Balance; + +@Disabled("Needs authenticated exchange and real balance change. Set env vars GATEIO_API_KEY/GATEIO_API_SECRET") +class GateioStreamingAccountServiceIntegration extends GateioStreamingExchangeIT { + + @BeforeEach + void authConfigured() { + assumeTrue(StringUtils.isNotEmpty(exchange.getExchangeSpecification().getApiKey()), "Needs auth"); + assumeTrue(StringUtils.isNotEmpty(exchange.getExchangeSpecification().getSecretKey()), "Needs auth"); + } + + + @Test + void spot_balances() { + Observable observable = exchange + .getStreamingAccountService() + .getBalanceChanges(Currency.USDT); + + TestObserver testObserver = observable.test(); + + Balance balance = testObserver + .assertSubscribed() + .awaitCount(1, TestWaitStrategy.SLEEP_10MS, 2000000) + .assertNoTimeout() + .values().get(0); + + testObserver.dispose(); + + assertThat(balance).hasNoNullFieldsOrProperties(); + } + + +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingAccountServiceTest.java b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingAccountServiceTest.java new file mode 100644 index 00000000000..00f2195920a --- /dev/null +++ b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingAccountServiceTest.java @@ -0,0 +1,82 @@ +package info.bitrich.xchangestream.gateio; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.databind.ObjectMapper; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import info.bitrich.xchangestream.gateio.dto.response.balance.GateioMultipleSpotBalanceNotification; +import io.reactivex.Observable; +import io.reactivex.observers.TestObserver; +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Date; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.dto.account.Balance; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class GateioStreamingAccountServiceTest { + + @Mock + GateioStreamingService gateioStreamingService; + GateioStreamingAccountService gateioStreamingAccountService; + + ObjectMapper objectMapper = Config.getInstance().getObjectMapper(); + + + @BeforeEach + public void setup() { + gateioStreamingAccountService = new GateioStreamingAccountService(gateioStreamingService); + } + + + @Test + void spot_balances() throws Exception { + GateioWsNotification multipleNotification = readNotification("spot.balance.update.json"); + assertThat(multipleNotification).isInstanceOf(GateioMultipleSpotBalanceNotification.class); + GateioWsNotification notification = ((GateioMultipleSpotBalanceNotification) multipleNotification).toSingleNotifications().get(0); + + when(gateioStreamingService.subscribeChannel(eq("spot.balances"))) + .thenReturn(Observable.just(notification)); + + Observable observable = gateioStreamingAccountService.getBalanceChanges(Currency.USDT); + + TestObserver testObserver = observable.test(); + + Balance actual = testObserver + .assertSubscribed() + .awaitCount(1) + .assertNoTimeout() + .values().get(0); + + testObserver.dispose(); + + Balance expected = new Balance.Builder() + .available(new BigDecimal("42.06583427604872431142")) + .currency(Currency.USDT) + .frozen(BigDecimal.ONE) + .timestamp(Date.from(Instant.ofEpochMilli(1691707273890L))) + .total(new BigDecimal("43.06583427604872431142")) + .build(); + + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + + private GateioWsNotification readNotification(String resourceName) throws IOException { + return objectMapper.readValue( + getClass().getClassLoader().getResourceAsStream(resourceName), + GateioWsNotification.class + ); + } + + +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingAuthHelperTest.java b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingAuthHelperTest.java new file mode 100644 index 00000000000..0bf85f3adf3 --- /dev/null +++ b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingAuthHelperTest.java @@ -0,0 +1,22 @@ +package info.bitrich.xchangestream.gateio; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class GateioStreamingAuthHelperTest { + + @Test + void sign() { + String time = "1691678147"; + String channel = "spot.balances"; + String event = "subscribe"; + String apiSecret = "a"; + GateioStreamingAuthHelper gateioStreamingAuthHelper = new GateioStreamingAuthHelper(apiSecret); + + String actual = gateioStreamingAuthHelper.sign(channel, event, time); + String expected = "36481b07c37f27bbfda955c312580e70652d856e7626698f7a5250cf31ffb4052be60f2ec42e6e8816c9fb93d7e26d33f8ef245346b7fd2316ad99dd05ad8176"; + assertThat(actual).isEqualTo(expected); + + } +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingExchangeIT.java b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingExchangeIT.java new file mode 100644 index 00000000000..8553614e150 --- /dev/null +++ b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingExchangeIT.java @@ -0,0 +1,52 @@ +package info.bitrich.xchangestream.gateio; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import info.bitrich.xchangestream.core.StreamingExchangeFactory; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.knowm.xchange.ExchangeSpecification; + +public class GateioStreamingExchangeIT { + + public static GateioStreamingExchange exchange; + + @BeforeAll + public static void setup() { + try { + + ExchangeSpecification spec = + StreamingExchangeFactory.INSTANCE + .createExchangeWithoutSpecification(GateioStreamingExchange.class) + .getDefaultExchangeSpecification(); + spec.setApiKey(System.getenv("GATEIO_API_KEY")); + spec.setSecretKey(System.getenv("GATEIO_API_SECRET")); + + exchange = + (GateioStreamingExchange) StreamingExchangeFactory.INSTANCE.createExchange(spec); + + exchange.connect().blockingAwait(); + } + catch (Exception ignored) { + + } + } + + + @BeforeEach + void exchangeReachable() { + assumeTrue(exchange.isAlive(), "Exchange is unreachable"); + } + + + @AfterAll + public static void cleanup() { + if (exchange.isAlive()) { + exchange.disconnect().blockingAwait(); + } + } + + + +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingMarketDataServiceIntegration.java b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingMarketDataServiceIntegration.java new file mode 100644 index 00000000000..9b69d6517f5 --- /dev/null +++ b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingMarketDataServiceIntegration.java @@ -0,0 +1,86 @@ +package info.bitrich.xchangestream.gateio; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.reactivex.Observable; +import io.reactivex.observers.BaseTestConsumer.TestWaitStrategy; +import io.reactivex.observers.TestObserver; +import java.time.Duration; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.marketdata.OrderBook; +import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.dto.marketdata.Trade; + +public class GateioStreamingMarketDataServiceIntegration extends GateioStreamingExchangeIT { + + @Test + void order_book() { + Observable observable = exchange + .getStreamingMarketDataService() + .getOrderBook(CurrencyPair.BTC_USDT, 10, Duration.ofMillis(100)); + + TestObserver testObserver = observable.test(); + + OrderBook orderBook = testObserver + .assertSubscribed() + .awaitCount(1, TestWaitStrategy.SLEEP_10MS, 20000) + .assertNoTimeout() + .values().get(0); + + testObserver.dispose(); + + assertThat(orderBook).hasNoNullFieldsOrProperties(); + assertThat(orderBook.getBids()).hasSize(10); + assertThat(orderBook.getAsks()).hasSize(10); + + // bids should be lower than asks + assertThat(orderBook.getBids().get(0).getLimitPrice()).isLessThan(orderBook.getAsks().get(0).getLimitPrice()); + } + + + @Test + void trades() { + Observable observable = exchange + .getStreamingMarketDataService() + .getTrades(CurrencyPair.BTC_USDT); + + TestObserver testObserver = observable.test(); + + Trade trade = testObserver + .assertSubscribed() + .awaitCount(1, TestWaitStrategy.SLEEP_10MS, 20000) + .assertNoTimeout() + .values().get(0); + + testObserver.dispose(); + + assertThat(trade).hasNoNullFieldsOrPropertiesExcept("makerOrderId", "takerOrderId"); + assertThat(trade.getInstrument()).isEqualTo(CurrencyPair.BTC_USDT); + + } + + + @Test + void ticker() { + Observable observable = exchange + .getStreamingMarketDataService() + .getTicker(CurrencyPair.BTC_USDT); + + TestObserver testObserver = observable.test(); + + Ticker ticker = testObserver + .assertSubscribed() + .awaitCount(1, TestWaitStrategy.SLEEP_10MS, 70000) + .assertNoTimeout() + .values() + .get(0); + + testObserver.dispose(); + + assertThat(ticker).hasNoNullFieldsOrPropertiesExcept("open", "vwap", "bidSize", "askSize"); + assertThat(ticker.getInstrument()).isEqualTo(CurrencyPair.BTC_USDT); + } + + +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingMarketDataServiceTest.java b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingMarketDataServiceTest.java new file mode 100644 index 00000000000..5c644a47763 --- /dev/null +++ b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingMarketDataServiceTest.java @@ -0,0 +1,140 @@ +package info.bitrich.xchangestream.gateio; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.databind.ObjectMapper; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import io.reactivex.Observable; +import io.reactivex.observers.TestObserver; +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.dto.marketdata.OrderBook; +import org.knowm.xchange.dto.marketdata.Ticker; +import org.knowm.xchange.dto.marketdata.Trade; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class GateioStreamingMarketDataServiceTest { + + @Mock + GateioStreamingService gateioStreamingService; + GateioStreamingMarketDataService gateioStreamingMarketDataService; + + ObjectMapper objectMapper = Config.getInstance().getObjectMapper(); + + @BeforeEach + public void setup() { + gateioStreamingMarketDataService = new GateioStreamingMarketDataService(gateioStreamingService); + } + + + @Test + void order_book() throws Exception { + GateioWsNotification notification = readNotification("spot.order_book.update.json"); + when(gateioStreamingService.subscribeChannel(eq("spot.order_book"), eq(CurrencyPair.BTC_USDT), eq(10), eq(Duration.ofMillis(100)))) + .thenReturn(Observable.just(notification)); + + Observable observable = gateioStreamingMarketDataService + .getOrderBook(CurrencyPair.BTC_USDT, 10, Duration.ofMillis(100)); + + TestObserver testObserver = observable.test(); + + OrderBook actual = testObserver + .assertSubscribed() + .awaitCount(1) + .assertNoTimeout() + .values().get(0); + + testObserver.dispose(); + + assertThat(actual.getTimeStamp()).isEqualTo(Date.from(Instant.ofEpochMilli(1691757151367L))); + assertThat(actual.getBids()).hasSize(10); + assertThat(actual.getAsks()).hasSize(10); + } + + + @Test + void ticker() throws Exception { + GateioWsNotification notification = readNotification("spot.ticker.update.json"); + when(gateioStreamingService.subscribeChannel(eq("spot.tickers"), eq(CurrencyPair.BTC_USDT))) + .thenReturn(Observable.just(notification)); + + Observable observable = gateioStreamingMarketDataService.getTicker(CurrencyPair.BTC_USDT); + + TestObserver testObserver = observable.test(); + + Ticker actual = testObserver + .assertSubscribed() + .awaitCount(1) + .assertNoTimeout() + .values().get(0); + + testObserver.dispose(); + + Ticker expected = new Ticker.Builder() + .instrument(CurrencyPair.BTC_USDT) + .timestamp(Date.from(Instant.ofEpochMilli(1691620566926L))) + .ask(new BigDecimal("29573.7")) + .bid(new BigDecimal("29573.6")) + .high(new BigDecimal("30232.8")) + .low(new BigDecimal("29176.1")) + .last(new BigDecimal("29573.7")) + .percentageChange(new BigDecimal("-0.6601")) + .quoteVolume(new BigDecimal("171784719.492586746")) + .volume(new BigDecimal("5777.7777606776")) + .build(); + + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + @Test + void trades() throws Exception { + GateioWsNotification notification = readNotification("spot.trades.update.json"); + when(gateioStreamingService.subscribeChannel(eq("spot.trades"), eq(CurrencyPair.BTC_USDT))) + .thenReturn(Observable.just(notification)); + + Observable observable = gateioStreamingMarketDataService.getTrades(CurrencyPair.BTC_USDT); + + TestObserver testObserver = observable.test(); + + Trade actual = testObserver + .assertSubscribed() + .awaitCount(1) + .assertNoTimeout() + .values().get(0); + + testObserver.dispose(); + + Trade expected = new Trade.Builder() + .instrument(CurrencyPair.BTC_USDT) + .id("6064666343") + .originalAmount(new BigDecimal("0.0003009")) + .price(new BigDecimal("29573.7")) + .timestamp(Date.from(Instant.ofEpochMilli(1691620568789L))) + .type(OrderType.BID) + .build(); + + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + + private GateioWsNotification readNotification(String resourceName) throws IOException { + return objectMapper.readValue( + getClass().getClassLoader().getResourceAsStream(resourceName), + GateioWsNotification.class + ); + } + +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingServiceTest.java b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingServiceTest.java new file mode 100644 index 00000000000..43dc7a8b4eb --- /dev/null +++ b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingServiceTest.java @@ -0,0 +1,64 @@ +package info.bitrich.xchangestream.gateio; + + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.databind.ObjectMapper; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +public class GateioStreamingServiceTest { + + GateioStreamingService gateioStreamingService = new GateioStreamingService("", null, null); + ObjectMapper objectMapper = Config.getInstance().getObjectMapper(); + + @Test + void channel_name_from_orderbook_update() throws Exception { + GateioWsNotification notification = readNotification("spot.order_book.update.json"); + String actual = gateioStreamingService.getChannelNameFromMessage(notification); + assertThat(actual).isEqualTo("spot.order_book-BTC/USDT"); + } + + + @Test + void channel_name_from_ticker_update() throws Exception { + GateioWsNotification notification = readNotification("spot.ticker.update.json"); + String actual = gateioStreamingService.getChannelNameFromMessage(notification); + assertThat(actual).isEqualTo("spot.tickers-BTC/USDT"); + } + + + @Test + void channel_name_from_trade_update() throws Exception { + GateioWsNotification notification = readNotification("spot.trades.update.json"); + String actual = gateioStreamingService.getChannelNameFromMessage(notification); + assertThat(actual).isEqualTo("spot.trades-BTC/USDT"); + } + + + @Test + void channel_name_from_subscribe_event() throws Exception { + GateioWsNotification notification = readNotification("subscribe.event.json"); + String actual = gateioStreamingService.getChannelNameFromMessage(notification); + assertThat(actual).isEqualTo("spot.order_book"); + } + + + @Test + void channel_name_from_unsubscribe_event() throws Exception { + GateioWsNotification notification = readNotification("unsubscribe.event.json"); + String actual = gateioStreamingService.getChannelNameFromMessage(notification); + assertThat(actual).isEqualTo("spot.trades"); + } + + private GateioWsNotification readNotification(String resourceName) throws IOException { + return objectMapper.readValue( + getClass().getClassLoader().getResourceAsStream(resourceName), + GateioWsNotification.class + ); + } + + +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingTradeServiceIntegration.java b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingTradeServiceIntegration.java new file mode 100644 index 00000000000..9939f254b67 --- /dev/null +++ b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingTradeServiceIntegration.java @@ -0,0 +1,66 @@ +package info.bitrich.xchangestream.gateio; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import io.reactivex.Observable; +import io.reactivex.observers.BaseTestConsumer.TestWaitStrategy; +import io.reactivex.observers.TestObserver; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.trade.UserTrade; + +@Disabled("Needs authenticated exchange and real user trade. Set env vars GATEIO_API_KEY/GATEIO_API_SECRET") +class GateioStreamingTradeServiceIntegration extends GateioStreamingExchangeIT { + + @BeforeEach + void authConfigured() { + assumeTrue(StringUtils.isNotEmpty(exchange.getExchangeSpecification().getApiKey()), "Needs auth"); + assumeTrue(StringUtils.isNotEmpty(exchange.getExchangeSpecification().getSecretKey()), "Needs auth"); + } + + + @Test + void user_trades_btc() { + Observable observable = exchange + .getStreamingTradeService() + .getUserTrades(CurrencyPair.BTC_USDT); + + TestObserver testObserver = observable.test(); + + UserTrade userTrade = testObserver + .assertSubscribed() + .awaitCount(1, TestWaitStrategy.SLEEP_10MS, 2000000) + .assertNoTimeout() + .values().get(0); + + testObserver.dispose(); + + assertThat(userTrade).hasNoNullFieldsOrPropertiesExcept("makerOrderId", "takerOrderId"); + assertThat(userTrade.getInstrument()).isEqualTo(CurrencyPair.BTC_USDT); + } + + + @Test + void user_trades_all() { + Observable observable = exchange + .getStreamingTradeService() + .getUserTrades(); + + TestObserver testObserver = observable.test(); + + UserTrade userTrade = testObserver + .assertSubscribed() + .awaitCount(1, TestWaitStrategy.SLEEP_10MS, 2000000) + .assertNoTimeout() + .values().get(0); + + testObserver.dispose(); + + assertThat(userTrade).hasNoNullFieldsOrPropertiesExcept("makerOrderId", "takerOrderId"); + } + +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingTradeServiceTest.java b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingTradeServiceTest.java new file mode 100644 index 00000000000..7ff2c905165 --- /dev/null +++ b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/GateioStreamingTradeServiceTest.java @@ -0,0 +1,88 @@ +package info.bitrich.xchangestream.gateio; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.databind.ObjectMapper; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.GateioWsNotification; +import info.bitrich.xchangestream.gateio.dto.response.usertrade.GateioMultipleUserTradeNotification; +import io.reactivex.Observable; +import io.reactivex.observers.TestObserver; +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Date; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.currency.CurrencyPair; +import org.knowm.xchange.dto.Order.OrderType; +import org.knowm.xchange.dto.trade.UserTrade; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class GateioStreamingTradeServiceTest { + + @Mock + GateioStreamingService gateioStreamingService; + GateioStreamingTradeService gateioStreamingTradeService; + + ObjectMapper objectMapper = Config.getInstance().getObjectMapper(); + + @BeforeEach + public void setup() { + gateioStreamingTradeService = new GateioStreamingTradeService(gateioStreamingService); + } + + + @Test + void user_trades_btc() throws Exception { + GateioWsNotification multipleNotification = readNotification("spot.usertrades.update.json"); + assertThat(multipleNotification).isInstanceOf(GateioMultipleUserTradeNotification.class); + GateioWsNotification notification = ((GateioMultipleUserTradeNotification) multipleNotification).toSingleNotifications().get(0); + + when(gateioStreamingService.subscribeChannel(eq("spot.usertrades"), eq(CurrencyPair.BTC_USDT))) + .thenReturn(Observable.just(notification)); + + Observable observable = gateioStreamingTradeService.getUserTrades(CurrencyPair.BTC_USDT); + + TestObserver testObserver = observable.test(); + + UserTrade actual = testObserver + .assertSubscribed() + .awaitCount(1) + .assertNoTimeout() + .values().get(0); + + testObserver.dispose(); + + UserTrade expected = new UserTrade.Builder() + .instrument(CurrencyPair.BTC_USDT) + .id("6068323582") + .orderId("381004078014") + .orderUserReference("3") + .originalAmount(new BigDecimal("0.00003")) + .feeAmount(new BigDecimal("0.00000006")) + .feeCurrency(Currency.BTC) + .price(new BigDecimal("29441.1")) + .timestamp(Date.from(Instant.ofEpochMilli(1691692159330L))) + .type(OrderType.BID) + .build(); + + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + + private GateioWsNotification readNotification(String resourceName) throws IOException { + return objectMapper.readValue( + getClass().getClassLoader().getResourceAsStream(resourceName), + GateioWsNotification.class + ); + } + + +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/dto/response/GateioWsNotificationTest.java b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/dto/response/GateioWsNotificationTest.java new file mode 100644 index 00000000000..d4d615fab28 --- /dev/null +++ b/xchange-stream-gateio/src/test/java/info/bitrich/xchangestream/gateio/dto/response/GateioWsNotificationTest.java @@ -0,0 +1,62 @@ +package info.bitrich.xchangestream.gateio.dto.response; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.databind.ObjectMapper; +import info.bitrich.xchangestream.gateio.config.Config; +import info.bitrich.xchangestream.gateio.dto.response.balance.GateioMultipleSpotBalanceNotification; +import info.bitrich.xchangestream.gateio.dto.response.orderbook.GateioOrderBookNotification; +import info.bitrich.xchangestream.gateio.dto.response.ticker.GateioTickerNotification; +import info.bitrich.xchangestream.gateio.dto.response.trade.GateioTradeNotification; +import info.bitrich.xchangestream.gateio.dto.response.usertrade.GateioMultipleUserTradeNotification; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +public class GateioWsNotificationTest { + + ObjectMapper objectMapper = Config.getInstance().getObjectMapper(); + + @Test + void deserialize_trades() throws Exception { + GateioWsNotification notification = readNotification("spot.trades.update.json"); + assertThat(notification).isInstanceOf(GateioTradeNotification.class); + } + + + @Test + void deserialize_ticker() throws Exception { + GateioWsNotification notification = readNotification("spot.ticker.update.json"); + assertThat(notification).isInstanceOf(GateioTickerNotification.class); + } + + + @Test + void deserialize_orderbook() throws Exception { + GateioWsNotification notification = readNotification("spot.order_book.update.json"); + assertThat(notification).isInstanceOf(GateioOrderBookNotification.class); + } + + + @Test + void deserialize_usertrades() throws Exception { + GateioWsNotification notification = readNotification("spot.usertrades.update.json"); + assertThat(notification).isInstanceOf(GateioMultipleUserTradeNotification.class); + } + + + @Test + void deserialize_balances() throws Exception { + GateioWsNotification notification = readNotification("spot.balance.update.json"); + assertThat(notification).isInstanceOf(GateioMultipleSpotBalanceNotification.class); + } + + + private GateioWsNotification readNotification(String resourceName) throws IOException { + return objectMapper.readValue( + getClass().getClassLoader().getResourceAsStream(resourceName), + GateioWsNotification.class + ); + } + + +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/resources/OrderBookResponse.json b/xchange-stream-gateio/src/test/resources/OrderBookResponse.json deleted file mode 100644 index f1774b94836..00000000000 --- a/xchange-stream-gateio/src/test/resources/OrderBookResponse.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "time": 1606295412, - "channel": "spot.order_book", - "event": "update", - "result": { - "t": 1606295412123, - "lastUpdateId": 48791820, - "s": "BTC_USDT", - "bids": [ - [ - "19079.55", - "0.0195" - ], - [ - "19079.07", - "0.7341" - ], - [ - "19076.23", - "0.00011808" - ], - [ - "19073.9", - "0.105" - ], - [ - "19068.83", - "0.1009" - ] - ], - "asks": [ - [ - "19080.24", - "0.1638" - ], - [ - "19080.91", - "0.1366" - ], - [ - "19080.92", - "0.01" - ], - [ - "19081.29", - "0.01" - ], - [ - "19083.8", - "0.097" - ] - ] - } -} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/resources/TradeResponses.json b/xchange-stream-gateio/src/test/resources/TradeResponses.json deleted file mode 100644 index 29e96d07612..00000000000 --- a/xchange-stream-gateio/src/test/resources/TradeResponses.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "time": 1606292218, - "channel": "spot.trades", - "event": "update", - "result": { - "id": 309143071, - "create_time": 1606292218, - "create_time_ms": "1606292218213.4578", - "side": "sell", - "currency_pair": "GT_USDT", - "amount": "16.4700000000", - "price": "0.4705000000" - } -} diff --git a/xchange-stream-gateio/src/test/resources/rest/order-book.http b/xchange-stream-gateio/src/test/resources/rest/order-book.http new file mode 100644 index 00000000000..1472dd5c419 --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/rest/order-book.http @@ -0,0 +1,42 @@ +### Best bid or ask price +WEBSOCKET {{base_url}} +Content-Type: application/json + +{ + "time": {{$timestamp}}, + "channel": "spot.book_ticker", + "event": "subscribe", + "payload": [ + "BTC_USDT" + ] +} + + +### Changed order book levels +WEBSOCKET {{base_url}} +Content-Type: application/json + +{ + "time": {{$timestamp}}, + "channel": "spot.order_book_update", + "event": "subscribe", + "payload": [ + "BTC_USDT", "100ms" + ] +} + + +### Limited-Level Full Order Book Snapshot +WEBSOCKET {{base_url}} +Content-Type: application/json + +{ + "time": {{$timestamp}}, + "channel": "spot.order_book", + "event": "subscribe", + "payload": [ + "BTC_USDT", "5", "100ms" + ] +} + + diff --git a/xchange-stream-gateio/src/test/resources/rest/sign.js b/xchange-stream-gateio/src/test/resources/rest/sign.js new file mode 100644 index 00000000000..0a3f134d156 --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/rest/sign.js @@ -0,0 +1,5 @@ +export function gen_sign(channel, event, timestamp) { + const payloadToSign = `channel=${channel}&event=${event}&time=${timestamp}`; + const apiSecret = request.environment.get("api_secret"); + return crypto.hmac.sha512().withTextSecret(apiSecret).updateWithText(payloadToSign).digest().toHex(); +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/resources/rest/spot-balance.http b/xchange-stream-gateio/src/test/resources/rest/spot-balance.http new file mode 100644 index 00000000000..05553448a7d --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/rest/spot-balance.http @@ -0,0 +1,26 @@ +### Spot Balance Channel +< {% + import {gen_sign} from 'sign.js' + const event = "subscribe"; + const channel = "spot.balances"; + const timestamp = Math.floor(Date.now() / 1000).toFixed(); + + request.variables.set("timestamp", timestamp); + request.variables.set("channel", channel); + request.variables.set("event", event); + request.variables.set("sign", gen_sign(channel, event, timestamp)); +%} + +WEBSOCKET {{base_url}} +Content-Type: application/json + +{ + "time": {{timestamp}}, + "channel": "{{channel}}", + "event": "{{event}}", + "auth": { + "method": "api_key", + "KEY": "{{api_key}}", + "SIGN": "{{sign}}" + } +} diff --git a/xchange-stream-gateio/src/test/resources/rest/system.http b/xchange-stream-gateio/src/test/resources/rest/system.http new file mode 100644 index 00000000000..413ab4fe560 --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/rest/system.http @@ -0,0 +1,8 @@ +### Application ping pong +WEBSOCKET {{base_url}} +Content-Type: application/json + +{ + "time": {{$timestamp}}, + "channel": "spot.ping" +} diff --git a/xchange-stream-gateio/src/test/resources/rest/tickers.http b/xchange-stream-gateio/src/test/resources/rest/tickers.http new file mode 100644 index 00000000000..4aceb5a87ef --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/rest/tickers.http @@ -0,0 +1,13 @@ +### Tickers Channel +WEBSOCKET {{base_url}} +Content-Type: application/json + +{ + "time": {{$timestamp}}, + "channel": "spot.tickers", + "event": "subscribe", + "payload": [ + "BTC_USDT" + ] +} + diff --git a/xchange-stream-gateio/src/test/resources/rest/user-trades.http b/xchange-stream-gateio/src/test/resources/rest/user-trades.http new file mode 100644 index 00000000000..f089e052457 --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/rest/user-trades.http @@ -0,0 +1,29 @@ +### User Trades Channel +< {% + import {gen_sign} from 'sign.js' + const event = "subscribe"; + const channel = "spot.usertrades"; + const timestamp = Math.floor(Date.now() / 1000).toFixed(); + + request.variables.set("timestamp", timestamp); + request.variables.set("channel", channel); + request.variables.set("event", event); + request.variables.set("sign", gen_sign(channel, event, timestamp)); +%} + +WEBSOCKET {{base_url}} +Content-Type: application/json + +{ + "time": {{timestamp}}, + "channel": "{{channel}}", + "event": "{{event}}", + "payload": [ + "BTC_USDT" + ], + "auth": { + "method": "api_key", + "KEY": "{{api_key}}", + "SIGN": "{{sign}}" + } +} diff --git a/xchange-stream-gateio/src/test/resources/spot.balance.update.json b/xchange-stream-gateio/src/test/resources/spot.balance.update.json new file mode 100644 index 00000000000..d656efcd6d5 --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/spot.balance.update.json @@ -0,0 +1,20 @@ +{ + "time": 1691707273, + "time_ms": 1691707273893, + "channel": "spot.balances", + "event": "update", + "result": [ + { + "timestamp": "1691707273", + "timestamp_ms": "1691707273890", + "user": "13439717", + "currency": "USDT", + "change": "0", + "total": "43.06583427604872431142", + "available": "42.06583427604872431142", + "freeze": "1", + "freeze_change": "1.00000000000", + "change_type": "order-create" + } + ] +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/resources/spot.order_book.update.json b/xchange-stream-gateio/src/test/resources/spot.order_book.update.json new file mode 100644 index 00000000000..87a391a2c5a --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/spot.order_book.update.json @@ -0,0 +1,95 @@ +{ + "time": 1691757151, + "time_ms": 1691757151420, + "channel": "spot.order_book", + "event": "update", + "result": { + "t": 1691757151367, + "lastUpdateId": 14604776153, + "s": "BTC_USDT", + "bids": [ + [ + "29426.3", + "1.12547" + ], + [ + "29424.5", + "1.15" + ], + [ + "29424.4", + "0.00074" + ], + [ + "29424.2", + "0.01371" + ], + [ + "29424.1", + "0.036" + ], + [ + "29423.8", + "1.15" + ], + [ + "29423.7", + "0.0434" + ], + [ + "29422.5", + "0.0331" + ], + [ + "29421.4", + "1" + ], + [ + "29421.2", + "0.27187" + ] + ], + "asks": [ + [ + "29426.4", + "2.42147" + ], + [ + "29426.5", + "1.15" + ], + [ + "29426.6", + "0.03" + ], + [ + "29426.7", + "2.47262" + ], + [ + "29426.8", + "1" + ], + [ + "29427.8", + "0.00037" + ], + [ + "29427.9", + "0.0005" + ], + [ + "29428", + "0.00012" + ], + [ + "29428.9", + "0.00051" + ], + [ + "29429", + "0.00025" + ] + ] + } +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/resources/spot.ticker.update.json b/xchange-stream-gateio/src/test/resources/spot.ticker.update.json new file mode 100644 index 00000000000..69d9dba1b5d --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/spot.ticker.update.json @@ -0,0 +1,17 @@ +{ + "time": 1691620566, + "time_ms": 1691620566926, + "channel": "spot.tickers", + "event": "update", + "result": { + "currency_pair": "BTC_USDT", + "last": "29573.7", + "lowest_ask": "29573.7", + "highest_bid": "29573.6", + "change_percentage": "-0.6601", + "base_volume": "5777.7777606776", + "quote_volume": "171784719.492586746", + "high_24h": "30232.8", + "low_24h": "29176.1" + } +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/resources/spot.trades.update.json b/xchange-stream-gateio/src/test/resources/spot.trades.update.json new file mode 100644 index 00000000000..715e2ee38d5 --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/spot.trades.update.json @@ -0,0 +1,15 @@ +{ + "time": 1691620568, + "time_ms": 1691620568796, + "channel": "spot.trades", + "event": "update", + "result": { + "id": 6064666343, + "create_time": 1691620568, + "create_time_ms": "1691620568789.0", + "side": "buy", + "currency_pair": "BTC_USDT", + "amount": "0.0003009", + "price": "29573.7" + } +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/resources/spot.usertrades.update.json b/xchange-stream-gateio/src/test/resources/spot.usertrades.update.json new file mode 100644 index 00000000000..c735261ba1d --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/spot.usertrades.update.json @@ -0,0 +1,27 @@ +{ + "time": 1691692159, + "time_ms": 1691692159338, + "channel": "spot.usertrades", + "event": "update", + "result": [ + { + "id": 6068323582, + "user_id": 13439717, + "order_id": "381004078014", + "currency_pair": "BTC_USDT", + "create_time": 1691692159, + "create_time_ms": "1691692159330.475", + "side": "buy", + "amount": "0.00003", + "role": "taker", + "price": "29441.1", + "fee": "0.00000006", + "fee_currency": "BTC", + "point_fee": "0", + "gt_fee": "0", + "text": "3", + "amend_text": "-", + "biz_info": "-" + } + ] +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/resources/subscribe.event.json b/xchange-stream-gateio/src/test/resources/subscribe.event.json new file mode 100644 index 00000000000..70e815c2dc5 --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/subscribe.event.json @@ -0,0 +1,10 @@ +{ + "time": 1691622305, + "time_ms": 1691622305567, + "id": 8578128440611119318, + "channel": "spot.order_book", + "event": "subscribe", + "result": { + "status": "success" + } +} \ No newline at end of file diff --git a/xchange-stream-gateio/src/test/resources/unsubscribe.event.json b/xchange-stream-gateio/src/test/resources/unsubscribe.event.json new file mode 100644 index 00000000000..111be8d2e72 --- /dev/null +++ b/xchange-stream-gateio/src/test/resources/unsubscribe.event.json @@ -0,0 +1,10 @@ +{ + "time": 1691622369, + "time_ms": 1691622369168, + "id": 2644197385697665557, + "channel": "spot.trades", + "event": "unsubscribe", + "result": { + "status": "success" + } +} \ No newline at end of file diff --git a/xchange-stream-service-netty/src/main/java/info/bitrich/xchangestream/service/netty/NettyStreamingService.java b/xchange-stream-service-netty/src/main/java/info/bitrich/xchangestream/service/netty/NettyStreamingService.java index 412ec789c01..53524381c0a 100644 --- a/xchange-stream-service-netty/src/main/java/info/bitrich/xchangestream/service/netty/NettyStreamingService.java +++ b/xchange-stream-service-netty/src/main/java/info/bitrich/xchangestream/service/netty/NettyStreamingService.java @@ -434,7 +434,7 @@ public Observable subscribeChannel(String channelName, Object... args) { () -> { if (channels.remove(subscriptionUniqueId) != null) { try { - sendMessage(getUnsubscribeMessage(subscriptionUniqueId)); + sendMessage(getUnsubscribeMessage(subscriptionUniqueId, args)); } catch (IOException e) { LOG.debug("Failed to unsubscribe channel: {} {}", subscriptionUniqueId, e.toString()); } catch (Exception e) {