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 extends BitstampAuthenticatedV2> 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 extends IRestProxyFactory> 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